删除请求体的替代方案

虽然 HTTP 1.1规范删除请求上似乎是 允许消息体,但它似乎表明服务器应该忽略它,因为它没有定义的语义。

4.3讯息正文

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

我已经回顾了关于 SO 及其他相关主题的几个相关讨论,例如:

大多数讨论似乎一致认为,在 DELETE 上提供消息正文可能是 允许,但通常不推荐使用 允许

此外,我注意到在各种 HTTP 客户端库中出现了一种趋势,似乎有越来越多的增强被记录在这些库中,以支持 DELETE 上的请求主体。大多数图书馆似乎都乐于助人,尽管偶尔也会有一些最初的抵触情绪。

我的用例需要在 DELETE 上添加一些必需的元数据(例如,删除的“原因”,以及删除所需的其他元数据)。我考虑了以下选项,没有一个看起来是完全合适的,并且符合 HTTP 规范和/或 REST 最佳实践:

  • Message Body -该规范指出 DELETE 上的消息体没有语义值; HTTP 客户端不完全支持; 不是标准实践
  • 自定义 HTTP Header -需要自定义 Header 通常是 反对标准做法; 使用它们与我的其他 API 不一致,其中没有一个需要自定义 Header; 此外,没有好的 HTTP 响应可用来指示错误的自定义 Header 值(可能是一个完全独立的问题)
  • 标准 HTTP 头 -不适合使用标准头
  • 查询参数 -添加查询参数实际上改变了被删除的 Request-URI;
  • POST Method - (e.g. POST /resourceToDelete { deletemetadata }) POST is not a semantic option for deleting; POST actually represents the 相反 action desired (i.e. POST creates resource subordinates; but I need to delete the resource)
  • 多个方法 -将 DELETE 请求分割成两个操作(例如 PUT DELETE 元数据,然后 DELETE)将一个原子操作分割成两个,可能会留下不一致的状态。删除原因(和其他相关元数据)不是资源表示本身的一部分。

My first preference would probably be to use the message body, second to custom HTTP headers; however, as indicated, there are some downsides to these approaches.

对于在 DELETE 请求中包含这些必需的元数据,是否有符合 REST/HTTP 标准的建议或最佳实践?还有其他我没考虑过的选择吗?

95992 次浏览

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

  • 发送 PUT 或 PATCH : 由于需要删除原因,我推断删除操作是虚拟的。因此,我认为通过 PUT/PATCH 操作更新记录是一种有效的方法,即使它本身不是 DELETE 操作。
  • 使用查询参数 : 资源 uri 没有被更改。我实际上认为这也是一种有效的方法。您链接的问题是,如果查询参数丢失,则不允许删除。在您的例子中,如果查询字符串中没有指定原因,那么我将只有一个默认原因。资源仍然是 resource/:id。对于每个原因,都可以使用资源上的 Link 头来发现它(每个链接头上都有一个 rel标记来标识原因)。
  • 每个原因使用一个单独的端点 : 使用像 resource/:id/canceled这样的 URL。这实际上改变了 Request-URI,而且肯定不是 RESTful。同样,链接标题可以使这一点可以被发现。

记住 REST 不是法律或教条。你可以把它当成是一种指引。因此,如果不遵循问题领域的指导是有意义的,那么就不要遵循。只要确保您的 API 使用者被告知了差异。

I suggest you include the required metadata as part of the URI hierarchy itself. An example (Naive):

If you need to delete entries based on a date range, instead of passing the start date and end date in body or as query parameters, structure the URI such a way that you pass the required information as part of the URI.

例如:。

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

希望这个能帮上忙。

你似乎想要的是两样东西中的一样,而这两样东西都不是纯 DELETE:

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

DELETE请求上需要消息体的任何操作都可以分解为最一般的 POSTDELETE,前者用于完成消息体的所有必要任务。我认为没有理由打破 HTTP 的语义。

Despite some recommendations not to use the message body for DELETE requests, this approach may be appropriate in certain use cases. This is the approach we ended up using after evaluating the other options mentioned in the question/answers, and after collaborating with consumers of the service.

虽然消息正文的使用并不理想,但其他选项也都不完全合适。请求体 DELETE 允许我们轻松清晰地添加 DELETE 操作所需的附加数据/元数据的语义。

我仍然对其他的想法和讨论持开放态度,但是希望在这个问题上结束循环。感谢大家对这个话题的思考和讨论!

我认为查询参数是资源定义的一部分,因此您可以使用它们来定义操作的范围,然后“应用”操作。 我的结论是 查询参数正如您所定义的那样是最好的方法。