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

Haa*_*ked 651 rest http

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

Tom*_*lak 527

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

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

如果DELETE请求包含实体主体,则忽略正文[...]

另外这里是RFC2616(HTTP 1.1)对请求的说法:

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

对于回复,这已经定义:

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

  • 尽管规范并未禁止DELETE请求拥有消息体,[第4.3节](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3)似乎表明_身体应该被servers_忽略,因为[DELETE](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7)实体主体没有"定义的语义":"服务器应该是什么在任何请求上读取和转发消息体;**如果请求方法不包含实体体的定义语义,则在处理请求时应该忽略消息体**. (78认同)
  • 请注意,许多客户也无法发送带有正文的DELETE.这只是在Android上烧毁了我. (64认同)
  • @Jason绝对.您还可以使用自定义标头传递其他数据,但为什么不使用请求正文. (7认同)
  • 关于与 HTTP 规范混合的实现的大量讨论。客户将以他们解释规范的方式实现事物,不要将其与规范的含义混淆。事实是,规范使这一点模棱两可。我不同意这样的解释,因为实体主体没有定义的语义,暗示它应该被忽略。我认为人们正在从存在的客户特定解释(泽西岛、Android 测试客户端等)向后工作,并试图证明解释的合理性,而不是试图忠实于规范。人类是会犯错的。 (5认同)
  • @Tomalak,语言有点含糊,但意图并非如此。我问作者,雪莱(在这个线程中)是正确的。HTTP 规范的下一版本将更加精确。 (3认同)
  • @KarmicCoder:好点子。更多信息:[在 Android 中发送 HTTP DELETE 请求](http://stackoverflow.com/q/10338615/459391)。 (2认同)

grz*_*zes 154

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

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

  • @Evert我也不同意。例如,如果您想要指示服务器一次删除多个实体,并且通过正文中的有效负载向请求提供 ID(通过 URL 查询参数传递这些实体可能太大),则 DELETE 请求中的正文非常有用。 (12认同)
  • 版本26建议您可以允许正文:`DELETE请求消息中的有效负载没有定义的语义; 在DELETE请求上发送一个有效负载体可能会导致一些现有的实现拒绝该请求.因此它带有一个向后兼容性警告,它暗示下一个标准将会说:'是的!`DELETE`可以有一个身体`. (11认同)
  • @Evert 我不同意 - 我想在我的 REST API 中实现一个“安全”的“DELETE”方法,该方法需要一些必须设置的附加参数,以便仅访问 URL 不会造成任何损害。在我看来,将其放入体内是理想的选择。 (7认同)
  • [RFC 7231第4.3.5节](https://tools.ietf.org/html/rfc7231#section-4.3.5)使用"DELETE请求消息中的有效负载没有定义的语义"来完成第26版的语言.所以身体是允许的. (4认同)
  • 最新未经批准的规范版本删除了此要求.最新批准的版本仍然是上面引用的RFC2616. (3认同)
  • 哪个版本?版本20仍然具有与上面链接的版本19相同的措辞:"DELETE请求上的实体没有定义的语义.请注意,在DELETE请求上发送正文可能会导致某些现有实现拒绝请求." (3认同)
  • 正文是允许的,但与请求无关。使用它绝对没有意义。 (3认同)
  • 不管怎样,我把这个问题提交给了开发 HTTP 规范的小组,并且该语言正在针对下一个版本进行调整。这就是现在所说的:“客户端不应该在 DELETE 请求中生成正文。DELETE 请求中收到的有效负载没有定义的语义,无法更改请求的含义或目标,并且可能导致某些实现拒绝该请求。 (3认同)

eva*_*ard 53

某些版本的Tomcat和Jetty似乎忽略了实体主体(如果存在).如果您打算收到它可能会令人讨厌.

  • 我和Tomcat v6u39一起工作过. (4认同)
  • 适用于Tomcat 7 (4认同)
  • 和Tomcat 8.0.12 (4认同)
  • Google App Engine 实例化并传递一个空的默认实体而不是请求正文。 (2认同)

Nei*_*gan 45

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

您阅读了记录的第1版.

GET /some-resource/1
200 OK { id:1, status:"unimportant", version:1 }
Run Code Online (Sandbox Code Playgroud)

您的同事阅读记录的第1版.

GET /some-resource/1
200 OK { id:1, status:"unimportant", version:1 }
Run Code Online (Sandbox Code Playgroud)

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

PUT /some-resource/1 { id:1, status:"important", version:1 }
200 OK { id:1, status:"important", version:2 }
Run Code Online (Sandbox Code Playgroud)

您尝试删除记录:

DELETE /some-resource/1 { id:1, version:1 }
409 Conflict
Run Code Online (Sandbox Code Playgroud)

您应该获得乐观的锁定异常.重新阅读记录,看它是重要的,也许不能删除它.

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

DELETE /messages
[{id:1, version:2},
{id:99, version:3}]
204 No Content
Run Code Online (Sandbox Code Playgroud)

请注意,每条消息都有自己的版本.也许你可以使用多个标题来指定多个版本,但是通过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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 当规范不禁止DELETE中的身体时,它是如何明确地误解它的? (15认同)
  • 在GET(和DELETE)中拥有主体显然是在虐待HTTP和REST.还有其他处理并发控制的机制(例如If-Modified-Since和etags). (14认同)
  • 这是完全相同的问题:GET允许您检索由URI标识的资源的表示,DELETE删除由URI标识的资源.如果要删除特定版本,请为其他版本使用不同的URI.URI应该是HTTP/REST中资源的唯一标识符.如果你需要处理并发性,请在头文件中使用元数据(例如`If-Unmodified-Since`或`Etag`,这就是他们的用途). (13认同)
  • 因为你不打算对身体做任何事情.请参阅:http://stackoverflow.com/a/983458/372643 (3认同)
  • 使用ETag标头代替正文中的版本字段 (3认同)
  • 如果您在一个请求中删除多个资源,则已经偏离了 HTTP 规范中动词的含义,即“DELETE 方法请求源服务器删除目标资源与其当前功能之间的关联”。“DELETE /messages”的“目标资源”是“/messages”,这意味着整个集合将被删除。如果您想保持 RESTful,更合适的机制可能是使用“PATCH /messages”,将集合视为单个资源,并将删除视为对该资源的更改。 (3认同)
  • 很抱歉在一年之后很难记住这个讨论.不,不一定(这里的主要问题是建议在DELETE中使用正文).URI是标识符,由您来选择它们识别的内容,更重要的是,您的意思*.例如,[这个答案](http://stackoverflow.com/a/18141127/372643)有一个URI(实际上是多个,具体取决于谁分享它),但每个版本都有自己的URI [1]( http://stackoverflow.com/revisions/18141127/1)和[2](http://stackoverflow.com/revisions/18141127/2).这一切都取决于你用标识符识别的意思. (2认同)
  • 我会使用标头传递版本,而不是正文。 (2认同)
  • 显式地将`DELETE`与语义请求主体一起使用意味着您将抛弃HTTP,并定义自己的变体。您是否更喜欢对请求正文使用GET或DELETE是无关紧要的。一致的HTTP工具将删除或忽略主体。 (2认同)

Ada*_*eld 27

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

从第4.3节:

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

第9.7节:

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

如果响应包括描述状态的实体,则成功响应应为200(OK),如果操作尚未执行,则应为202(已接受);如果操作已颁布但响应不包括,则应为204(无内容)一个实体.

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

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


Ben*_*ied 17

只是抬头,如果您在DELETE请求中提供正文并使用Google云端HTTPS负载均衡器,它将拒绝您的请求,并出现400错误.我正撞在墙上,发现Google出于某种原因认为带有正文的DELETE请求是一个格式错误的请求.

  • 规范并没有"说出来"它只是说身体没有特别定义.如果它没有被定义,并且你想忽略它,那么很酷......继续并忽略它.但是完全拒绝这个要求似乎极端而且没必要. (14认同)
  • 似乎谷歌现在允许正文删除,在这里:https://cloud.google.com/load-balancing/docs/release-notes#December_04_2018 (5认同)
  • `无论出于何种原因` - 因为规范是这样说的:P (3认同)
  • 不要依赖未定义的行为。这是一个非常常见的最佳实践。 (3认同)
  • @Evert 存在明确的未定义行为(例如您在 C 语言规范中看到的描述),并且存在允许但根本未描述的行为。在 DELETE 中使用消息体是后者。 (3认同)

Rob*_*lli 14

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

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

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

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

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

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

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


小智 10

将 DELETE 与 Body 一起使用是有风险的......我更喜欢这种列表操作而不是 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 根据正文中的自定义查询创建一个列表

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

  • 我使用 POST 来具体对象,PUT 来更新它们,并使用 PATCH 来进行部分更新。POST /objects/deleteList 对我来说没有任何意义,“休息方式”应该调用 DELETE /objects/{object_id} 或 DELETE /objects?例如,objectNameLike={object_prefix}。 (3认同)
  • 使用“POST”并不是创建新资源的良好 RESTy 方式,因为 POST 响应的语义不清楚,尤其是在 Location 标头的上下文中。您实质上是把 HTTP 抛在后面,而将 RPC 放在上面。正确的“HTTP/REST 方式”是使用带有“If-None-Match: *”标头的“PUT”来创建资源(或指定正确的 HTTP 方法,请参阅“MKCOL”等)。 (2认同)
  • @JJCV 每个技术解决方案都解决一个业务问题。您不能使业务问题无效。通常,后台将提供选择大量实体并对它们应用批量操作的可能性。采用 16 个字符的实体 ID 和 2048 个 url 最大长度,仅选择 128 个对象,这对于后台来说并不算多。话虽如此,您建议如何规避技术问题? (2认同)

Eve*_*ert 9

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

RFC7231中的这一段被引用了几次,总结了一下。

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

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

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

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

  • “语义上无意义”与“没有定义的语义”并不相同。前者意味着它_不能_具有任何意义。后者只是意味着 RFC 本身没有指定这些语义可能是什么。(我写 RFC) (6认同)
  • 如果是这种情况,则 7231 措辞不佳,应该说“必须忽略有效负载主体”。你上面指的是哪个草案? (4认同)
  • 换句话说,如果 API 的实现者希望为自己定义一些语义,他们完全可以自由地这样做。 (2认同)
  • @Alnitak 这绝对是一种误解。根据该定义,_any_ HTTP 请求正文没有定义的语义,但 DELETE 和 GET 在规范中被明确调用。这是一个尚未发布的草案中的一个片段,专门讨论了 GET 请求: (2认同)
  • @Alnitak 我的来源 https://tools.ietf.org/html/draft-ietf-httpbis-bcp56bis-06 我 100% 同意你的观点,我认为当前的措辞也负责向 GET 添加请求主体。HTTP WG 正在研究 HTTP 规范的新版本,所以也许现在是清理该语言的好时机。 (2认同)
  • 我在 httpwg 上提出了这个问题,我不确定这是否是决定性的,但这里是:https://github.com/httpwg/http-core/issues/202 (2认同)

Cle*_*man 8

值得注意的是,版本3.0的OpenAPI规范放弃了对具有正文的DELETE方法的支持:

这里这里的参考

这可能会影响您将来实施,记录或使用这些API.

  • 值得注意的是,他们在一番讨论后又加入了进来。请参阅[此处](https://github.com/OAI/OpenAPI-Specification/issues/1801)以供参考。 (3认同)

Seb*_*ber 7

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

这意味着Netty支持这一点.

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

  • 链接的文档现在只包含POST请求,没有DELETE.可能值得为这个答案添加注释吗? (3认同)
  • 死链接 - 很棒.我们需要更多这些链接答案 - 不是 (2认同)

Mos*_*atz 7

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

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

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

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


小智 6

这没有定义.

DELETE请求消息中的有效负载没有定义的语义; 在DELETE请求上发送有效负载主体可能会导致某些现有实现拒绝该请求.
https://tools.ietf.org/html/rfc7231#page-29

  • 之前的答案中已经包含此确切的报价,应删除此答案。 (3认同)