Eliminar varios registros usando REST

97

¿Cuál es la forma REST-ful de eliminar varios elementos?

Mi caso de uso es que tengo una colección Backbone en la que necesito poder eliminar varios elementos a la vez. Las opciones parecen ser:

  1. Envíe una solicitud DELETE para cada registro (lo que parece una mala idea si hay potencialmente docenas de elementos);
  2. Envíe un DELETE donde los ID a eliminar están encadenados en la URL (es decir, "/ registros / 1; 2; 3");
  3. De forma que no sea REST, envíe un objeto JSON personalizado que contenga los ID marcados para su eliminación.

Todas las opciones son menos que ideales.

Esto parece un área gris de la convención REST.

Donald Taylor
fuente
3
Posible duplicado de Restful way para eliminar un montón de elementos
Luka Žitnik

Respuestas:

92
  1. Es una opción RESTful viable, pero obviamente tiene las limitaciones que ha descrito.
  2. No hagas esto. Los intermediarios lo interpretarían en el sentido de “BORRAR el recurso (único) en /records/1;2;3”. Por lo tanto, una respuesta 2xx a esto puede hacer que eliminen su caché /records/1;2;3; no purgar /records/1, /records/2o /records/3; proxy una respuesta 410 para /records/1;2;3, u otras cosas que no tienen sentido desde su punto de vista.
  3. Esta elección es la mejor y se puede hacer DESCANSO. Si está creando una API y desea permitir cambios masivos en los recursos, puede usar REST para hacerlo, pero para muchos no es inmediatamente obvio cómo exactamente. Un método es crear un recurso de 'solicitud de cambio' (por ejemplo, publicando un cuerpo como records=[1,2,3]to /delete-requests) y sondear el recurso creado (especificado por el Locationencabezado de la respuesta) para averiguar si su solicitud ha sido aceptada, rechazada o está en curso. o ha completado. Esto es útil para operaciones de larga duración. Otra forma es enviar una PATCHsolicitud al recurso de lista ,/records, cuyo cuerpo contiene una lista de recursos y acciones para realizar en esos recursos (en el formato que desee admitir). Esto es útil para operaciones rápidas donde el código de respuesta para la solicitud puede indicar el resultado de la operación.

Todo se puede lograr mientras se mantiene dentro de las limitaciones de REST, y normalmente la respuesta es convertir el "problema" en un recurso y darle una URL.
Por lo tanto, las operaciones por lotes, como eliminar aquí o POSTAR varios elementos en una lista, o realizar la misma edición en una franja de recursos, se pueden manejar creando una lista de "operaciones por lotes" y POSTANDO su nueva operación en ella.

No lo olvide, REST no es la única forma de resolver ningún problema. "REST" es solo un estilo arquitectónico y no tiene que adherirse a él (pero pierde ciertos beneficios de Internet si no lo hace). Le sugiero que consulte esta lista de arquitecturas de API HTTP y elija la que más le convenga. Solo sea consciente de lo que pierde si elige otra arquitectura y tome una decisión informada basada en su caso de uso.

¿Hay algunas malas respuestas a esta pregunta sobre patrones para manejar operaciones por lotes en servicios web REST? que tienen demasiados votos a favor, pero también deben leerse.

Nicholas Shanks
fuente
2
No es su servidor de lo que debe preocuparse, son los intermediarios, CDN, proxies de almacenamiento en caché, etc. Internet es un sistema en capas. Esa es la razón por la que funciona tan bien. Roy determinó qué aspectos del sistema eran necesarios para su éxito y los llamó REST. Si emite una DELETEsolicitud, todo lo que se encuentre entre el solicitante y el servidor pensará que se está eliminando un solo recurso, en la URL especificada. Las cadenas de consulta son partes opacas de la URL para estos dispositivos, por lo que no importa cómo especifique su API, no están al tanto de este conocimiento, por lo que no pueden comportarse de manera diferente.
Nicholas Shanks
3
/ records / 1; 2; 3 no funcionará si tiene muchos recursos para eliminar debido a restricciones de longitud de URI
dukethrash
3
Tenga en cuenta que si considera BORRAR y un organismo que define los recursos a depurar, es posible que algunos intermediarios no reenvíen el organismo. Además, algunos clientes HTTP no pueden agregar un cuerpo a DELETE. Ver stackoverflow.com/questions/299628/…
Luke Puplett
3
@LukePuplett Simplemente diría que DELETEestá prohibido pasar un cuerpo de solicitud con una solicitud. No lo hagas. Si lo hace, me comeré a sus hijos. NOM Nom Nom.
Nicholas Shanks
3
El problema con el argumento del n. ° 3 es que conlleva la misma penalización que el argumento en contra del n. ° 2. La creación de recursos para eliminar no es algo que los servidores proxy ascendentes sepan cómo manejar, el mismo argumento contrario que se plantea en contra del enfoque n. ° 2.
LB2
16

Si GET /records?filteringCriteriadevuelve una matriz de todos los registros que coinciden con los criterios, DELETE /records?filteringCriteriapodría eliminar todos esos registros.

En este caso, la respuesta a su pregunta sería DELETE /records?id=1&id=2&id=3.

Martín Ždila
fuente
1
También llegué a esta conclusión: simplemente cambia el verbo a lo que quieres hacer. No entiendo cómo lo que se aplica a GET no se aplica a DELETE.
Luke Puplett
9
GET /records?id=1&id=2&id=3no no significa “sacar los tres registros con identificadores 1, 2 y 3”, que significa “sacar el único recurso con la ruta URL / registros? id = 1 & id = 2 & id = 3”, que podría ser una imagen de un nabo, un texto sin formato documento que contiene el número "42" en chino, o puede que no exista.
Nicholas Shanks
Considere lo siguiente: se envían dos solicitudes secuenciales para /records?id=1y /records?id=2, y sus respuestas se almacenan en caché por algún intermediario (por ejemplo, su navegador o ISP). Si Internet supiera lo que su aplicación quería decir con esto, entonces es lógico que /records?id=1&id=2la caché pueda devolver una solicitud de simplemente fusionando (de alguna manera) los dos resultados que ya tiene, sin tener que preguntar al servidor de origen. Pero esto no es posible. /records?id=1&id=2puede no ser válido (solo se permite 1 identificación por solicitud) o puede devolver algo completamente diferente (un nabo).
Nicholas Shanks
Este es un problema básico de almacenamiento en caché de recursos. Si mi DBA mutó el estado directamente, las cachés ahora están desincronizadas. Usted da un ejemplo 410 devuelto por el intermediario, pero 410 es para eliminaciones permanentes, al BORRAR un caché podría borrar su ranura para esa URL, pero no enviará un 410 o un 404, ya que no sabe si un DBA no acaba de devolver el recurso inmediatamente al origen.
Luke Puplett
4
@NicholasShanks Realmente no estoy de acuerdo. Si los resultados se almacenan en caché, es culpa del servidor. Y si está hablando del diseño de la API, es de esperar que sea usted quien escriba el código para el servidor. Ya sea que use id[]=1&id[]=2o id=1&id=2en la cadena de consulta para representar una matriz de valores, esa cadena de consulta representa precisamente eso. Y creo que es una práctica muy común y buena que la cadena de consulta represente un filtro. Además, si permite eliminaciones y actualizaciones, no almacene las GETsolicitudes en caché . Si lo hace, los clientes mantendrán un estado obsoleto.
Joseph Nields
8

Creo que Mozilla Storage Service SyncStorage API v1.5 es una buena manera de eliminar varios registros usando REST.

Elimina una colección completa.

DELETE https://<endpoint-url>/storage/<collection>

Elimina varios BSO de una colección con una sola solicitud.

DELETE https://<endpoint-url>/storage/<collection>?ids=<ids>

ids : elimina los BSO de la colección cuyos identificadores se encuentran en la lista separada por comas proporcionada. Se puede proporcionar un máximo de 100 ID.

Elimina la BSO en la ubicación indicada.

DELETE https://<endpoint-url>/storage/<collection>/<id>

http://moz-services-docs.readthedocs.io/en/latest/storage/apis-1.5.html#api-instructions

bootsoon
fuente
Esta parece una buena solución. Supongo que si mozilla cree que es correcto, entonces debe serlo. Entonces, la única pregunta es el manejo de errores. Supongamos que pasan? Ids = 1,2,3 y el id 3 no existe, ¿borra 1 y 2 y luego responde con un 200 porque el solicitante quiere que 3 se hayan ido y no está allí, así que no importa? o si están autorizados a borrar 1 pero no 2 ... ¿borras nada y respondes con un error o borras lo que puedes y dejas los demás ...?
tempcke
Por lo general, devolveré una respuesta exitosa porque el estado final es el mismo independientemente. Esto también simplifica la lógica en el cliente, ya que ya no tienen que manejar ese estado de error. En cuanto al caso de autorización, simplemente fallaría toda la solicitud ... pero realmente depende de su caso de uso.
Nathan Phetteplace
3

Esto parece un área gris de la convención REST.

Sí, hasta ahora solo me he encontrado con una guía de diseño de API REST que menciona operaciones por lotes (como una eliminación por lotes): la guía de diseño de API de Google .

Esta guía menciona la creación de métodos "personalizados" que se pueden asociar a través de un recurso usando 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 se puede asociar a un recurso, una colección o un servicio. Puede tomar una solicitud arbitraria y devolver una respuesta arbitraria, y también admite solicitudes y respuestas 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 para el rendimiento, puede ser útil proporcionar métodos personalizados por lotes para reducir la sobrecarga por solicitud .

Entonces, podría hacer lo siguiente de acuerdo con la guía de API de Google:

POST /api/path/to/your/collection:batchDelete

... para eliminar un montón de elementos de su recurso de colección.

B12 Tostadora
fuente
¿Es una solución viable que la lista de elementos se comunique a través de una matriz con formato JSON?
Daniele
si seguro. puede PUBLICAR una carga útil en la que los identificadores se envían a través de una matriz json.
B12Toaster
Es interesante lo que dijo la guía API de Google If the HTTP verb used for the custom method does not accept an HTTP request body (GET, DELETE), the HTTP configuration of such method must not use the body clause at all,en el capítulo Método personalizado. Pero la GET accounts.locations.batchGetAPI es el método GET con body. Eso es raro. developers.google.com/my-business/reference/rest/v4/…
鄭元傑
@ 鄭元傑 de acuerdo, se ve un poco extraño a primera vista, pero si miras de cerca, en realidad se POSTusa un método http y solo se nombra el método personalizado batchGet. Supongo que Google lo hace para (a) ceñirse a su regla de que todos los métodos personalizados deben ser POST(consulte mi respuesta) y (b) para facilitar que las personas pongan un "filtro" en el cuerpo para que usted no tenga que hacerlo escape o codifique el filtro como con las cadenas de consulta. la desventaja, por supuesto, es que esto ya no se puede
almacenar en
https://service.name/v1/some/resource/name:customVerbno es RESTful por definición.
Deamon
2

Permití un reemplazo total de una colección, por ejemplo, PUT ~/people/123/shoesdonde el cuerpo es la representación de la colección completa.

Esto funciona para colecciones de elementos de niños pequeños en las que el cliente quiere revisar los elementos y eliminar algunos y agregar otros y luego actualizar el servidor. Podrían PONER una colección vacía para borrar todo.

Esto significaría GET ~/people/123/shoes/9que aún permanecería en el caché a pesar de que un PUT lo eliminó, pero eso es solo un problema de almacenamiento en caché y sería un problema si otra persona eliminara el zapato.

Mis API de datos / sistemas siempre usan ETag en lugar de tiempos de caducidad, por lo que el servidor recibe cada solicitud y necesito encabezados de versión / concurrencia correctos para mutar los datos. Para las API que son de solo lectura y alineadas con vistas / informes, utilizo tiempos de vencimiento para reducir las visitas al origen, por ejemplo, una tabla de clasificación puede ser válida durante 10 minutos.

Para colecciones mucho más grandes, por ejemplo ~/people, no suelo necesitar una eliminación múltiple, el caso de uso tiende a no surgir de forma natural y, por lo tanto, un DELETE único funciona bien.

En el futuro, y de la experiencia con la creación de API REST y con los mismos problemas y requisitos, como auditoría, me inclinaría a usar solo verbos GET y POST y diseñar en torno a eventos, por ejemplo, POST un evento de cambio de dirección, aunque sospecho que vendrá con su propio conjunto de problemas :)

También permitiría a los desarrolladores de front-end crear sus propias API que consuman API de back-end más estrictas, ya que a menudo existen razones prácticas y válidas del lado del cliente por las que no les gustan los diseños de API REST estrictos "Fielding fanáticos", y para la productividad y razones de capas de caché.

Luke Puplett
fuente
Me encantó esta respuesta hasta que leí la última oración :) Nunca he visto un caso de uso en el que la aplicación de REST estricto haya tenido un efecto perjudicial neto. Seguro que puede hacer que se escriba más código en ambos extremos, pero terminará con un sistema más seguro, más limpio y menos acoplado.
Nicholas Shanks
Jaja. ¡En realidad se ha convertido en un patrón! Backend para front-end se llama en el radar de tecnología ThoughtWorks. También permite que se escriba más lógica de aplicación que sería engorrosa, digamos, en JavaScript, y obviamente se puede actualizar sin un cliente, así que actualizar, digamos, para una aplicación iOS.
Luke Puplett
Al leer los primeros cuatro resultados de Google, parece que esta técnica de BFF solo puede funcionar cuando los clientes están bajo su control . Los desarrolladores del cliente desarrollan la API que desean, asignando llamadas a API de microservicio que son el verdadero back-end. En este diagrama: samnewman.io/patterns/architectural/bff/#bff Colocaría la línea "Perímetro" debajo de las casillas de BFF; cada casilla es simplemente parte del cliente. Incluso podría vivir fuera del centro de datos que alberga los microservicios. Tampoco veo cómo REST no se aplica a ambas interfaces (cliente / BFF y BFF / microservicio).
Nicholas Shanks
1
Sí, ese es un buen punto. Por lo general, es para cuando tienes un equipo de microservicios de construcción y un equipo que crea una aplicación angular, por ejemplo, y ese equipo de desarrollo son más tipos de front-end a los que no les gusta tener que trabajar contra un montón de pequeños servicios puristas. Aunque no veo ninguna razón por la que no pueda usar el mismo patrón para abstraer microservicios y agregación en una fachada más utilizable para sus clientes, de modo que los microservicios se puedan cambiar sin afectar la fachada.
Luke Puplett
Un punto final de API debe modelar las necesidades del dominio y la empresa. Codifique para resolver esos problemas y evitar la ingeniería excesiva para adherirse a especificaciones estrictas y inflexibles muchas veces. REST no es más que pautas de todos modos.
Victorio Berra