Ist ein Entitätskörper für eine HTTP-DELETE-Anforderung zulässig?

717

Bei der Ausgabe einer HTTP-DELETE-Anforderung sollte der Anforderungs-URI die zu löschende Ressource vollständig identifizieren. Ist es jedoch zulässig, zusätzliche Metadaten als Teil des Entitätstextes der Anforderung hinzuzufügen?

Gehackt
quelle
4
In ASP.NET WebApi 2 werden FromBody-Parameter für HttpDelete-Endpunkte ignoriert.
Jenny O'Reilly
2
Ich habe ähnliche Bedenken, aber mein Fall ist anders. Ich möchte eine Stapellöschanforderung ausgeben, wenn ich hundert Objekte löschen möchte. Sicherlich ist es eine große Leistungssteigerung für Netzwerke vor HTTP 2.0.
Singagirl
1
Wurden Änderungen an HTTP / 2 vorgenommen?
Jyotman Singh

Antworten:

570

Die Spezifikation verbietet oder entmutigt sie nicht ausdrücklich, daher würde ich eher sagen, dass sie zulässig ist.

Microsoft sieht das genauso (ich kann im Publikum ein Murmeln hören), heißt es im MSDN-Artikel über die DELETE-Methode von ADO.NET Data Services Framework :

Wenn eine DELETE-Anforderung einen Entitätskörper enthält, wird der Körper ignoriert [...]

Zusätzlich ist hier, was RFC2616 (HTTP 1.1) zu Anfragen zu sagen hat:

  • Ein Entitätskörper ist nur vorhanden, wenn ein Nachrichtentext vorhanden ist (Abschnitt 7.2).
  • Das Vorhandensein eines Nachrichtentexts wird durch die Aufnahme eines Content-Lengthoder eines Transfer-EncodingHeaders signalisiert (Abschnitt 4.3).
  • Ein Nachrichtentext darf nicht enthalten sein, wenn die Angabe der Anforderungsmethode das Senden eines Entitätstexts nicht zulässt (Abschnitt 4.3).
  • Ein Entity-Body ist ausdrücklich nur in TRACE-Anforderungen verboten, alle anderen Anforderungstypen sind nicht eingeschränkt (Abschnitt 9 und 9.8 speziell).

Für Antworten wurde Folgendes definiert:

  • Ob ein Nachrichtentext enthalten ist, hängt sowohl von der Anforderungsmethode als auch vom Antwortstatus ab (Abschnitt 4.3).
  • Ein Nachrichtentext ist in Antworten auf HEAD-Anfragen ausdrücklich verboten (Abschnitt 9 und 9.4 speziell).
  • Ein Nachrichtentext ist in 1xx (informativ), 204 (kein Inhalt) und 304 (nicht geändert) Antworten ausdrücklich verboten (Abschnitt 4.3).
  • Alle anderen Antworten enthalten einen Nachrichtentext, der jedoch möglicherweise keine Länge hat (Abschnitt 4.3).
Tomalak
quelle
7
@ Jason Auf jeden Fall. Sie können auch benutzerdefinierte Header verwenden, um zusätzliche Daten zu übergeben. Verwenden Sie jedoch den Anforderungshauptteil.
Tomalak
86
Obwohl die Spezifikation nicht verbietet, dass DELETE-Anforderungen einen Nachrichtentext haben, scheint Abschnitt 4.3 darauf hinzuweisen, dass der Text von Servern ignoriert werden sollte, da es keine "definierte Semantik" für DELETE- Entitätskörper gibt: "Ein Server sollte a lesen und weiterleiten Nachrichtentext bei jeder Anforderung; Wenn die Anforderungsmethode keine definierte Semantik für einen Entitätstext enthält, sollte der Nachrichtentext bei der Bearbeitung der Anforderung ignoriert werden . "
Shelley
72
Bitte beachten Sie, dass viele Kunden auch keine DELETE mit einem Body senden können. Das hat mich gerade auf Android verbrannt.
Karmic Coder
1
@ KarmicCoder: Großartiger Punkt. Weitere Informationen: Senden einer HTTP DELETE-Anfrage in Android .
MS Dousti
2
Viele Diskussionen über die Implementierung gemischt mit HTTP-Spezifikation. Clients implementieren die Dinge so, wie sie die Spezifikation interpretieren. Verwechseln Sie dies nicht mit der Bedeutung der Spezifikation. Tatsache ist, dass die Spezifikation dies mehrdeutig lässt. Ich bin mit der Interpretation nicht einverstanden, dass, da es keine definierte Semantik für den Entitätskörper gibt, die Implikation besteht, dass sie ignoriert werden sollte. Ich denke, die Leute arbeiten rückwärts von kundenspezifischen Interpretationen (Jersey, Android-Testclients usw.) und versuchen, die Interpretation zu rechtfertigen, anstatt zu versuchen, den Spezifikationen zu entsprechen. Menschen sind fehlbar.
Gibron
169

Das neueste Update der HTTP 1.1-Spezifikation ( RFC 7231 ) erlaubt explizit einen Entitätstext in einer DELETE-Anforderung:

Eine Nutzlast innerhalb einer DELETE-Anforderungsnachricht hat keine definierte Semantik. Das Senden eines Nutzdatenkörpers bei einer DELETE-Anforderung kann dazu führen, dass einige vorhandene Implementierungen die Anforderung ablehnen.

grzes
quelle
3
Die neueste nicht genehmigte Version der Spezifikation beseitigt diese Anforderung. Die letzte genehmigte Version ist immer noch der oben zitierte RFC2616.
BishopZ
4
Welche Version? Version 20 hat immer noch den gleichen Wortlaut wie Version 19, die ich oben verlinkt habe: "Körper in DELETE-Anforderungen haben keine definierte Semantik. Beachten Sie, dass das Senden eines Körpers in einer DELETE-Anforderung dazu führen kann, dass einige vorhandene Implementierungen die Anforderung ablehnen."
grzes
11
Version 26 schlägt vor, dass Sie einen Body zulassen können: Es wird 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.also eine Abwärtskompatibilitätswarnung angezeigt, die darauf hinweist, dass der nächste Standard lautet: 'yep! DELETEkann einen Körper haben`.
Pure.Krome
4
RFC 7231 Abschnitt 4.3.5 schließt die Sprache ab Version 26 mit ab A payload within a DELETE request message has no defined semantics. Also ist der Körper erlaubt.
Mndrix
6
Körper ist erlaubt, sollte aber für die Anfrage nicht relevant sein. Es macht absolut keinen Sinn, es zu benutzen.
Evert
54

Einige Versionen von Tomcat und Jetty scheinen einen Entity-Body zu ignorieren, wenn er vorhanden ist. Was ein Ärgernis sein kann, wenn Sie es erhalten möchten.

evan.leonard
quelle
2
Google App Engine instanziiert und übergibt eine leere Standardentität anstelle des Anforderungshauptteils.
Oliver Hausler
Weitere Informationen zu Tomcat: Wie Apache Tomcat die DELETE-Methode akzeptiert .
MS Dousti
50

Ein Grund für die Verwendung des Body in einer Löschanforderung ist die optimistische Parallelitätskontrolle.

Sie lesen Version 1 eines Datensatzes.

GET /some-resource/1
200 OK { id:1, status:"unimportant", version:1 }

Ihr Kollege liest Version 1 des Datensatzes.

GET /some-resource/1
200 OK { id:1, status:"unimportant", version:1 }

Ihr Kollege ändert den Datensatz und aktualisiert die Datenbank, wodurch die Version auf 2 aktualisiert wird:

PUT /some-resource/1 { id:1, status:"important", version:1 }
200 OK { id:1, status:"important", version:2 }

Sie versuchen, den Datensatz zu löschen:

DELETE /some-resource/1 { id:1, version:1 }
409 Conflict

Sie sollten eine optimistische Sperrausnahme erhalten. Lesen Sie den Datensatz erneut, stellen Sie sicher, dass er wichtig ist, und löschen Sie ihn möglicherweise nicht.

Ein weiterer Grund für die Verwendung besteht darin, mehrere Datensätze gleichzeitig zu löschen (z. B. ein Raster mit Kontrollkästchen für die Zeilenauswahl).

DELETE /messages
[{id:1, version:2},
{id:99, version:3}]
204 No Content

Beachten Sie, dass jede Nachricht eine eigene Version hat. Vielleicht können Sie mehrere Versionen mit mehreren Headern angeben, aber von George ist dies einfacher und viel bequemer.

Dies funktioniert in Tomcat (7.0.52) und Spring MVC (4.05), möglicherweise auch in früheren Versionen:

@RestController
public class TestController {

    @RequestMapping(value="/echo-delete", method = RequestMethod.DELETE)
    SomeBean echoDelete(@RequestBody SomeBean someBean) {
        return someBean;
    }
}
Neil McGuigan
quelle
15
Körper in GET (und DELETE) zu haben, misshandelt eindeutig HTTP und REST. Es gibt andere Mechanismen für den Umgang mit der Parallelitätskontrolle (z. B. If-Modified-Since und etags).
Bruno
19
Wie misshandelt es KLAR, wenn die Spezifikation den Körper in DELETE nicht verbietet?
Neil McGuigan
5
Weil du nichts mit dem Körper machen sollst. Siehe: stackoverflow.com/a/983458/372643
Bruno
14
Dies ist genau das gleiche Problem: Mit GET können Sie die Darstellung der durch den URI identifizierten Ressource abrufen, und mit DELETE wird die durch den URI identifizierte Ressource gelöscht. Verwenden Sie für andere Versionen einen anderen URI, wenn Sie bestimmte Versionen löschen möchten. Der URI sollte die einzige Kennung der Ressource in HTTP / REST sein. Verwenden Sie Metadaten in Headern, wenn Sie gleichzeitig arbeiten müssen (z. B. If-Unmodified-Sinceoder dafür Etagsind sie da).
Bruno
5
Verwenden Sie den ETag-Header anstelle eines Versionsfelds in einem Body
Malhal
26

Es scheint mir, dass RFC 2616 dies nicht spezifiziert.

Aus Abschnitt 4.3:

Das Vorhandensein eines Nachrichtentexts in einer Anforderung wird durch die Aufnahme eines Headerfelds für Inhaltslänge oder Übertragungscodierung in die Nachrichtenkopfzeilen der Anforderung signalisiert. Ein Nachrichtentext DARF NICHT in eine Anforderung aufgenommen werden, wenn die Angabe der Anforderungsmethode (Abschnitt 5.1.1) das Senden eines Entitätstexts in Anforderungen nicht zulässt. Ein Server sollte bei jeder 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.

Und Abschnitt 9.7:

Die DELETE-Methode fordert den Ursprungsserver auf, die durch den Request-URI identifizierte Ressource zu löschen. Diese Methode kann durch menschliches Eingreifen (oder auf andere Weise) auf dem Ursprungsserver überschrieben werden. Dem Client kann nicht garantiert werden, dass der Vorgang ausgeführt wurde, selbst wenn der vom Ursprungsserver zurückgegebene Statuscode anzeigt, dass die Aktion erfolgreich abgeschlossen wurde. Der Server sollte jedoch KEINEN Erfolg anzeigen, es sei denn, er beabsichtigt zum Zeitpunkt der Antwort, die Ressource zu löschen oder an einen unzugänglichen Ort zu verschieben.

Eine erfolgreiche Antwort sollte 200 (OK) sein, wenn die Antwort eine Entität enthält, die den Status beschreibt, 202 (Akzeptiert), wenn die Aktion noch nicht ausgeführt wurde, oder 204 (Kein Inhalt), wenn die Aktion ausgeführt wurde, die Antwort jedoch nicht enthält eine Entität.

Wenn die Anforderung einen Cache durchläuft und der Anforderungs-URI eine oder mehrere aktuell zwischengespeicherte Entitäten identifiziert, MÜSSEN diese Einträge als veraltet behandelt werden. Antworten auf diese Methode können nicht zwischengespeichert werden. C.

Es ist also nicht explizit erlaubt oder nicht erlaubt, und es besteht die Möglichkeit, dass ein Proxy auf dem Weg den Nachrichtentext entfernt (obwohl er ihn lesen und weiterleiten sollte).

Adam Rosenfield
quelle
19

Nur ein Kopf hoch, wenn Sie einen Text in Ihrer DELETE-Anfrage angeben und einen Google Cloud HTTPS-Load-Balancer verwenden, wird Ihre Anfrage mit einem 400-Fehler abgelehnt. Ich schlug meinen Kopf gegen eine Wand und stellte fest, dass Google aus irgendeinem Grund eine DELETE-Anfrage mit einem Körper für eine fehlerhafte Anfrage hält.

Ben Fried
quelle
1
for whatever reason- weil die Spezifikation so sagt: P
Mardoxx
20
Die Spezifikation "sagt es nicht", sondern nur, dass der Körper nicht spezifisch definiert ist. Wenn es nicht definiert ist und du es ignorieren willst, cool ... mach weiter und ignoriere es. Die vollständige Ablehnung der Anfrage erscheint jedoch extrem und unnötig.
Ben Fried
1
Verlassen Sie sich nicht auf undefiniertes Verhalten. Es ist eine ziemlich verbreitete Best Practice.
Evert
@Evert Es gibt explizit undefiniertes Verhalten (wie Sie es beispielsweise in den Spezifikationen der C-Sprache beschreiben sehen) und es gibt Verhalten, das zulässig, aber einfach nicht beschrieben ist. DELETELetzteres ist die Verwendung eines Nachrichtentexts .
Alnitak
9

Es ist erwähnenswert, dass die OpenAPI-Spezifikation für Version 3.0 die Unterstützung für DELETE-Methoden mit einem Body eingestellt hat:

siehe hier und hier für Referenzen

Dies kann sich in Zukunft auf Ihre Implementierung, Dokumentation oder Verwendung dieser APIs auswirken.

CleverPatrick
quelle
7

Es scheint, dass ElasticSearch dies verwendet: https://www.elastic.co/guide/en/elasticsearch/reference/5.x/search-request-scroll.html#_clear_scroll_api

Was bedeutet, dass Netty dies unterstützt.

Wie in den Kommentaren erwähnt, ist dies möglicherweise nicht mehr der Fall

Sebastien Lorber
quelle
1
Wenn Sie den Apache-http-Client verwenden, können Sie ganz einfach Ihre eigenen Versionen von GET und DELETE erstellen, indem Sie HttpEntityEnclosingRequestBase erweitern und die Methode getMethod () GET oder DELETE zurückgeben lassen. Wir verwenden dies, um mit elasticsearch zu sprechen.
Jilles van Gurp
2
toter Link - großartig. Wir brauchen mehr von diesen
Linkantworten
3
Die verknüpfte Dokumentation enthält jetzt nur POST-Anforderungen, keine DELETEs. Könnte es sich lohnen, dieser Antwort eine Notiz hinzuzufügen?
Dshepherd
Elasticsearch verwendet body auch mit GET-Anforderungen.
Nidhin David
7

Roy Fielding auf der HTTP-Mailingliste stellt klar, dass auf der http-Mailingliste https://lists.w3.org/Archives/Public/ietf-http-wg/2020JanMar/0123.html :

Es ist absolut verboten, dass GET / DELETE-Körper irgendwelche Auswirkungen auf die Verarbeitung oder Interpretation der Anfrage haben

Dies bedeutet, dass der Body das Verhalten des Servers nicht ändern darf. Dann fügt er hinzu:

abgesehen von der Notwendigkeit, die empfangenen Bytes zu lesen und zu verwerfen, um den Nachrichtenrahmen aufrechtzuerhalten.

Und schließlich der Grund, den Körper nicht zu verbieten:

Der einzige Grund, warum wir das Senden eines Körpers nicht verboten haben, ist, dass dies zu verzögerten Implementierungen führen würde, vorausgesetzt, dass kein Körper gesendet wird.

Während Clients den Payload-Body senden können, sollten Server ihn löschen und APIs sollten bei diesen Anforderungen keine Semantik für den Payload-Body definieren.

Roberto Polli
quelle
5

Dies ist nicht definiert .

Eine Nutzlast innerhalb einer DELETE-Anforderungsnachricht hat keine definierte Semantik. Das Senden eines Nutzdatenkörpers bei einer DELETE-Anforderung kann dazu führen, dass einige vorhandene Implementierungen die Anforderung ablehnen.
https://tools.ietf.org/html/rfc7231#page-29

Simon Jin
quelle
Insbesondere RFC 7231 Abschnitt 4.3.5
Mndrix
3
Dieses genaue Zitat war bereits in früheren Antworten enthalten, diese Antwort sollte gelöscht werden.
Madbreaks
5

Die Verwendung von DELETE mit einem Body ist riskant ... Ich bevorzuge diesen Ansatz für Listenoperationen gegenüber REST:

Regelmäßiger Betrieb

GET / objects / Ruft alle Objekte ab

GET / object / ID Ruft ein Objekt mit der angegebenen ID ab

POST / Objekte Fügt ein neues Objekt hinzu

PUT / Objekt / ID Fügt ein Objekt mit der angegebenen ID hinzu und aktualisiert ein Objekt

LÖSCHEN / Objekt / ID Löscht das Objekt mit der angegebenen ID

Alle benutzerdefinierten Aktionen sind POST

POST / objects / addList Fügt eine Liste oder ein Array von Objekten hinzu, die im Hauptteil enthalten sind

POST / objects / deleteList Löscht eine Liste der im Hauptteil enthaltenen Objekte

POST / objects / customQuery Erstellt eine Liste basierend auf einer benutzerdefinierten Abfrage im Hauptteil

Wenn ein Client Ihre erweiterten Vorgänge nicht unterstützt, kann er normal arbeiten.

Elieser Garza
quelle
Die Verwendung von a POSTist keine gute RESTy-Methode zum Erstellen neuer Ressourcen, da die Semantik von POST-Antworten unklar ist, insbesondere im Zusammenhang mit Standortheadern. Sie lassen HTTP im Wesentlichen hinter sich und stapeln RPC oben drauf. Der richtige "HTTP / REST-Weg" besteht darin, Ressourcen mit PUTdem If-None-Match: *Header zu erstellen (oder geeignete HTTP-Methoden anzugeben, siehe MKCOLusw.).
hnh
4

Ich glaube nicht, dass eine gute Antwort darauf veröffentlicht wurde, obwohl es viele großartige Kommentare zu vorhandenen Antworten gab. Ich werde den Kern dieser Kommentare in eine neue Antwort umwandeln:

Dieser Absatz aus RFC7231 wurde einige Male zitiert, was es zusammenfasst.

Eine Nutzlast innerhalb einer DELETE-Anforderungsnachricht hat keine definierte Semantik. Das Senden eines Nutzdatenkörpers bei einer DELETE-Anforderung kann dazu führen, dass einige vorhandene Implementierungen die Anforderung ablehnen.

Was ich bei den anderen Antworten vermisst habe, war die Implikation. Ja, es ist erlaubt, einen Text in DELETEAnfragen aufzunehmen, aber es ist semantisch bedeutungslos. Dies bedeutet wirklich, dass das Ausgeben einer DELETEAnforderung mit einem Anforderungshauptteil semantisch dem Nichteinschließen eines Anforderungshauptteils entspricht.

Das Einschließen eines Anforderungshauptteils sollte keine Auswirkungen auf die Anforderung haben, daher macht es keinen Sinn, sie einzuschließen.

tl; dr: Technisch gesehen ist eine DELETEAnfrage mit einem Anfragetext zulässig, dies ist jedoch niemals sinnvoll.

Evert
quelle
2
"semantisch bedeutungslos" bedeutet nicht dasselbe wie "hat keine definierte Semantik". Ersteres bedeutet , dass es nicht kann irgendeine Bedeutung haben. Letzteres bedeutet einfach, dass der RFC selbst nicht angibt, wie diese Semantik aussehen könnte. (Ich schreibe RFCs)
Alnitak
1
Mit anderen Worten, wenn der Implementierer einer API eine Semantik für sich selbst definieren möchte, steht es ihm völlig frei, dies zu tun.
Alnitak
1
@Alnitak das ist definitiv eine Fehlinterpretation. Nach dieser Definition hat jeder HTTP-Anforderungshauptteil keine definierte Semantik, aber DELETE und GET werden in der Spezifikation speziell aufgerufen. Hier ist ein Ausschnitt aus einem noch zu veröffentlichenden Entwurf, der speziell über die GET-Anfrage spricht:
Evert
1
Ich bin nicht anderer Meinung als Sie, dass dies in den derzeit veröffentlichten RFCs unklar ist, aber wenn Sie mir nicht glauben, würde ich Sie einladen, die Autoren nach ihrer Absicht für DELETE und GET zu fragen. Sie werden feststellen, dass es mit meiner Antwort übereinstimmt. Wie Sie bin ich auch in Normungsgremien involviert und nicht nur eine Einzelperson mit einer Meinung darüber, wie der RFC interpretiert werden sollte.
Evert
2
Wenn dies der Fall ist, ist 7231 schlecht formuliert und hätte sagen sollen, dass der Nutzlastkörper ignoriert werden muss. Auf welchen Entwurf beziehen Sie sich oben?
Alnitak
3

Falls jemand auf dieses Problem stößt, wird es nicht allgemein unterstützt.

Ich teste derzeit mit Sahi Pro und es ist sehr offensichtlich, dass ein http DELETE-Aufruf alle bereitgestellten Körperdaten entfernt (eine große Liste von IDs, die gemäß dem Endpunktdesign in großen Mengen gelöscht werden sollen).

Ich war mehrere Male mit ihnen in Kontakt und habe drei separate Pakete mit Skripten, Bildern und Protokollen gesendet, damit sie sie überprüfen können, und sie haben dies immer noch nicht bestätigt. Ein fehlgeschlagener Patch und verpasste Telefonkonferenzen durch ihren Support später, und ich habe immer noch keine solide Antwort erhalten.

Ich bin mir sicher, dass Sahi dies nicht unterstützt, und ich würde mir vorstellen, dass viele andere Tools der Suite folgen.

Parker
quelle
Es ist in der neuesten Version von Sahi Pro implementiert. Da Sahi Java verwendet, um HTTP-Aufrufe zu tätigen, und Java vor Version 1.8 einen Fehler hatte, konnte der Benutzer keine DELETE-Anfrage stellen. Ab Java 1.8 und Sahi Pro 6.1.1 (in Kürze verfügbar) können Benutzer in Sahi eine DELETE-Anfrage mit body stellen.
Vivek V Dwivedi
-1

Möglicherweise hilft Ihnen die unten stehende GitHUb-URL, die Antwort zu erhalten. Tatsächlich lehnt Application Server wie Tomcat, Weblogic den Aufruf von HTTP.DELETE mit Anforderungsnutzdaten ab. Unter Berücksichtigung all dieser Dinge habe ich ein Beispiel in Github hinzugefügt. Bitte schauen Sie sich das an

https://github.com/ashish720/spring-examples

Ashish
quelle
-1

Ich konnte die DELETE-Operation mit einem Anforderungshauptteil implementieren. Ich habe AWS Lambda und AWS API Gateway verwendet und die Sprache Go verwendet.

Dattatray
quelle
3
Sie sprechen von Standards, nicht von der Fähigkeit. Sie können sogar eine GET-Anfrage mit body haben, was nicht gut ist
ReZa