HTTP GET mit Anforderungshauptteil

2110

Ich entwickle einen neuen RESTful-Webservice für unsere Anwendung.

Bei einem GET für bestimmte Entitäten können Clients den Inhalt der Entität anfordern. Wenn sie einige Parameter hinzufügen möchten (z. B. eine Liste sortieren), können sie diese Parameter in die Abfragezeichenfolge einfügen.

Alternativ möchte ich, dass die Benutzer diese Parameter im Anforderungshauptteil angeben können. HTTP / 1.1 scheint dies nicht ausdrücklich zu verbieten. Auf diese Weise können sie mehr Informationen angeben und möglicherweise komplexe XML-Anforderungen leichter angeben.

Meine Fragen:

  • Ist das insgesamt eine gute Idee?
  • Haben HTTP-Clients Probleme mit der Verwendung von Anforderungskörpern innerhalb einer GET-Anforderung?

http://tools.ietf.org/html/rfc2616

Evert
quelle
552
Der Vorteil besteht darin, dass XML- oder JSON-Anforderungskörper problemlos gesendet werden können, keine Längenbeschränkung besteht und die Codierung einfacher ist (UTF-8).
Evert
29
Wenn Sie nach einer sicheren und idempotenten Methode suchen, die Anforderungskörper zulässt, sollten Sie sich SEARCH, PROPFIND und REPORT ansehen. Wenn Sie GET nicht verwenden und einen Anfragetext haben, wird das Caching mehr oder weniger besiegt.
Julian Reschke
226
@fijiaaron: Es ist 3 Jahre später und seitdem habe ich umfangreiche Erfahrung mit dem Schreiben von Webservices. Es ist im Grunde alles, was ich in den letzten Jahren getan habe. Ich kann mit Sicherheit sagen, dass es in der Tat eine sehr schlechte Idee ist, einer GET-Anfrage einen Body hinzuzufügen. Die beiden besten Antworten stehen wie ein Stein.
Evert
26
@Ellesedil: Einfach ausgedrückt: Welche Vorteile die Verwendung von GET gegenüber POST bietet, liegt in der Gestaltung von HTTP. Diese Vorteile bestehen nicht mehr, wenn Sie auf diese Weise gegen den Standard verstoßen. Daher gibt es nur noch einen Grund, GET + einen Anforderungshauptteil anstelle von POST zu verwenden: Ästhetik. Verzichten Sie nicht auf robustes Design gegenüber Ästhetik.
Evert
7
Um zu unterstreichen, was Evert sagte: "Es gibt keine Längenbeschränkung". Wenn Ihr GET mit Abfrageparametern die Längenbeschränkung (von 2048) durchbricht, gibt es keine andere Wahl, als die Abfragezeichenfolgeninformationen in ein JSON-Objekt einzufügen, z. B. in den Hauptteil der Anforderung.
Kieran Ryan

Antworten:

1725

Roy Fieldings Kommentar zum Einfügen eines Körpers mit einer GET-Anfrage .

Ja. Mit anderen Worten, jede HTTP-Anforderungsnachricht darf einen Nachrichtentext enthalten und muss daher Nachrichten unter diesem Gesichtspunkt analysieren. Die Serversemantik für GET ist jedoch so eingeschränkt, dass ein etwaiger Textkörper keine semantische Bedeutung für die Anforderung hat. Die Anforderungen an das Parsen unterscheiden sich von den Anforderungen an die Methodensemantik.

Also, ja, Sie können einen Körper mit GET senden, und nein, es ist niemals nützlich, dies zu tun.

Dies ist Teil des mehrschichtigen Designs von HTTP / 1.1, das nach der Partitionierung der Spezifikation (in Arbeit) wieder deutlich wird.

.... Roy

Ja, Sie können einen Anfragetext mit GET senden, dieser sollte jedoch keine Bedeutung haben. Wenn Sie ihm eine Bedeutung geben, indem Sie ihn auf dem Server analysieren und Ihre Antwort basierend auf seinem Inhalt ändern , ignorieren Sie diese Empfehlung in der HTTP / 1.1-Spezifikation, Abschnitt 4.3 :

[...] , wenn die Anforderung Methode nicht definiert Semantik enthält für eine Entität-Körper, dann ist die Nachricht Körper sollten ignoriert werden , wenn die Anforderung bearbeitet wird .

Und die Beschreibung der GET-Methode in der HTTP / 1.1-Spezifikation, Abschnitt 9.3 :

Die GET-Methode bedeutet, dass alle Informationen ([...]) abgerufen werden, die durch den Request-URI identifiziert werden.

Dies besagt, dass der Anforderungshauptteil nicht Teil der Identifizierung der Ressource in einer GET-Anforderung ist, sondern nur der Anforderungs-URI.

Update Der als "HTTP / 1.1-Spezifikation" bezeichnete RFC2616 ist jetzt veraltet. Im Jahr 2014 wurde es durch RFCs 7230-7237 ersetzt. Zitat "Der Nachrichtentext sollte bei der Bearbeitung der Anfrage ignoriert werden" wurde gelöscht. Es ist jetzt nur noch "Anforderungsnachrichten-Framing ist unabhängig von der Methodensemantik, auch wenn die Methode keine Verwendung für einen Nachrichtentext definiert". Das zweite Anführungszeichen "Die GET-Methode bedeutet, alle Informationen abzurufen, die durch den Anforderungs-URI identifiziert werden." wurde gelöscht. - Aus einem Kommentar

Paul Morgan
quelle
71
Caching / Proxy sind die beiden Dinge, die Sie am wahrscheinlichsten brechen, ja. "Semantik" ist nur eine andere Art zu sagen, "wie Leute, die andere Komponenten herstellen, erwarten, dass andere Komponenten funktionieren". Wenn Sie gegen die Semantik verstoßen, ist es wahrscheinlicher, dass Dinge an Stellen kaputt gehen, an denen Leute Dinge geschrieben haben, von denen erwartet wurde, dass Sie diese Semantik einhalten.
Stuart P. Bentley
108
Elasticsearch ist ein ziemlich wichtiges Produkt, das HTTP-Anforderungskörper in GET verwendet. Laut ihrem Handbuch ist nicht definiert, ob eine HTTP-Anfrage das Vorhandensein eines Körpers unterstützen soll oder nicht. Ich persönlich mag es nicht, einen GET-Anfragetext zu füllen, aber sie scheinen eine andere Meinung zu haben und müssen wissen, was sie tun. elastic.co/guide/en/elasticsearch/guide/current/…
GordonM
25
@iwein, das GET-Anforderungsstellen eine Bedeutung gibt, ist in der Tat keine Verletzung der Spezifikation. HTTP / 1.1 gibt an, dass Server den Body ignorieren sollten, RFC 2119 gibt jedoch an, dass Implementierer "SHOULD" -Klauseln ignorieren dürfen, wenn sie einen guten Grund dafür haben. Vielmehr ein Client tut die Spezifikation verstoßen , wenn sie davon ausgehen , dass der GET - Körper ändert , wird nicht die Antwort ändern.
Emil Lundberg
107
Der als "HTTP / 1.1-Spezifikation" bezeichnete RFC2616 ist jetzt veraltet. Im Jahr 2014 wurde es durch RFCs 7230-7237 ersetzt. Zitat " Der Nachrichtentext sollte bei der Bearbeitung der Anfrage ignoriert werden " wurde gelöscht . Es ist jetzt nur noch " Anforderungsnachrichten-Framing ist unabhängig von der Methodensemantik, auch wenn die Methode keine Verwendung für einen Nachrichtentext definiert ". Das zweite Anführungszeichen " Die GET-Methode bedeutet, alle Informationen abzurufen, die durch den Anforderungs-URI identifiziert werden. " wurde gelöscht . Also, ich schlage vor, die Antwort @Jarl
Artem Nakonechny
28
Ich weiß, dass es ein alter Faden ist - ich bin darauf gestoßen. @Artem Nakonechny ist technisch korrekt, aber in der neuen Spezifikation heißt es: "Eine Nutzlast in einer GET-Anforderungsnachricht hat keine definierte Semantik. Das Senden eines Nutzdatenkörpers auf eine GET-Anforderung kann dazu führen, dass einige vorhandene Implementierungen die Anforderung ablehnen." Es ist also immer noch keine wirklich gute Idee, wenn dies vermieden werden kann.
Fastcatch
289

Sie können dies zwar tun, sofern dies in der HTTP-Spezifikation nicht ausdrücklich ausgeschlossen ist. Ich würde jedoch empfehlen, dies zu vermeiden, nur weil die Leute nicht erwarten, dass die Dinge so funktionieren. Es gibt viele Phasen in einer HTTP-Anforderungskette, und obwohl sie "meistens" der HTTP-Spezifikation entsprechen, können Sie nur sicher sein, dass sie sich wie traditionell von Webbrowsern verwendet verhalten. (Ich denke an Dinge wie transparente Proxys, Beschleuniger, A / V-Toolkits usw.)

Dies ist der Geist hinter dem Robustheitsprinzip, das ungefähr "liberal sein in dem, was Sie akzeptieren, und konservativ in dem, was Sie senden". Sie möchten die Grenzen einer Spezifikation nicht ohne guten Grund überschreiten.

Wenn Sie jedoch einen guten Grund haben, versuchen Sie es.

Caskey
quelle
228
Das Robustheitsprinzip ist fehlerhaft. Wenn Sie liberal in dem sind, was Sie akzeptieren, werden Sie Mist bekommen, wenn Sie Erfolg in Bezug auf Adoption haben, nur weil Sie Mist akzeptieren. Dadurch wird es für Sie schwieriger, Ihre Benutzeroberfläche weiterzuentwickeln. Schauen Sie sich einfach HTML an. Das ist das Reboustness-Prinzip in Aktion.
Eugene Beresovsky
27
Ich denke, der Erfolg und die Breite der Annahme (und des Missbrauchs) der Protokolle sprechen für den Wert des Robustheitsprinzips.
Caskey
39
Haben Sie jemals versucht, echtes HTML zu analysieren? Es ist nicht machbar, es selbst zu implementieren, deshalb haben fast alle - einschließlich der wirklich großen Player wie Google (Chrome) und Apple (Safari) - dies nicht getan, sondern sich auf vorhandene Implementierungen verlassen (am Ende haben sich alle auf KDEML von KDE verlassen). Diese Wiederverwendung ist natürlich nett, aber haben Sie versucht, HTML in einer .net-Anwendung anzuzeigen? Es ist ein Albtraum, da Sie entweder eine nicht verwaltete IE-Komponente (oder eine ähnliche Komponente) mit ihren Problemen und Abstürzen einbetten müssen oder die verfügbare (auf Codeplex) verwaltete Komponente verwenden, mit der Sie nicht einmal Text auswählen können.
Eugene Beresovsky
6
Die HTTP-Spezifikation erlaubt nicht nur Body-Daten mit GET-Anforderung, sondern dies ist auch gängige Praxis: Die _search-API der beliebten ElasticSearch-Engine empfiehlt GET-Anforderungen mit der in einem JSON-Body angehängten Abfrage. Als Zugeständnis an unvollständige HTTP-Client-Implementierungen werden hier auch POST-Anforderungen zugelassen.
Christian Pietsch
3
@ChristianPietsch, das ist heute üblich. Vor vier Jahren war es nicht. Während die Spezifikation einem Client ausdrücklich erlaubt, optional eine Entität in eine Anforderung aufzunehmen (MAI) (Abschnitt 7), ist die Bedeutung von MAI in RFC2119 definiert, und ein (beschissener) Proxyserver könnte spezifikationskonform sein, während Entitäten in GET-Anforderungen entfernt werden. Insbesondere solange es nicht abstürzt, kann es durch Weiterleiten der Anforderungsheader und nicht der enthaltenen Entität eine "reduzierte Funktionalität" bereitstellen. Ebenso gibt es eine Vielzahl von Regeln darüber, welche Versionsänderungen beim Proxy zwischen verschiedenen Protokollebenen vorgenommen werden MÜSSEN / KÖNNEN / SOLLTEN.
Caskey
151

Sie werden wahrscheinlich auf Probleme stoßen, wenn Sie jemals versuchen, das Caching zu nutzen. Proxies werden nicht im GET-Body nachsehen, ob die Parameter einen Einfluss auf die Reaktion haben.

Darrel Miller
quelle
10
Die Verwendung von ETag / Last-Modified-Headerfeldern hilft auf folgende Weise: Wenn ein "bedingtes GET" verwendet wird, können die Proxys / Caches auf diese Informationen reagieren.
Jldupont
2
@jldupont-Caches verwenden das Vorhandensein von Validatoren, um festzustellen, ob eine veraltete Antwort erneut validiert werden kann. Sie werden jedoch nicht als Teil des primären oder sekundären Cache-Schlüssels verwendet.
Darrel Miller
Sie können dies mit einer Prüfsumme des Körpers in einem Abfrageparameter beheben
Adrian
73

Weder der Restclient noch die REST-Konsole unterstützen dies, Curl jedoch.

Die HTTP-Spezifikation sagt in Abschnitt 4.3

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.

Abschnitt 5.1.1 leitet uns zu Abschnitt 9.x für die verschiedenen Methoden weiter. Keiner von ihnen verbietet ausdrücklich die Aufnahme eines Nachrichtentexts. Jedoch...

Abschnitt 5.2 sagt

Die genaue Ressource, die durch eine Internetanforderung identifiziert wird, wird ermittelt, indem sowohl der Anforderungs-URI als auch das Host-Headerfeld untersucht werden.

und Abschnitt 9.3 sagt

Die GET-Methode bedeutet, dass alle Informationen (in Form einer Entität) abgerufen werden, die durch den Request-URI identifiziert werden.

Die zusammen legen nahe , dass , wenn eine GET - Anfrage ist , ein Server nicht erforderlich , etwas anderes zu prüfen , dass der Request-URI und Host - Header - Feld.

Zusammenfassend lässt sich sagen, dass die HTTP-Spezifikation Sie nicht daran hindert, einen Nachrichtentext mit GET zu senden, aber es gibt genügend Unklarheiten, die mich nicht wundern würden, wenn er nicht von allen Servern unterstützt würde.

Dave Durbin
quelle
2
Paw hat auch die Option, GET-Anfragen mit Körpern zu unterstützen, muss jedoch in den Einstellungen aktiviert sein.
Daniel
"Die GET-Methode bedeutet, dass alle Informationen (in Form einer Entität) abgerufen werden, die durch den Request-URI identifiziert werden." Ist es dann technisch illegal / falsch, einen GET-Endpunkt zu haben, der alle Entitäten abruft? ZB GET /contacts/100/addressesgibt eine Sammlung von Adressen für die Person mit zurück id=100.
Josh M.
Die versicherte Java-Bibliothek zum Testen von REST-APIs unterstützt keine GET-Anforderung mit einem Body. Apache HttpClient unterstützt dies ebenfalls nicht.
Paulo Merson
Django unterstützt auch das Parsen eines GET-Körpers
Bruno Finger
60

Elasticsearch akzeptiert GET-Anfragen mit einem Body. Es scheint sogar, dass dies der bevorzugte Weg ist: Elasticsearch-Handbuch

Einige Client-Bibliotheken (wie der Ruby-Treiber) können den Befehl cry im Entwicklungsmodus in stdout protokollieren und verwenden diese Syntax häufig.

jlecour
quelle
5
Ich habe mich gefragt, warum Elasticsearch dies zulässt. Das bedeutet, dass diese Abfrage zum Zählen aller Dokumente mit Nutzdaten zu einer GET-Anforderung curl -XGET 'http://localhost:9200/_count?pretty' -d ' { "query": { "match_all": {} } }' dem Einschließen der Nutzdaten als sourceParameter entspricht: curl -XGET 'http://localhost:9200/_count?pretty&source=%7B%22query%22%3A%7B%22match_all%22%3A%7B%7D%7D%7D'
Arun
40
Komplexe Abfragen können die maximale Länge des http-Headers erreichen.
Daniel
11
Es war das Lesen der Elasticsearch-Dokumentation, die mich zu dieser Frage führte, da ich dachte, es sei eine schlechte Praxis, einen Körper aufzunehmen
PatrickWalker
1
Es muss nicht einmal eine komplexe Abfrage sein. Selbst ein einfacher Bildlauf kann eine sehr lange scroll_id (in einem Cluster mit vielen Shards) zurückgeben, was dazu führt, dass die maximale URL-Länge überschritten wird, wenn sie dort hinzugefügt wird.
Brent Hronik
11
Elasticsearch unterstützt dieselbe Anforderung mithilfe von POST. Sie haben sich nur dafür entschieden, einen Body in einem GET zuzulassen, weil sie der Meinung waren, dass ein GET beim Abfragen von Daten semantisch korrekter ist als ein POST. Es ist lustig, dass Elasticsearch in diesem Thread so oft erwähnt wird. Ich würde kein Beispiel (wenn auch von einem beliebten Produkt) als Grund verwenden, der Praxis zu folgen.
DSO
33

Was Sie erreichen möchten, wurde lange Zeit mit einer viel häufigeren Methode durchgeführt, die nicht auf der Verwendung einer Nutzlast mit GET beruht.

Sie können einfach Ihren spezifischen Suchmediatyp erstellen oder, wenn Sie REST-fähiger sein möchten, OpenSearch verwenden und die Anforderung an den URI senden, den der Server angewiesen hat, z. B. / search. Der Server kann dann das Suchergebnis generieren oder den endgültigen URI erstellen und mit einem 303 umleiten.

Dies hat den Vorteil, dass die traditionelle PRG-Methode angewendet wird, Vermittler die Ergebnisse zwischenspeichern können usw.

Das heißt, URIs werden sowieso für alles codiert, was nicht ASCII ist, ebenso wie application / x-www-form-urlencoded und multipart / form-data. Ich würde empfehlen, dies zu verwenden, anstatt ein weiteres benutzerdefiniertes JSON-Format zu erstellen, wenn Sie ReSTful-Szenarien unterstützen möchten.

SerialSeb
quelle
4
Sie können einfach Ihren spezifischen Suchmediatyp erstellen. Könnten Sie das näher erläutern?
Piotr Dobrogost
2
Damit sagte ich, dass Sie einen Medientyp namens application / vnd.myCompany.search + json erstellen könnten, der die Art von Suchvorlage enthält, die ein Client ausgeben soll, und der Client könnte diese dann als POST senden. Wie ich bereits erwähnt habe, gibt es dafür bereits einen Medientyp mit dem Namen OpenSearch. Die Wiederverwendung eines vorhandenen Medientyps sollte über der benutzerdefinierten Route ausgewählt werden, wenn Sie Ihr Szenario mit vorhandenen Standards implementieren können.
SerialSeb
14
Das ist klug, aber zu komplex und ineffizient. Jetzt müssen Sie einen POST mit Ihren Suchkriterien senden, einen URI als Antwort von Ihrem POST zurückerhalten, dann einen GET mit dem Suchkriterien-URI an den Server senden, damit dieser die Kriterien abruft und das Ergebnis an Sie zurücksendet. (Abgesehen davon, dass das Einfügen eines URI in einen URI technisch unmöglich ist, da Sie nicht etwas senden können, das bis zu 255 Zeichen innerhalb von etwas enthalten kann, das nicht mehr als 255 Zeichen enthalten darf. Daher müssen Sie dann einen Teilbezeichner und Ihren Server verwenden muss wissen, wie man den URI für Ihre POSTed-Suchkriterien auflöst.)
Fijiaaron
30

Sie können entweder ein GET mit einem Körper senden oder einen POST senden und die RESTish-Religiosität aufgeben (es ist nicht so schlimm, vor 5 Jahren gab es nur ein Mitglied dieses Glaubens - seine Kommentare sind oben verlinkt).

Es sind auch keine guten Entscheidungen, aber das Senden eines GET-Körpers kann Probleme für einige Clients - und einige Server - verhindern.

Das Ausführen eines POST kann bei einigen RESTish-Frameworks zu Hindernissen führen.

Julian Reschke schlug oben vor, einen nicht standardmäßigen HTTP-Header wie "SEARCH" zu verwenden, der eine elegante Lösung sein könnte, mit der Ausnahme, dass er noch weniger unterstützt wird.

Es ist möglicherweise am produktivsten, Clients aufzulisten, die die oben genannten Aufgaben ausführen können und nicht.

Clients, die kein GET mit body senden können (von denen ich weiß):

  • XmlHTTPRequest Fiddler

Clients, die ein GET mit body senden können:

  • die meisten Browser

Server und Bibliotheken, die einen Body von GET abrufen können:

  • Apache
  • PHP

Server (und Proxys), die einen Body von GET entfernen:

  • ?
Fidschiaaron
quelle
2
Squid 3.1.6 entfernt auch GET-Körper, wenn Content-Length 0 oder nicht festgelegt ist, und sendet andernfalls eine HTTP 411-Länge zurück, die erforderlich ist, obwohl die Länge festgelegt ist
rkok
2
Fiddler wird, aber es warnt dich.
Toddmo
Wollen Sie damit sagen, dass eine SEARCHMethode möglicherweise auf dem Weg brechen würde? Wenn Proxies eine Methode nicht verstehen, wird von ihnen erwartet, dass sie sie so weitergeben, wie sie ist. Ich bin mir also nicht sicher, warum Sie glauben, dass sie irgendetwas kaputt machen würde ...
Alexis Wilke
22

Ich habe diese Frage an die IETF HTTP WG gestellt. Der Kommentar von Roy Fielding (Autor des http / 1.1-Dokuments von 1998) war der folgende

"... eine Implementierung wäre fehlerhaft, um etwas anderes zu tun, als diesen Körper zu analysieren und zu verwerfen, wenn er empfangen wird."

In RFC 7213 (HTTPbis) heißt es:

"Eine Nutzlast in einer GET-Anforderungsnachricht hat keine definierte Semantik."

Es scheint jetzt klar zu sein, dass die Absicht bestand, dass die semantische Bedeutung von GET-Anforderungskörpern verboten ist, was bedeutet, dass der Anforderungskörper nicht verwendet werden kann, um das Ergebnis zu beeinflussen.

Es gibt Proxies da draußen, die definitiv werden die Ihre Anfrage auf verschiedene Weise brechen, wenn Sie eine Stelle in GET aufnehmen.

Also zusammenfassend, tu es nicht.

Adrien
quelle
21

Welcher Server wird es ignorieren? - Fidschiaaron 30. August 12 um 21:27 Uhr

Google zum Beispiel geht es schlechter als es zu ignorieren, es wird es als Fehler betrachten !

Probieren Sie es selbst mit einem einfachen Netcat:

$ netcat www.google.com 80
GET / HTTP/1.1
Host: www.google.com
Content-length: 6

1234

(Auf den 1234-Inhalt folgt CR-LF, das sind also insgesamt 6 Bytes.)

und du wirst bekommen:

HTTP/1.1 400 Bad Request
Server: GFE/2.0
(....)
Error 400 (Bad Request)
400. That’s an error.
Your client has issued a malformed or illegal request. That’s all we know.

Sie erhalten auch 400 schlechte Anfragen von Bing, Apple usw., die von AkamaiGhost bedient werden.

Daher würde ich nicht empfehlen, GET-Anforderungen mit einer Body-Entität zu verwenden.

user941239
quelle
65
Dieses Beispiel ist sinnlos, da normalerweise, wenn GETBenutzer Anforderungen Text hinzufügen , dies darauf zurückzuführen ist, dass ihr eigener benutzerdefinierter Server damit umgehen kann. Die Frage ist also, ob die anderen "beweglichen Teile" (Browser, Caches usw.) ordnungsgemäß funktionieren.
Pacerier
6
Dies ist eine schlechte Anfrage, da Ihre Nutzlast für einen GET bestimmten Endpunkt nicht erwartet (oder sinnvoll) ist - dies hat nichts mit der Verwendung GETim allgemeinen Fall zu tun . Eine zufällige Nutzlast könnte eine POSTebenso leicht brechen und dieselbe zurückgeben 400 Bad Request, wenn der Inhalt nicht in einem Format vorliegt, das im Kontext der spezifischen Anforderung sinnvoll ist.
Nobar
Und das nicht nur auf diesem Endpunkt als Ganzes, sondern auf dieser bestimmten URL .
Lawrence Dol
1
Dies ist irrelevant, da es sich nur um die Serverimplementierung von Google unter dieser URL handelt. Es macht also keinen Sinn auf die Frage
Joel Duckworth
Für mich war es nützlich, da ich versucht habe, Firebase-Funktionen mit einem get request + body zu verwenden, und dieser Fehler kann sehr kryptisch und schwer zu verstehen sein.
Scrimau
19

Aus RFC 2616, Abschnitt 4.3 , "Nachrichtentext":

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.

Das heißt, Server sollten immer einen bereitgestellten Anforderungshauptteil aus dem Netzwerk lesen (überprüfen Sie die Inhaltslänge oder lesen Sie einen Teilkörper usw.). Außerdem sollten Proxies jede solche Anforderungsstelle weiterleiten, die sie erhalten. Wenn der RFC dann die Semantik für den Body für die angegebene Methode definiert, kann der Server den Anfragetext tatsächlich zum Generieren einer Antwort verwenden. Wenn der RFC dies jedoch nicht tut Semantik für den Body definiert, sollte der Server diese ignorieren.

Dies steht im Einklang mit dem obigen Zitat von Fielding.

Abschnitt 9.3 , "GET", beschreibt die Semantik der GET-Methode und erwähnt keine Anforderungskörper. Daher sollte ein Server jeden Anforderungshauptteil ignorieren, den er bei einer GET-Anforderung empfängt.

izrik
quelle
In Abschnitt 9.5 , "POST", werden auch keine Anforderungskörper erwähnt, daher ist diese Logik fehlerhaft.
CarLuva
9
@CarLuva Der POST-Abschnitt sagt "Die POST-Methode wird verwendet, um anzufordern, dass der Ursprungsserver die eingeschlossene Entität akzeptiert ..." Der Entitätskörperabschnitt sagt "Der Entitätskörper wird vom Nachrichtentext erhalten ..." Der POST-Abschnitt erwähnt den Nachrichtentext, obwohl indirekt auf den Entitätstext verwiesen wird, der vom Nachrichtentext der POST-Anforderung übertragen wird.
Frederickickf
9

Laut XMLHttpRequest ist es nicht gültig. Aus dem Standard :

4.5.6 Die send()Methode

client . send([body = null])

Initiiert die Anfrage. Das optionale Argument stellt den Anforderungshauptteil bereit. Das Argument wird ignoriert, wenn die Anforderungsmethode GEToder ist HEAD.

Löst eine InvalidStateErrorAusnahme aus, wenn einer der beiden Zustände nicht geöffnet oder das send()Flag gesetzt ist.

Die Methode muss folgende Schritte ausführen:send(body)

  1. Wenn der Status nicht geöffnet ist , werfen Sie eineInvalidStateError Ausnahme ausgelöst.
  2. Wenn die send()Flagge gesetzt ist, werfen Sie eineInvalidStateError Ausnahme ausgelöst.
  3. Wenn die Anforderungsmethode GEToder ist HEAD, legen Sie body fest auf null.
  4. Wenn body null ist, fahren Sie mit dem nächsten Schritt fort.

Obwohl ich nicht denke, dass es sollte, weil GET-Anfrage möglicherweise großen Körperinhalt benötigt.

Wenn Sie sich also auf XMLHttpRequest eines Browsers verlassen, funktioniert dies wahrscheinlich nicht.

Rafael Sales
quelle
1
aufgrund der Tatsache, dass XMLHttpRequest eine Implementierung ist, herabgestimmt. Es spiegelt möglicherweise nicht die tatsächliche Spezifikation wider, die implementiert werden soll.
Floum
10
Die obige Ablehnung ist falsch. Wenn einige Implementierungen das Senden eines Körpers mit einem GET nicht unterstützen, kann dies ein Grund sein, dies unabhängig von der Spezifikation nicht zu tun. Genau dieses Problem ist mir in einem plattformübergreifenden Produkt begegnet, an dem ich arbeite. Nur die Plattform, die XMLHttpRequest verwendet, konnte den get nicht senden.
pjcard
8

Wenn Sie wirklich einen zwischenspeicherbaren JSON / XML-Text an eine Webanwendung senden möchten, ist die Abfragezeichenfolge, die mit RFC4648: Base 64-Codierung mit URL und Dateiname Safe Alphabet codiert ist, der einzig vernünftige Ort, um Ihre Daten zu speichern . Natürlich können Sie auch JSON einfach mit einem Urlencode versehen und den Wert von URL param eingeben, aber Base64 liefert ein kleineres Ergebnis. Beachten Sie, dass es Einschränkungen hinsichtlich der URL-Größe gibt. Weitere Informationen finden Sie unter Was ist die maximale Länge einer URL in verschiedenen Browsern? .

Möglicherweise denken Sie, dass =das Auffüllzeichen von Base64 für den Parameterwert der URL schlecht ist, dies scheint jedoch nicht der Fall zu sein - siehe folgende Diskussion: http://mail.python.org/pipermail/python-bugs-list/2007-February/037195.html . Sie sollten jedoch keine codierten Daten ohne Parameternamen einfügen, da codierte Zeichenfolgen mit Auffüllung als Parameterschlüssel mit leerem Wert interpretiert werden. Ich würde so etwas benutzen ?_b64=<encodeddata>.

gertas
quelle
Ich denke, das ist eine ziemlich schlechte Idee :) Aber wenn ich so etwas machen würde, würde ich stattdessen einen benutzerdefinierten HTTP-Header verwenden (und sicherstellen, dass ich immer Vary zurückschicke: in der Antwort).
Evert
Schlecht oder nicht, aber machbar :) Bei Daten im Header gibt es ein ähnliches Problem mit der Datengröße, siehe stackoverflow.com/questions/686217/… . Vielen Dank, dass Sie den VaryHeader erwähnt haben. Ich war mir jedoch des tatsächlichen Potenzials nicht bewusst.
Gertas
6

Ich würde dies nicht empfehlen, es widerspricht den Standardpraktiken und bietet nicht so viel Gegenleistung. Sie möchten den Textkörper für Inhalte behalten, nicht für Optionen.

Wolkenkopf
quelle
5

Was ist mit fehlerhaften Base64-codierten Headern? "ETWAS APP-PARAMS: sdfSD45fdg45 / aS"

Längenbeschränkungen hm. Können Sie Ihre POST-Behandlung nicht zwischen den Bedeutungen unterscheiden lassen? Wenn Sie einfache Parameter wie Sortieren möchten, verstehe ich nicht, warum dies ein Problem wäre. Ich denke, es ist sicher, dass Sie sich Sorgen machen.

chaz
quelle
Sie können beliebige Parameter mit dem x-Präfix senden. Jede Begrenzung der Länge der Header wäre eine willkürliche Begrenzung des Servers.
Chris Marisic
4

Ich bin verärgert, dass REST als Protokoll OOP und nicht unterstützt Get Methode ein Beweis ist. Als Lösung können Sie Ihr DTO in JSON serialisieren und anschließend eine Abfragezeichenfolge erstellen. Auf der Serverseite können Sie die Abfragezeichenfolge für das DTO deserialisieren.

Schauen Sie sich an:

Der nachrichtenbasierte Ansatz kann Ihnen bei der Lösung der Einschränkung der Get-Methode helfen. Sie können jedes DTO wie mit dem Anforderungshauptteil senden

Das Nelibur-Webdienst-Framework bietet Funktionen, die Sie verwenden können

var client = new JsonServiceClient(Settings.Default.ServiceAddress);
var request = new GetClientRequest
    {
        Id = new Guid("2217239b0e-b35b-4d32-95c7-5db43e2bd573")
    };
var response = client.Get<GetClientRequest, ClientResponse>(request);

as you can see, the GetClientRequest was encoded to the following query string

http://localhost/clients/GetWithResponse?type=GetClientRequest&data=%7B%22Id%22:%2217239b0e-b35b-4d32-95c7-5db43e2bd573%22%7D
GSerjo
quelle
8
Sie sollten nur POST verwenden. Wenn die URL einen Methodennamen enthält, verletzen Sie das grundlegende Restdesign. Dies ist RPC, verwenden Sie POST.
Evert
3
Ich denke nicht, dass das eine große Sache ist, wir haben mehr Probleme während der Entwicklung mit RESTful URL (dh Bestellungen / 1). Was mich betrifft, stimmt etwas mit der Get-Methode nicht, sie ist nicht mit OOP kompatibel. Und wen interessiert es, wie die URL aussieht :) Aber mit dem nachrichtenbasierten Ansatz können wir eine stabile Remote-Schnittstelle erstellen, und das ist wirklich wichtig. PS Es ist nicht RPC, es ist nachrichtenbasiert
GSerjo
3
Ich denke, Sie verpassen den ganzen Sinn von REST. Wenn Sie sagen, wen interessiert es, wie die URL aussieht, kümmert sich REST sehr darum. Und warum sollte REST mit OOP kompatibel sein?
shmish111
Nein, ich habe nicht nur ein bisschen weiter gesehen
GSerjo
4

Zum Beispiel funktioniert es mit Curl, Apache und PHP.

PHP-Datei:

<?php
echo $_SERVER['REQUEST_METHOD'] . PHP_EOL;
echo file_get_contents('php://input') . PHP_EOL;

Konsolenbefehl:

$ curl -X GET -H "Content-Type: application/json" -d '{"the": "body"}' 'http://localhost/test/get.php'

Ausgabe:

GET
{"the": "body"}
mnv
quelle
Lustiges Experiment! PHP liest nur ein, $_POSTwenn der Text mit einer POST-Anfrage und gesendet wird application/x-www-form-urlencoded. Das bedeutet, dass der Text in einer GETAnforderung ignoriert wird . In diesem Fall: $_GETund $_POSTsind an dieser Stelle ohnehin sehr irreführend. Also besser nutzenphp://input
Martin Muzatko
3

IMHO könnten Sie einfach die JSONverschlüsselte (dh encodeURIComponent) in der senden URL, auf diese Weise verletzen Sie nicht die HTTPSpezifikationen und bringen Ihre JSONan den Server.

EthraZa
quelle
28
Ja, aber das Hauptproblem ist die Längenbeschränkung. Wie gehen wir damit um?
Sebas
2

Sie haben eine Liste von Optionen, die weitaus besser sind als die Verwendung eines Anforderungshauptteils mit GET.

Angenommen, Sie haben Kategorien und Elemente für jede Kategorie. Beide sind durch eine ID zu identifizieren ("catid" / "itemid" für dieses Beispiel). Sie möchten nach einem anderen Parameter "sortby" in einer bestimmten "Reihenfolge" sortieren. Sie möchten Parameter für "sortby" und "order" übergeben:

Du kannst:

  1. Verwenden Sie Abfragezeichenfolgen, z example.com/category/{catid}/item/{itemid}?sortby=itemname&order=asc
  2. Verwenden Sie mod_rewrite (oder ähnliches) für Pfade: example.com/category/{catid}/item/{itemid}/{sortby}/{order}
  3. Verwenden Sie einzelne HTTP-Header, die Sie mit der Anforderung übergeben
  4. Verwenden Sie eine andere Methode, z. B. POST, um eine Ressource abzurufen.

Alle haben ihre Nachteile, sind aber weitaus besser als die Verwendung eines GET mit einem Körper.

Xenonit
quelle
0

Selbst wenn ein beliebtes Tool dies verwendet, wie auf dieser Seite häufig zitiert, halte ich es für eine ziemlich schlechte Idee, zu exotisch zu sein, obwohl dies nicht durch die Spezifikation verboten ist.

Viele Zwischeninfrastrukturen lehnen solche Anfragen möglicherweise einfach ab.

Beispiel vorangehen , vergessen Sie einige der verfügbaren CDN vor Ihrer Website, wie diese mit einem :

Wenn eine Viewer- GETAnforderung einen Text enthält, gibt CloudFront einen HTTP-Statuscode 403 (Verboten) an den Viewer zurück.

Und ja, Ihre Client-Bibliotheken unterstützen möglicherweise auch nicht das Senden solcher Anforderungen, wie in diesem Kommentar angegeben .

Frédéric
quelle
0

Ich verwende das RestTemplate des Spring-Frameworks in meinem Client-Programm und habe auf der Serverseite eine GET-Anforderung mit einem Json-Body definiert. Mein Hauptzweck ist der gleiche wie bei Ihnen: Wenn die Anforderung zahlreiche Parameter enthält, erscheint es ordentlicher, sie in den Body einzufügen, als sie in die verlängerte URI-Zeichenfolge einzufügen. Ja?

Aber leider funktioniert es nicht! Die Serverseite hat die folgende Ausnahme ausgelöst:

org.springframework.http.converter.HttpMessageNotReadableException: Erforderlicher Anforderungshauptteil fehlt ...

Aber ich bin mir ziemlich sicher, dass der Nachrichtentext von meinem Client-Code korrekt bereitgestellt wird. Was ist also falsch?

Ich habe die Methode RestTemplate.exchange () verfolgt und Folgendes gefunden:

// SimpleClientHttpRequestFactory.class
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
    ...
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
        ...
        if (!"POST".equals(httpMethod) && !"PUT".equals(httpMethod) && !"PATCH".equals(httpMethod) && !"DELETE".equals(httpMethod)) {
            connection.setDoOutput(false);
        } else {
            connection.setDoOutput(true);
        }
        ...
    }
}

// SimpleBufferingClientHttpRequest.class
final class SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttpRequest {
    ...
    protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
        ...
        if (this.connection.getDoOutput() && this.outputStreaming) {
            this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
        }

        this.connection.connect();
        if (this.connection.getDoOutput()) {
            FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
        } else {
            this.connection.getResponseCode();
        }
        ...
    }
}

Bitte beachten Sie, dass in der Methode executeInternal () das Eingabeargument 'bufferedOutput' den von meinem Code bereitgestellten Nachrichtentext enthält. Ich habe es durch den Debugger gesehen.

Aufgrund von prepareConnection () gibt getDoOutput () in executeInternal () jedoch immer false zurück, wodurch der bufferedOutput vollständig ignoriert wird! Es wird nicht in den Ausgabestream kopiert.

Folglich hat mein Serverprogramm keinen Nachrichtentext empfangen und diese Ausnahme ausgelöst.

Dies ist ein Beispiel für das RestTemplate des Spring-Frameworks. Der Punkt ist, dass einige Client- oder Serverbibliotheken oder Frameworks möglicherweise der alten Spezifikation entsprechen und den Nachrichtentext aus der GET-Anforderung ablehnen, selbst wenn der Nachrichtentext nicht mehr durch die HTTP-Spezifikation verboten ist.

Zhou
quelle
Sie lesen die Spezifikation oder die Kommentare hier nicht richtig. Clients und Server, die den Anforderungshauptteil löschen, liegen innerhalb der Spezifikation. Verwenden Sie keine GET-Anforderungskörper.
Evert
@Evert Ich habe die Kommentare nicht richtig gelesen oder du nicht? :) Wenn Sie zu Paul Morgans Antwort (der Antwort mit den meisten Treffern) scrollen und die Kommentare sorgfältig lesen, werden Sie Folgendes feststellen: "Der als" HTTP / 1.1-Spezifikation "bezeichnete RFC2616 ist jetzt veraltet. 2014 wurde er durch ersetzt RFCs 7230-7237. Zitat "Der Nachrichtentext sollte bei der Bearbeitung der Anforderung ignoriert werden" wurde gelöscht. Es ist jetzt nur noch "Anforderungsnachrichtenrahmen ist unabhängig von der Methodensemantik, selbst wenn die Methode keine Verwendung für einen Nachrichtentext definiert." ... "
Zhou
@Evert Außerdem habe ich das REST-Testdienstprogramm "Rest-Assured" verwendet, um mein Spring-Boot-Backend zu testen. Sowohl die versicherte als auch die Spring-Boot-Serverseite behielten den Json-Body für GET-Anfragen! Nur das RestTemplate des Sping-Frameworks löscht den Body von GET-Anforderungen, also Spring-Boot, Rest-Assured und RestTemplate, was ist / sind falsch?
Zhou
@Evert Last but not lease, ich habe die Leute nicht dazu gedrängt, body in GET-Anfragen zu verwenden, im Gegenteil, ich schlug vor, dies NICHT zu tun, indem ich den Quellcode des RestTemplate des Sping-Frameworks analysiere. Warum haben Sie meine also heruntergestimmt? Antworten?
Zhou
Ich habe Ihre Antwort nicht abgelehnt. Ich Klärung nur , dass jede HTTP - Implementierung Abwurf GET - Anforderung ist in spec.
Evert