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 aBinder
-
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:
- ¿Reemplazado por el documento que envía (es decir, las propiedades faltantes en la solicitud se eliminarán y ya se sobrescribirán)?
- ¿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 .