我正在为我们的应用程序开发一个新的RESTful webservice。
当客户端对某些实体进行GET操作时,客户端可以请求实体的内容。如果他们想添加一些参数(例如对列表进行排序),他们可以将这些参数添加到查询字符串中。
或者,我希望人们能够在请求正文中指定这些参数。HTTP/1.1似乎并没有明确禁止这样做。这将使他们能够指定更多信息,可能会使指定复杂的XML请求变得更容易。
我的问题:
- 总体而言,这是个好主意吗?
- 使用请求正文在GET请求中会导致HTTP客户端出现问题吗?
我正在为我们的应用程序开发一个新的RESTful webservice。
当客户端对某些实体进行GET操作时,客户端可以请求实体的内容。如果他们想添加一些参数(例如对列表进行排序),他们可以将这些参数添加到查询字符串中。
或者,我希望人们能够在请求正文中指定这些参数。HTTP/1.1似乎并没有明确禁止这样做。这将使他们能够指定更多信息,可能会使指定复杂的XML请求变得更容易。
我的问题:
是的。换句话说,任何HTTP请求消息都允许包含消息正文,并且因此必须考虑解析带有该正文的消息。然而,对于GET的服务器语义是受限制的,因此如果有正文,则对请求没有语义意义。解析要求与方法语义的要求是分开的。
因此,是的,您可以使用GET发送正文,但是不,这永远没有用。
这是HTTP / 1.1分层设计的一部分,一旦规范被分区(正在进行中),它将再次变得清晰。
....罗伊
是的,你可以使用GET发送请求正文,但它不应该有任何含义。如果您通过在服务器上解析它并根据其内容更改响应来赋予它含义,则忽略了HTTP / 1.1规范第4.3节中的此建议:
......如果请求方法不包括实体正文的定义语义,则在处理请求时应忽略消息正文SHOULD。
以下是HTTP/1.1规范第9.3节中GET方法的描述:
GET方法意味着检索由请求URI标识的任何信息([...])。
这表明,在GET请求中,请求正文不是资源标识的一部分,只有请求URI。
更新
被称为“HTTP/1.1规范”的RFC2616现已过时。在2014年,它被RFC7230-7237取代。引用“处理请求时应忽略消息正文”已被删除。现在只是“请求消息框架与方法语义无关,即使方法没有定义消息正文的使用”。第二个引用“GET方法意味着检索由请求URI标识的任何信息…”已被删除。-来自评论
GET请求消息中的有效载荷没有定义的语义;在GET请求上发送有效载荷正文可能会导致某些现有实现拒绝请求。
虽然在HTTP规范中没有明确禁止这样做,你是可以这么做的,但我建议避免这样做,因为人们不希望以这种方式操作。HTTP请求链有许多阶段,尽管它们“大多数”符合HTTP规范,但你所能保证的只有它们会按照Web浏览器的传统用法行事。(我考虑的是像透明代理、加速器、A /V工具包等东西。)
这是稳健性原则的精神大致上是“在接受时要宽松,在发送时要谨慎”,你不希望无故扩展规范的边界。
然而,如果你有充分的理由,请去做。
如果您尝试利用缓存,可能会遇到问题。代理服务器不会查看GET
请求体以查看参数是否影响响应。
Elasticsearch接受带有请求正文的GET请求。甚至似乎这是首选方式:Elasticsearch指南
一些客户端库(如Ruby驱动程序)可以在开发模式下将哭泣命令记录到标准输出,并广泛使用该语法。
curl -XGET 'http://localhost:9200/_count?pretty' -d ' { "query": { "match_all": {} } }'
等同于将有效负载包含在“source”参数中:curl -XGET 'http://localhost:9200/_count?pretty&source=%7B%22query%22%3A%7B%22match_all%22%3A%7B%7D%7D%7D'
- arun无论是restclient还是REST控制台都不支持此功能,但curl可以。
HTTP规范在第4.3节中指出:
如果请求方法的规范(第5.1.1节)不允许在请求中发送实体主体,则不得在请求中包含消息主体。
第5.1.1节将我们重定向到第9.x节以获取各种方法。它们中没有一个明确禁止包含消息主体。然而...
第5.2节说:
“通过检查请求URI和主机头字段来确定Internet请求所识别的确切资源。”第9.3节说:“GET方法意味着检索由请求URI标识的任何信息(以实体形式)。”这表明,在处理GET请求时,服务器不需要检查除请求URI和主机头字段之外的任何内容。总之,HTTP规范不会阻止您使用GET发送消息正文,但存在足够的歧义,使我不会惊讶如果并非所有服务器都支持它。”GET /contacts/100/addresses
返回具有id = 100
的人的地址集合。" - Josh M.GET
带有请求体?从规范上来看,你是可以的,但是不明智地这样做是不好的,正如我们所看到的。
RFC 7231 §4.3.1指出请求体“没有定义的语义”,但这并不意味着禁止这样做。如果你在请求中附加一个请求体,你的服务器/应用程序会对其进行解释。RFC进一步指出,GET可以是“对各种数据库记录的编程视图”。显然,这种视图很多时候需要大量的输入参数,而这些参数并不总是方便或安全地放在请求目标的查询组件中。
好处:我喜欢这种措辞。很清楚,使用此方法读取/获取资源不会对服务器产生任何可观测的副作用(该方法是“安全的”),而且无论第一次请求的结果如何,都可以重复请求以达到相同的预期效果(该方法是“幂等的”)。
缺点: HTTP/1.1 的早期草稿禁止 GET 请求具有正文,据称一些实现甚至直到今天都会删除正文、忽略正文或拒绝消息。例如,一个愚蠢的 HTTP 缓存可能仅从请求目标构造缓存键,对正文的存在或内容毫不知情。更加愚蠢的服务器可能如此无知,以至于将正文视为新请求,这有效地被称为“请求走私”(即“向一个设备发送请求,而另一个设备不知道”的行为-来源)。
由于我认为主要是因为实现方面的问题,正在进行的工作建议将 GET 正文归类为“SHOULD NOT”,除非[该请求]直接发给已经明确指出过这样的请求具有目的并将得到充分支持的原始服务器(强调是我的)。
解决方法:对于此方法存在的问题,可以采用一些小技巧来解决。例如,通过在查询组件中附加从主体派生的哈希值,无法识别主体的缓存将间接地变得可识别主体,或通过从服务器响应 cache-control: no-cache
标头来完全禁用缓存。
然而,当涉及到请求链时,人们通常不能控制或甚至意识到所有现有和未来的 HTTP 中介者以及它们如何处理 GET 请求主体。因此,这种方法必须被视为不可靠的。
POST
不具有幂等性!POST
是一个可替代的方法。POST 请求通常包括一个消息主体(只是为了记录,主体并非必需,参见 RFC 7230 §3.3.2)。RFC 7231 的第一个用例示例 (§4.3.3) 就是“向数据处理过程提供数据块”。因此,就像带有主体的 GET 请求一样,后端如何处理主体取决于您。
优点:可能是应用请求主体时更常用的方法,无论出于什么目的,所以,很可能会产生最少的团队成员噪音(有些人仍然错误地认为 POST 必须创建资源)。
此外,我们常常将参数传递给一个操作不断演变的数据的搜索函数,只有在响应中提供了明确的新鲜度信息时,POST响应才是可缓存的。
问题所在:POST请求没有被定义为幂等的,这导致请求重试存在犹豫。例如,在页面重新加载时,浏览器不愿意在未提示用户的情况下重新提交HTML表单。
解决方式:即使POST没有被定义为幂等,也并不意味着它不能是幂等的。事实上,RFC 7230 §6.3.1 写道:“知道(通过设计或配置)向给定资源发出POST请求是安全的用户代理可以自动重复该请求”。因此,除非您的客户端是HTML表单,否则这可能不是真正的问题。
QUERY
是至高无上的有一个新方法的提案,叫做QUERY
,它定义了消息体的语义,并将该方法定义为幂等。请参见此文档。
编辑:顺便说一下,我在发现一个代码库只使用PUT
请求进行服务器端搜索功能后,偶然遇到了这个StackOverflow问题。这是他们想要通过将参数作为请求体来实现幂等性的想法。然而,PUT
请求的问题在于请求体具有非常精确的语义。具体来说,PUT
“请求使用请求体中的状态创建或替换目标资源的状态”(RFC 7231 §4.3.4)。显然,这排除了PUT
作为可行选项。
你可以使用带有主体的GET或发送POST并放弃RESTish信仰(这并不是很糟糕,5年前只有一个该信仰的成员——他的评论链接在上面)。
两种方法都不是很好,但发送带有主体的GET可能可以防止一些客户端和服务器的问题。
执行POST可能会在一些RESTish框架中遇到障碍。
Julian Reschke建议使用非标准HTTP头,例如“SEARCH”,这可能是一种优雅的解决方案,但更不可能被支持。
最具生产力的做法可能是列出每个方法可行和不可行的客户端。
无法发送带有主体的GET请求的客户端(我所知道的):
可以发送带有主体的GET请求的客户端:
可以从GET请求中检索主体的服务器和库:
可能会从GET请求中删除主体的服务器(和代理):
SEARCH
方法可能会在中途出错吗?如果代理不理解一个方法,它们应该按原样通过,所以我不太确定你为什么认为它会导致任何错误... - Alexis Wilke根据RFC 2616第4.3节, “消息正文”:
服务器应在任何请求上读取和转发消息正文;如果请求方法未包含实体主体的已定义语义,则处理请求时应忽略消息正文。
也就是说,服务器应始终从网络中读取任何提供的请求正文(检查Content-Length或读取分块正文等)。此外,代理应转发它们收到的任何此类请求正文。然后,如果RFC对于给定方法的正文定义了语义,服务器可以实际使用请求正文生成响应。但是,如果RFC 未定义正文的语义,则服务器应将其忽略。
这与上面来自Fielding的引用一致。
第9.3节,“GET”描述了GET方法的语义,并且没有提到请求正文。因此,服务器应忽略在GET请求上接收到的任何请求正文。