题 带请求正文的HTTP GET


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

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

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

我的问题:

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

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


1415
2018-06-10 20:47


起源


将params放入请求体中有什么好处? - Rich Apodaca
优点是允许轻松发送XML或JSON请求体,它没有长度限制,并且更容易编码(UTF-8)。 - Evert
如果您所追求的是一种允许请求主体的安全且幂等的方法,您可能需要查看SEARCH,PROPFIND和REPORT。当然不使用GET并且请求主体或多或少地破坏了缓存。 - Julian Reschke
@fijiaaron:3年后,从那时起我就有了丰富的网络服务经验。这基本上是我过去几年一直在做的事情。我可以肯定地说,在GET请求中添加一个主体确实是一个非常糟糕的主意。前两个答案就像一块石头。 - Evert
@Ellesedil:简单地说:由于HTTP的设计方式,存在使用GET而不是POST的优点。当你以这种方式违反标准时,这些优势就不复存在了。因此,只有一个理由可以使用GET +请求体而不是POST:Aesthetics。不要牺牲坚固的设计而不是美学。 - Evert


答案:


罗伊菲尔丁关于将一个机构纳入GET请求的评论

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

所以,是的,你可以发送一个GET的主体,不,它永远不会有用   这样做。

这是HTTP / 1.1分层设计的一部分   一旦规范被分区(正在进行中),就再次清除。

罗伊....

是的,您可以使用GET发送请求正文,但它不应该有任何意义。如果你通过在服务器上解析它来赋予它意义 根据内容更改您的回复,那你忽略了这个推荐 HTTP / 1.1规范,第4.3节

[...]如果是请求方法      不包含实体主体的定义语义,然后是      邮件正文 应该 处理请求时被忽略。

和GET方法的描述 HTTP / 1.1规范,第9.3节

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

其中声明请求主体不是GET请求中资源标识的一部分,只是请求URI。


1208
2018-06-11 20:27



哪个服务器会忽略它? - fijiaaron
缓存/代理是你最容易破解的两件事,是的。 “语义学”只是另一种说法“制造其他组件的人希望其他组件运行的方式”。如果你违反了语义,那么你更有可能在人们编写期望你尊重这些语义的东西的地方看到事情发生。 - Stuart P. Bentley
Elasticsearch是一个在GET中使用HTTP请求主体的相当重要的产品。根据他们的手册,HTTP请求是否应该支持拥有正文是未定义的。我个人不熟悉填写GET请求机构,但他们似乎有不同的意见,他们必须知道他们在做什么。 elastic.co/guide/en/elasticsearch/guide/current/... - GordonM
@iwein给GET请求机构的意思实际上就是这样 不 违反规范。 HTTP / 1.1指定服务器应该忽略主体,但是 RFC 2119 指定如果实施者有充分的理由可以忽略“应该”的条款。相反,客户 不 如果它假定改变GET身体将违反规范 不 改变反应。 - Emil Lundberg
引用为“HTTP / 1.1规范”的RFC2616现已过时。 2014年,它被RFC 7230-7237取代。引用“处理请求时,应该忽略message-body“一直都是 删除。它现在只是“请求消息成帧独立于方法语义,即使该方法没有为消息体定义任何用途“第二个引用”GET方法意味着检索由Request-URI标识的任何信息“是的 删除。所以,我建议编辑@Jarl的答案 - Artem Nakonechny


当你 能够 这样做,只要HTTP规范没有明确排除,我建议避免使用它只是因为人们不希望事情以这种方式工作。 HTTP请求链中有许多阶段,虽然它们“大部分”符合HTTP规范,但您唯一可以肯定的是它们的行为与Web浏览器传统上一样。 (我在想透明代理,加速器,A / V工具包等等)

这就是背后的精神 稳健性原则 大致“你接受的是自由主义,你发送的东西是保守的”,你不想在没有充分理由的情况下突破规范的界限。

但是,如果你有充分的理由,那就去吧。


234
2018-06-10 20:53



鲁棒性原则是有缺陷的。如果你对自己接受的东西很自由,那么如果你在领养方面取得任何成功,你就会得到废话,只因为你接受废话。这将使您更难以发展您的界面。只看HTML。这就是行动中的反叛原则。 - Eugene Beresovsky
我认为协议的采用(和滥用)的成功和广度说明了鲁棒性原则的价值。 - caskey
你有没有试过解析真正的HTML?自己实施它是不可行的,这就是为什么几乎每个人 - 包括谷歌(Chrome)和苹果(Safari)等真正的大玩家都没有这样做,而是依靠现有的实现(最终他们都依赖于KDE的KHTML)。这种重用当然很好,但你试过在.net应用程序中显示html吗?这是一场噩梦,因为你要么必须嵌入一个 - 非托管的 - IE(或类似的)组件,它的问题和崩溃,要么你使用可用的(在codeplex上)托管组件甚至不允许你选择文本。 - Eugene Beresovsky
HTTP规范不仅允许具有GET请求的正文数据,而且这也是常见做法:流行的ElasticSearch引擎的_search API建议使用JSON正文中附加的查询进行GET请求。作为对不完整HTTP客户端实现的让步,它还允许POST请求。 - Christian Pietsch
@ user437899如果尝试发送带有GET请求的正文,.NET Framework将抛出异常。 - Sam Harwell


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


116
2018-06-10 21:10



使用ETag / Last-Modified标头字段以这种方式提供帮助:当使用“条件GET”时,代理/缓存可以对此信息起作用。 - jldupont
@jldupont缓存使用验证器的存在来了解是否可以重新验证过时响应,但是,它们不会用作主缓存密钥或辅助缓存密钥的一部分。 - Darrel Miller


也不 RESTClient实现 也不 REST控制台 支持这个,但卷曲确实。

HTTP规范 在第4.3节中说

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

第5.1.1节 将我们重定向到第9.x节的各种方法。他们都没有明确禁止包含消息体。然而...

第5.2节 说

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

第9.3节 说

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

这一起表明,在处理GET请求时,服务器不是 需要 检查Request-URI和Host头字段的其他任何内容。

总之,HTTP规范并不会阻止您使用GET发送消息体,但是如果所有服务器都不支持它,那么它就不会让我感到惊讶。


60
2018-03-27 10:41



Paw还可以选择支持使用正文的GET请求,但必须在设置中启用它。 - s.Daniel
“GET方法意味着检索由Request-URI标识的任何信息(以实体的形式)。”那么,拥有获取所有实体的GET端点在技术上是非法/错误的吗?例如。 GET /contacts/100/addresses 返回该人员的地址集合 id=100。 - Josh M.
用于测试REST API的可靠的Java库不支持使用正文的GET请求。 Apache HttpClient也不支持它。 - Paulo Merson


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

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


37
2017-12-03 11:15



想知道为什么Elasticsearch允许这样做。这意味着此查询将有效负载的所有文档计数到GET请求 curl -XGET 'http://localhost:9200/_count?pretty' -d ' { "query": { "match_all": {} } }'  相当于包含有效载荷 source PARAM: curl -XGET 'http://localhost:9200/_count?pretty&source=%7B%22query%22%3A%7B%22match_all%22%3A%7B%7D%7D%7D' - arun
复杂查询可以达到http标头最大长度。 - s.Daniel
它正在阅读弹性搜索文档,这些文档将我带到了这个问题,因为我认为包含一个正文被认为是不好的做法 - PatrickWalker
它甚至不需要是一个复杂的查询。即使是简单的滚动也可以返回一个非常长的scroll_id(在具有许多分片的群集中),如果在那里添加,将导致超出最大URL长度。 - Brent Hronik
Elasticsearch使用POST支持相同的请求。他们只选择在GET中允许一个正文,因为他们认为在查询数据时GET在语义上比POST更正确。有趣的是Elasticsearch在这个帖子中被提到了这么多。我不会用一个例子(虽然来自受欢迎的产品)作为遵循这种做法的理由。 - DSO


您尝试实现的目标已经使用更常见的方法完成了很长时间,并且不依赖于使用GET的有效负载。

您可以简单地构建特定的搜索媒体类型,或者如果您想要更加RESTful,请使用类似OpenSearch的内容,并将请求POST到服务器指示的URI,例如/ search。然后,服务器可以生成搜索结果或构建最终URI并使用303重定向。

这具有遵循传统PRG方法的优点,有助于缓存中介缓存结果等。

也就是说,无论如何,URI都会被编码为非ASCII的任何东西,application / x-www-form-urlencoded和multipart / form-data也是如此。如果您的目的是支持ReSTful场景,我建议使用此而不是创建另一种自定义json格式。


25
2018-06-10 22:47



您可以简单地构建特定的搜索媒体类型 你能详细说说吗? - Piotr Dobrogost
我说你可以创建一个名为application / vnd.myCompany.search + json的媒体类型,其中包含您希望客户端发出的搜索模板类型,然后客户端可以将其作为POST发送。正如我所强调的那样,已经存在一种媒体类型,它被称为OpenSearch,当您可以使用现有标准实现场景时,应该在自定义路由上重用现有媒体类型。 - SerialSeb
这很聪明,但过于复杂,效率低下。现在你必须发送一个带有你的搜索条件的POST,从你的POST获得一个URI作为回复,然后将带有搜索条件URI的GET发送到服务器,以便获得标准并将结果发回给你。 (除了在URI中包含URI在技术上是不可能的,因为你不能在不超过255个字符的内容中发送最多255个字符的内容 - 所以你必须使用部分标识符和服务器然后需要知道如何解析POSTed搜索条件的URI。) - fijiaaron


哪个服务器会忽略它? - fijiaaron 2012年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.

您还可以从Bing,Apple等获得400 Bad Request,这是由AkamaiGhost提供的。

所以我不建议将GET请求与body实体一起使用。


23
2018-06-29 21:26



这个例子毫无意义,因为通常当人们要添加身体时 GET 请求,这是因为他们自己的自定义服务器能够处理它。因此,问题是其他“移动部件”(浏览器,缓存等)是否能够正常工作。 - Pacerier


你可以发送一个GET与一个正文或发送一个POST并放弃RESTish宗教信仰(它不是那么糟糕,5年前只有一个信仰的成员 - 他的评论在上面联系)。

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

执行POST可能会遇到一些RESTish框架的障碍。

Julian Reschke建议上面使用像“SEARCH”这样的非标准HTTP标头,这可能是一个优雅的解决方案,除了它更不可能得到支持。

列出能够和不能完成上述各项工作的客户可能是最有效的。

无法通过身体发送GET的客户(我知道):

  • XmlHTTPRequest Fiddler

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

  • 大多数浏览器

可以从GET检索主体的服务器和库:

  • 阿帕奇
  • PHP

从GET中剥离主体的服务器(和代理):


21
2017-08-30 21:41



当Content-Length为0或未设置时,Squid 3.1.6也会剥离GET主体,否则即使设置了长度,也会发回HTTP 411 Length - rkok
提琴手会,但它警告你。 - toddmo