RESTful Alternativen zu DELETE Request Body

93

Während die HTTP 1.1 - Spezifikation zu scheint erlauben Nachrichtentexte auf DELETE - Anfragen, so scheint es , um anzuzeigen , dass die Server ignorieren sollten , da es keine definierte Semantik für sie ist.

4.3 Nachrichtentext

Ein Server sollte auf jede Anfrage einen Nachrichtentext lesen und weiterleiten. Wenn die Anforderungsmethode keine definierte Semantik für einen Entitätskörper enthält, MUSS der Nachrichtentext bei der Verarbeitung der Anforderung ignoriert werden.

Ich habe bereits mehrere verwandte Diskussionen zu diesem Thema auf SO und darüber hinaus besprochen, wie zum Beispiel:

Die meisten Diskussionen scheinen darin übereinzustimmen, dass das Bereitstellen eines Nachrichtentexts auf einem DELETE zulässig ist , dies wird jedoch im Allgemeinen nicht empfohlen.

Außerdem habe ich einen Trend in verschiedenen HTTP-Client-Bibliotheken festgestellt, bei denen immer mehr Verbesserungen für diese Bibliotheken protokolliert werden, um Anforderungskörper auf DELETE zu unterstützen. Die meisten Bibliotheken scheinen sich zu verpflichten, wenn auch gelegentlich mit ein wenig anfänglichem Widerstand.

In meinem Anwendungsfall müssen einige erforderliche Metadaten zu einem DELETE hinzugefügt werden (z. B. der "Grund" für das Löschen sowie einige andere zum Löschen erforderliche Metadaten). Ich habe die folgenden Optionen in Betracht gezogen, von denen keine völlig angemessen erscheint und mit den HTTP-Spezifikationen und / oder REST-Best Practices übereinstimmt:

  • Nachrichtentext - Die Spezifikation gibt an, dass Nachrichtentexte in DELETE keinen semantischen Wert haben. von HTTP-Clients nicht vollständig unterstützt; keine Standardpraxis
  • Benutzerdefinierte HTTP-Header - Das Erfordernis benutzerdefinierter Header verstößt im Allgemeinen gegen Standardpraktiken . Ihre Verwendung ist nicht mit dem Rest meiner API vereinbar, für die keine benutzerdefinierten Header erforderlich sind. Außerdem ist keine gute HTTP-Antwort verfügbar, um schlechte benutzerdefinierte Header-Werte anzuzeigen (wahrscheinlich eine separate Frage insgesamt).
  • Standard-HTTP-Header - Es sind keine Standard-Header geeignet
  • Abfrageparameter - Hinzufügen von Abfrage params ändert tatsächlich die Request-URI gelöscht werden; gegen Standardpraktiken
  • POST-Methode - (z. B. POST /resourceToDelete { deletemetadata }) POST ist keine semantische Option zum Löschen. POST stellt tatsächlich die entgegengesetzte gewünschte Aktion dar (dh POST erstellt Ressourcenuntergebene; ich muss die Ressource jedoch löschen).
  • Mehrere Methoden - Die Aufteilung der DELETE-Anforderung in zwei Operationen (z. B. PUT-Löschmetadaten, dann DELETE) teilt eine atomare Operation in zwei auf, wodurch möglicherweise ein inkonsistenter Zustand verbleibt. Der Löschgrund (und andere verwandte Metadaten) sind nicht Teil der Ressourcendarstellung selbst.

Meine erste Präferenz wäre wahrscheinlich die Verwendung des Nachrichtentexts nach benutzerdefinierten HTTP-Headern. Wie bereits erwähnt, weisen diese Ansätze jedoch einige Nachteile auf.

Gibt es Empfehlungen oder Best Practices im Einklang mit den REST / HTTP-Standards, um solche erforderlichen Metadaten in DELETE-Anforderungen aufzunehmen? Gibt es andere Alternativen, die ich nicht in Betracht gezogen habe?

Shelley
quelle
2
Bestimmte Implementierungen wie Jerseyerlauben keinen Body für deleteAnfragen.
Basiljames

Antworten:

44

Trotz einiger Empfehlungen, den Nachrichtentext nicht für DELETE-Anforderungen zu verwenden, kann dieser Ansatz in bestimmten Anwendungsfällen geeignet sein. Dies ist der Ansatz, den wir letztendlich gewählt haben, nachdem wir die anderen in den Fragen / Antworten genannten Optionen bewertet und mit den Verbrauchern des Dienstes zusammengearbeitet haben.

Obwohl die Verwendung des Nachrichtentexts nicht ideal ist, war auch keine der anderen Optionen perfekt geeignet. Mit dem Anforderungshauptteil DELETE konnten wir einfach und klar Semantik für zusätzliche Daten / Metadaten hinzufügen, die für die DELETE-Operation erforderlich waren.

Ich wäre immer noch offen für andere Gedanken und Diskussionen, wollte aber den Kreis dieser Frage schließen. Ich schätze die Gedanken und Diskussionen aller zu diesem Thema!

Shelley
quelle
12
Das ist eine schlechte Idee. Ein Ort, an dem Sie Probleme bekommen, ist, wenn Sie sich später für einen HTTP-Beschleunigungsdienst wie Akamai EdgeConnect entscheiden. Ich weiß, dass EdgeConnect Körper von HTTP-DELETE-Anforderungen entfernt (da sie Bandbreite verbrauchen, sind sie wahrscheinlich ungültig). Es ist auch wahrscheinlich, dass ähnliche Dienste dasselbe tun (siehe die Beschleunigungsfunktion des Kindle und andere CDN-ähnliche Dienste). Sie sollten wahrscheinlich neu gestalten, um keine HTTP-Verben für Ihren Dienst zu verwenden. Die meisten APIs sind mit HTTP-Verben / klassischem REST wenig sinnvoll, und Probleme beim Transport von HTTP-Verben sind sehr schwer zu beheben.
Gabe
3
Ich stimme @Gabe zu und sende einen Körper mit Methoden, die keinen Körper haben per Definition ist ein sicherer Weg, um zufällig Daten zu verlieren, wenn Ihre Bits die Internet-Pipes durchlaufen, und Sie werden es sehr schwer haben, sie zu debuggen.
Nicholas Shanks
3
Diese Kommentare gegen die Verwendung von DELETE sind irrelevant, bis sie die sehr gültigen Probleme des OP ansprechen. Ich versuche mein Bestes, um mich an den Geist von REST zu halten, aber ein Löschen ohne optimistische Parallelität und ein Löschen ohne atomare Stapeloperation ist in realen Situationen nicht praktikabel. Dies ist ein schwerwiegender Mangel des REST-Musters.
Quarkly
Ich bin mit @Quarkly dabei. Ich verstehe nicht, was die RESTFUL-Idee ist, wie wir Parallelität usw. überprüfen sollen. Parallelitätsprüfungen gehören nicht zum Client.
Dirk Wessels
13

Was Sie zu wollen scheinen, ist eines von zwei Dingen, von denen keines rein ist DELETE:

  1. Sie haben zwei Vorgänge, einen PUTder Löschgründe, gefolgt von einem DELETEder Ressourcen. Nach dem Löschen ist der Inhalt der Ressource für niemanden mehr zugänglich. Der 'Grund' darf keinen Hyperlink zur gelöschten Ressource enthalten. Oder,
  2. Sie versuchen, eine Ressource mithilfe der Methode von state=activebis zu ändern . Ressourcen mit dem Status = gelöscht werden von Ihrer Haupt-API ignoriert, können jedoch möglicherweise von einem Administrator oder einer Person mit Datenbankzugriff gelesen werden. Dies ist zulässig. Sie müssen die Sicherungsdaten für eine Ressource nicht löschen, sondern nur die an dieser URI verfügbare Ressource entfernen.state=deletedDELETEDELETE

Jede Operation, die einen Nachrichtentext für eine DELETEAnforderung erfordert, kann im Allgemeinen in a unterteilt werden POST, um alle erforderlichen Aufgaben mit dem Nachrichtentext auszuführen, und a DELETE. Ich sehe keinen Grund, die Semantik von HTTP zu brechen.

Nicholas Shanks
quelle
2
Was passiert, wenn die PUTVernunft erfolgreich ist und die DELETERessource ausfällt? Wie kann ein inkonsistenter Zustand verhindert werden?
Lightman
1
@ Lightman der PUT gibt nur die Absicht an. Es kann ohne ein entsprechendes LÖSCHEN existieren, was darauf hinweist, dass jemand löschen wollte, aber entweder ist es fehlgeschlagen oder sie haben ihre Meinung geändert. Das Umkehren der Reihenfolge der Aufrufe würde es auch ermöglichen, dass DELETEs ohne Grund auftreten - die Angabe eines Grundes würde dann lediglich als Anmerkung betrachtet. Aus diesen beiden Gründen würde ich empfehlen, Option 2 aus dem oben genannten zu verwenden, dh den Status des zugrunde liegenden Datensatzes so zu ändern, dass die HTTP-Ressource von ihrer aktuellen URL verschwindet. Ein Garbage Collector / Admin kann dann Datensätze löschen
Nicholas Shanks
7

In Anbetracht Ihrer Situation würde ich einen der folgenden Ansätze verfolgen:

  • Senden eines PUT oder PATCH : Ich schließe daraus, dass der Löschvorgang virtuell ist, da ein Löschgrund erforderlich ist. Daher halte ich das Aktualisieren des Datensatzes über eine PUT / PATCH-Operation für einen gültigen Ansatz, auch wenn es sich nicht um eine DELETE-Operation an sich handelt.
  • Verwenden Sie die Abfrageparameter : Die Ressourcen-URL wird nicht geändert. Ich denke tatsächlich, dass dies auch ein gültiger Ansatz ist. Bei der von Ihnen verknüpften Frage ging es darum, das Löschen nicht zuzulassen, wenn der Abfrageparameter fehlte. In Ihrem Fall hätte ich nur einen Standardgrund, wenn der Grund nicht in der Abfragezeichenfolge angegeben ist. Die Ressource wird weiterhin sein resource/:id. Sie können es mit Link-Headern in der Ressource für jeden Grund erkennbar machen (mit einem relTag auf jedem, um den Grund zu identifizieren).
  • Verwenden Sie einen separaten Endpunkt pro Grund : Verwenden Sie eine URL wie resource/:id/canceled. Dies ändert tatsächlich den Request-URI und ist definitiv nicht RESTful. Auch hier können Link-Header dies auffindbar machen.

Denken Sie daran, dass REST kein Gesetz oder Dogma ist. Betrachten Sie es eher als Anleitung. Wenn es also sinnvoll ist, die Anweisungen für Ihre Problemdomäne nicht zu befolgen, tun Sie dies nicht. Stellen Sie einfach sicher, dass Ihre API-Konsumenten über die Abweichung informiert sind.

Codeprogression
quelle
In Bezug auf die Verwendung von Abfrageparametern verstehe ich, dass die Abfrage Teil des Request-URI gemäß Abschnitt 3.2 ist und daher die Verwendung dieses Ansatzes (oder auch der separaten Endpunkte) gegen die Definition der DELETE- Methode verstößt , sodass "resource" identifiziert durch den Request-URI "wird gelöscht.
Shelley
Die Ressource wird durch den Uri-Pfad identifiziert. Ein GET to /orders/:idwürde also dieselbe Ressource wie zurückgeben /orders/:id?exclude=orderdetails. Die Abfragezeichenfolge gibt dem Server nur Hinweise - in diesem Fall, um Bestelldetails in der Antwort auszuschließen (falls unterstützt). Wenn Sie DELETE an /orders/:idoder /orders/:id?reason=canceledoder senden /orders/:id?reason=bad_credit, agieren Sie weiterhin auf dieselbe zugrunde liegende Ressource. Um eine "einheitliche Schnittstelle" beizubehalten, hätte ich einen Standardgrund, so dass das Senden des Abfrageparameters nicht erforderlich ist.
Codeprogression
@shelley Sie haben Recht mit Ihren Bedenken hinsichtlich Abfragezeichenfolgen. Die Abfragezeichenfolge ist Teil des URI. /foo?123Wenn Sie eine DELETE-Anforderung an senden , bedeutet dies, dass Sie eine andere Ressource löschen, als wenn Sie DELETE an senden würden /foo?456.
Nicholas Shanks
@codeprogression Entschuldigung, aber vieles, was Sie sagen, ist falsch. Die Ressource wird durch den gesamten URI identifiziert, nicht nur durch den Pfad. Unterschiedliche Abfragezeichenfolgen sind unterschiedliche Ressourcen (im HTTP-Sinne des Wortes "Ressource"). Außerdem ist für eine einheitliche Schnittstelle kein Standardgrund erforderlich. Dieser Begriff bezieht sich auf die Verwendung von GET, PUT, POST, PATCH und DELETE, wie sie von HTTP definiert wurden. Die Gemeinsamkeit besteht zwischen Anbietern (Benutzeragentenanbieter, API-Anbieter, Caching-Proxy-Anbieter, ISPs usw.) und nicht innerhalb der eigenen API (obwohl auch dies für die Gesundheit der Benutzer einheitlich gestaltet sein sollte!).
Nicholas Shanks
@Nicholas Ich verstehe nicht, dass Sie eine Diskussion argumentieren müssen, die vor drei Jahren endete. Die Antwort und die Kommentare, die ich gemacht habe, sind aus REST-zentrierter Sicht gültig und korrekt. REST ist weder HTTP (noch eine Herstellerimplementierung von HTTP). Im Kontext von REST sind die Ressourcen gleich. Und wie ich in meiner Antwort sagte, ist REST kein Gesetz oder Dogma, sondern Führung.
Codeprogression
0

Ich schlage vor, Sie fügen die erforderlichen Metadaten als Teil der URI-Hierarchie selbst hinzu. Ein Beispiel (naiv):

Wenn Sie Einträge basierend auf einem Datumsbereich löschen müssen, anstatt das Start- und Enddatum im Hauptteil oder als Abfrageparameter zu übergeben, strukturieren Sie den URI so, dass Sie die erforderlichen Informationen als Teil des URI übergeben.

z.B

DELETE /entries/range/01012012/31122012 - Löschen Sie alle Einträge zwischen dem 1. Januar 2012 und dem 31. Dezember 2012

Hoffe das hilft.

Suresh Kumar
quelle
5
Gilt nicht für Fälle wie das Senden eines Löschgrunds, z. B. eines Kommentarfelds.
Kugel
3
Beeindruckend. Das ist eine schreckliche Idee. Wenn Sie zu viele Metadaten haben, werden die Größenbeschränkungen für URI aufgebläht.
Balaji Boggaram Ramanarayan
1
Dieser Ansatz folgt nicht den RESTful-Praktiken und wird nicht empfohlen, da Sie eine verschlungene URI-Struktur haben. Endpunkte werden durch die Verflechtung von Ressourcenidentifikation und Metadaten durcheinander gebracht und werden mit der Zeit zu einem Alptraum für die Wartung, wenn sich Ihre API ändert. Es ist viel bevorzugter, die rangein Abfrageparametern oder Nutzdaten angegebenen Werte zu haben, die das Kernstück dieser Frage sind: Um den Best-Practice-Ansatz für das Problem zu verstehen, würde ich sagen, dass dies nicht der Fall ist.
Digitaldreamer
@digitaldreamer - Ich verstehe nicht, was Sie unter verschlungener URI-Struktur verstehen? Dies ist auch ein HTTP-LÖSCHEN, sodass Nutzdaten keine Option sind, sondern Parameter abfragen.
Suresh Kumar