带请求正文的HTTP GET

我正在为我们的应用程序开发一个新的RESTful Web服务。

在某些实体上进行GET时,客户端可以请求实体的内容。 如果他们想要添加一些参数(例如sorting列表),他们可以在查询string中添加这些参数。

或者我希望人们能够在请求正文中指定这些参数。 HTTP / 1.1似乎并没有明确地禁止这个。 这将允许他们指定更多的信息,可以更容易地指定复杂的XML请求。

我的问题:

  • 这完全是一个好主意吗?
  • HTTP客户端在GET请求中使用请求体是否有问题?

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

Roy Fielding关于包含GET请求的内容的评论 。

是。 换句话说,任何HTTP请求消息都被允许包含一个消息体,因此必须用这个消息parsing消息。 但是,GET的服务器语义是受限制的,如果有的话,主体对请求没有语义含义。 parsing的要求与方法语义的要求是分开的。

所以,是的,你可以发送一个GET的身体,不,这样做是没有用的。

这是HTTP / 1.1的分层devise的一部分,一旦规范分区(工作正在进行),它将再次变得清晰。

罗伊….

是的,你可以用GET发送一个请求体,但它不应该有任何意义。 如果通过在服务器上parsing它并根据其内容更改响应来赋予其意义,那么您将忽略HTTP / 1.1规范中的这一build议, 即第4.3节 :

如果请求方法不包含实体主体的已定义语义,则在处理请求时应该忽略消息主体。

以及HTTP / 1.1规范中 GET方法的描述,第9.3节 :

GET方法意味着检索由Request-URI标识的任何信息([…])。

其中规定request-body不是GET请求中资源标识的一部分,而只是请求URI。

虽然你可以这样做,只要它没有被HTTP规范明确排除,我build议避免它,只是因为人们不希望事情这样工作。 在HTTP请求链中有很多阶段,尽pipe它们“大部分”符合HTTP规范,但唯一确定的是它们将像Web浏览器一样使用。 (我正在考虑诸如透明代理,加速器,A / V工具包等等)

这就是“ 稳健性原则 ”背后的精神,大体上“在你接受的内容中是自由的,在你所发的内容上是保守的”,你不想没有理由地推动规范的界限。

但是,如果你有一个很好的理由,就去做吧。

如果您尝试利用caching,则可能会遇到问题。 代理不会在GET主体中查看参数是否对响应有影响。

restclient和REST控制台都不支持这个,但curl确实。

HTTP规范在4.3节中提到

如果请求方法(5.1.1节)的规范不允许在请求中发送实体主体,则消息主体不能包含在请求中。

第5.1.1节将我们redirect到第9.x节的各种方法。 他们都没有明确禁止列入一个消息体。 然而…

第5.2节说

由Internet请求标识的确切资源是通过检查Request-URI和Host头字段来确定的。

和9.3节说

GET方法意味着检索任何信息(以实体的forms)由Request-URI标识。

它们一起表明,在处理GET请求时,服务器不需要检查Request-URI和Host头字段以外的任何东西。

总之,HTTP规范并不妨碍你使用GET发送消息体,但是如果所有服务器都不支持它,那么就不足为奇了。

Elasticsearch接受带有主体的GET请求。 甚至似乎这是首选的方式: http : //www.elasticsearch.org/guide/en/elasticsearch/reference/current/common-options.html#_request_body_in_query_string

某些客户端库(如Ruby驱动程序)可以在开发模式下将cry命令logging到stdout,并且正在广泛使用该语法。

哪个服务器会忽略它? – fijiaaron 12年8月30日在21:27

谷歌比忽略它更糟糕,它会认为这是一个错误

用一个简单的netcat自己尝试一下:

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

(1234内容之后是CR-LF,所以总共有6个字节)

你会得到:

 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. 

AkamaiGhost还提供了Bing,Apple等的400个错误请求。

所以我不会build议使用GET请求与身体实体。

你试图实现已经很长时间以更常见的方法完成,并且不依赖GET使用有效载荷。

您可以简单地构build您的特定search媒体types,或者如果您希望更加RESTful,请使用OpenSearch之类的内容,并将请求发送到服务器指示的URI(称为/ search)。 然后服务器可以生成search结果或者build立最终的URI并使用303redirect。

这具有遵循传统PRG方法的优点,有助于caching中间人caching结果等。

也就是说,对于任何不是ASCII的东西,URI都是编码的,application / x-www-form-urlencoded和multipart / form-data也是如此。 如果你的意图是支持ReSTful场景,我build议使用这个而不是创build另一个定制的json格式。

你可以发送一个GET的身体,或发送一个POST,放弃REST的宗教信仰(这不是那么糟糕,5年前,只有一个信仰的成员 – 他的意见链接上面)。

两者都不是很好的决定,但是发送一个GET机构可以防止某些客户端和某些服务器出现问题。

做一个POST可能会有一些REST框架的障碍。

Julian Reschkebuild议上面使用非标准的HTTP标头,比如“SEARCH”,这可能是一个很好的解决scheme,只是它不太可能被支持。

列出可以或不可以做上述每一项的客户可能是最有成效的。

无法发送身体(我知道)的客户端:

  • XmlHTTPRequest提琴手

可以发送GET正文的客户端:

  • 大多数浏览器

可以从GET获取正文的服务器和库:

  • 阿帕奇
  • PHP

从GET中删除正文的服务器(和代理):

RFC 2616第4.3节 “消息体”:

服务器应该读取并转发任何请求的消息体; 如果请求方法不包含实体主体的定义的语义,那么在处理请求时应该忽略消息主体。

也就是说,服务器应该总是从networking中读取任何提供的请求主体(检查Content-Length或者读取分块的主体等)。 另外,代理人应该转发他们收到的任何请求。 然后,如果RFC为给定的方法定义了主体的语义,那么服务器实际上可以使用请求主体来生成响应。 但是,如果RFC 没有定义正文的语义,那么服务器应该忽略它。

这与上面的Fielding的引用一致。

第9.3节 “GET”描述了GET方法的语义,并没有提到请求体。 因此,服务器应该忽略它在GET请求上收到的任何请求体。

我把这个问题提交给IETF HTTP工作组。 Roy Fielding(1998年http / 1.1文档的作者)的评论是这样的

“……实施将被打破,除了parsing和丢弃该机构,如果收到”

RFC 7213(HTTPbis)状态

“GET请求消息中的有效载荷没有定义的语义;”

现在看来很清楚,意图是GET请求体的语义是被禁止的,这意味着请求体不能被用来影响结果。

那里有代理,如果你在GET上包含一个正文, 肯定会以各种方式破坏你的请求。

所以总之,不要这样做。

如果你真的想发送可caching的JSON / XML正文到Web应用程序,唯一合理的地方放置您的数据是查询string编码与RFC4648:Base 64编码与URL和文件名安全字母表 。 当然,你可以urlencode JSON,并把它放在URL参数的值,但Base64给出的结果较小。 请记住,存在URL大小限制,请参阅不同浏览器中URL的最大长度是多less? 。

你可能会认为Base64的padding = character可能对URL的参数值不好,但是看起来并不是这样 – 请看这个讨论: http : //mail.python.org/pipermail/python-bugs-list/2007-February/037195.html 。 但是,不应该把没有参数名称的编码数据,因为带有填充的编码string将被解释为具有空值的参数键。 我会使用像?_b64=<encodeddata>

我很不高兴REST作为协议不支持OOP和Get方法是certificate。 作为解决scheme,您可以将您的DTO序列化为JSON,然后创build查询string。 在服务器端,您可以将查询string反序列化到DTO。

看看:

  • ServiceStack中基于消息的devise
  • 使用WCF构build基于RESTful消息的Web服务

基于消息的方法可以帮助您解决Get方法限制。 您可以像请求主体一样发送任何DTO

Nelibur Web服务框架提供了可以使用的function

 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 

我不会提出这个build议,这是违背标准的做法,并没有提供太多的回报。 你想保持正文的内容,而不是选项。

那么不合格的base64编docker呢? “SOMETHINGAPP-PARAMS:sdfSD45fdg45 / AS”

长度限制hm。 你不能让你的POST处理区分意义吗? 如果你想要简单的参数如sorting,我不明白为什么这将是一个问题。 我想这是你担心的确定性。

恕我直言,你可以在URL发送JSON编码(即encodeURIComponent ),这样你就不会违反HTTP规范,并让你的JSON到服务器。

例如,它适用于Curl,Apache和PHP。

PHP文件:

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

控制台命令:

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

输出:

 GET {"the": "body"} 

根据XMLHttpRequest,这是无效的。 从标准 :

4.5.6 send()方法

 client . send([ body = null]) 

发起请求。 可选参数提供请求主体。 如果请求方法是GETHEAD则参数将被忽略。

如果任何状态未打开send()标志已设置,则会引发InvalidStateErrorexception。

send( body )方法必须运行这些步骤:

  1. 如果状态没有打开 ,则抛出一个InvalidStateErrorexception。
  2. 如果send()标志被设置,则抛出一个InvalidStateErrorexception。
  3. 如果请求方法是GETHEAD ,请将body设置为null。
  4. 如果body为null,则转到下一步。

虽然,我不认为这应该是因为GET请求可能需要大量的内容。

所以,如果你依赖于浏览器的XMLHttpRequest,那很可能不起作用。

谷歌,IBM,微软和apigee似乎使用头来指定真正预期的方法,如X-HTTP-Method-Override:GET。
根据上面的博客,原来,这是以POST的forms作为PUT引入的。
我认为这个解决scheme可以用在这个问题上。

换句话说,我认为POST与X-HTTP-Method-Override:GET头是更好的解决scheme。