Una forma tranquila de eliminar un montón de elementos.

98

En el artículo wiki para REST se indica que si usa http://example.com/resources DELETE, eso significa que está eliminando toda la colección.

Si usa http://example.com/resources/7HOU57Y DELETE, eso significa que está eliminando ese elemento.

Estoy haciendo un SITIO WEB, tenga en cuenta que NO ES SERVICIO WEB.

Tengo una lista que tiene 1 casilla de verificación para cada elemento de la lista. Una vez que seleccione varios elementos para eliminarlos, permitiré a los usuarios presionar un botón llamado ELIMINAR SELECCIÓN. Si el usuario presiona el botón, aparecerá un cuadro de diálogo js pidiéndole al usuario que confirme la eliminación. si el usuario confirma, se eliminan todos los elementos.

Entonces, ¿cómo debo ocuparme de eliminar varios elementos de una manera RESTABLECIDA?

NOTA, actualmente para DELETE en una página web, lo que hago es usar la etiqueta FORM con POST como acción pero incluyo un _method con el valor DELETE ya que esto es lo que otros indicaron en SO sobre cómo hacer una eliminación RESTful para la página web .

Kim Stacks
fuente
1
¿Es fundamental que estas eliminaciones se realicen de forma atómica? ¿Realmente desea revertir la eliminación de los primeros 30 elementos si el 31 no se puede eliminar?
Darrel Miller
@darrelmiller buena pregunta. Yo pensaba que si las eliminaciones se realizan automáticamente, lo que será menos eficiente. Por lo tanto, me inclino hacia ELIMINAR DEL nombre de la tabla DONDE ID EN ({lista de identificadores}). Si alguien me puede señalar si esto es una buena idea o corregirme. Eso sería muy apreciado. Además, no requiero lo contrario de la eliminación para los primeros 20 elementos si se elimina el 21. Una vez más, agradezco que alguien pueda mostrarme la diferencia en el enfoque en el que necesito retroceder frente al que NO necesito retroceder
Kim Stacks
1
Nota: puede haber límites para la cláusula "IN"; por ejemplo, en Oracle puede poner un máximo de 1000 identificadores.
Robar el
La guía de diseño de API de Google ofrece una solución para crear operaciones personalizadas (por lotes) en una API REST; consulte mi respuesta aquí: stackoverflow.com/a/53264372/2477619
B12Toaster

Respuestas:

54

Creo que la respuesta de rojoca es la mejor hasta ahora. Una ligera variación podría ser eliminar la confirmación de JavaScript en la misma página y, en cambio, crear la selección y redirigirla, mostrando un mensaje de confirmación en esa página. En otras palabras:

De:
http://example.com/resources/

hacer un

ENVÍE con una selección de ID para:
http://example.com/resources/selections

que, si tiene éxito, debería responder con:

HTTP / 1.1 201 creado y un encabezado de ubicación para:
http://example.com/resources/selections/DF4XY7

En esta página, verá un cuadro de confirmación (javascript), que si confirma, hará una solicitud de:

ELIMINAR http://example.com/resources/selections/DF4XY7

que, si tiene éxito, debería responder con: HTTP / 1.1 200 Ok (o lo que sea apropiado para una eliminación exitosa)

Dabbler decente
fuente
Me gusta esta idea porque no necesitas redirecciones. Al incorporar AJAX, puede hacer todo esto sin salir de la página.
rojoca
Después de este DELETE example.com/resources/selections/DF4XY7 , ¿me volverían a redirigir a example.com/resources?
Kim Stacks
7
@fireeyeboy Este enfoque de dos pasos parece ser una forma tan común de realizar una eliminación múltiple, pero ¿por qué? ¿Por qué no simplemente envía una solicitud DELETE a un uri como http://example.com/resources/selections/y en la carga útil (cuerpo) de la solicitud envía los datos de los elementos que desea eliminar? Por lo que yo sé, no hay nada que te impida hacer esto, pero siempre me encuentro con "pero no es RESTfull".
thecoshman
6
DELETE puede hacer que la infraestructura HTTP ignore el cuerpo: stackoverflow.com/questions/299628/…
Luke Puplett
DELETE puede tener un cuerpo, pero muchas de sus implementaciones prohíben su cuerpo de forma predeterminada
dmitryvim
54

Una opción es crear una "transacción" de eliminación. Entonces, POSTa algo así como http://example.com/resources/deletesun nuevo recurso que consiste en una lista de recursos que se eliminarán. Luego, en su aplicación, simplemente haga la eliminación. Cuando lo haga la entrada que debe devolver una ubicación de su transacción creada por ejemplo, http://example.com/resources/deletes/DF4XY7. A GETsobre esto podría devolver el estado de la transacción (completa o en curso) y / o una lista de recursos que se eliminarán.

rojoca
fuente
2
Nada que ver con tu base de datos. Por transacción me refiero simplemente a una lista de operaciones a realizar. En este caso, es una lista de eliminaciones. Lo que debe hacer es crear una nueva lista (de eliminaciones) como recurso en su aplicación. Su aplicación web puede procesar esa lista como desee. Ese recurso tiene un URI, por ejemplo, example.com/resources/deletes/DF4XY7 . Esto significa que puede verificar el estado de la eliminación a través de un GET a ese URI. Esto sería útil si, al realizar una eliminación, tuviera que eliminar imágenes de Amazon S3 o alguna otra CDN y esa operación podría tardar mucho en completarse.
rojoca
2
+1 esta es una buena solución. En lugar de enviar un DELETE a cada recurso, @rojoca propone crear una instancia de un nuevo tipo de recurso cuya única tarea es eliminar una lista de recursos. Por ejemplo, tiene una colección de recursos de usuario y desea eliminar a los usuarios Bob, Dave y Amy de su colección, por lo que crea un nuevo recurso de eliminación POSTING Bob, Dave y Amy como parámetros de creación. Se crea el recurso de eliminación y representa el proceso asincrónico de eliminar a Bob, Dave y Amy de la colección de usuarios.
Mike Tunnicliffe
1
Lo siento. Todavía tengo una ligera dificultad para comprender algunos problemas. el DF4XY7. ¿Cómo diablos se genera esta cadena? Este recurso de eliminación. ¿Necesito insertar algún dato en la base de datos? Pido disculpas si repito algunas preguntas. Es un poco desconocido para mí.
Kim Stacks
1
Supongo que DF4XY7 es una identificación única generada, tal vez sea más natural usar la identificación generada cuando se guarda en la base de datos, por ejemplo, example.com/resources/deletes/7. Mi opinión sería crear el modelo de eliminación y guardarlo en la base de datos, puede hacer que el proceso asincrónico de eliminación de los otros registros actualice el modelo de eliminación con el estado de finalización y cualquier error relevante.
Mike Tunnicliffe
2
@rojoca sí, creo que el problema es que HTTP es mucho 'DELETE es para eliminar un solo recurso'. Hagas lo que hagas, la eliminación múltiple es un truco. Aún puede devolver un 'trabajo' al cliente diciendo que se está trabajando en esta tarea (y que podría llevar algo de tiempo) pero use este URI para verificar el progreso. Leí la especificación y entendí que DELETE puede tener un cuerpo, al igual que otras solicitudes.
thecoshman
33

Esto es lo que hizo Amazon con su API REST S3.

Solicitud de eliminación individual:

DELETE /ObjectName HTTP/1.1
Host: BucketName.s3.amazonaws.com
Date: date
Content-Length: length
Authorization: authorization string (see Authenticating Requests (AWS Signature Version 4))

Solicitud de eliminación de varios objetos :

POST /?delete HTTP/1.1
Host: bucketname.s3.amazonaws.com
Authorization: authorization string
Content-Length: Size
Content-MD5: MD5

<?xml version="1.0" encoding="UTF-8"?>
<Delete>
    <Quiet>true</Quiet>
    <Object>
         <Key>Key</Key>
         <VersionId>VersionId</VersionId>
    </Object>
    <Object>
         <Key>Key</Key>
    </Object>
    ...
</Delete>           

Pero Facebook Graph API , Parse Server REST API y Google Drive REST API van aún más lejos al permitirle realizar operaciones individuales "por lotes" en una sola solicitud.

Aquí hay un ejemplo de Parse Server.

Solicitud de eliminación individual:

curl -X DELETE \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

Solicitud de lote:

curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "requests": [
          {
            "method": "POST",
            "path": "/1/classes/GameScore",
            "body": {
              "score": 1337,
              "playerName": "Sean Plott"
            }
          },
          {
            "method": "POST",
            "path": "/1/classes/GameScore",
            "body": {
              "score": 1338,
              "playerName": "ZeroCool"
            }
          }
        ]
      }' \
  https://api.parse.com/1/batch
Luka Žitnik
fuente
13

Yo diría DELETE http://example.com/resources/id1,id2,id3,id4 o DELETE http://example.com/resources/id1+id2+id3+id4 . Como "REST es una arquitectura (...) [no] un protocolo", para citar este artículo de wikipedia, creo que no hay una única forma de hacerlo.

Soy consciente de que lo anterior no es posible sin JS con HTML, pero tengo la sensación de que REST fue:

  • Creado sin pensar en detalles menores como transacciones. ¿Quién necesitaría operar en más de un artículo? Esto se justifica de alguna manera en el protocolo HTTP, ya que no estaba destinado a servir a través de él nada más que páginas web estáticas.
  • No es necesario que se ajuste bien a los modelos actuales, incluso de HTML puro.
Maciej Piechotka
fuente
thx - ¿y si quisiera eliminar toda la colección? ¿Deberían omitirse los ID?
BKSpurgeon
"Tengo la sensación de que REST fue ... creado sin pensar en detalles menores como transacciones" - No creo que eso sea del todo cierto. Si entiendo correctamente, en REST, las transacciones están representadas por recursos, no por un método. Hay una buena discusión que culmina en este comentario en esta publicación de blog .
Paul D. Waite
10

Curiosamente, creo que se aplica el mismo método que para PATCHAR varias entidades, y requiere pensar en lo que queremos decir con nuestra URL, parámetros y método REST.

  1. devolver todos los elementos 'foo':

    [GET] api/foo

  2. devolver elementos 'foo' con filtrado para identificadores específicos:

    [GET] api/foo?ids=3,5,9

En el sentido de que la URL y el filtro determinan "¿con qué elementos estamos tratando?", Y el método REST (en este caso "GET") dice "¿qué hacer con esos elementos?"

  1. Por lo tanto, PATCH múltiples registros para marcarlos como leídos

    [PATCH] api/foo?ids=3,5,9

..con los datos foo [leer] = 1

  1. Finalmente, para eliminar varios registros, este punto final es más lógico:

    [DELETE] api/foo?ids=3,5,9

Por favor, comprenda que no creo que haya ninguna "regla" sobre esto; para mí, simplemente "tiene sentido"

fezfox
fuente
En realidad, con respecto a PATCH: dado que está destinado a significar una actualización parcial si piensa en la lista de entidades como una entidad en sí misma (incluso si es de tipo matriz), enviando una matriz parcial (solo los identificadores que desea actualizar) de entidades parciales, entonces puede omitir la cadena de consulta, por lo que no tiene una URL que represente más de una entidad.
Szabolcs Páll
2

Como dice la respuesta de Decent Dabbler y la respuesta de rojocas , lo más canónico es usar recursos virtuales para eliminar una selección de recursos, pero creo que eso es incorrecto desde una perspectiva REST, porque ejecutar un DELETE http://example.com/resources/selections/DF4XY7debería eliminar el recurso de selección en sí, no los recursos seleccionados.

Tomando el anwser Maciej Piechotka o el respuesta de fezfox , solo tengo una objeción: hay una forma más canónica de pasar una matriz de identificadores, y está usando el operador de matriz:

DELETE /api/resources?ids[]=1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d&ids[]=7e8f9a0b-1c2d-3e4f-5a6b-7c8d9e0f1a2b

De esta manera, está atacando el punto final Delete Collection pero filtrando la eliminación con una cadena de consulta de la manera correcta.

mangelsnc
fuente
-1

Como no existe una forma 'adecuada' de hacer esto, lo que hice en el pasado es:

envíe DELETE a http://example.com/something con datos codificados xml o json en el cuerpo.

cuando reciba la solicitud, verifique DELETE, si es verdadero, luego lea el cuerpo de los que se eliminarán.

usuario103219
fuente
Este es el enfoque que tiene sentido para mí, simplemente envías los datos en una solicitud, pero siempre me encuentro con "pero no es RESTfull". ¿Tiene alguna fuente que sugiera que este es un método viable y 'RESTfull' para hacer esto?
thecoshman
10
El problema con este enfoque es que las operaciones DELETE no esperan un cuerpo, por lo que algunos de los enrutadores intermedios en Internet pueden eliminarlo sin su control o conocimiento. ¡Entonces usar body para DELETE no es seguro!
Alex White
Referencia para el comentario de Alex: stackoverflow.com/questions/299628/…
Luke Puplett
1
A payload within a DELETE request message has no defined semantics; sending a payload body on a DELETE request might cause some existing implementations to reject the request.de tools.ietf.org/html/rfc7231#section-4.3.5
cottton
-1

Tuve la misma situación al eliminar varios elementos. Esto es lo que terminé haciendo. Usé la operación DELETE y los identificadores de los elementos que se eliminarían eran parte del encabezado HTTP.

Sherin siríaco
fuente