json - practicas - REST API-procesamiento de archivos(es decir, imágenes)-mejores prácticas
json rest api (4)
No hay una solución fácil.
Cada camino tiene sus pros y sus contras.
Pero la forma canónica es usar la primera opción:
multipart/form-data
.
Como dice la
guía de recomendaciones W3
El tipo de contenido "multipart / form-data" debe usarse para enviar formularios que contienen archivos, datos no ASCII y datos binarios.
Realmente no estamos enviando formularios, pero el principio implícito aún se aplica. Usar base64 como representación binaria es incorrecto porque está usando la herramienta incorrecta para lograr su objetivo, por otro lado, la segunda opción obliga a sus clientes API a hacer más trabajo para consumir su servicio API. Debe hacer el trabajo duro en el lado del servidor para proporcionar una API fácil de consumir. La primera opción no es fácil de depurar, pero cuando lo haces, probablemente nunca cambie.
Usando
multipart/form-data
te apegas a la filosofía REST / http.
Puede ver una respuesta a una pregunta similar
here
.
Otra opción si se mezclan las alternativas, puede usar datos multiparte / formulario, pero en lugar de enviar cada valor por separado, puede enviar un valor denominado carga útil con la carga útil json dentro de él. (Probé este enfoque usando ASP.NET WebAPI 2 y funciona bien).
Estamos desarrollando un servidor con REST API, que acepta y responde con JSON. El problema es si necesita cargar imágenes del cliente al servidor.
Tenga en cuenta también que estoy hablando de casos de uso, donde la entidad (usuario) puede tener archivos (carPhoto, licensePhoto) y también tener otras propiedades (nombre, correo electrónico ...), pero cuando crea un nuevo usuario, no envía estas imágenes , se agregan después del proceso de registro.
Conozco las soluciones, pero cada una de ellas tiene algunos defectos.
1. Use multipart / form-data en lugar de JSON
bueno : las solicitudes POST y PUT son tan RESTful como sea posible, pueden contener entradas de texto junto con el archivo.
contras : ya no es JSON, que es mucho más fácil de probar, depurar, etc., en comparación con multipart / form-data
2. Permitir actualizar archivos separados
La solicitud POST para crear un nuevo usuario no permite agregar imágenes (lo cual está bien en nuestro caso de uso, como dije al principio), la carga de imágenes se realiza mediante solicitud PUT como datos multiparte / formulario a, por ejemplo, / users / 4 / carPhoto
bueno : todo (excepto el archivo que se carga) permanece en JSON, es fácil de probar y depurar (puede registrar solicitudes JSON completas sin tener miedo de su longitud)
contras
: no es intuitivo, no puede POST o PUT todas las variables de entidad a la vez y también esta dirección
/users/4/carPhoto
puede considerarse más como una colección (el caso de uso estándar para REST API se ve así
/users/4/shipments
).
Por lo general, no puede (y no desea) OBTENER / PONER cada variable de entidad, por ejemplo usuarios / 4 / nombre.
Puede obtener el nombre con GET y cambiarlo con PUT en users / 4.
Si hay algo después de la identificación, generalmente es otra colección, como users / 4 / reviews
3. Use Base64
Envíalo como JSON pero codifica archivos con Base64.
bueno : igual que la primera solución, es un servicio lo más RESTful posible.
Contras : una vez más, las pruebas y la depuración son mucho peores (el cuerpo puede tener megabytes de datos), hay un aumento en el tamaño y también en el tiempo de procesamiento tanto en el cliente como en el servidor
Realmente me gustaría usar la solución no. 2, pero tiene sus contras ... ¿Alguien me puede dar una mejor idea de la solución "cuál es la mejor"?
Mi objetivo es tener servicios RESTful con tantos estándares incluidos como sea posible, mientras quiero que sea lo más simple posible.
Su segunda solución es probablemente la más correcta.
Debe usar la especificación HTTP y los tipos mime de la forma en que fueron diseñados y cargar el archivo a través de
multipart/form-data
.
En cuanto al manejo de las relaciones, usaría este proceso (teniendo en cuenta que sé cero sobre sus supuestos o el diseño del sistema):
-
POST
a/users
para crear la entidad de usuario. -
POST
la imagen en/images
, asegurándote de devolver un encabezado deLocation
donde se pueda recuperar la imagen según la especificación HTTP. -
PATCH
a/users/carPhoto
y asígnele la ID de la foto dada en el encabezadoLocation
del paso 2.
Hay varias decisiones que tomar :
-
El primero sobre la ruta del recurso :
-
Modele la imagen como un recurso por sí solo:
-
Anidado en usuario (/ user /: id / image): la relación entre el usuario y la imagen se establece implícitamente
-
En la ruta raíz (/ imagen):
-
El cliente es responsable de establecer la relación entre la imagen y el usuario, o;
-
Si se proporciona un contexto de seguridad con la solicitud POST utilizada para crear una imagen, el servidor puede establecer implícitamente una relación entre el usuario autenticado y la imagen.
-
-
-
Incruste la imagen como parte del usuario
-
-
La segunda decisión es sobre cómo representar el recurso de imagen :
- Como carga útil JSON codificada Base 64
- Como una carga útil multiparte
Esta sería mi pista de decisión:
- Por lo general, prefiero el diseño al rendimiento, a menos que exista un argumento sólido para ello. Hace que el sistema sea más fácil de mantener y los integradores puedan entenderlo más fácilmente.
-
Entonces, mi primer pensamiento es buscar una representación Base64 del recurso de imagen porque le permite mantener todo JSON.
Si elige esta opción, puede modelar la ruta de recursos como desee.
- Si la relación entre el usuario y la imagen es de 1 a 1, preferiría modelar la imagen como un atributo, especialmente si ambos conjuntos de datos se actualizan al mismo tiempo. En cualquier otro caso, puede elegir libremente modelar la imagen como un atributo, actualizándola a través de PUT o PATCH, o como un recurso separado.
- Si elige la carga útil de varias partes, me sentiría obligado a modelar la imagen como un recurso propio, de modo que otros recursos, en nuestro caso, el recurso del usuario, no se vean afectados por la decisión de usar una representación binaria para la imagen.
Luego viene la pregunta: ¿Hay algún impacto en el rendimiento al elegir base64 vs multiparte? . Podríamos pensar que el intercambio de datos en formato multiparte debería ser más eficiente. Pero este artículo muestra cuán poco difieren ambas representaciones en términos de tamaño.
Mi elección Base64:
- Decisión de diseño consistente
- Impacto insignificante en el rendimiento
- Como los navegadores entienden los URI de datos (imágenes codificadas en base64), no hay necesidad de transformarlos si el cliente es un navegador
- No votaré si tenerlo como un atributo o recurso independiente, depende de su dominio del problema (que no sé) y de su preferencia personal.
OP aquí (estoy respondiendo esta pregunta después de dos años, la publicación hecha por Daniel Cerecedo no fue mala en ningún momento, pero los servicios web se están desarrollando muy rápido)
Después de tres años de desarrollo de software a tiempo completo (con enfoque también en arquitectura de software, gestión de proyectos y arquitectura de microservicios) definitivamente elijo la segunda forma (pero con un punto final general) como la mejor.
Si tiene un punto final especial para las imágenes, le da mucho más poder sobre el manejo de esas imágenes.
Tenemos la misma API REST (Node.js) para ambas aplicaciones móviles (iOS / Android) y frontend (usando React). Esto es 2017, por lo tanto, no desea almacenar imágenes localmente, desea cargarlas para que vengan al almacenamiento en la nube (Google cloud, s3, cloudinary, ...), por lo tanto, desea un manejo general sobre ellas.
Nuestro flujo típico es que, tan pronto como selecciona una imagen, comienza a cargarse en segundo plano (generalmente POST en / punto final de imágenes), devolviéndole la ID después de cargarla. Esto es realmente fácil de usar, porque el usuario elige una imagen y luego normalmente procede con otros campos (es decir, dirección, nombre, ...), por lo tanto, cuando presiona el botón "enviar", la imagen ya está cargada. No espera y mira la pantalla diciendo "cargando ...".
Lo mismo vale para obtener imágenes. Especialmente gracias a los teléfonos móviles y los datos móviles limitados, no desea enviar imágenes originales, desea enviar imágenes redimensionadas, por lo que no toman tanto ancho de banda (y para que sus aplicaciones móviles sean más rápidas, a menudo no desea para cambiar su tamaño, desea que la imagen se ajuste perfectamente a su vista). Por esta razón, las buenas aplicaciones están usando algo como cloudinary (o tenemos nuestro propio servidor de imágenes para cambiar el tamaño).
Además, si los datos no son privados, envía de vuelta a la aplicación / interfaz solo URL y lo descarga directamente desde el almacenamiento en la nube, lo que supone un gran ahorro de ancho de banda y tiempo de procesamiento para su servidor. En nuestras aplicaciones más grandes hay muchos terabytes descargados cada mes, no desea manejar eso directamente en cada uno de su servidor REST API, que se centra en la operación CRUD. Desea manejar eso en un lugar (nuestro Imageserver, que tiene almacenamiento en caché, etc.) o dejar que los servicios en la nube se encarguen de todo.
Contras: Las únicas "desventajas" en las que debe pensar son "imágenes no asignadas". El usuario selecciona imágenes y continúa rellenando otros campos, pero luego dice "nah" y apaga la aplicación o pestaña, pero mientras tanto ha subido con éxito la imagen. Esto significa que ha subido una imagen que no está asignada en ningún lado.
Hay varias formas de manejar esto. La más fácil es "No me importa", que es relevante, si esto no sucede muy a menudo o incluso desea almacenar cada imagen que el usuario le envíe (por cualquier motivo) y no desea ninguna supresión.
Otra también es fácil: tiene CRON y, es decir, todas las semanas y elimina todas las imágenes sin asignar de más de una semana.