在设计RESTful service时,可不可以在一个GET请求中携带body? 答案是:最好不要这些做。

有一些RESTful service允许GET请求携带request body,比如Elasticsearch的search接口。 但这不是一种好的实现。 HTTP的规范在定义GET的语义时,规定server端应该只根据请求的URI来返回response。

[…] if the request method does not include defined semantics for an entity-body, then the message-body SHOULD be ignored when handling the request. […] The GET method means retrieve whatever information ([…]) is identified by the Request-URI. […] A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.

一个特定的http server,比如Elasticsearch,可以支持带body的GET。 但是也有很多http server会遵守规范,忽略GET的body,比如代理、cache server等。 假如在Elasticsearch前放置一个http cache server,比如Varnish,那么Varnish在丢弃GET的body后会返回一个错误的响应。

另外,一些http的客户端也可能不支持发送带body的GET请求,比如一些Javascprit的http client。 (虽然用X-HTTP-Method-Override header可以让http client以POST的方式发送GET请求来绕过这个限制, 但是如果请求流过某些代理,这些自定义的header可能会被丢弃。)

所以如果要在GET请求里携带数据,最好放在query string里,或者自定义的header(比如X-CSRF-TOKEN)、cookie里。

实际上,Elasticsearch也意识到这个问题,所以它支持通过query string来进行搜索。 (把整个body url encode之后放在source参数里,然后在source_content_type参数里指定source的内容格式是application/json。) Elasticsearch还保留着对带body的GET的支持,其中一个原因是大部分http client对URL的长度有限制。 如果你不像Elasticsearch这样需要在GET请求中发送大量数据,最好还是使用query string来发送数据。