HTTP DELETE请求是否允许实体主体?

发出HTTP DELETE请求时,请求URI应该完全标识要删除的资源。但是,是否允许添加额外的元数据作为请求实体主体的一部分?

678662 次浏览

规格没有明确禁止或阻止它,所以我倾向于说它是允许的。

微软以同样的方式看待它(我可以听到观众的喃喃自语),他们在MSDN关于ADO.NET数据服务框架的DELETE方法的文章中声明:

如果DELETE请求包含实体主体,则忽略该主体[…]

此外,以下是RFC2616(HTTP 1.1)对请求的说明:

  • 只有当消息正文存在时,主体才存在(第7.2节)
  • 消息正文的存在通过包含Content-LengthTransfer-Encoding标头来表示(第4.3节)
  • 当请求方法的规范不允许发送主体时,不得包含消息正文(第4.3节)
  • 仅在TRACE请求中明确禁止主体,所有其他请求类型都不受限制(第9节,特别是9.8节)

对于响应,这已被定义为:

  • 是否包含消息正文取决于请求方法响应状态(第4.3节)
  • 在对HEAD请求的响应中明确禁止消息正文(第9节,特别是9.4节)
  • 在1xx(信息),204(无内容)和304(未修改)响应中明确禁止消息正文(第4.3节)
  • 所有其他响应都包含一个消息主体,尽管它的长度可能为零(第4.3节)

在我看来,rfc2616没有指定这一点。

第4.3节:

请求中消息体的存在由中包含内容长度或传输编码标头字段请求的消息标头。消息正文不得包含在如果请求方法的规范(第5.1.1节),则请求不允许在请求中发送实体主体。服务器应该在任何请求上读取并转发消息正文;如果请求方法不包括实体体的定义语义学,那么处理请求时应该忽略消息体。

第9.7节:

DELETE方法请求源服务器删除资源由请求URI标识。此方法可能会被人类覆盖对源服务器进行干预(或其他方式)。客户端不能保证操作已经进行,即使从源服务器返回的状态码表明该操作已成功完成。但是,服务器不应该表示成功,除非在给出响应时,它打算删除资源或将其移动到无法访问的位置。

成功的响应应该是200(OK),如果响应包含描述状态的实体,202(接受)如果操作没有已颁布或204(无内容),如果该行动已颁布但是响应不包括实体。

如果请求通过缓存并且请求URI标识一个或多个当前缓存的实体,这些条目应该是处理为陈旧。对此方法的响应不可缓存。c

因此,它没有明确允许或禁止,并且沿途的代理可能会删除消息正文(尽管它应该读取并转发它)。

Tomcat和Jetty的某些版本似乎忽略了存在的实体主体。如果您打算接收它,这可能会很麻烦。

2014年对HTTP 1.1规范的更新(rfc7231)明确允许DELETE请求中的实体主体:

DELETE请求消息中的有效负载没有定义的语义学;在DELETE请求上发送有效负载正文可能会导致某些现有实现拒绝该请求。

ElasticSearch似乎使用了这个:https://www.elastic.co/guide/en/elasticsearch/reference/5.x/search-request-scroll.html#_clear_scroll_api

这意味着Netty支持这一点。

就像评论中提到的那样,情况可能不再是这样了

在删除请求中使用主体的一个原因是乐观并发控制。

您读取记录的版本1。

GET /some-resource/1200 OK { id:1, status:"unimportant", version:1 }

您的同事阅读记录的版本1。

GET /some-resource/1200 OK { id:1, status:"unimportant", version:1 }

您的同事更改记录并更新数据库,从而将版本更新为2:

PUT /some-resource/1 { id:1, status:"important", version:1 }200 OK { id:1, status:"important", version:2 }

您尝试删除记录:

DELETE /some-resource/1 { id:1, version:1 }409 Conflict

您应该得到一个乐观锁异常。重新读取记录,看看它是否重要,也许不要删除它。

使用它的另一个原因是一次删除多个记录(例如,带有行选择复选框的网格)。

DELETE /messages[{id:1, version:2},{id:99, version:3}]204 No Content

请注意,每条消息都有自己的版本。也许您可以使用多个标头指定多个版本,但乔治,这更简单,也更方便。

这适用于Tomcat(7.0.52)和Spring MVC(4.05),也可能是早期版本:

@RestControllerpublic class TestController {
@RequestMapping(value="/echo-delete", method = RequestMethod.DELETE)SomeBean echoDelete(@RequestBody SomeBean someBean) {return someBean;}}

如果有人遇到这个问题测试,不,它不是普遍支持的。

我目前正在使用Sahi Pro进行测试,很明显,超文本传输协议DELETE调用会剥离任何提供的主体数据(根据端点设计,要批量删除的大量ID列表)。

我已经和他们联系了好几次,还发了三个单独的脚本、图像和日志包给他们审查,他们仍然没有确认这一点。一个失败的补丁,后来他们的支持错过了电话会议,我仍然没有得到一个可靠的答案。

我确信Sahi不支持这一点,我想许多其他工具也会跟随套件。

提醒一下,如果你在DELETE请求中提供了一个正文,并且正在使用谷歌云HTTPS负载均衡器,它将以400错误拒绝你的请求。我把头撞在墙上,发现谷歌,不管出于什么原因,认为带有正文的DELETE请求是一个格式错误的请求。

这不是定义

DELETE请求消息中的有效负载没有定义语义学;在DELETE请求上发送有效负载正文可能会导致一些现有的拒绝请求的实现。
https://www.rfc-editor.org/rfc/rfc7231#page-29

值得注意的是,3.0版的OpenAPI规范放弃了对带有主体的DELETE方法的支持:

参考文献见这里这里

这可能会影响您将来对这些API的实现、留档或使用。

下面的GitHUb url可能会帮助你找到答案。实际上,像Tomcat、Weblogic这样的应用服务器拒绝了带有请求有效负载的HTTP.DELETE调用。所以记住这些,我在github中添加了示例,请查看

https://github.com/ashish720/spring-examples

我不认为已经发布了一个很好的答案,尽管对现有答案有很多很好的评论。我将把这些评论的要点提升到一个新的答案中:

本文来自RFC7231已经被引用了几次,这确实总结了它。

DELETE请求消息中的有效负载没有定义语义学;在DELETE请求上发送有效负载正文可能会导致一些现有的拒绝请求的实现。

我从其他答案中错过的是含义。是的,允许在DELETE请求上包含一个body,但这在语义上没有意义。这真正的意思是,发出带有请求主体的DELETE请求在语义上等同于不包括请求主体。

包含请求主体不应该对请求有任何影响,因此包含它是没有意义的。

tl; dr:从技术上讲,带有请求主体的DELETE请求是允许的,但这样做永远没有用。

对主体使用DELETE是有风险的……我更喜欢这种列表操作而不是REST的方法:

常规业务

GET/objects/获取所有对象

GET/objectID获取具有指定ID的对象//请求参数

POST/objects添加一个新对象

PUT/objectID添加具有指定ID的对象,更新对象//强

删除/objectID删除具有指定ID的对象

所有自定义操作都是POST

POST/objects添加列表添加主体中包含的对象列表或数组

POST/objects删除列表删除主体中包含的对象列表

POST/objects/自定义查询基于主体中的自定义查询创建列表

如果客户端不支持您的扩展操作,他们可以正常工作。

HTTP邮件列表上的Roy Fielding澄清了超文本传输协议邮件列表https://lists.w3.org/Archives/Public/ietf-http-wg/2020JanMar/0123.html并说:

GET/DELETE正文是绝对禁止有任何影响关于请求的处理或解释

这意味着主体不能修改服务器的行为。然后他补充说:

除了读取和丢弃接收到的字节以维护的必要性消息框架。

最后是不禁止身体的原因:

我们没有禁止运送身体的唯一原因是因为这会导致懒惰的实现假设没有身体会发送

因此,虽然客户端可以发送有效负载正文,但服务器应该丢弃它并且API不应该为这些请求的有效负载主体定义语义。

实际答案:否

一些客户端和服务器会忽略甚至删除DELETE请求中的主体。在极少数情况下,它们会失败并返回错误。

几个其他答案提到了RFC 7231,它有效地说DELETE请求允许有一个主体,但不推荐。

在2022年,RFC 7231被RFC 9110:HTTP语义取代,它现在说:

[…]在DELETE请求中接收到的内容没有一般定义的语义学,不能改变请求的含义或目标,并且可能导致一些实现拒绝请求并关闭连接[…]。客户端不应该在DELETE请求中生成内容,除非它直接向源服务器发出,该服务器之前已经在频段内或频段外指示此类请求有目的并将得到充分支持。源服务器不应该依赖私人协议来接收内容,因为HTTP通信的参与者通常不知道请求链上的中介。

这种语言已经从以前的语言中得到了加强,即使它是允许的,在使用它时你真的需要非常小心,因为(例如)一些用户可能在一个代理后面,该代理会从请求中剥离主体以对抗"请求走私"