2013-01-14 26 views
69

虽然HTTP 1.1 spec似乎允许请求DELETE邮件正文,但似乎表明服务器应该忽略它,因为没有定义它的语义。DELETE请求正文的RESTful替代

4.3邮件正文

服务器应该阅读和任何请求转发消息体;如果 请求方法不包括实体主体的定义的语义,则 ,那么在处理请求时应该忽略消息主体。

我已经审查了SO内外有关这个主题的几个相关的讨论,如:

大多数讨论似乎同意在DELETE上提供一个消息正文可能是,但通常不推荐。另外,我注意到了各种HTTP客户端库中的趋势,越来越多的增强功能似乎被记录在这些库中以支持DELETE上的请求体。大多数图书馆似乎都有责任,尽管偶尔会遇到一些初步阻力。

我的用例需要在DELETE上添加一些必需的元数据(例如删除的“原因”以及删除所需的其他一些元数据)。我已经考虑了以下选项,其中没有一个似乎完全适当的,内嵌HTTP规范和/或REST最佳实践:

  • 消息体 - 该规范指出消息体对DELETE没有语义值; HTTP客户端不完全支持;不是标准做法
  • 自定义HTTP标头 - 要求自定义标头通常是against standard practices;使用它们与我的其余API不一致,其中没有一个需要自定义标题;此外,可没有好HTTP响应,指示坏习惯头值(可能是完全独立的问题)
  • 标准HTTP标头 - 没有标准的标题是适当
  • 查询参数 - 添加查询参数实际上改变了请求 - URI被删除; against standard practices
  • POST方法 - (例如POST /resourceToDelete { deletemetadata })POST不是删除的语义选项; POST实际上代表所需的相反动作(即POST创建资源下属;但我需要删除该资源)
  • 多种方法 - 拆分删除请求为两个操作(例如PUT删除的元数据,然后删除)分裂的原子操作分为两个,可能会留下不一致的状态。删除原因(和其他相关的元数据)不是资源表示本身的一部分。

我的第一选择可能是使用邮件正文,其次是自定义HTTP头;然而,正如所指出的那样,这些方法有一些缺点。

是否有任何建议或最佳实践与REST/HTTP标准一起在DELETE请求中包含这些必需的元数据?有没有其他的选择我没有考虑过?

+1

像某些实现'Jersey'不允许身体'delete'请求。 – basiljames

+0

对这个问题看起来好像没有好的答案。 – niico

回答

32

尽管一些建议,不使用在邮件正文中DELETE请求,这种方法可能在某些情况下,使用适当的。这是我们在评估问题/答案中提及的其他选项并与服务消费者合作后最终使用的方法。

虽然使用消息体的不理想,没有其他选择是完全嵌合任一。请求主体DELETE允许我们轻松而清楚地添加伴随DELETE操作所需的附加数据/元数据的语义。

我仍然开放给其他的想法和讨论,但希望结束对这一问题的循环。我感谢大家对此主题的想法和讨论!

+7

这是一个坏主意。如果您以后决定使用像Akamai EdgeConnect这样的HTTP加速服务,那么这会让您陷入困境。我知道EdgeConnect从HTTP DELETE请求中删除正文(因为它们消耗的带宽可能无效)。类似的服务也可能具有相同的功能(请参阅Kindle的加速功能以及其他CDN类服务)。您可能应该重新设计,不要为您的服务使用HTTP动词。大多数API使用HTTP-Verbs/classical-REST和HTTP动词传输问题很难排除故障。 – Gabe

+1

我同意@Gabe,根据定义发送一个没有身体**的方法的身体**是一种确定的方式随机丢失数据,因为你的位遍历互联网管道,你将有一个非常困难的时间调试它。 –

5

鉴于你的情况,我会采取以下方法之一:

  • 发送PUT或者PATCH:我推断出删除操作是虚拟的,由需要删除的性质原因。因此,我认为通过PUT/PATCH操作更新记录是一种有效的方法,即使它本身不是DELETE操作。
  • 使用查询参数:资源URI未被更改。我其实认为这也是一种有效的方法。您链接的问题是如果查询参数丢失,则不允许删除。在你的情况下,如果原因没有在查询字符串中指定,我只会有一个默认原因。该资源仍将是resource/:id。您可以根据各种原因使用资源上的链接标题对其进行检测(每个标识上都有一个rel标记,用于标识原因)。
  • 每个原因使用一个单独的终结点:使用类似resource/:id/canceled的网址。这实际上改变了Request-URI,绝对不是RESTful。同样,链接头可以使这个发现。

请记住,REST不是法律或教条。把它看作指导。所以,如果不遵循针对问题域的指导意义,则不要。只要确保您的API消费者了解了差异。

+0

关于使用查询参数的,我的理解是,该查询是每Request-URI中[3.2节](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.2的一部分),因此使用这种方法(或者类似地,单独的端点)违背了[DELETE](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html)方法的定义,使得“由Request-URI标识的资源“被删除。 – shelley

+0

资源由uri路径标识。所以GET到'/ orders /:id'会返回与'/ orders /:id?exclude = orderdetails'相同的资源。查询字符串仅向服务器提供提示 - 在这种情况下,为了排除响应中的orderdetails(如果支持)。同样,如果您将DELETE发送到'/ orders /:id'或'/ orders /:id?reason = cancel'或'/ orders /:id?reason = bad_credit',您仍然在使用相同的底层资源。为了保持'统一的接口',我会有一个默认的原因,以便发送查询参数不是必需的。 – codeprogression

+0

@shelley您对查询字符串的担忧是正确的。查询字符串是URI的一部分。发送一个DELETE请求到'/ foo?123'意味着你正在删除一个不同的资源,而不是你发送DELETE到'/ foo?456'。 –

0

我建议你将所需的元数据作为URI层次本身的一部分。一个例子(朴素):

如果您需要根据日期范围删除条目,而不是将正文的开始日期和结束日期作为正文或查询参数传递,那么可以通过构造URI的方式来传递所需的信息作为URI的一部分。

例如

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

希望这有助于。

+4

不包括发送删除原因即提交字段的情况。 – Kugel

+2

哇。这是一个可怕的想法。如果你有太多的元数据,它会膨胀URI的大小限制。 –

+0

这种方法不遵循REST风格的做法,因为您会遇到令人费解的URI结构,所以不推荐这样做。端点混淆与交织的资源识别与元数据,随着您的API变化,及时将成为维护的噩梦。在查询参数或有效载荷中指定'范围'是更加优选的,这是这个问题的核心:理解对于问题的最佳实践方法,我想说的不是这个。 – digitaldreamer

8

你似乎什么要的是两件事情之一,这两者都不是一个纯粹的DELETE

  1. 你有两个操作,一个的删除原因,其次是资源的DELETEPUT。一旦删除,任何人都无法再访问该资源的内容。 “原因”不能包含指向已删除资源的超链接。或者,
  2. 您正在尝试使用DELETE方法将资源state=active更改为state=deleted。状态=已删除的资源会被主API忽略,但对于管理员或具有数据库访问权限的用户而言仍然可读。这是允许的 - DELETE不必删除资源的后备数据,只能删除在该URI处公开的资源。

这需要在DELETE请求的消息体可以被分解为它的最一般的任何操作,POST做所有的邮件正文和DELETE必要的任务。我没有理由打破HTTP的语义。

+2

如果'PUT'原因成功并且'DELETE'资源失败会发生什么?如何防止不一致的状态? – Lightman

+0

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