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?
Antworten:
Roy Fieldings Kommentar zum Einfügen eines Körpers mit einer GET-Anfrage .
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 :
Und die Beschreibung der GET-Methode in der HTTP / 1.1-Spezifikation, Abschnitt 9.3 :
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
quelle
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.
quelle
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.
quelle
Weder der Restclient noch die REST-Konsole unterstützen dies, Curl jedoch.
Die HTTP-Spezifikation sagt in Abschnitt 4.3
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
und Abschnitt 9.3 sagt
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.
quelle
GET /contacts/100/addresses
gibt eine Sammlung von Adressen für die Person mit zurückid=100
.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.
quelle
curl -XGET 'http://localhost:9200/_count?pretty' -d ' { "query": { "match_all": {} } }'
dem Einschließen der Nutzdaten alssource
Parameter entspricht:curl -XGET 'http://localhost:9200/_count?pretty&source=%7B%22query%22%3A%7B%22match_all%22%3A%7B%7D%7D%7D'
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.
quelle
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ß):
Clients, die ein GET mit body senden können:
Server und Bibliotheken, die einen Body von GET abrufen können:
Server (und Proxys), die einen Body von GET entfernen:
quelle
SEARCH
Methode 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 ...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
In RFC 7213 (HTTPbis) heißt es:
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.
quelle
Google zum Beispiel geht es schlechter als es zu ignorieren, es wird es als Fehler betrachten !
Probieren Sie es selbst mit einem einfachen Netcat:
(Auf den 1234-Inhalt folgt CR-LF, das sind also insgesamt 6 Bytes.)
und du wirst bekommen:
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.
quelle
GET
Benutzer 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.GET
bestimmten Endpunkt nicht erwartet (oder sinnvoll) ist - dies hat nichts mit der VerwendungGET
im allgemeinen Fall zu tun . Eine zufällige Nutzlast könnte einePOST
ebenso leicht brechen und dieselbe zurückgeben400 Bad Request
, wenn der Inhalt nicht in einem Format vorliegt, das im Kontext der spezifischen Anforderung sinnvoll ist.Aus RFC 2616, Abschnitt 4.3 , "Nachrichtentext":
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.
quelle
Laut XMLHttpRequest ist es nicht gültig. Aus dem Standard :
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.
quelle
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>
.quelle
Vary
Header erwähnt haben. Ich war mir jedoch des tatsächlichen Potenzials nicht bewusst.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.
quelle
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.
quelle
x-
Präfix senden. Jede Begrenzung der Länge der Header wäre eine willkürliche Begrenzung des Servers.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
quelle
Zum Beispiel funktioniert es mit Curl, Apache und PHP.
PHP-Datei:
Konsolenbefehl:
Ausgabe:
quelle
$_POST
wenn der Text mit einer POST-Anfrage und gesendet wirdapplication/x-www-form-urlencoded
. Das bedeutet, dass der Text in einerGET
Anforderung ignoriert wird . In diesem Fall:$_GET
und$_POST
sind an dieser Stelle ohnehin sehr irreführend. Also besser nutzenphp://input
IMHO könnten Sie einfach die
JSON
verschlüsselte (dhencodeURIComponent
) in der sendenURL
, auf diese Weise verletzen Sie nicht dieHTTP
Spezifikationen und bringen IhreJSON
an den Server.quelle
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:
example.com/category/{catid}/item/{itemid}?sortby=itemname&order=asc
example.com/category/{catid}/item/{itemid}/{sortby}/{order}
Alle haben ihre Nachteile, sind aber weitaus besser als die Verwendung eines GET mit einem Körper.
quelle
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 :
Und ja, Ihre Client-Bibliotheken unterstützen möglicherweise auch nicht das Senden solcher Anforderungen, wie in diesem Kommentar angegeben .
quelle
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:
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.
quelle