繁体   English   中英

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

[英]Is an entity body allowed for an HTTP DELETE request?

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

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

微软也有同样的看法(我能听到观众的低语),他们在 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 节)

HTTP 1.1 规范 ( RFC 7231 ) 的 2014 更新明确允许在 DELETE 请求中使用实体主体:

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

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

在删除请求中使用正文的一个原因是为了乐观并发控制。

您阅读了记录的第 1 版。

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

您的同事阅读了记录的第 1 版。

GET /some-resource/1
200 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

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

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

@RestController
public class TestController {

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

请注意,如果您在 DELETE 请求中提供正文并使用谷歌云 HTTPS 负载均衡器,它将拒绝您的请求并显示 400 错误。 我正用头撞墙,结果发现无论出于何种原因,Google 都认为带有正文的 DELETE 请求是格式错误的请求。

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

GET/DELETE 正文绝对禁止对请求的处理或解释产生任何影响

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

除了必须读取和丢弃接收到的字节以维护消息帧之外。

最后是不禁身的原因:

我们没有禁止发送正文的唯一原因是因为这会导致延迟实现,假设不会发送正文。

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

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

从第 4.3 节开始:

请求中消息体的存在通过在请求的消息头中包含 Content-Length 或 Transfer-Encoding 头字段来表示。 如果请求方法的规范(第 5.1.1 节)不允许在请求中发送实体主体,则消息主体不得包含在请求中。 服务器应该在任何请求上读取并转发消息体; 如果请求方法不包括为实体主体定义的语义,则在处理请求时应该忽略消息主体。

第 9.7 节:

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

如果响应包含描述状态的实体,则成功的响应应该是 200(OK),如果操作尚未制定,则为 202(已接受),或者如果操作已经制定但响应不包括,则应为 204(无内容)一个实体。

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

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

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

请参阅此处此处以获取参考

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

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

RFC7231 中的这一段已经被引用了几次,它确实总结了它。

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

我从其他答案中错过的是暗示。 是的,允许在DELETE请求中包含正文,但它在语义上没有意义。 这真正意味着发出带有请求正文的DELETE请求在语义上等同于不包括请求正文。

包含请求正文不应该对请求产生任何影响,因此包含它从来没有意义。

tl; dr:从技术上讲,允许带有请求正文的DELETE请求,但这样做从来没有用处。

将 DELETE 与 Body 一起使用是有风险的......我更喜欢这种方法用于列表操作而不是 REST:

常规操作

GET /objects/获取所有对象

GET /object/ ID获取具有指定 ID 的 Object

POST /objects添加一个新对象

PUT /object/ ID添加一个具有指定 ID 的对象,更新一个对象

DELETE /object/ ID删除指定 ID 的对象

所有自定义操作都是 POST

POST /objects/ addList添加包含在正文中的对象列表或数组

POST /objects/ deleteList删除包含在正文中的对象列表

POST /objects/ customQuery根据正文中的自定义查询创建一个列表

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

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

这意味着 Netty 支持这一点。

就像评论中提到的那样,情况可能不再如此

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

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

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

我确信 Sahi 不支持这一点,我想还有很多其他工具会效仿。

这没有定义

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

实际答案:否

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

其他几个答案提到了 RFC 7231,它有效地表示允许DELETE请求具有正文,但不建议这样做。

2022 年,RFC 7231 被RFC 9110: HTTP Semantics取代,现在说:

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

这种语言已经从以前的语言中得到了加强,也就是说,即使它被允许,你在使用它时确实需要非常小心,因为(例如)某些用户可能在代理后面,该代理会从请求中删除正文为了打击“请求走私”

最好不要在 DELETE 中使用 body。 许多工具都不支持它,Swagger 就是一个例子。

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

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

我能够使用请求主体实现 DELETE 操作。 我使用了 AWS Lambda 和 AWS API 网关并使用了 Go 语言。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM