web-dev-qa-db-fra.com

HTTP GET avec le corps de la requête

Je développe un nouveau service Web RESTful pour notre application.

Lors de l'exécution d'une opération GET sur certaines entités, les clients peuvent demander le contenu de l'entité. S'ils souhaitent ajouter des paramètres (par exemple, trier une liste), ils peuvent les ajouter à la chaîne de requête.

Sinon, je veux que les gens puissent spécifier ces paramètres dans le corps de la demande. HTTP/1.1 ne semble pas explicitement interdire cela. Cela leur permettra de spécifier plus d'informations et facilitera la spécification de requêtes XML complexes.

Mes questions:

  • Est-ce une bonne idée?
  • Les clients HTTP auront-ils des problèmes d'utilisation des corps de demande dans une demande GET?

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

1786
Evert

commentaire de Roy Fielding sur l'inclusion d'un corps dans une requête GET .

Oui. En d'autres termes, tout message de requête HTTP est autorisé à contenir un corps de message et doit donc analyser les messages dans cet esprit. La sémantique de serveur pour GET est toutefois restreinte, de sorte qu'un corps, s'il en existe un, n'a pas de signification sémantique pour la requête. Les exigences relatives à l'analyse sont distinctes des exigences relatives à la sémantique de la méthode.

Donc, oui, vous pouvez envoyer un corps avec GET, et non, il n'est jamais utile de le faire.

Cela fait partie de la conception en couches de HTTP/1.1 qui redeviendra claire une fois la spécification partitionnée (travail en cours).

.... Roy

Oui, vous pouvez envoyer un corps de requête avec GET, mais cela ne doit avoir aucune signification. Si vous lui donnez un sens en l’analysant sur le serveur et en modifiant votre réponse en fonction de son contenu , vous ignorez cette recommandation dans la spécification HTTP/1.1, section 4. :

[...] si la méthode de requête n'inclut pas de sémantique définie pour un corps d'entité, le corps de message DEVRAIT être ignoré lors du traitement de la demande.

Et la description de la méthode GET dans spécification HTTP/1.1, section 9. :

La méthode GET signifie de récupérer toutes les informations ([...]) identifiées par l'URI de demande.

qui stipule que le corps de la demande ne fait pas partie de l'identification de la ressource dans une demande GET, mais uniquement l'URI de la demande.

Update La RFC2616 référencée sous le nom "HTTP/1.1 spec" est maintenant obsolète. En 2014, il a été remplacé par les RFC 7230-7237. Citer "le corps du message DEVRAIT être ignoré lors du traitement de la demande" a été supprimé. C’est désormais juste "Le cadrage du message de requête est indépendant de la sémantique de la méthode, même si la méthode ne définit aucune utilisation du corps du message" Le second guillemet "La méthode GET signifie de récupérer toutes les informations ... identifiées par l'URI de demande" A été supprimée. - d'un commentaire

1488
Paul Morgan

Bien que vous puissiez le faire, dans la mesure où cela n’est pas explicitement exclu par la spécification HTTP, je suggérerais de l’éviter simplement parce que les gens ne s’attendent pas à des choses travailler de cette façon. Une chaîne de requêtes HTTP comporte de nombreuses phases et, bien qu’elles se conforment "pour la plupart" à la spécification HTTP, la seule chose dont vous êtes assuré, c’est qu’elles se comporteront de la même manière que les navigateurs Web. (Je pense à des choses telles que les proxies transparents, les accélérateurs, les kits d’audiovisuel, etc.)

C’est l’esprit qui se cache derrière le principe de robustesse en gros "soyez libéral dans ce que vous acceptez, et conservateur dans ce que vous envoyez", vous ne voulez pas repousser les limites d’une spécification sans motif valable.

Cependant, si vous avez une bonne raison, allez-y.

263
caskey

Vous rencontrerez probablement des problèmes si vous tentez de tirer parti de la mise en cache. Les mandataires ne vont pas regarder dans le corps de GET pour voir si les paramètres ont un impact sur la réponse.

135
Darrel Miller

Ni restclient ni console REST ne le supporte, mais curl le fait.

La spécification HTTP indique dans la section 4.3

Un corps de message NE DOIT PAS être inclus dans une demande si la spécification de la méthode de demande (paragraphe 5.1.1) ne permet pas d'envoyer un corps d'entité dans les demandes.

La section 5.1.1 nous redirige vers la section 9.x pour les différentes méthodes. Aucun d'entre eux n'interdit explicitement l'inclusion d'un corps de message. Pourtant...

La section 5.2 dit

La ressource exacte identifiée par une requête Internet est déterminée en examinant à la fois les champs Requête-URI et En-tête de l'hôte.

et La section 9.3 dit

La méthode GET signifie extraire toutes les informations (sous la forme d'une entité) identifiées par l'URI de demande.

Ce qui, ensemble, suggère que lors du traitement d'une demande GET, un serveur n'est pas requis pour examiner autre chose que le champ d'en-tête Request-URI et Host.

En résumé, la spécification HTTP ne vous empêche pas d'envoyer un corps de message avec GET, mais il y a suffisamment d'ambiguïté pour que cela ne me surprenne pas s'il n'était pas pris en charge par tous les serveurs.

66
Dave Durbin

Elasticsearch accepte les requêtes GET avec un corps. Il semble même que ce soit la méthode préférée: http://www.elasticsearch.org/guide/fr/elasticsearch/reference/current/common-options.html#_request_body_in_query_string

Certaines bibliothèques clientes (comme le pilote Ruby) peuvent consigner la commande cry sur stdout en mode de développement et cette syntaxe est utilisée de manière intensive.

45
jlecour

Ce que vous essayez de réaliser a été fait pendant longtemps avec une méthode beaucoup plus courante, qui ne repose pas sur l’utilisation d’une charge utile avec GET.

Vous pouvez simplement créer votre type de média spécifique à la recherche ou, si vous voulez être plus RESTful, utiliser quelque chose comme OpenSearch et POST la demande à l'URI indiquée par le serveur, par exemple/search. Le serveur peut ensuite générer le résultat de la recherche ou créer l'URI final et rediriger à l'aide de l'adresse 303.

Cela présente l'avantage de suivre la méthode PRG traditionnelle, aide les intermédiaires en cache à mettre en cache les résultats, etc.

Cela dit, les URI sont néanmoins codés pour tout ce qui n'est pas ASCII, de même que application/x-www-form-urlencenced et multipart/form-data. Je vous recommande d'utiliser cette option plutôt que de créer un autre format JSON personnalisé si votre intention est de prendre en charge les scénarios ReSTful.

29
SerialSeb

Vous pouvez envoyer un GET avec un corps ou un POST et abandonner la religiosité RESTish (ce n'est pas si grave, il y a 5 ans, il n'y avait qu'un seul membre de cette religion - ses commentaires sont liés ci-dessus).

Ce n’est pas non plus une excellente décision, mais l’envoi d’un corps GET peut éviter des problèmes pour certains clients - et certains serveurs.

Faire un POST peut rencontrer des obstacles avec certains frameworks RESTish.

Julian Reschke a suggéré ci-dessus d'utiliser un en-tête HTTP non standard, tel que "SEARCH", qui pourrait constituer une solution élégante, à la différence près qu'il est encore moins susceptible d'être pris en charge.

Il serait peut-être plus productif de lister les clients qui peuvent et ne peuvent pas faire chacune des choses ci-dessus.

Clients qui ne peuvent pas envoyer un message GET avec un corps (à ma connaissance):

  • Fiddler XmlHTTPRequest

Clients pouvant envoyer un message GET avec un corps:

  • la plupart des navigateurs

Serveurs et bibliothèques pouvant extraire un corps de GET:

  • Apache
  • PHP

Serveurs (et mandataires) qui dépouillent un corps de GET:

  • ?
22
fijiaaron

Quel serveur l'ignorera? - Fijiaaron 30 août 12 à 21:27

Google par exemple fait pire qu'ignorer, il considérera qu'il s'agit d'un erreur!

Essayez vous-même avec un simple netcat:

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

1234

(le contenu 1234 est suivi de CR-LF, soit un total de 6 octets)

et vous obtiendrez:

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.

Vous recevez également 400 demandes non sollicitées de Bing, Apple, etc., desservies par AkamaiGhost.

Donc, je ne conseillerais pas d'utiliser des requêtes GET avec une entité body.

21
user941239

De RFC 2616, section 4. , "Corps du message":

Un serveur DEVRAIT lire et transmettre un corps de message pour toute demande. si la méthode de demande n'inclut pas de sémantique définie pour un corps d'entité, le corps du message DEVRAIT être ignoré lors du traitement de la demande.

C'est-à-dire que les serveurs doivent toujours lire tout corps de requête fourni sur le réseau (vérifiez Content-Length ou lisez un corps fragmenté, etc.). En outre, les mandataires doivent transmettre tout organe de requête de ce type qu’ils reçoivent. Ensuite, si le RFC définit la sémantique du corps pour la méthode donnée, le serveur peut utiliser le corps de la requête pour générer une réponse. Cependant, si la RFC ne définit pas la sémantique du corps, le serveur doit l'ignorer.

Ceci est conforme à la citation de Fielding ci-dessus.

Section 9. , "GET" décrit la sémantique de la méthode GET et ne mentionne pas les corps de requête. Par conséquent, un serveur doit ignorer tout corps de requête qu'il reçoit lors d'une requête GET.

17
izrik

J'ai posé cette question au groupe de travail HTTP de l'IETF. Le commentaire de Roy Fielding (auteur du document http/1.1 en 1998) était que

"... une implémentation serait cassée pour faire autre chose que pour analyser et jeter ce corps si reçu"

La RFC 7213 (HTTPbis) déclare:

"Une charge utile dans un message de requête GET n'a pas de sémantique définie;"

Il semble maintenant clair que l’intention était que la signification sémantique sur le corps des requêtes GET soit interdite, ce qui signifie que le corps de la requête ne peut pas être utilisé pour affecter le résultat.

Il existe des serveurs proxy qui définitivement casseront votre demande de différentes manières si vous incluez un corps dans GET.

Donc, en résumé, ne le faites pas.

15
Adrien

Selon XMLHttpRequest, ce n'est pas valide. De la standard :

4.5.6 La méthode send()

client . send([body = null])

Initie la demande. L'argument optionnel fournit le corps de la demande. L'argument est ignoré si la méthode de requête est GET ou HEAD.

Lève une exception InvalidStateError si l'un des états n'est pas ouvert ou si l'indicateur send() est défini.

La méthode send(body) doit exécuter ces étapes:

  1. Si l'état n'est pas ouvert , lève une exception InvalidStateError.
  2. Si l'indicateur send() est défini, lève une exception InvalidStateError.
  3. Si la méthode de demande est GET ou HEAD, définissez body sur null.
  4. Si le corps est nul, passez à l'étape suivante.

Bien que, je ne pense pas que ce soit le cas, car la requête GET pourrait nécessiter un contenu volumineux.

Donc, si vous utilisez XMLHttpRequest d'un navigateur, il est probable que cela ne fonctionnera pas.

7
Rafael Sales

Si vous voulez vraiment envoyer un corps JSON/XML pouvant être masqué à une application Web, le seul endroit raisonnable pour placer vos données est la chaîne de requête codée avec RFC4648: Codage en base 64 avec URL et nom de fichier: Alphabet sans danger . Bien sûr, vous pouvez simplement coder JSON en urlenc et mettre est dans la valeur du paramètre d'URL, mais Base64 donne un résultat plus petit. N'oubliez pas qu'il existe des restrictions de taille d'URL, voir Quelle est la longueur maximale d'une URL dans différents navigateurs? .

Vous pensez peut-être que le caractère de remplissage = de Base64 peut être mauvais pour la valeur param de l'URL, mais il ne semble pas - voir cette discussion: http://mail.python.org/pipermail/python-bugs-list /2007-février/037195.html . Cependant, vous ne devriez pas mettre de données codées sans nom de paramètre, car une chaîne codée avec un remplissage sera interprétée comme une clé de paramètre avec une valeur vide. Je voudrais utiliser quelque chose comme ?_b64=<encodeddata>.

7
gertas

Par exemple, cela fonctionne avec Curl, Apache et PHP.

Fichier PHP:

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

Commande de console:

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

Sortie:

GET
{"the": "body"}
5
mnv

Je ne le conseillerais pas, cela va à l'encontre des pratiques habituelles et n'offre pas beaucoup en retour. Vous voulez conserver le corps pour le contenu, pas pour les options.

5
cloudhead

Je suis fâché que REST en tant que protocole ne supporte pas la méthode OOP et Get en soit la preuve. En tant que solution, vous pouvez sérialiser votre DTO en JSON, puis créer une chaîne de requête. Sur le serveur, vous pourrez désérialiser la chaîne de requête vers le DTO.

Jetez un coup d'oeil sur:

Une approche basée sur les messages peut vous aider à résoudre la restriction de la méthode Get. Vous pourrez envoyer n'importe quel DTO comme avec le corps de la requête

Le framework de services Web Nelibur fournit des fonctionnalités que vous pouvez utiliser

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 vous pouvez simplement envoyer le JSON encodé (c'est-à-dire encodeURIComponent) dans le URL, de cette façon vous ne violerez pas les spécifications HTTP et obtiendrez votre JSON au serveur.

4
EthraZa

Qu'en est-il des en-têtes codés en base64 non conformes? "SOMETHINGAPP-PARAMS: sdfSD45fdg45/aS"

Restrictions de longueur hm. Ne pouvez-vous pas faire en sorte que votre traitement POST fasse la distinction entre les significations? Si vous voulez des paramètres simples comme le tri, je ne vois pas pourquoi cela poserait un problème. Je suppose que c'est la certitude qui vous inquiète.

4
chaz

Vous disposez d'une liste d'options bien meilleures que l'utilisation d'un corps de requête avec GET.

Supposons que vous avez des catégories et des éléments pour chaque catégorie. Les deux doivent être identifiés par un identifiant ("catid"/"itemid" pour les besoins de cet exemple). Vous voulez trier selon un autre paramètre "sortby" dans un "ordre" spécifique. Vous voulez passer des paramètres pour "sortby" et "order":

Vous pouvez:

  1. Utilisez des chaînes de requête, par exemple example.com/category/{catid}/item/{itemid}?sortby=itemname&order=asc
  2. Utilisez mod_rewrite (ou similaire) pour les chemins: example.com/category/{catid}/item/{itemid}/{sortby}/{order}
  3. Utiliser les en-têtes HTTP individuels que vous transmettez avec la demande
  4. Utilisez une méthode différente, par exemple POST, pour récupérer une ressource.

Tous ont leurs inconvénients, mais sont bien meilleurs que d'utiliser un GET avec un corps.

1
Xenonite