Ruhige Möglichkeit zum Löschen einer Reihe von Elementen

97

Im Wiki-Artikel für REST wird angegeben, dass bei Verwendung von http://example.com/resources DELETE die gesamte Sammlung gelöscht wird.

Wenn Sie http://example.com/resources/7HOU57Y DELETE verwenden, bedeutet dies, dass Sie dieses Element löschen.

Ich mache eine WEBSITE, beachte NICHT WEBSERVICE.

Ich habe eine Liste mit 1 Kontrollkästchen für jedes Element in der Liste. Sobald ich mehrere Elemente zum Löschen ausgewählt habe, können Benutzer eine Schaltfläche mit dem Namen DELETE SELECTION drücken. Wenn der Benutzer die Taste drückt, wird ein js-Dialogfeld angezeigt, in dem der Benutzer aufgefordert wird, den Löschvorgang zu bestätigen. Wenn der Benutzer bestätigt, werden alle Elemente gelöscht.

Wie sollte ich also dafür sorgen, dass mehrere Elemente RESTFUL gelöscht werden?

HINWEIS: Derzeit verwende ich für DELETE auf einer Webseite das FORM-Tag mit POST als Aktion, füge jedoch eine _method mit dem Wert DELETE hinzu, da dies von anderen in SO angegeben wurde, wie das RESTful-Löschen für eine Webseite durchgeführt wird .

Kim Stacks
quelle
1
Ist es wichtig, dass diese Löschungen atomar durchgeführt werden? Möchten Sie das Löschen der ersten 30 Elemente wirklich rückgängig machen, wenn das 31. nicht gelöscht werden kann?
Darrel Miller
@darrelmiller gute frage. Ich dachte, wenn die Löschungen atomar durchgeführt werden, wird es weniger effizient sein. Daher neige ich zu DELETE FROM tablename WHERE ID IN ({Liste der IDs}). Wenn mich jemand darauf hinweisen kann, ob dies eine gute Idee ist oder mich korrigiert. das wäre sehr dankbar. Auch ich benötige nicht die Umkehrung des Löschens für die ersten 20 Elemente, wenn das 21. gelöscht wird. Wieder schätze ich es, wenn mir jemand den Unterschied in der Herangehensweise zeigen kann, wo ich umkehren muss und wo ich NICHT umkehren muss
Kim Stacks
1
Hinweis: Die "IN" -Klausel kann begrenzt sein. In Oracle können Sie beispielsweise maximal 1000 IDs eingeben.
Rob
Das API- Designhandbuch von
B12Toaster

Antworten:

53

Ich denke, Rojocas Antwort ist die bisher beste. Eine geringfügige Abweichung kann darin bestehen, die Javascript-Bestätigung auf derselben Seite zu beseitigen und stattdessen die Auswahl zu erstellen und zu dieser umzuleiten, wobei auf dieser Seite eine Bestätigungsmeldung angezeigt wird. Mit anderen Worten:

Von:
http://example.com/resources/

mach a

POST mit einer Auswahl der IDs an:
http://example.com/resources/selections

die, wenn sie erfolgreich ist, antworten sollte mit:

HTTP / 1.1 201 erstellt und ein Standortheader an:
http://example.com/resources/selections/DF4XY7

Auf dieser Seite sehen Sie dann ein Bestätigungsfeld (Javascript), das bei Bestätigung Folgendes anfordert:

LÖSCHEN http://example.com/resources/selections/DF4XY7

die bei Erfolg antworten sollte mit: HTTP / 1.1 200 Ok (oder was auch immer für einen erfolgreichen Löschvorgang geeignet ist)

Anständiger Dabbler
quelle
Ich mag diese Idee, weil Sie keine Weiterleitungen benötigen. Mit AJAX können Sie dies alles tun, ohne die Seite zu verlassen.
Rojoca
Würde ich nach diesem DELETE example.com/resources/selections/DF4XY7 zurück zu example.com/resources umgeleitet?
Kim Stacks
7
@fireeyeboy Dieser zweistufige Ansatz scheint eine häufig vorgeschlagene Methode zur Durchführung eines Mehrfachlöschvorgangs zu sein, aber warum? Warum senden Sie nicht einfach eine DELETE-Anfrage an einen Uri-Like http://example.com/resources/selections/und senden in der Nutzlast (Text) der Anfrage die Daten, für die Sie Elemente löschen möchten. Soweit ich das beurteilen kann, hindert Sie nichts daran, dies zu tun, aber ich werde immer mit "aber es ist nicht RESTfull" konfrontiert.
Thecoshman
6
DELETE kann möglicherweise den Körper von der HTTP-Infrastruktur ignorieren lassen: stackoverflow.com/questions/299628/…
Luke Puplett
DELETE kann einen Body haben, aber viele seiner Implementierungen haben seinen Body standardmäßig verboten
dmitryvim
54

Eine Möglichkeit besteht darin, eine Lösch- "Transaktion" zu erstellen. Sie gelangen also POSTzu http://example.com/resources/deleteseiner neuen Ressource, die aus einer Liste der zu löschenden Ressourcen besteht. Dann führen Sie in Ihrer Anwendung einfach das Löschen durch. Wenn Sie den Beitrag ausführen, sollten Sie einen Speicherort Ihrer erstellten Transaktion zurückgeben, z http://example.com/resources/deletes/DF4XY7. A kann GETden Status der Transaktion (abgeschlossen oder in Bearbeitung) und / oder eine Liste der zu löschenden Ressourcen zurückgeben.

Rojoca
quelle
2
Nichts mit Ihrer Datenbank zu tun. Mit Transaktion meine ich nur eine Liste der auszuführenden Operationen. In diesem Fall handelt es sich um eine Liste von Löschvorgängen. Sie erstellen eine neue Liste (von Löschvorgängen) als Ressource in Ihrer Anwendung. Ihre Webanwendung kann diese Liste nach Belieben verarbeiten. Diese Ressource hat einen URI, z . B. example.com/resources/deletes/DF4XY7 . Dies bedeutet, dass Sie den Status des Löschvorgangs über ein GET für diesen URI überprüfen können. Dies ist praktisch, wenn Sie beim Löschen Bilder aus Amazon S3 oder einem anderen CDN löschen mussten und dieser Vorgang möglicherweise lange dauert.
Rojoca
2
+1 das ist eine schöne lösung. Anstatt ein DELETE an jede Ressource zu senden, schlägt @rojoca vor, eine Instanz eines neuen Ressourcentyps zu erstellen, dessen einzige Aufgabe darin besteht, eine Liste von Ressourcen zu löschen. Sie haben beispielsweise eine Sammlung von Benutzerressourcen und möchten die Benutzer Bob, Dave und Amy aus Ihrer Sammlung löschen. Daher erstellen Sie eine neue Löschressource, indem Sie Bob, Dave und Amy als Erstellungsparameter POSTEN. Die Löschressource wird erstellt und repräsentiert den asynchronen Prozess des Löschens von Bob, Dave und Amy aus der Users-Sammlung.
Mike Tunnicliffe
1
Tut mir leid. Ich habe immer noch leichte Schwierigkeiten, einige Probleme zu verstehen. der DF4XY7. Wie um alles in der Welt erzeugen Sie diese Zeichenfolge? Diese Löschressource. Muss ich Daten in die Datenbank einfügen? Ich entschuldige mich, wenn ich einige Fragen wiederhole. Es ist mir nur ein wenig unbekannt.
Kim Stacks
1
Ich gehe davon aus, dass DF4XY7 eine generierte eindeutige ID ist. Vielleicht ist es natürlicher, nur die ID zu verwenden, die beim Speichern in der Datenbank generiert wird, z. B. example.com/resources/deletes/7. Ich würde das Löschmodell erstellen und in der Datenbank speichern. Der asynchrone Prozess zum Löschen der anderen Datensätze kann das Löschmodell mit dem Abschlussstatus und allen relevanten Fehlern aktualisieren.
Mike Tunnicliffe
2
@rojoca Ja, ich denke, das Problem ist, dass HTTP sehr viel "LÖSCHEN ist zum Entfernen einer einzelnen Ressource" ist. Was auch immer Sie tun, das mehrfache Löschen ist ein Hack. Sie können dem Client weiterhin einen 'Job' zurückgeben, der besagt, dass an dieser Aufgabe gearbeitet wird (und einige Zeit dauern kann), aber diesen URI verwenden, um den Fortschritt zu überprüfen. Ich habe die Spezifikation gelesen und angenommen, dass DELETE einen Körper haben kann, genau wie andere Anfragen.
Thecoshman
33

Hier ist, was Amazon mit seiner S3-REST-API gemacht hat.

Individuelle Löschanforderung:

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))

Löschen mehrerer Objekte :

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>           

Aber Facebook Graph API , Parse Server REST API und Google Drive REST API gehen jedoch noch weiter, indem Sie einzelne Vorgänge in einer Anforderung "stapeln" können.

Hier ist ein Beispiel von Parse Server.

Individuelle Löschanforderung:

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

Chargenanforderung:

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
quelle
13

Ich würde DELETE http://example.com/resources/id1,id2,id3,id4 oder DELETE http://example.com/resources/id1+id2+id3+id4 sagen . Da "REST ein Architekturprotokoll (...) [nicht]" ist, um diesen Wikipedia-Artikel zu zitieren, gibt es meines Erachtens keine einzige Möglichkeit, dies zu tun.

Mir ist bewusst, dass oben ohne JS mit HTML nicht möglich ist, aber ich habe das Gefühl, dass REST war:

  • Erstellt ohne an kleinere Details wie Transaktionen zu denken. Wer müsste mehr als nur einen Gegenstand bearbeiten? Dies ist im HTTP-Protokoll irgendwie gerechtfertigt, da es nicht dazu gedacht war, etwas anderes als statische Webseiten zu verwenden.
  • Nicht unbedingt gut an aktuelle Modelle anpassen - auch nicht an reinem HTML.
Maciej Piechotka
quelle
thx - was ist, wenn Sie die gesamte Sammlung löschen möchten - sollten die IDs dann weggelassen werden?
BKSpurgeon
"Ich habe das Gefühl, dass REST ... erstellt wurde, ohne an kleinere Details wie Transaktionen zu denken" - ich denke nicht, dass das ganz richtig ist. Wenn ich das richtig verstehe, werden Transaktionen in REST durch Ressourcen und nicht durch eine Methode dargestellt. Es gibt einige gute Diskussionen, die in diesem Kommentar zu diesem Blog-Beitrag gipfeln .
Paul D. Waite
10

Interessanterweise denke ich, dass die gleiche Methode für das PATCHEN mehrerer Entitäten gilt und dass wir darüber nachdenken müssen, was wir mit unserer URL, unseren Parametern und unserer REST-Methode meinen.

  1. Alle 'foo'-Elemente zurückgeben:

    [GET] api/foo

  2. Rückgabe von 'foo'-Elementen mit Filterung nach bestimmten IDs:

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

Wobei der Sinn darin besteht, dass die URL und der Filter bestimmen, mit welchen Elementen wir es zu tun haben, und die REST-Methode (in diesem Fall "GET") sagt, was mit diesen Elementen zu tun ist.

  1. PATCHEN Sie daher mehrere Datensätze, um sie als gelesen zu markieren

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

..mit den Daten foo [read] = 1

  1. Um mehrere Datensätze zu löschen, ist dieser Endpunkt am logischsten:

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

Bitte haben Sie Verständnis dafür, dass ich nicht glaube, dass es dafür "Regeln" gibt - für mich macht es einfach "Sinn"

Fezfox
quelle
Eigentlich in Bezug auf PATCH: Da dies eine Teilaktualisierung bedeuten soll, wenn Sie die Liste der Entitäten als eine Entität selbst betrachten (auch wenn sie vom Typ Array ist), ein Teilarray (nur die IDs, die Sie aktualisieren möchten) von Teilentitäten senden, dann Sie kann die Abfragezeichenfolge weglassen, sodass keine URL mehr als eine Entität darstellt.
Szabolcs Páll
2

Wie die Antwort von Decent Dabbler und die Antwort von Rojocas besagen, ist es am kanonischsten, virtuelle Ressourcen zu verwenden, um eine Auswahl von Ressourcen zu löschen. Ich denke jedoch, dass dies aus REST-Sicht falsch ist, da a ausgeführt wirdDELETE http://example.com/resources/selections/DF4XY7 die Auswahlressource selbst und nicht die ausgewählten Ressourcen entfernt werden sollte.

Wenn ich die Antwort von Maciej Piechotka oder die Antwort von fezfox nehme , habe ich nur einen Einwand: Es gibt eine kanonischere Möglichkeit, ein Array von IDs zu übergeben, und ich verwende den Array-Operator:

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

Auf diese Weise greifen Sie den Endpunkt "Sammlung löschen" an, filtern die Löschung jedoch mit einem Querystring auf die richtige Weise.

mangelsnc
quelle
0

Ich hatte die gleiche Situation, um mehrere Elemente zu löschen. Das habe ich letztendlich getan. Ich habe die DELETE-Operation verwendet und die IDs der zu löschenden Elemente waren Teil des HTTP-Headers.

Sherin Syriac
quelle
-1

Da es keinen "richtigen" Weg gibt, dies zu tun, habe ich in der Vergangenheit Folgendes getan:

Senden Sie DELETE an http://example.com/something mit XML- oder JSON-codierten Daten im Body.

Wenn Sie die Anfrage erhalten, prüfen Sie, ob DELETE vorhanden ist, falls dies der Fall ist, und lesen Sie dann den Text für die zu löschenden.

user103219
quelle
Dies ist der Ansatz, der für mich sinnvoll ist. Sie senden die Daten einfach in einer Anfrage, aber ich werde immer mit "aber es ist nicht RESTfull" konfrontiert. Haben Sie Quellen, die darauf hinweisen, dass dies eine praktikable und "RESTfull" -Methode dafür ist?
Thecoshman
9
Das Problem bei diesem Ansatz besteht darin, dass DELETE-Vorgänge keinen Body erwarten. Einige der Zwischenrouter im Internet entfernen ihn möglicherweise ohne Ihre Kontrolle oder Ihr Wissen für Sie. Die Verwendung von body for DELETE ist also nicht sicher!
Alex White
Referenz für Alex 'Kommentar: 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.von tools.ietf.org/html/rfc7231#section-4.3.5
Cottton