Estamos desarrollando un servidor con REST API, que acepta y responde con JSON. El problema es que si necesita cargar imágenes del cliente al servidor.
Nota: y también estoy hablando de un caso de uso en el que la entidad (usuario) puede tener múltiples archivos (carPhoto, licensePhoto) y también tener otras propiedades (nombre, correo electrónico ...), pero cuando crea un nuevo usuario, no No envíe 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.
Respuestas:
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 en algún 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 el fondo (generalmente POST en / punto final de imágenes), devolviéndole la ID después de la carga. 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 ocupan 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 los 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 solo lugar (nuestro Imageserver, que tiene almacenamiento en caché, etc.) o dejar que los servicios en la nube lo manejen todo.
Contras: Los únicos "contras" en los que debe pensar son "imágenes no asignadas". El usuario selecciona imágenes y continúa rellenando otros campos, pero luego dice "no" y apaga la aplicación o la pestaña, pero mientras tanto ha cargado correctamente 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 es fácil también: tiene CRON y, es decir, todas las semanas y elimina todas las imágenes sin asignar de más de una semana.
fuente
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 realiza 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 :
Esta sería mi pista de decisión:
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:
fuente
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
para/users
crear la entidad de usuario.POST
la imagen a/images
, asegurándose de devolver unLocation
encabezado a donde se pueda recuperar la imagen según la especificación HTTP.PATCH
a/users/carPhoto
y asignarle el ID de la foto dada en elLocation
encabezado de la etapa 2.fuente
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 W3Realmente 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.
Al
multipart/form-data
usarlo, estás apegado a la filosofía REST / http. Puede ver una respuesta a una pregunta similar aquí .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 llamado carga útil con la carga útil json dentro de él. (Probé este enfoque usando ASP.NET WebAPI 2 y funciona bien).
fuente