虽然HTTP 1.1规范似乎允许DELETE请求上的消息体,但它似乎表明服务器应该忽略它,因为它没有定义的语义。
4.3 Message Body
A server SHOULD read and forward a message-body on any request; if the
request method does not include defined semantics for an entity-body,
then the message-body SHOULD be ignored when handling the request.
我已经回顾了有关SO及其他内容的几个相关讨论,例如:
-
实体主体是否允许HTTP DELETE请求?
-
HTTP请求方法的有效负载
-
带请求正文的HTTP GET
大多数讨论似乎都同意允许在DELETE上提供消息体,但通常不建议这样做。
此外,我注意到各种HTTP客户端库中的趋势,这些库中似乎记录了越来越多的增强功能,以支持DELETE上的请求主体。大多数图书馆似乎都有责任,尽管偶尔会有一些初步阻力。
我的用例要求在DELETE上添加一些必需的元数据(例如删除的"原因",以及删除所需的一些其他元数据)。我已经考虑了以下选项,其中任何一个看起来都不合适,并且与HTTP规范和/或REST最佳实践内联:
-
消息正文 - 规范表明DELETE上的消息体没有语义值; HTTP客户端不完全支持;不是标准做法
-
自定义HTTP标头 - 需要自定义标头通常违反标准做法;使用它们与我的其余API不一致,其中没有一个需要自定义标头;此外,没有好的HTTP响应可用于指示错误的自定义标头值(可能是一个单独的问题)
-
标准HTTP标头 - 没有标准标头是合适的
-
查询参数 - 添加查询参数实际上会更改要删除的Request-URI;违反标准做法
-
POST方法 - (例如POST /resourceToDelete { deletemetadata })POST不是删除的语义选项; POST实际上代表了所需的相反操作(即POST创建资源下属;但我需要删除资源)
-
多个方法 - 将DELETE请求拆分为两个操作(例如,PUT删除元数据,然后删除DELETE)将原子操作拆分为两个,可能会留下不一致的状态。删除原因(和其他相关元数据)不是资源表示本身的一部分。
我的第一个偏好可能是使用消息体,第二个是自定义HTTP头;但是,如上所述,这些方法存在一些缺点。
是否有任何与REST / HTTP标准一致的建议或最佳实践,以便在DELETE请求中包含此类必需的元数据?还有其他我没有考虑的替代方案吗?
-
像Jersey这样的某些实现不允许delete请求的主体。
尽管有些建议不要将消息体用于DELETE请求,但这种方法可能适用于某些用例。这是我们在评估问题/答案中提到的其他选项以及与服务的消费者合作之后最终使用的方法。
虽然消息体的使用并不理想,但其他选项都不是完全适合的。请求体DELETE允许我们轻松,清晰地添加伴随DELETE操作所需的其他数据/元数据的语义。
我仍然对其他想法和讨论持开放态度,但想要关闭这个问题的循环。我感谢大家对这个主题的想法和讨论!
-
这是一个坏主意。如果您以后决定使用像Akamai EdgeConnect这样的HTTP加速服务,那么这会让您遇到麻烦。我知道EdgeConnect从HTTP DELETE请求中剥离主体(因为它们消耗带宽可能无效)。类似的服务也可能做同样的事情(参见Kindle的加速功能和其他类似CDN的服务)。您可能应该重新设计为不为您的服务使用HTTP谓词。大多数API使用HTTP-verbs / classical-REST没有多大意义,HTTP动词传输问题很难排除故障。
-
我同意@Gabe,根据定义发送一个没有正文的方法的主体是一种可靠的方法,当你的位遍历互联网管道时随机丢失数据,你将很难调试它。
你似乎想要的是两件事之一,它们都不是纯粹的DELETE:
您有两个操作,一个PUT的删除原因后跟一个DELETE的资源。删除后,任何人都无法再访问该资源的内容。"原因"不能包含指向已删除资源的超链接。要么,
您正尝试使用DELETE方法将资源从state=active更改为state=deleted。主API会忽略state = deleted的资源,但对于管理员或具有数据库访问权限的人员可能仍然可以读取。这是允许的 - DELETE不必擦除资源的后备数据,只删除在该URI上公开的资源。
任何需要DELETE请求上的消息体的操作都可以分解为最常见的,POST来完成消息体的所有必要任务,以及DELETE。我认为没有理由打破HTTP的语义。
-
如果PUT原因成功且DELETE资源失败会发生什么?如何防止不一致的状态?
-
@Lightman PUT只指定意图。它可以在没有相应的DELETE的情况下存在,这表示有人想删除但是它失败了或者他们改变了主意。颠倒调用的顺序也允许DELETE在没有理由的情况下发生 - 因此提供原因仅仅被视为注释。出于这两个原因,我建议使用上面的选项2,即更改基础记录的状态,例如使HTTP资源从其当前URL中消失。然后垃圾收集器/管理员可以清除记录
鉴于你的情况,我会采取以下方法之一:
-
发送PUT或PATCH:由于需要删除原因,我推断删除操作是虚拟的。因此,我认为通过PUT / PATCH操作更新记录是一种有效的方法,即使它本身不是DELETE操作。
-
使用查询参数:资源uri未更改。我实际上认为这也是一种有效的方法。您链接的问题是在缺少查询参数时讨论不允许删除。在您的情况下,如果未在查询字符串中指定原因,我将只有默认原因。资源仍然是resource/:id。您可以使用资源上的链接标头为每个原因(在每个原因上使用rel标记来识别原因)使其可被发现。
-
每个原因使用一个单独的端点:使用类似resource/:id/canceled的URL。这确实改变了Request-URI,绝对不是RESTful。同样,链接标题可以使这个可被发现。
请记住,REST不是法律或教条。将其视为指导。因此,如果不遵循问题域的指导是有意义的,请不要这样做。只需确保您的API使用者了解差异。
-
关于查询参数的使用,我的理解是查询是3.2节中Request-URI的一部分,因此使用这种方法(或类似地,单独的端点)违反DELETE方法的定义,例如"资源"由Request-URI标识的"被删除。
-
资源由uri路径标识。因此GET到/orders/:id将返回与/orders/:id?exclude=orderdetails相同的资源。查询字符串仅向服务器提供提示 - 在这种情况下,排除响应中的orderdetails(如果支持)。同样,如果要将DELETE发送到/orders/:id或/orders/:id?reason=canceled或/orders/:id?reason=bad_credit,则仍然在使用相同的底层资源。为了保持"统一接口",我有一个默认的原因,因此不需要发送查询参数。
-
@shelley你对查询字符串的关注是正确的。查询字符串是URI的一部分。向/foo?123发送DELETE请求意味着您要删除的资源与向/foo?456发送DELETE的资源不同。
-
@codeprogression很抱歉,但你说的很多都是错的。资源由整个URI标识,而不仅仅是路径。不同的查询字符串是不同的资源(在"资源"一词的HTTP意义上)。此外,统一接口不需要默认原因。该术语指的是使用GET,PUT,POST,PATCH和DELETE,它们通过HTTP定义它们。通用性是供应商(用户代理供应商,API供应商,缓存代理供应商,ISP等)之间的共性,而不是在自己的API内(尽管在设计上也应该统一其用户的理智!)。
-
@Nicholas我不明白你需要争论三年前结束的讨论。从REST中心的观点来看,我所做的答案和评论是有效和正确的。 REST不是HTTP(也不是HTTP的任何供应商实现)。在REST的上下文中,资源是相同的。正如我在回答中所说,REST不是法律或教条,而是指导。
-
道歉CP。我没有在这些评论中发现这一年,并认为它们只有一周之久! - 我回到这里是因为有人投了我的答案,我不记得这个特别的问题了,所以重新阅读了。我仍然坚持你的评论。查询字符串和统一界面的构成是不正确的,但我经常也告诉人们"REST不是法律或教条,而是指导",指向文章,如web.archive.org/web/20140917143640/http://nordsc的.com / EXT /…并让他们选择最适合他们的东西。
-
@nicholas谢谢。我很欣赏道歉。我们的答案非常相似。我们的风格有点不同。所有美好的事物。 :)
我建议您将所需的元数据作为URI层次结构本身的一部分。一个例子(天真):
如果您需要根据日期范围删除条目,而不是将开始日期和结束日期作为正文或查询参数传递,请构造URI,以便将所需信息作为URI的一部分传递。
例如
DELETE /entries/range/01012012/31122012 - 删除2012年1月1日至2012年12月31日期间的所有条目
希望这可以帮助。
-
不包括发送删除原因即提交字段等情况。
-
哇。这是一个糟糕的主意。如果你有太多的元数据,它将膨胀对URI的大小限制。
-
此方法不遵循RESTful实践,因为您将拥有一个复杂的URI结构,因此不建议这样做。端点因交织的资源识别与元数据而变得混乱,随着API的变化,及时将成为维护的噩梦。在查询参数或有效负载中指定range是更优选的,这是问题的关键:了解问题的最佳实践方法,我想说的不是这个。
-
@digitaldreamer - 我不明白你的错综复杂的URI结构是什么意思?此外,这是一个HTTP DELETE,因此有效负载不是一个选项,但查询参数是。