json - protocol - RESTO: Actualización de múltiples recursos con una sola solicitud: ¿es estándar o debe evitarse?
rest protocol (3)
Una API REST simple:
- GET: items / {id} - Devuelve una descripción del artículo con la identificación dada
- PUT: items / {id} - Actualiza o crea el elemento con la identificación dada
- ELIMINAR: elementos / {id} - Elimina el elemento con la identificación dada
Ahora la extensión API en cuestión:
- GET: ítems? Filter - Devuelve todos los identificadores de ítems que coinciden con el filtro
- PUT: items: actualiza o crea un conjunto de elementos según lo descrito por la carga útil de JSON
- [[ ELIMINAR: elementos: elimina una lista de elementos descritos por la carga útil de JSON ]] <- No correcto
Ahora estoy interesado en la funcionalidad de reciclaje de la operación DELETE y PUT a la que se puede acceder fácilmente mediante PUT / DELETE items / {id}.
Pregunta: ¿Es común proporcionar una API como esta?
Alternativa: en la era de la conexión única, las solicitudes múltiples emitir múltiples solicitudes es barato y funcionaría de manera más atómica ya que un cambio tiene éxito o falla, pero en la era de la base de datos NOSQL, un cambio en la lista podría haber sucedido incluso si el procesamiento de la solicitud muere con servidor interno o lo que sea debido a cualquier razón.
[ACTUALIZAR]
Después de considerar los Estándares Web de la Casa Blanca y Wikipedia: Ejemplos REST, ahora se propone la siguiente API de ejemplo:
Una API REST simple:
- GET: items / {id} - Devuelve una descripción del artículo con la identificación dada
- PUT: items / {id} - Actualiza o crea el elemento con la identificación dada
- ELIMINAR: elementos / {id} - Elimina el elemento con la identificación dada
API de recursos principales:
- GET: ítems? Filter - Devuelve todos los identificadores de ítems que coinciden con el filtro
- POST: elementos: actualiza o crea un conjunto de elementos según lo descrito por la carga útil de JSON
PUT and DELETE on / items no es compatible y está prohibido.
El uso de POST parece ser el truco para crear nuevos elementos en un recurso adjunto sin reemplazar sino agregar.
Lecturas POST de la semántica HTTP :
Extender una base de datos a través de una operación de agregar
Donde los métodos PUT requerirían reemplazar la colección completa para devolver una representación equivalente según lo citado por HTTP Semantics PUT :
Un PUT exitoso de una representación dada sugeriría que un GET posterior en el mismo recurso objetivo dará como resultado una representación equivalente que se devolverá en una respuesta 200 (OK).
[ACTUALIZACIÓN2]
Una alternativa que parece aún más consistente para el aspecto de actualización de múltiples objetos parece ser el método PATCH. La diferencia entre PUT y PATCH se describe en el Draft RFC 5789 como:
La diferencia entre las solicitudes PUT y PATCH se refleja en la forma en que el servidor procesa la entidad adjunta para modificar el recurso identificado por el URI de solicitud. En una solicitud PUT, la entidad adjunta se considera una versión modificada del recurso almacenado en el servidor de origen, y el cliente solicita que se reemplace la versión almacenada. Sin embargo, con PATCH, la entidad adjunta contiene un conjunto de instrucciones que describen cómo se debe modificar un recurso que actualmente reside en el servidor de origen para producir una nueva versión. El método PATCH afecta el recurso identificado por el Request-URI, y también PUEDE tener efectos secundarios en otros recursos; es decir, se pueden crear nuevos recursos o modificar los existentes mediante la aplicación de un PATCH.
Entonces, en comparación con POST, PATCH también puede ser una mejor idea, ya que PATCH permite una ACTUALIZACIÓN donde POST solo permite agregar algo que significa agregar sin la posibilidad de modificación.
Entonces, POST parece estar equivocado aquí y necesitamos cambiar nuestra API propuesta para:
Una API REST simple:
- GET: items / {id} - Devuelve una descripción del artículo con la identificación dada
- PUT: items / {id} - Actualiza o crea el elemento con la identificación dada
- ELIMINAR: elementos / {id} - Elimina el elemento con la identificación dada
API de recursos principales:
- GET: ítems? Filter - Devuelve todos los identificadores de ítems que coinciden con el filtro
- POST: elementos: crea uno o más elementos según lo descrito por la carga útil de JSON
- PATCH: items: crea o actualiza uno o más elementos según lo descrito por la carga útil de JSON
Actualización de múltiples recursos con una sola solicitud: ¿es estándar o debe evitarse?
Bueno, a veces simplemente necesita realizar operaciones atómicas por lotes u otras operaciones relacionadas con los recursos que simplemente no se ajustan al esquema típico de una API REST simple, pero si lo necesita, no puede evitarlo.
¿Es estándar?
No existe un estándar REST API universalmente aceptado, por lo que esta pregunta es difícil de responder. Pero al observar algunas pautas de diseño de api comúnmente citadas, como jsonapi.org , restfulapi.net , la guía de diseño de microsoft api o las Convenciones API REST de IBM , que no mencionan las operaciones por lotes, se puede inferir que tales operaciones no se entienden comúnmente como siendo una característica estándar de las API REST.
Dicho esto, una excepción es la guía de diseño de API de Google que
mentions
la creación de métodos "personalizados" que se pueden asociar a través de un recurso utilizando dos puntos, por ejemplo,
https://service.name/v1/some/resource/name:customVerb
, también menciona explícitamente las operaciones por lotes como caso de uso:
Un método personalizado puede asociarse con un recurso, una colección o un servicio. Puede tomar una solicitud arbitraria y devolver una respuesta arbitraria, y también admite la solicitud y respuesta de transmisión. [...] Los métodos personalizados deben usar el verbo HTTP POST ya que tiene la semántica más flexible [...] Para los métodos críticos de rendimiento, puede ser útil proporcionar métodos por lotes personalizados para reducir la sobrecarga por solicitud .
Entonces, en el caso de ejemplo que proporcionó, hace lo siguiente de acuerdo con la guía de API de Google:
POST /api/items:batchUpdate
Además, algunas API públicas decidieron ofrecer un punto final central
/batch
, por ejemplo,
la API de gmail de google
.
Además, como se mentioned en restfulapi.net, también existe el concepto de una "tienda" de recursos, en la que almacena y recupera listas enteras de elementos a la vez a través de PUT; sin embargo, este concepto no cuenta para las colecciones de recursos administrados por el servidor:
Una tienda es un repositorio de recursos administrado por el cliente. Un recurso de tienda permite que un cliente API ingrese recursos, los retire y decida cuándo eliminarlos. Una tienda nunca genera nuevos URI. En cambio, cada recurso almacenado tiene un URI que fue elegido por un cliente cuando se puso inicialmente en la tienda.
Después de haber respondido a sus preguntas originales, aquí hay otro enfoque para su problema que aún no se ha mencionado. Tenga en cuenta que este enfoque es un poco poco convencional y no se ve tan bonito como el típico esquema de nombres de punto final API REST. Personalmente, no estoy siguiendo este enfoque, pero aún siento que debería pensarse :)
La idea es que podría hacer una distinción entre las operaciones CRUD en un recurso y otras operaciones relacionadas con los recursos (por ejemplo, operaciones por lotes) a través de su esquema de denominación de ruta de punto final.
Por ejemplo, considere una API RESTful que le permite realizar operaciones CRUD en un recurso de "empresa" y también desea realizar algunas operaciones relacionadas con la "empresa" que no encajan en el esquema CRUD orientado a los recursos típicamente asociado con API de reposo. - como las operaciones por lotes que mencionó.
Ahora, en lugar de exponer sus recursos directamente debajo de
/api/companies
(por ejemplo,
/api/companies/22
), puede distinguir entre:
-
/api/companies/items
- es decir, una colección de recursos de la compañía -
/api/companies/ops
- es decir, operaciones relacionadas con los recursos de la compañía
Para los
items
aplican los métodos http de RESTful habituales y los esquemas de nomenclatura de url de recursos (por ejemplo, como se describe
here
o
here
)
POST /api/companies/items
GET /api/companies/items
GET /api/companies/items/{id}
DELETE /api/companies/items/{id}
PUT /api/companies/items/{id}
Ahora, para las operaciones relacionadas con la empresa, puede usar el
/api/companies/ops/
route-prefix y llamar a las operaciones a través de POST.
POST /api/companies/ops/batch-update
POST /api/companies/ops/batch-delete
POST /api/companies/ops/garbage-collect-old-companies
POST /api/companies/ops/increase-some-timestamps-just-for-fun
POST /api/companies/ops/perform-some-other-action-on-companies-collection
Dado que las solicitudes POST no tienen que resultar en la creación de un recurso, POST es el método correcto para usar aquí:
La acción realizada por el método POST podría no dar como resultado un recurso que pueda ser identificado por un URI. https://tools.ietf.org/html/rfc2616#section-9.5
Hasta donde entiendo el concepto REST, cubre una actualización de múltiples recursos con una sola solicitud. En realidad, el truco aquí es asumir un contenedor alrededor de esos múltiples recursos y tomarlo como un solo recurso. Por ejemplo, podría suponer que la lista de ID identifica un recurso que contiene varios otros recursos.
En esos ejemplos en Wikipedia también hablan sobre recursos en Plural.
Podrías parchear la colección, por ejemplo
PATCH /items
[ { id: 1, name: ''foo'' }, { id: 2, name: ''bar'' } ]
Técnicamente, PATCH identificaría el registro en la URL (es decir, PATCH
/items/1
y no en el cuerpo de la solicitud, pero esto parece una buena solución pragmática.
Para admitir la eliminación, creación y actualización en una sola llamada, eso no es realmente compatible con las convenciones REST estándar. Una posibilidad es un servicio especial "por lotes" que le permite reunir llamadas juntas:
POST /batch
[
{ method: ''POST'', path: ''/items'', body: { title: ''foo'' } },
{ method: ''DELETE'', path: ''/items/bar'' }
]
que devuelve una respuesta con códigos de estado para cada solicitud incorporada:
[ 200, 403 ]
No es realmente estándar, pero lo he hecho y funciona.