是否允许实体正文进行HTTP DELETE请求?


717

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


4
在ASP.NET WebApi 2中,HttpDelete端点将忽略FromBody参数。
珍妮·奥雷利

2
我也有类似的担忧,但我的情况有所不同。当我要删除数百个对象时,我要发出批处理删除请求。对于HTTP 2.0之前的网络来说,无疑可以极大地提高性能。
Singagirl

1
HTTP / 2是否有任何更改?
Jyotman Singh'2

Answers:


570

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

微软认为它以同样的方式(我可以听到观众低声),他们MSDN文章关于国有ADO.NET数据服务框架的删除方法

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

另外,这是RFC2616(HTTP 1.1)关于请求必须说的内容:

  • 仅当消息主体存在时,实体主体才存在(第7.2节)
  • 通过包含或标头来表示消息正文的存在(第4.3节)Content-LengthTransfer-Encoding
  • 一个消息体必须不被包括在所述请求的方法的规范不允许发送实体主体(第4.3节)
  • 一个实体主体在TRACE明确禁止仅请求,所有其他请求类型不受限制(第9节和9.8特异性)

对于响应,已定义:

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

7
@Jason绝对。您还可以使用自定义标头来传递其他数据,但是为什么不使用请求正文。
Tomalak

86
尽管规范并未禁止DELETE请求具有消息主体,但第4.3节似乎表明服务器应忽略该主体,因为DELETE实体主体没有“已定义的语义” :“服务器应读取并转发一个任何请求上的消息主体;如果请求方法不包括实体主体的已定义语义,则在处理请求时应忽略消息主体。”
Shelley

72
请注意,许多客户也无法发送带有正文的DELETE。这只是在Android上烧死了我。
Karmic Coder 2014年

1
@ KarmicCoder:很好。更多信息:在Android中发送HTTP DELETE请求
MS Dousti

2
与HTTP规范混合的有关实现的大量讨论。客户将以他们解释规范的方式来实现事物,不要将其与规范的含义相混淆。事实是,规格说明不明确。我不同意这种解释,因为没有针对实体的定义的语义,这意味着应该忽略它。我认为人们正在从客户现有的特定解释(Jersey,Android测试客户端等)中倒退,并试图证明该解释的合理性,而不是试图遵循规范。人类是容易犯错误的。
Gibron

169

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

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


3
最新的未经批准的规范版本删除了此要求。最新批准的版本仍然是上面引用的RFC2616。
BishopZ 2012年

4
哪个版本?版本20仍具有与我上面链接的版本19相同的措辞:“ DELETE请求上的实体没有定义的语义。请注意,在DELETE请求上发送正文可能会导致某些现有实现拒绝该请求。”
grzes

11
版本26建议您可以允许使用主体:A payload within a DELETE request message has no defined semantics; sending a payload body on a DELETE request might cause some existing implementations to reject the request.因此它带有向后兼容性警告,它暗示下一个标准将是:“是的!DELETE可以有一个身体。
Pure.Krome 2014年

4
RFC 7231第4.3.5节使用最终确定了版本26中的语言A payload within a DELETE request message has no defined semantics。所以身体是允许的。
mndrix

6
正文是允许的,但与请求无关。使用它绝对没有意义。
Evert


50

在删除请求中使用主体的原因之一是为了实现乐观的并发控制。

您阅读记录的版本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;
    }
}

15
在GET(和DELETE)中包含主体显然会误用HTTP和REST。还有其他处理并发控制的机制(例如If-Modified-Since和etags)。
布鲁诺

19
如果规格未禁止身体处于删除状态,那么如何明显清除它?
尼尔·麦圭根

5
因为您无意对身体做任何事情。参见:stackoverflow.com/a/983458/372643
Bruno

14
这是完全相同的问题:GET允许您检索URI标识的资源的表示形式,而DELETE删除URI标识的资源。如果要删除特定版本,请对其他版本使用不同的URI。URI应该是HTTP / REST中资源的唯一标识符。如果需要处理并发,请在标头中使用元数据(例如If-Unmodified-SinceEtag,这就是它们的用途)。
布鲁诺

5
使用ETag标头代替正文中的版本字段
malhal

26

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

从第4.3节开始:

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

以及第9.7节:

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

如果响应包含描述状态的实体,则成功响应应为200(确定);如果尚未执行该操作,则为202(接受);如果已执行该动作但响应不包括该响应,则返回204(无内容)。一个实体。

如果请求通过缓存,并且Request-URI标识一个或多个当前缓存的实体,则应将这些条目视为过期。对此方法的响应不可缓存。

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


19

请注意,如果您在DELETE请求中提供了正文,并且正在使用Google Cloud HTTPS负载均衡器,它将拒绝您的请求,并显示400错误。我撞墙碰壁,结果发现Google出于某种原因认为带有主体的DELETE请求是格式错误的请求。


1
for whatever reason-因为规范是这样说的:P
Mardoxx '18

20
规范并没有“这么说”,只是说没有特别定义主体。如果未定义它,而您想忽略它,请冷却...继续并忽略它。但是,完全拒绝该请求似乎是极端且不必要的。
本·弗里德

1
不要依赖未定义的行为。这是一个非常普遍的最佳实践。
Evert

@Evert有一个明确未定义的行为(例如,您看到了C语言规范中的描述),并且存在允许但没有描述的行为。DELETE后者使用消息正文。
Alnitak

9

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

这里这里参考

这将来可能会影响您的实现,文档或这些API的使用。


7

似乎ElasticSearch使用以下代码:https ://www.elastic.co/guide/zh-CN/elasticsearch/reference/5.x/search-request-scroll.html#_clear_scroll_api

这意味着Netty支持这一点。

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


1
如果使用apache http客户端,则可以通过扩展HttpEntityEnclosingRequestBase并使getMethod()方法返回GET或DELETE来轻松创建自己的GET和DELETE版本。我们用它来与elasticsearch交谈。
吉尔·范古普(Jilles van Gurp)2015年

2
死链接-太好了。我们需要更多这些链接答案-不需要
cottton's

3
链接的文档现在仅包含POST请求,没有DELETE。可能值得在此答案中添加注释?
dshepherd

Elasticsearch也将主体与GET请求一起使用。
Nidhin David '18

7

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

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

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

除了必须读取和丢弃接收到的字节以维持消息帧外,还需要其他方法。

最后,禁止身体的原因:

我们不禁止发送正文的唯一原因是,如果不发送正文,这将导致延迟实现。

因此,尽管客户端可以发送有效内容主体,但服务器应丢弃它,并且API不应在这些请求上为有效内容主体定义语义。



5

与主体一起使用DELETE是有风险的...与REST相比,我更喜欢这种用于列表操作的方法:

常规运营

GET / objects / 获取所有对象

GET / object / ID 获取具有指定ID的对象

POST / objects 添加一个新的对象

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

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

所有自定义操作均为POST

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

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

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

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


使用POSTREST并不是创建新资源的良好RESTy方法,因为POST响应的语义尚不清楚,尤其是在Location标头的上下文中。您实际上是将HTTP留在了后面,而将RPC堆叠在了上面。正确的“ HTTP / REST方式”是使用PUT带有If-None-Match: *标头的资源(或指定正确的HTTP方法,请参见MKCOL等)来创建资源。
hnh

4

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

RFC7231的这一段已经被引用了几次,并对其进行了总结。

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

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

包含请求正文对请求不会有任何影响,因此将其包含在内永远没有意义。

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


2
“语义上毫无意义”并不等同于“没有定义的语义”。前者意味着它没有任何意义。后者只是意味着RFC本身并未指定这些语义可能是什么。(我写RFC)
Alnitak

1
换句话说,如果API的实现者希望自己定义一些语义,那么他们完全可以这样做。
Alnitak

1
@Alnitak这绝对是一种误解。根据该定义,任何 HTTP请求主体都没有定义的语义,但是在规范中专门调用了DELETE和GET。以下是尚未发布的草案的摘要,专门讨论了GET请求:
Evert

1
我不同意您的看法,在当前发布的RFC中尚不清楚,但是如果您不相信我,我将邀请您在其提出DELETE和GET意图之后询问作者。您会发现它与我的答案一致。像您一样,我也参与标准组织,我不仅是一个单身人士,对如何解释RFC有意见。
Evert

2
如果是这种情况,那么7231的措词很差,应该说“必须忽略有效载荷主体”。您上面提到的是哪个草稿?
Alnitak

3

如果有人正在测试此问题,则不普遍不支持它。

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

我已经与他们联系了好几次,并以三个单独的纸条,图像,日志包发送给他们进行审核,但他们仍然没有确认。补丁程序失败,后来错过了他们的支持所召开的电话会议,但我仍然没有得到可靠的答案。

我确定Sahi不支持此功能,并且我可以想象还有许多其他工具会随套件提供。


它在Sahi Pro的最新版本中实现。由于Sahi使用Java进行HTTP调用,并且Java在1.8版之前存在一个错误,该错误使用户无法发出DELETE请求。因此,从Java 1.8开始和Sahi Pro 6.1.1(即将公开),人们可以在Sahi中使用body进行DELETE请求。
Vivek V Dwivedi

-1

可能是下面的GitHUb网址会帮助您,获得答案。实际上,像Tomcat这样的Application Server,Weblogic拒绝带有请求有效负载的HTTP.DELETE调用。因此请牢记所有这些内容,我在github中添加了示例,请查看一下

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


-1

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


3
他们在谈论标准,而不是能力。您甚至可以通过身体
收到
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.