删除请求正文的RESTful替代方案


93

尽管HTTP 1.1规范似乎允许DELETE请求上的消息正文,但它似乎表明服务器应该忽略它,因为没有定义的语义。

4.3邮件正文

服务器应根据任何请求读取并转发消息正文;如果请求方法不包括为实体主体定义的语义,则在处理请求时应忽略消息主体。

我已经回顾了关于SO及其它主题的一些相关讨论,例如:

大多数讨论似乎都同意,可以允许在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请求中?还有其他我没有考虑过的选择吗?


2
某些实现Jersey不允许正文进行delete请求。
basiljames

Answers:


44

尽管有一些建议不要将消息正文用于DELETE请求,但是这种方法在某些用例中可能是合适的。这是我们在评估问题/答案中提到的其他选项之后,并与服务使用者合作后最终使用的方法。

虽然使用消息正文并不理想,但其他任何选项都不是最合适的。请求主体DELETE使我们可以轻松,清楚地在DELETE操作所需的其他数据/元数据周围添加语义。

我仍然对其他想法和讨论持开放态度,但想在这个问题上结一个圈。我感谢大家对此主题的想法和讨论!


12
这是一个坏主意。如果您后来决定使用HTTP加速服务(如Akamai EdgeConnect),这将给您带来麻烦。我知道EdgeConnect从HTTP DELETE请求中剥离主体(因为它们消耗带宽很可能是无效的)。类似的服务也有可能做同样的事情(请参阅Kindle的加速功能以及其他类似CDN的服务)。您可能应该重新设计为不将HTTP动词用于您的服务。使用HTTP-verbs / classical-REST时,大多数API毫无意义,并且HTTP verbs传输问题很难解决。
2014年

3
我同意@Gabe的观点,用没有定义的body的方法发送body 是肯定的方法,因为当您的位遍历Internet管道时,它会随机丢失数据,并且调试起来将非常困难。
Nicholas Shanks 2014年

3
这些针对使用DELETE的评论是无关紧要的,除非它们解决了OP所存在的非常有效的问题。我正在尽最大努力坚持REST的精神,但是在现实情况下,没有乐观并发的删除和没有原子批处理操作的删除是不实际的。这是REST模式的严重缺陷。
Quarkly,

我在此上使用@Quarkly。我不了解关于如何检查并发性等的RESTFUL想法是什么。并发检查不属于客户端。
Dirk Wessels

13

您似乎想要的是两件事之一,都不是纯粹的DELETE

  1. 你有两个业务,一个PUT的删除原因,然后一个DELETE资源的。删除后,任何人都将无法再访问该资源的内容。“原因”不能包含指向已删除资源的超链接。要么,
  2. 您正在试图改变资源state=activestate=deleted使用DELETE方法。主API会忽略state = deleted的资源,但管理员或具有数据库访问权限的人仍可以读取。这是允许的- DELETE不必删除资源的后备数据,只需删除在该URI处公开的资源即可。

DELETE请求上需要消息正文的任何​​操作都可以分解为最一般的POST操作:使用消息正文执行所有必需的任务,然后使用DELETE。我认为没有理由破坏HTTP的语义。


2
如果PUT原因成功而DELETE资源失败怎么办?如何防止状态不一致?
莱特曼

1
@Lightman PUT仅指定意图。它可以不带相应的DELETE而存在,DELETE表示有人要删除,但是删除失败或他们改变了主意。颠倒调用顺序也将允许DELETE发生而没有理由-提供理由则仅被视为注释。出于这两个原因,我建议您使用上述选项2,即更改基础记录的状态,以使HTTP资源从其当前URL中消失。然后,垃圾收集器/管理员可以清除记录
Nicholas Shanks

7

考虑到您的情况,我将采用以下方法之一:

  • 发送一个PUT或PATCH:由于需要删除原因,我推断删除操作是虚拟的。因此,我相信通过PUT / PATCH操作更新记录是一种有效的方法,尽管它本身并不是DELETE操作。
  • 使用查询参数:不会更改资源uri。我实际上认为这也是一种有效的方法。您链接的问题是谈论如果缺少查询参数则不允许删除。在您的情况下,如果查询字符串中未指定原因,则只有默认原因。资源仍然是resource/:id。您可以使用每种原因的资源上的Link标头使它可被发现(在每个原因上带有一个rel标记以标识原因)。
  • 根据理由使用单独的端点:使用类似的网址resource/:id/canceled。实际上,这确实会更改Request-URI,并且绝对不是RESTful的。同样,链接头可以使此发现。

请记住,REST不是法律或教条。可以将其更多地视为指导。因此,当有意义的是不遵循针对您的问题域的指导时,请不要这样做。只需确保您的API使用者已了解差异即可。


关于查询参数的使用,我的理解是,查询是3.2节中 Request-URI的一部分,因此使用此方法(或类似地,使用单独的端点)违反了DELETE方法的定义,因此“由“ Request-URI标识”。
雪莱

资源由uri路径标识。因此GET to /orders/:id将返回与相同的资源/orders/:id?exclude=orderdetails。查询字符串仅向服务器提供提示-在这种情况下,将在响应中排除orderdetails(如果支持)。同样,如果将DELETE发送到/orders/:id/orders/:id?reason=canceled/orders/:id?reason=bad_credit,则您仍在作用于相同的基础资源。为了保持“统一的界面”,我会有一个默认原因,因此不需要发送查询参数。
codeprogression

@shelley您对查询字符串的关注是正确的。查询字符串是URI的一部分。向发送DELETE请求/foo?123意味着您要删除与向DELETE发送资源不同的资源/foo?456
Nicholas Shanks

@codeprogression对不起,但是您所说的很多内容都是错误的。资源由整个URI标识,而不仅仅是路径。不同的查询字符串是不同的资源(在HTTP中,“资源”一词)。另外,统一界面也不需要默认原因。该术语指的是通过HTTP定义它们的方式使用GET,PUT,POST,PATCH和DELETE。通用性是在供应商(用户代理供应商,API供应商,缓存代理供应商,ISP等)之间,而不是在自己的API内部(尽管对于用户的理智而言,它们在设计上也应该是统一的!)。
Nicholas Shanks

@尼古拉斯(Nicholas)我不理解您是否需要辩论三年前结束的讨论。从以REST为中心的角度来看,我的回答和评论是正确的。REST不是HTTP(也不是HTTP的任何供应商实现)。在REST的上下文中,资源是相同的。正如我在回答中所说的那样,REST不是法律或教条,而是指导。
codeprogression '16

0

我建议您将所需的元数据包含在URI层次结构本身中。一个例子(朴素):

如果您需要删除基于日期范围的条目,而不是在正文中传递开始日期和结束日期或作为查询参数,请构造URI,以便将所需信息作为URI的一部分传递。

例如

DELETE /entries/range/01012012/31122012 -删除2012年1月1日至2012年12月31日之间的所有条目

希望这可以帮助。


5
不涵盖发送删除原因(例如,评价字段)的情况。
Kugel 2014年

3
哇。多数民众赞成在一个可怕的想法。如果元数据太多,它将使URI的大小限制膨胀。
Balaji Boggaram Ramanarayan 2015年

1
此方法不遵循RESTful做法,因此不建议使用,因为您将使用复杂的URI结构。端点由于资源标识与元数据的纠缠而混乱,随着API的更改,及时将成为维护的噩梦。更可取的是range在查询参数或有效负载中指定特定的内容,这是解决此问题的关键:要理解针对此问题的最佳实践方法,我会说不是这样。
digitaldreamer

@digitaldreamer-我不明白您所说的复杂URI结构是什么意思?另外,这是HTTP DELETE,因此有效负载不是选项,而是查询参数。
Suresh Kumar
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.