带请求正文的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])
发起请求。 可选参数提供请求主体。 如果请求方法是
GET
或HEAD
则参数将被忽略。如果任何状态未打开或
send()
标志已设置,则会引发InvalidStateError
exception。
send( body )
方法必须运行这些步骤:
- 如果状态没有打开 ,则抛出一个
InvalidStateError
exception。- 如果
send()
标志被设置,则抛出一个InvalidStateError
exception。- 如果请求方法是
GET
或HEAD
,请将body设置为null。- 如果body为null,则转到下一步。
虽然,我不认为这应该是因为GET请求可能需要大量的内容。
所以,如果你依赖于浏览器的XMLHttpRequest,那很可能不起作用。
- http://fandry.blogspot.jp/2012/03/x-http-header-method-override-and-rest.html
- http://docs.apigee.com/api-services/reference/endpoint-properties-reference:enable.method.override
谷歌,IBM,微软和apigee似乎使用头来指定真正预期的方法,如X-HTTP-Method-Override:GET。
根据上面的博客,原来,这是以POST的forms作为PUT引入的。
我认为这个解决scheme可以用在这个问题上。
换句话说,我认为POST与X-HTTP-Method-Override:GET头是更好的解决scheme。