使用HTTP DELETE删除资源时第二次返回的状态码是什么?

219

鉴于HTTP中的DELETE动词是幂等的,当我发出以下请求时,第二次(或第三次、第四次等)应该发生什么?

DELETE /person/123
第一次,资源被删除并返回204(成功,无内容)。在随后的调用中,我应该返回204还是404(未找到)?
答案:取决于你对API的设计,如果你的API旨在让客户端知道资源的存在或不存在,那么在随后的调用中应该返回404;如果你的API旨在让客户端知道资源已经被删除了,那么在随后的调用中应该返回204。

2
一个更通用的关于幂等性是否包括状态码的问答:https://dev59.com/RWAf5IYBdhLWcg3wXh2z 揭示一下,它并不包括状态码。 - Palec
7个回答

255

在无状态系统中,由于HTTP请求应该是独立的,因此一个请求的结果不应该依赖于前一个请求。考虑一下如果两个用户同时对同一资源进行删除操作会发生什么。第二个请求返回404是有意义的。如果一个用户发出两个请求,也应该是如此。

我猜想,让DELETE请求返回两个不同的响应对您来说可能感觉不幂等。我认为将幂等请求看作使系统保持相同状态更有用,而不一定要求得到相同的响应。因此,无论您删除现有资源还是尝试删除不存在的资源,服务器资源状态都是相同的。


8
谢谢。那很有道理。我确实认为幂等指的是返回相同的响应。 - Craig Wilson
129
如果您删除一个不存在的内容,应返回204状态码(即使该资源以前从未存在)。客户端希望该资源被删除并且已经被删除。返回404状态码会暴露无关紧要的内部处理信息给客户端,导致不必要的错误状态。 - Brian
11
我理解这里的关键概念是,您不应使用DELETE来检查资源是否存在,而应先使用GET。如果响应为200,则执行DELETE;否则不需要执行DELETE。因此,我认为始终在DELETE上返回204是有意义的。 - manei_cc
26
RFC文档指出,它应该像rm命令一样运作。如果文件不存在,则rm会返回一个错误。https://tools.ietf.org/html/rfc7231#section-4.3.5 - Dax Fohl
5
从技术上讲,这句话的意思是“这种方法类似于rm命令”,我理解为这是一种比喻,并不是对行为的推荐。 - plantbeard
显示剩余12条评论

163
我同意当前被选中的答案所说,即第二个(和第三个、第四个……)DELETE应该得到404。我注意到这个答案有143个赞成票,但也有一个相反的评论,有54个赞成票,所以社区分为大致3:1的两个阵营。以下是更多信息,以解决这场长期争论。
  1. 首先,让我们不要从“我”认为,“你”认为或另一位书籍作者认为开始。让我们从HTTP规范即RFC 7231开始。
RFC 7231, section 4.3.5 DELETE仅提到成功响应应为2xx,但没有说明后续DELETE会得到什么。因此让我们深入探讨。 RFC 7231,section 6.5.4 404 Not Found表示404响应是指资源不存在。既然没有特定的HTTP方法(特别是不是DELETE)被调用来处理其他情况,我们可以直观地得出印象(也是正确的),即我的请求DELETE /some/resource/which/does/not/exist应该返回404。那么,DELETE /some/resource/which/happened/to/be/removed/by/someone/else/five/days/ago也可能返回404。那么,为什么DELETE /some/resource/i/deleted/five/seconds/ago会有所不同呢? “但幂等性呢?!”我听到你在尖叫。等一下,我们将进入这个问题。 历史上,RFC 2616于1999年发布,是HTTP 1.1规范中最常被引用的。不幸的是,其关于幂等性的描述很模糊,这留下了所有这些争论的余地。但是,该规范已被RFC 7231取代。引用自RFC 7231,section 4.2.2 Idempotent Methods,强调我的:

如果使用该方法进行多个相同请求的预期效果对服务器的影响与单个此类请求的效果相同,则认为请求方法是“幂等”的。在本规范中定义的请求方法中,PUT、DELETE和安全请求方法是幂等的

因此,规范中写明了幂等性与服务器上的效果有关。第一个DELETE返回204,然后随后的DELETE返回404,这些不同的状态代码并不会使DELETE变得不幂等。用这个论点来证明随后的204返回是毫无意义的。
好的,所以这与幂等性无关。但接下来的问题可能是,如果我们仍然选择在随后的DELETE中使用204,那么可以吗?这是一个很好的问题。动机是可以理解的:允许客户端仍然达到预期结果,而不用担心错误处理。我认为,在随后的DELETE中返回204是一种基本无害的服务器端“善意谎言”,客户端不会立即察觉到区别。这就是为什么有大约25%的人在野外这样做,而且似乎仍然有效。只需记住这样的谎言可能被认为是语义上奇怪的,因为GET /non-exist返回404,但DELETE /non-exist返回204,此时客户端会发现你的服务并没有完全遵守section 6.5.4 404 Not Found。但是我想指出,RFC 7231暗示的预期方式,即在随后的DELETE中返回404,本质上不应该成为问题。有3倍以上的开发者选择这样做,你有听说过由于客户端无法处理404而导致的重大事件或投诉吗?大概是没有的,因为任何实现HTTP DELETE(或任何HTTP方法)的体面客户端都不会盲目地假定结果总是成功的2xx。然后,一旦开发人员开始考虑错误处理,404 Not Found将是首先想到的错误之一。此时,他/她可能会得出结论,对于HTTP DELETE操作忽略404错误是语义上安全的。他们就这样做了。问题解决了。

18
“幂等性主要关注的是对服务器的影响。” 您的回答非常细致,做得很好!我相信使用404状态码来处理后续的DELETE请求。 - nwayve
25
"你让我一见钟情"的时刻是"_GET /non-exist 返回404,但 DELETE /non-exist 返回204_。" - aalaap
3
“所以,在规格说明中写道,幂等性都是关于对服务器的影响。” 我对规格说明的理解不同。它不是指服务器上发生了什么样的影响,而是指调用者打算在服务器上产生的预期影响 - Remy van Duijkeren
1
请参阅RFC 7231,第4.3.4节PUT有关意图的内容。它提到了调用者的意图,而不是服务器如何执行它:“HTTP并没有精确定义PUT方法对源服务器状态的影响方式,除了可以通过用户代理请求的意图和源服务器响应的语义来表达之外。它没有定义资源可能是什么,在任何意义上,超出了通过HTTP提供的接口。” - Remy van Duijkeren
3
@RémyvanDuijkeren,欢迎您的有建设性的讨论。关于您的评论,我不明白您强调“意图”这个词会如何有所区别。那个句子“如果请求方法对服务器的预期影响...则被视为幂等”是在谈论幂等性,而不是状态码。虽然我们都同意HTTP DELETE是幂等的,但我的观点是状态码是无关紧要的。您从HTTP PUT中引用的另一个语句也与此无关,因为它是在不同的背景下讨论客户端和服务器之间PUT表示差异的问题。 - RayLuo
显示剩余12条评论

39

RESTful web服务食谱是这方面的一份很好的资源。偶然间,它的谷歌预览显示了有关DELETE(第11页)的页面:

DELETE方法是幂等的。这意味着服务器必须返回响应代码200(OK),即使服务器在以前的请求中已删除资源。但在实践中,将DELETE实现为幂等操作需要服务器跟踪所有已删除的资源。否则,它可能会返回404(未找到)。


是的,那看起来是一个很好的资源。然而,删除部分对我来说没有出现(它在第23页,预览中已经被编辑掉了)。你读过这本书吗?你知道我的问题的答案吗? - Craig Wilson
7
根据《Cookbook》,即使您已经删除了资源,也应返回200 OK。然而,在实践中,这将需要服务器跟踪所有已删除的资源,因此您可以使用404。书中还指出,安全方面的考虑可能要求您始终返回404。第11页。 - Darrel Miller
太棒了,谢谢!我在第11页错过了这个。我一定会购买这本书的。 - Craig Wilson
40
好的,这本书是错误的。幂等性并不意味着状态码将是相同的。相关的是服务器的最终状态。 - Julian Reschke
2
根据你的情况而定。PayPal返回相同的代码。像我这样的人想知道是否找到并删除了它。我编写了客户端,我将按照自己的需求处理它。在某些情况下,我确实想知道。在其他情况下,只要它不再存在,返回什么都无所谓。有些示例说要这样做,有些则说要另一种方式。这取决于你和你的情况。如果你永远不需要关心它是否已删除...返回相同的代码,使你的客户端代码更简单,只要它适合你的项目 :) - PerryCS
显示剩余3条评论

21

首次DELETE: 返回200或204。

后续DELETE: 返回200或204。

原因: DELETE应该是幂等的。如果第二个DELETE返回404,则您的响应将从成功代码更改为错误代码。客户端程序可能会根据DELETE失败的假设采取不正确的操作。

示例:

  • 假设您的DELETE操作是客户端程序执行的多步操作(或“ saga”)的一部分。
  • 客户端程序可以是执行银行交易的移动应用程序,例如。
  • 假设客户端程序对DELETE操作进行了自动重试(这是有意义的,因为DELETE应该是幂等的)。
  • 假设第一个DELETE已成功执行,但200响应在返回客户端程序的途中丢失。
  • 客户端程序将重试DELETE。
  • 如果第二次尝试返回404,则客户端程序可能会取消整个操作,因为出现此错误代码。
  • 但是由于第一个DELETE已经在服务器上成功执行,系统可能处于不一致状态
  • 如果第二次尝试返回200或204,则客户端程序将按预期进行。

只是为了说明这种方法的使用,PayPal的HTTP API风格指南有以下指南:

DELETE:在大多数情况下,此方法应返回状态代码204,因为没有必要返回任何内容,请求是删除资源,并且已成功删除。

由于DELETE方法也必须是幂等的,因此即使资源已被删除,它仍应返回204。通常API使用者并不关心是否将资源作为此操作的一部分或之前已删除。这也是应返回204而不是404的原因。


1
问题是,对客户来说什么更重要,是它删除了资源,还是资源已被删除。如果在事件流期间其他客户端删除了该资源,那该怎么办?你真的想在客户端目标已实现的情况下失败吗? - Darrel Miller
1
@DarrelMiller 说得好。更重要的是取决于业务背景。但一般来说,即使资源已被其他客户端删除,我也宁愿在第二次DELETE尝试中返回204。我不希望服务失败(即404),因为客户端的目标已经实现。 - Paulo Merson
2
正如其他人所提到的,幂等性并不是你的响应代码,而是你的服务器状态。 - Niranjan
@Niranjan 我同意幂等性是关于服务器状态的,但是不同的响应代码可能会驱使客户端通过取消正在进行的 saga 来不必要地改变服务器状态。 - Paulo Merson
@Paulo Merson 如果客户要求删除从未存在的项目,您会返回什么代码?204?还是404?如果您始终返回204,那么检查返回代码的意义何在? - frenchone
1
@frenchone 如果您有一种方法可以知道该项从未存在过,则应在第一次和后续的DELETE尝试中返回404。如果没有,但客户端程序需要知道该项是否存在,则客户端程序始终可以在执行DELETE之前执行GET。当然,如果该项不存在(因为它从未存在过或已被删除),则GET将返回404。 - Paulo Merson

1
我打算加入"410 Gone"。RFC 7231, section 6.5.4 410 Gone说,"410 (Gone)"状态码表示在源服务器上无法再访问目标资源,并且这种情况可能是永久性的。
所以,对于第一个"DELETE"使用"204",对于后续的"DELETE"使用"410"。这样做也很一致,因为"GET"、"PUT"等操作也会返回"410"。
然而,这需要服务器跟踪已删除的内容,规范还指出:
如果源服务器不知道或无法确定条件是否是永久性的,应该使用状态码"404 (Not Found)"。
所以,如果跟踪已删除的资源很有价值(例如,如果您需要防止它们被重新创建),则使用410;否则,建议使用404,如本答案所推荐,或者换句话说{{link2:"这取决于情况"}}。

0
就我观察,大多数开发人员倾向于在DELETE请求中使用200作为响应代码,无论资源是否存在。然而,正确的做法是仅在成功删除现有记录后返回200,并在请求的资源未找到时返回404。404状态码专门用于表示资源未找到,而不仅仅是页面未找到错误。

https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404

因此,我建议在操作成功时使用200,在资源缺失时使用404。这还可以帮助管理测试用例,以确保正确的实施。

-1
另一种选择:
始终返回200(OK),并输出已删除资源的数量(0或1)。
优点:
- 如果您有另一条允许通过搜索/筛选删除多个对象的路由,这种方法是一致的。 - 您的API用户始终知道资源是否已被删除(检查很简单)。 - 状态码保持不变,这样感觉更合适,至少对我来说是这样。 - 返回已删除文档/行/对象的数量(可以为0)的方法在数据库中很常见。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接