www tool tag manager gtm googletagmanager google developers container rest api design restful-architecture

rest - tool - https www googletagmanager com ns html



API REST: creación o actualización masiva en una sola solicitud (4)

Supongamos que hay dos recursos Binder y Doc con relación de asociación, lo que significa que Doc y Binder mantienen solos. Doc podría o no pertenecer a Binder y Binder podría estar vacío.

Si quiero diseñar una API REST que permita a un usuario enviar una colección de documentos, EN UNA SOLA SOLICITUD , como la siguiente:

{ "docs": [ {"doc_number": 1, "binder": 1}, {"doc_number": 5, "binder": 8}, {"doc_number": 6, "binder": 3} ] }

Y por cada documento en los docs ,

  • Si el doc existe, asígnelo a Binder
  • Si el doc no existe, créelo y luego asígnelo

Estoy realmente confundido sobre cómo se debe implementar esto:

  • ¿Qué método HTTP usar?
  • ¿Qué código de respuesta debe devolverse?
  • ¿Es esto incluso calificado para REST?
  • ¿Cómo se vería el URI? /binders/docs ?
  • Manejo de solicitud masiva, ¿qué pasa si algunos elementos generan un error pero el otro pasa? ¿Qué código de respuesta debe devolverse? ¿Debería la operación masiva ser atómica?

Creo que podría usar un método POST o PATCH para manejar esto, ya que generalmente se diseñan para esto.

  • El uso de un método POST generalmente se usa para agregar un elemento cuando se usa en la lista de recursos, pero también puede admitir varias acciones para este método. Vea esta respuesta: Cómo actualizar una colección de recursos REST . También puede admitir diferentes formatos de representación para la entrada (si corresponden a una matriz o elementos individuales).

    En el caso, no es necesario definir su formato para describir la actualización.

  • El uso de un método PATCH también es adecuado ya que las solicitudes correspondientes corresponden a una actualización parcial. De acuerdo con RFC5789 ( http://tools.ietf.org/html/rfc5789 ):

    Varias aplicaciones que extienden el Protocolo de transferencia de hipertexto (HTTP) requieren una función para realizar una modificación parcial de los recursos. El método HTTP PUT existente solo permite un reemplazo completo de un documento. Esta propuesta agrega un nuevo método HTTP, PATCH, para modificar un recurso HTTP existente.

    En el caso, debe definir su formato para describir la actualización parcial.

Creo que en este caso, POST y PATCH son bastante similares, ya que realmente no es necesario describir la operación que se debe realizar para cada elemento. Yo diría que depende del formato de la representación a enviar.

El caso de PUT es un poco menos claro. De hecho, cuando se utiliza un método PUT , debe proporcionar la lista completa. De hecho, la representación proporcionada en la solicitud sustituirá a la lista de recursos.

Puede tener dos opciones con respecto a las rutas de recursos.

  • Usar la ruta de recursos para la lista de documentos

En este caso, debe proporcionar explícitamente el enlace de documentos con una carpeta en la representación que proporcione en la solicitud.

Aquí hay una ruta de muestra para este /docs .

El contenido de dicho enfoque podría ser para el método POST :

[ { "doc_number": 1, "binder": 4, (other fields in the case of creation) }, { "doc_number": 2, "binder": 4, (other fields in the case of creation) }, { "doc_number": 3, "binder": 5, (other fields in the case of creation) }, (...) ]

  • Usando la ruta del recurso secundario del elemento de carpeta

Además, también podría considerar aprovechar las rutas secundarias para describir el vínculo entre documentos y carpetas. Las sugerencias sobre la asociación entre un documento y un archivador no tienen que especificarse ahora dentro del contenido de la solicitud.

Aquí hay una ruta de muestra para este /binder/{binderId}/docs . En este caso, enviar una lista de documentos con un método POST o PATCH adjuntará documentos al cuaderno con el identificador binderId después de haber creado el documento si no existe.

El contenido de dicho enfoque podría ser para el método POST :

[ { "doc_number": 1, (other fields in the case of creation) }, { "doc_number": 2, (other fields in the case of creation) }, { "doc_number": 3, (other fields in the case of creation) }, (...) ]

En cuanto a la respuesta, depende de usted definir el nivel de respuesta y los errores a devolver. Veo dos niveles: el nivel de estado (nivel global) y el nivel de carga útil (nivel más delgado). También depende de usted definir si todas las inserciones / actualizaciones correspondientes a su solicitud deben ser atómicas o no.

  • Atómico

En este caso, puede aprovechar el estado HTTP. Si todo va bien, obtienes un estado 200 . Si no, otro estado como 400 si los datos proporcionados no son correctos (por ejemplo, la identificación de la carpeta no es válida) o algo más.

  • No atómico

En este caso, se devolverá un estado 200 y depende de la representación de la respuesta describir lo que se hizo y dónde eventualmente ocurren los errores. ElasticSearch tiene un punto final en su API REST para la actualización masiva. Esto podría darle algunas ideas a este nivel: http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/bulk.html .

  • Asincrónico

También puede implementar un procesamiento asincrónico para manejar los datos proporcionados. En este caso, los retornos de estado HTTP serán 202 . El cliente necesita extraer un recurso adicional para ver qué sucede.

Antes de terminar, también me gustaría notar que la especificación OData aborda el problema con respecto a las relaciones entre entidades con la función llamada enlaces de navegación . Quizás podrías echarle un vistazo a esto ;-)

El siguiente enlace también puede ayudarlo: https://templth.wordpress.com/2014/12/15/designing-a-web-api/ .

Espero que te ayude, Thierry


En un proyecto en el que trabajé, resolvimos este problema implementando algo que llamamos solicitudes ''Batch''. Definimos una ruta /batch donde aceptamos json en el siguiente formato:

[ { path: ''/docs'', method: ''post'', body: { doc_number: 1, binder: 1 } }, { path: ''/docs'', method: ''post'', body: { doc_number: 5, binder: 8 } }, { path: ''/docs'', method: ''post'', body: { doc_number: 6, binder: 3 } }, ]

La respuesta tiene el código de estado 207 (Multi-Status) y se ve así:

[ { path: ''/docs'', method: ''post'', body: { doc_number: 1, binder: 1 } status: 200 }, { path: ''/docs'', method: ''post'', body: { error: { msg: ''A document with doc_number 5 already exists'' ... } }, status: 409 }, { path: ''/docs'', method: ''post'', body: { doc_number: 6, binder: 3 }, status: 200 }, ]

También puede agregar soporte para encabezados en esta estructura. Implementamos algo que resultó útil, que fueron las variables que se usaron entre solicitudes en un lote, lo que significa que podemos usar la respuesta de una solicitud como entrada para otra.

Facebook y Google tienen implementaciones similares:
https://developers.google.com/gmail/api/guides/batch
https://developers.facebook.com/docs/graph-api/making-multiple-requests

Cuando desee crear o actualizar un recurso con la misma llamada, usaría POST o PUT según el caso. Si el documento ya existe, ¿desea que todo el documento sea:

  1. ¿Reemplazado por el documento que envía (es decir, las propiedades faltantes en la solicitud se eliminarán y ya se sobrescribirán)?
  2. ¿Combinado con el documento que envía (es decir, las propiedades que faltan en la solicitud no se eliminarán y las propiedades ya existentes se sobrescribirán)?

En caso de que desee el comportamiento de la alternativa 1, debe usar un POST y en caso de que desee el comportamiento de la alternativa 2, debe usar PUT.

http://restcookbook.com/HTTP%20Methods/put-vs-post/

Como la gente ya sugirió, también podría optar por PATCH, pero prefiero mantener las API simples y no usar verbos adicionales si no son necesarios.


Probablemente necesite usar POST o PATCH, porque es poco probable que una sola solicitud que actualice y cree múltiples recursos sea idempotente.

Hacer PATCH /docs es definitivamente una opción válida. Es posible que el uso de los formatos de parche estándar sea complicado para su escenario particular. No estoy seguro de esto.

Podría usar 200. También podría usar 207 - Estado múltiple

Esto se puede hacer de una manera RESTANTE. La clave, en mi opinión, es tener algún recurso diseñado para aceptar un conjunto de documentos para actualizar / crear.

Si usa el método PATCH, creo que su operación debería ser atómica. es decir, no usaría el código de estado 207 y luego reportaría éxitos y fallas en el cuerpo de respuesta. Si utiliza la operación POST, entonces el enfoque 207 es viable. Deberá diseñar su propio cuerpo de respuesta para comunicar qué operaciones tuvieron éxito y cuáles fallaron. No estoy al tanto de uno estandarizado.


PONER

PUT /binders/{id}/docs Crear o actualizar, y relacionar un solo documento con un cuaderno

p.ej:

PUT /binders/1/docs HTTP/1.1 { "docNumber" : 1 }

REMIENDO

PATCH /docs Crear documentos si no existen y relacionarlos con carpetas

p.ej:

PATCH /docs HTTP/1.1 [ { "op" : "add", "path" : "/binder/1/docs", "value" : { "doc_number" : 1 } }, { "op" : "add", "path" : "/binder/8/docs", "value" : { "doc_number" : 8 } }, { "op" : "add", "path" : "/binder/3/docs", "value" : { "doc_number" : 6 } } ]

Incluiré ideas adicionales más adelante, pero mientras tanto, si lo desea, eche un vistazo a http://tools.ietf.org/html/rfc5789 , RFC 6902 y William Durand''s Please. Entrada del blog Don''t Patch Like an Idiot .