HTTP GET avec le corps de la requête

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

Lorsque vous effectuez un GET sur certaines entités, les clients peuvent demander le contenu de l’entité. S’ils veulent append des parameters (par exemple, sortinger une liste), ils peuvent append ces parameters dans la chaîne de requête.

Sinon, je veux que les gens puissent spécifier ces parameters dans le corps de la requête. HTTP / 1.1 ne semble pas l’interdire explicitement. Cela leur permettra de spécifier plus d’informations, ce qui pourrait faciliter la spécification de requêtes XML complexes.

Mes questions:

  • Est-ce une bonne idée?
  • Les clients HTTP auront-ils des problèmes avec l’utilisation des corps de requête dans une requête GET?

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

Le commentaire de Roy Fielding sur l’inclusion d’un corps avec une demande GET .

Oui. En d’autres termes, tout message de requête HTTP est autorisé à contenir un corps de message et doit donc parsingr les messages en tenant compte de cela. La sémantique des serveurs pour GET, cependant, est restreinte de telle sorte qu’un corps, le cas échéant, n’a aucune signification sémantique à la requête. Les exigences relatives à l’parsing sont distinctes des exigences relatives à la sémantique des méthodes.

Donc, oui, vous pouvez envoyer un corps avec GET, et non, ce n’est jamais utile.

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 devrait pas avoir de sens. 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.3 :

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

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

La méthode GET signifie récupérer toute information ([…]) identifiée 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 requête GET, mais uniquement de l’URI de requête.

Bien que vous puissiez le faire, dans la mesure où la spécification HTTP ne l’interdit pas explicitement, je vous suggère de l’éviter simplement parce que les gens ne s’attendent pas à ce que les choses fonctionnent de cette façon. Il y a beaucoup de phases dans une chaîne de requêtes HTTP et bien qu’elles soient “principalement” conformes aux spécifications HTTP, la seule chose à laquelle vous êtes assuré est qu’elles se comporteront comme traditionnellement utilisées par les navigateurs Web. (Je pense à des éléments comme les mandataires transparents, les accélérateurs, les kits d’outils A / V, etc.)

C’est en gros l’esprit du principe de robustesse “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 raison valable.

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

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

Ni restclient ni la console REST ne supportent cela, mais curl le fait.

La spécification HTTP est décrite dans la section 4.3

Un corps de message NE DOIT PAS être inclus dans une requête si la spécification de la méthode de requête (section 5.1.1) ne permet pas d’envoyer un corps d’entité dans les requêtes.

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. Toutefois…

La section 5.2 dit

La ressource exacte identifiée par une requête Internet est déterminée en examinant le champ d’en-tête Request-URI et Host.

et la section 9.3 dit

La méthode GET signifie récupérer toute information (sous la forme d’une entité) identifiée par l’URI de demande.

Ce qui suggère que lors du traitement d’une requête GET, un serveur n’a pas besoin d’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 l’ambiguïté est telle que cela ne me surprendrait pas si elle n’était pas prise en charge par tous les serveurs.

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

Certaines bibliothèques clientes (comme le pilote Ruby) peuvent enregistrer la commande cry sur stdout en mode développement et utilisent cette syntaxe de manière extensive.

Quel serveur l’ignorera? – fijiaaron 30 août 12 à 21:27

Google, par exemple, fait pire que de l’ignorer, il considérera cela comme une 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 obtenez également 400 Bad Request de Bing, Apple, etc … qui sont servis par AkamaiGhost.

Donc, je ne conseillerais pas d’utiliser des requêtes GET avec une entité du corps.

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

Vous pouvez simplement créer votre type de recherche spécifique ou, si vous voulez être plus RESTful, utiliser quelque chose comme OpenSearch, et POST la requête à l’URI chargé par le serveur, disons / recherchez. Le serveur peut alors générer le résultat de la recherche ou créer l’URI final et redirect à l’aide d’un 303.

Cela a 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 codés de toute façon pour tout ce qui n’est pas ASCII, tout comme Application / x-www-form-urlencoded et multipart / form-data. Je vous recommande de l’utiliser plutôt que de créer un autre format json personnalisé si votre intention est de prendre en charge les scénarios ReSTful.

Vous pouvez soit envoyer un GET avec un corps ou envoyer un POST et renoncer à la religiosité RESTish (ce n’est pas si grave, il y a 5 ans, il n’y avait qu’un seul membre de cette foi – ses commentaires ci-dessus).

Ce ne sont pas non plus de grandes décisions, mais l’envoi d’un corps GET peut éviter certains problèmes pour certains clients et certains serveurs.

Faire un POST peut avoir 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 être une solution élégante, sauf qu’il est encore moins susceptible d’être pris en charge.

Il pourrait être plus productif de répertorier les clients qui peuvent et ne peuvent pas faire chacun des éléments ci-dessus.

Les clients qui ne peuvent pas envoyer un GET avec le corps (que je connais):

  • XmlHTTPRequest Fiddler

Clients pouvant envoyer un GET avec le corps:

  • la plupart des navigateurs

Serveurs et bibliothèques pouvant récupérer un corps depuis GET:

  • Apache
  • PHP

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

  • ?

De la RFC 2616, section 4.3 , “Corps du message”:

Un serveur DEVRAIT lire et transférer un message-corps sur n’importe quelle requête; Si la méthode de requête 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.

En d’autres termes, les serveurs doivent toujours lire tout corps de requête fourni sur le réseau (vérifiez la longueur du contenu ou lisez un corps fragmenté, etc.). En outre, les mandataires doivent transmettre tout organe de demande qu’ils reçoivent. Ensuite, si le RFC définit la sémantique du corps pour la méthode donnée, le serveur peut réellement utiliser le corps de la requête pour générer une réponse. Cependant, si le RFC ne définit pas la sémantique pour le corps, le serveur devrait l’ignorer.

Ceci est en ligne avec la citation de Fielding ci-dessus.

La section 9.3 , “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 sur une requête GET.

Je pose cette question au groupe de travail HTTP 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 parsingr et jeter ce corps s’il est reçu”

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 clair maintenant que l’intention était que la signification sémantique sur les corps de requête GET est interdite, ce qui signifie que le corps de la requête ne peut pas être utilisé pour affecter le résultat.

Si vous incluez un organe dans GET, il existe des procurations qui vont certainement briser votre demande de diverses manières.

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

Si vous voulez vraiment envoyer un corps JSON / XML cachable à une application Web, le seul endroit raisonnable pour placer vos données est la chaîne de requête encodée avec RFC4648: Encodage Base 64 avec URL et nom de fichier Alphabet sécurisé . Bien sûr, vous pouvez simplement lancer un code JSON et mettre dans la valeur du paramètre URL, mais Base64 donne un résultat plus petit. N’oubliez pas qu’il existe des ressortingctions 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 padding = Base64 peut être mauvais pour la valeur param de l’URL, mais cela ne semble pas être le cas – voir cette discussion: http://mail.python.org/pipermail/python-bugs-list/2007-February/037195.html . Cependant, vous ne devez pas mettre de données codées sans nom de paramètre car la chaîne codée avec remplissage sera interprétée comme une clé param avec une valeur vide. Je voudrais utiliser quelque chose comme ?_b64= .

Je suis contrarié que REST en tant que protocole ne supporte pas la POO et que la méthode Get soit la preuve. En tant que solution, vous pouvez sérialiser votre DTO en JSON, puis créer une chaîne de requête. Du côté du serveur, vous pourrez désérialiser la chaîne de requête vers le DTO.

Jetez un oeil sur:

  • Conception basée sur les messages dans ServiceStack
  • Création de services Web basés sur des messages RESTful avec WCF

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

Nelibur Web Service Framework 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(request); as you can see, the GetClientRequest was encoded to the following query ssortingng http://localhost/clients/GetWithResponse?type=GetClientRequest&data=%7B%22Id%22:%2217239b0e-b35b-4d32-95c7-5db43e2bd573%22%7D 

Qu’en est-il des en-têtes encodés en base64 non conformes? “SOMETHINGAPP-PARAMS: sdfSD45fdg45 / aS”

Ressortingctions de longueur hm. Ne pouvez-vous pas faire en sorte que votre gestion POST fasse la distinction entre les significations? Si vous voulez des parameters simples comme le sorting, je ne vois pas pourquoi cela poserait problème. Je suppose que c’est une certitude pour vous.

À mon humble avis, vous pourriez simplement envoyer le encodeURIComponent JSON (c.-à-d. encodeURIComponent ) dans l’ URL , de cette façon, vous ne violez pas les spécifications HTTP et obtenez votre JSON sur le serveur.

Je ne le conseillerais pas, cela va à l’encontre des pratiques standard et n’offre pas autant en retour. Vous voulez garder le corps pour le contenu, pas les options.

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

Fichier PHP:

  

Commande de la console:

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

Sortie:

 GET {"the": "body"} 

Selon XMLHttpRequest, ce n’est pas valide. De la norme :

4.5.6 La méthode send()

 client . send([ body = null]) 

Initie la demande. L’argument facultatif fournit le corps de la requête. 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 que l’indicateur send() est défini.

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

  1. Si l’état n’est pas ouvert , lancez une exception InvalidStateError .
  2. Si l’indicateur send() est défini, lancez une exception InvalidStateError .
  3. Si la méthode de requête est GET ou HEAD , définissez body sur null.
  4. Si body est nul, passez à l’étape suivante.

Cependant, je ne pense pas que cela devrait être le cas, car la requête GET pourrait nécessiter un gros contenu corporel.

Donc, si vous utilisez XMLHttpRequest d’un navigateur, cela ne fonctionnera probablement pas.

Google, IBM, Microsoft et apigee semblent utiliser l’entête pour désigner une méthode réellement attendue, quelque chose comme X-HTTP-Method-Override: GET.
Selon le blog ci-dessus, à l’origine, ceci est introduit pour POST en tant que PUT sous forme.
Je pense que cette solution peut être utilisée dans ce problème.

En d’autres termes, je pense que POST avec l’en-tête X-HTTP-Method-Override: GET est la meilleure solution.