it-swarm.com.de

HTTP GET mit Anforderungshauptteil

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 (zum Beispiel eine Liste sortieren), können sie diese Parameter in die Abfragezeichenfolge einfügen.

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

Meine Fragen:

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

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

1786
Evert

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 Nachrichten daher entsprechend analysieren. Die Serversemantik für GET ist jedoch so eingeschränkt, dass ein Körper, falls vorhanden, 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 Body mit GET senden, und nein, es ist niemals sinnvoll, dies zu tun.

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

Roy

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

[...] Wenn die Anforderungsmethode keine definierte Semantik für einen Entity-Body enthält, wird der Message-Body SHOULD bei der Bearbeitung der Anforderung ignoriert.

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

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

hiermit wird angegeben, dass der Anforderungshauptteil nicht Teil der Identifikation 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. Anführungszeichen "der Nachrichtentext sollte bei der Bearbeitung der Anfrage ignoriert werden" wurde gelöscht. Es ist jetzt nur "Request-Message-Framing ist unabhängig von der Methodensemantik, auch wenn die Methode keine Verwendung für einen Nachrichtentext definiert." wurde gelöscht. - Aus einem Kommentar

1488
Paul Morgan

Während Sie dies tun können , würde ich vorschlagen, dies zu vermeiden, nur weil die Leute keine Dinge erwarten so zu arbeiten. Es gibt viele Phasen in einer HTTP-Anforderungskette, und obwohl sie "größtenteils" der HTTP-Spezifikation entsprechen, können Sie nur sicher sein, dass sie sich so verhalten, wie sie traditionell von Webbrowsern verwendet werden. (Ich denke an Dinge wie transparente Proxies, Beschleuniger, A/V-Toolkits usw.)

Dies ist der Geist hinter dem Robustheitsprinzip ungefähr "sei liberal in dem, was du akzeptierst, und konservativ in dem, was du sendest", du willst nicht ohne guten Grund die Grenzen einer Spezifikation überschreiten.

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

263
caskey

Sie werden wahrscheinlich auf Probleme stoßen, wenn Sie jemals versuchen, das Caching zu nutzen. Proxies werden nicht im GET-Body nachsehen, ob sich die Parameter auf die Antwort auswirken.

135
Darrel Miller

Weder restclient noch REST-Konsole unterstützen dies, Curl jedoch.

Die HTTP-Spezifikation sagt in Abschnitt 4.3

Ein Nachrichtenkörper DARF NICHT in eine Anforderung aufgenommen werden, wenn die Spezifikation der Anforderungsmethode (Abschnitt 5.1.1) das Senden eines Entitätskörpers in Anforderungen nicht zulässt.

Abschnitt 5.1.1 leitet uns für die verschiedenen Methoden zu Abschnitt 9.x 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 durch Untersuchen sowohl des Anforderungs-URI- als auch des Host-Header-Felds bestimmt.

und Abschnitt 9. sagt

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

Dies deutet zusammen darauf hin, dass ein Server bei der Verarbeitung einer GET-Anfrage nicht verpflichtet ist , etwas anderes als den Request-URI und das Host-Header-Feld zu untersuchen .

Zusammenfassend lässt sich sagen, dass die HTTP-Spezifikation Sie nicht daran hindert, einen Nachrichtentext mit GET zu senden. Es gibt jedoch ausreichende Mehrdeutigkeiten, die mich nicht überraschen würden, wenn dies nicht von allen Servern unterstützt würde.

66
Dave Durbin

Elasticsearch akzeptiert GET-Anfragen mit einem Body. Es scheint sogar, dass dies der bevorzugte Weg ist: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/common-options.html#_request_body_in_query_string

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

45
jlecour

Was Sie erreichen möchten, wurde lange Zeit mit einer weitaus gängigeren Methode durchgeführt, die nicht auf der Verwendung einer Payload mit GET beruht.

Sie können einfach Ihren spezifischen Suchmedientyp erstellen oder, wenn Sie mehr REST wollen, OpenSearch und POST die Anfrage an die URI verwenden, die der Server angewiesen hat, sagen wir/search. Der Server kann dann das Suchergebnis generieren oder den endgültigen URI erstellen und mithilfe eines 303 umleiten.

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

Allerdings werden URIs für alles, was nicht ASCII ist, codiert, ebenso wie application/x-www-form-urlencoded und multipart/form-data. Ich würde empfehlen, dieses Format zu verwenden, anstatt ein weiteres benutzerdefiniertes JSON-Format zu erstellen, wenn Sie beabsichtigen, ReSTful-Szenarien zu unterstützen.

29
SerialSeb

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

Beides sind 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 Probleme verursachen.

Julian Reschke schlug oben die Verwendung eines nicht standardmäßigen HTTP-Headers wie "SEARCH" vor, der eine elegante Lösung sein könnte, mit der Ausnahme, dass er mit noch geringerer Wahrscheinlichkeit unterstützt wird.

Am produktivsten ist es möglicherweise, 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:

  • ?
22
fijiaaron

Welcher Server wird das ignorieren? - Fijiaaron 30. August 12 um 21:27 Uhr

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

Probieren Sie es mit einem einfachen Netcat aus:

$ 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 bekommst:

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 außerdem 400 Bad Request von Bing, Apple usw., die von AkamaiGhost bereitgestellt werden.

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

21
user941239

From RFC 2616, Abschnitt 4. , "Nachrichtentext":

Ein Server SOLLTE bei jeder Anfrage einen Nachrichtentext lesen und weiterleiten. Wenn die Anforderungsmethode keine definierte Semantik für einen Entity-Body enthält, MUSS der Message-Body bei der Bearbeitung 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 aufgeteilten Hauptteil usw.). Außerdem sollten Proxys alle empfangenen Anforderungsnachrichten weiterleiten. Wenn der RFC dann die Semantik für den Hauptteil der angegebenen Methode definiert, kann der Server den Anforderungshauptteil tatsächlich zum Generieren einer Antwort verwenden. Wenn der RFC jedoch keine Semantik für den Body definiert , sollte er vom Server ignoriert werden.

Dies steht im Einklang mit dem Zitat von Fielding oben.

Abschnitt 9. , "GET", beschreibt die Semantik der GET-Methode und erwähnt keine Anfragetexte. Daher sollte ein Server jeden Anforderungstext ignorieren, den er bei einer GET-Anforderung empfängt.

17
izrik

Ich habe diese Frage an die IETF HTTP WG gestellt. Der Kommentar von Roy Fielding (Autor des http/1.1-Dokuments im Jahr 1998) lautete:

"... eine Implementierung wäre kaputt, wenn sie nicht nur diesen Körper analysieren und verwerfen würde, wenn sie empfangen würde."

RFC 7213 (HTTPbis) besagt:

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

Es scheint jetzt klar zu sein, dass die Absicht war, 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, die definitiv Ihre Anfrage auf verschiedene Arten brechen, wenn Sie einen Body in GET aufnehmen.

Also zusammenfassend nicht tun.

15
Adrien

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

4.5.6 Die Methode send()

client . send([body = null])

Leitet die Anforderung ein. Das optionale Argument gibt den Anforderungshauptteil an. Das Argument wird ignoriert, wenn die Anforderungsmethode GET oder HEAD ist.

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

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

  1. Wenn der Status nicht geöffnet ist , wird eine InvalidStateError -Ausnahme ausgelöst.
  2. Wenn das Flag send() gesetzt ist, wird eine InvalidStateError -Ausnahme ausgelöst.
  3. Wenn die Anforderungsmethode GET oder HEAD ist, setzen Sie body auf null.
  4. Wenn body null ist, fahren Sie mit dem nächsten Schritt fort.

Ich denke jedoch nicht, dass dies der Fall sein sollte, da eine GET-Anforderung möglicherweise großen Inhalt benötigt.

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

7
Rafael Sales

Wenn Sie wirklich einen zwischenspeicherbaren JSON/XML-Text an eine Webanwendung senden möchten, ist der einzige sinnvolle Ort, an dem Sie Ihre Daten ablegen können, eine mit RFC4648: Base 64-Codierung mit URL und Dateiname Safe Alphabet . Natürlich können Sie auch einfach JSON urlenkodieren und in den Wert des URL-Parameters setzen, aber Base64 liefert ein kleineres Ergebnis. Beachten Sie, dass es Beschränkungen der URL-Größe gibt, siehe Wie lang darf eine URL in verschiedenen Browsern maximal sein? .

Möglicherweise denken Sie, dass das Fü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-Liste/2007-Februar/037195.html . Sie sollten jedoch keine verschlüsselten Daten ohne Parameternamen eingeben, da verschlüsselte Zeichenfolgen mit Auffüllung als Parameterschlüssel mit leerem Wert interpretiert werden. Ich würde so etwas wie ?_b64=<encodeddata> verwenden.

7
gertas

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"}
5
mnv

Ich würde das nicht empfehlen, es widerspricht den üblichen Praktiken und bietet nicht so viel als Gegenleistung. Sie möchten den Hauptteil für den Inhalt behalten, nicht für Optionen.

5
cloudhead

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

Schauen Sie sich an:

Der auf Nachrichten basierende Ansatz kann Ihnen dabei helfen, die Einschränkung der Methode Get zu lösen. Sie können jeden DTO wie mit Anfragekörper senden

Nelibur Web Service 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
4
GSerjo

IMHO könnten Sie nur die JSON verschlüsselt (dh encodeURIComponent) in URL senden, auf diese Weise verletzen Sie nicht die HTTP Spezifikationen und erhalten Ihre JSON zum Server.

4
EthraZa

Was ist mit fehlerhaften Base64-codierten Headern? "ETWASAPP-PARAMME: sdfSD45fdg45/aS"

Längenbeschränkungen hm. Können Sie Ihre POST -Handhabung 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 Gewissheit, dass du dir Sorgen machst.

4
chaz

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 müssen durch eine ID identifiziert werden ("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. POST, um eine Ressource abzurufen.

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

1
Xenonite