带请求正文的 HTTP GET

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

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

另外,我希望人们能够在请求正文中指定这些参数。 HTTP / 1.1似乎没有明确禁止这样做。这将使他们能够指定更多信息,可能使指定复杂的 XML 请求更加容易。

我的问题:

  • 这是个好主意吗?
  • HTTP 客户端在 GET 请求中使用请求主体时会遇到问题吗?

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

答案

罗伊 · 菲尔丁(Roy Fielding)的评论,其中包括一个带有 GET 请求的物体

是。换句话说,任何 HTTP 请求消息都允许包含消息主体,因此必须在解析消息时牢记这一点。但是,GET 的服务器语义受到限制,以使主体(如果有的话)对请求没有语义含义。解析要求与方法语义要求分开。

因此,是的,您可以使用 GET 发送正文,否,这样做永远没有用。

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

.... 罗伊

是的,您可以使用 GET 发送请求正文,但它没有任何意义。如果您通过在服务器上解析它并根据其内容更改响应来赋予它含义,那么您将忽略HTTP / 1.1 规范第 4.3 节建议:

[...] 如果请求方法不包括用于一个实体主体定义的语义,则该消息体应该被处理请求时忽略。

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

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

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

更新现在称为 “HTTP / 1.1 规范” 的 RFC2616 已过时。在 2014 年,它被 RFC 7230-7237 取代。引用 “处理请求时应忽略消息正文” 已被删除。现在只是 “请求消息框架独立于方法语义,即使该方法未定义消息主体的任何用法”,第二个引号 “GET 方法意味着检索任何由请求 URI 标识的信息……” 已被删除。 - 来自评论

尽管您可以这样做,但在 HTTP 规范未明确排除它的情况下,我建议您避免这样做仅是因为人们不希望事情以这种方式起作用。 HTTP 请求链中有许多阶段,尽管它们 “大部分” 都符合 HTTP 规范,但您唯一可以放心的是它们的行为将与 Web 浏览器的传统用法相同。 (我正在考虑透明代理,加速器,A / V 工具包等)

这是 “ 健壮性原则 ” 背后的精神,即 “在接受的内容上是自由的,在发送的内容上是保守的”,您不想在没有充分理由的情况下突破规范的界限。

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

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

restclientREST 控制台都不支持此功能,但是 curl 支持。

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 发送消息正文,但是有足够的歧义,如果不是所有服务器都支持的话,它也不会令我感到惊讶。

Elasticsearch 接受带有主体的 GET 请求。甚至似乎这是首选方式: Elasticsearch 指南

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

您试图实现的目标已经用一种更为通用的方法完成了很长时间,并且该方法不依赖于在 GET 中使用有效负载。

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

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

也就是说,无论如何,URI 都是针对非 ASCII 的内容进行编码的,因此 application / x-www-form-urlencoded 和 multipart / form-data 也是如此。如果您打算支持 ReSTful 场景,我建议使用此方法,而不是创建另一种自定义 json 格式。

您可以发送带有主体的 GET 或发送 POST 并放弃 RESTish 宗教信仰(这还不错,五年前,只有一种信仰的人 - 他的评论在上面链接)。

做出好的决定都不是,但是发送 GET 正文可能会防止某些客户端和某些服务器出现问题。

使用某些 RESTish 框架进行 POST 可能会遇到障碍。

朱利安 · 雷施克(Julian Reschke)上面建议使用非标准的 HTTP 标头(例如 “SEARCH”),这可能是一个很好的解决方案,只是它受到支持的可能性更低。

列出可以执行以上所有操作的客户端可能会最有效率。

无法发送带有主体的 GET 的客户端(我知道):

  • XmlHTTPRequest 提琴手

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

  • 大多数浏览器

可以从 GET 检索正文的服务器和库:

  • 阿帕奇
  • 的 PHP

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

哪个服务器会忽略它? – 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 错误请求,这些请求由 AkamaiGhost 提供。

因此,我不建议将 GET 请求与主体实体一起使用。

我将这个问题提交给 IETF HTTP WG。罗伊 · 菲尔丁(Roy Fielding,1998 年 http / 1.1 文件的作者)的评论是,

“... 实现将被破坏,无法执行任何其他操作,除非解析并丢弃该正文”

RFC 7213(HTTPbis)指出:

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

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

如果在 GET 上包含正文, 肯定有各种代理会以各种方式中断您的请求。

因此,总而言之,不要这样做。

RFC 2616 的第 4.3 节 “消息正文” 开始:

服务器应根据任何请求读取并转发消息正文;如果请求方法不包括为实体主体定义的语义,则在处理请求时应忽略消息主体。

也就是说,服务器应始终从网络读取任何提供的请求正文(检查 Content-Length 或读取分块正文等)。同样,代理应转发收到的任何此类请求正文。然后,如果 RFC 为给定方法定义了主体的语义,则服务器实际上可以使用请求主体来生成响应。但是,如果 RFC 没有为主体定义语义,则服务器应忽略它。

这与上面 Fielding 的报价一致。

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