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

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

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

我的问题:

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

https://www.rfc-editor.org/rfc/rfc2616


当前回答

你可以用身体发送GET,也可以发送POST,然后放弃RESTish宗教信仰(这并不是很糟糕,5年前只有一个信仰的成员——他在上面的评论)。

这两个都不是很好的决定,但发送GET主体可能会防止某些客户端和某些服务器出现问题。

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

Julian Reschke在上文中建议使用非标准HTTP头,如“SEARCH”,这可能是一个很好的解决方案,只是它不太可能被支持。

列出能够和不能做到上述每一项的客户可能是最有成效的。

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

XmlHTTPRequest Fiddler

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

大多数浏览器

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

阿帕奇PHP文件

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

?

其他回答

你可以用身体发送GET,也可以发送POST,然后放弃RESTish宗教信仰(这并不是很糟糕,5年前只有一个信仰的成员——他在上面的评论)。

这两个都不是很好的决定,但发送GET主体可能会防止某些客户端和某些服务器出现问题。

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

Julian Reschke在上文中建议使用非标准HTTP头,如“SEARCH”,这可能是一个很好的解决方案,只是它不太可能被支持。

列出能够和不能做到上述每一项的客户可能是最有成效的。

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

XmlHTTPRequest Fiddler

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

大多数浏览器

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

阿帕奇PHP文件

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

?

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

如果您希望允许带有主体的GET请求,一种方法是支持带有标题“X-HTTP-Method-Override:GET”的POST请求。此处描述如下:https://en.wikipedia.org/wiki/List_of_HTTP_header_fields.这个头意味着当方法是POST时,应该将请求视为GET。POST允许Body,因此您可以确定没有人会丢弃GET请求的有效负载。

此标头通常用于通过一些不识别这些方法的代理生成PATCH或HEAD请求,并用GET替换它们(调试总是很有趣!)。

得到,用身体!?

在规范方面,你可以,但是,这不是一个好主意,我们将看到这样做是不明智的。

RFC 7231§4.3.1规定身体“没有定义的语义”,但这并不是说它是被禁止的。如果你在请求中附加了一个主体,那么你的服务器/应用程序将从中得到什么,这取决于你。RFC继续声明GET可以是“各种数据库记录的编程视图”。显然,这样的视图多次由大量输入参数定制,这些参数放在请求目标的查询组件中并不总是方便甚至安全的。

好的:我喜欢冗长的辞藻。很明显,读取/获取资源不会对服务器产生任何可观察到的副作用(该方法是“安全的”),并且无论第一个请求的结果如何,都可以以相同的预期效果重复请求(该方法“幂等”)。

糟糕的是:HTTP/1.1的早期草案禁止GET具有主体,而且据称,某些实现直到今天都会丢弃主体、忽略主体或拒绝消息。例如,哑HTTP缓存可能只从请求目标构建缓存密钥,而忽略了主体的存在或内容。更愚蠢的服务器可能会如此无知,以至于将主体视为一个新的请求,这实际上被称为“请求走私”(这是将“请求发送到一个设备,而另一个设备不知道它”的行为-源)。

由于我认为主要关注的是实现之间的不可操作性,正在进行的工作建议将GET主体分类为“不应该”,“除非(请求)直接发送给一个源服务器,该服务器先前已在带内或带外表示此类请求有目的,并且将得到充分支持”(我的重点)。

解决方法:对于这种方法的一些问题,可以使用一些技巧。例如,不知道主体的缓存可以通过简单地将从主体派生的哈希附加到查询组件来间接地变得知道主体,或者通过响应缓存控制(没有来自服务器的缓存头)来完全禁用缓存。

唉,当涉及到请求链时,人们往往无法控制——甚至无法意识到所有当前和未来的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,它确实定义了消息体的语义,并将该方法定义为幂等。看看这个。

编辑:顺便说一句,我在发现了一个代码库后,无意中遇到了这个StackOverflow问题,在该代码库中,他们只将PUT请求用于服务器端搜索功能。这是他们的想法,包括一个带参数的体,并且也是幂等的。遗憾的是,PUT的问题在于请求体具有非常精确的语义。具体来说,PUT“请求创建目标资源的状态或将其替换为[正文]中的状态”(RFC 7231§4.3.4)。显然,这排除了PUT作为一个可行的选项。

根据RFC 2616第4.3节“消息正文”:

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

也就是说,服务器应该始终从网络中读取任何提供的请求体(检查内容长度或读取分块体等)。此外,代理应该转发他们收到的任何此类请求。然后,如果RFC为给定方法的主体定义了语义,那么服务器实际上可以使用请求主体来生成响应。然而,如果RFC没有定义主体的语义,那么服务器应该忽略它。

这与上面Fielding的引用一致。

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