使用REST删除多个记录


95

RESTful删除多个项目的方式是什么?

我的用例是我有一个Backbone Collection,其中我需要能够一次删除多个项目。选项似乎是:

  1. 为每条记录发送一个DELETE请求(如果可能有几十个项目,这似乎是个坏主意);
  2. 发送一个DELETE,将要删除的ID串在URL中(即“ / records / 1; 2; 3”);
  3. 通过非REST方式,发送包含ID标记为删除的自定义JSON对象。

所有选项都不理想。

这似乎是REST约定的灰色区域。


Answers:


89
  1. 是可行的RESTful选择,但显然存在您所描述的限制。
  2. 不要这样 中介机构会将其解释为“删除(单个)资源/records/1;2;3”,因此对此的2xx响应可能会导致他们清除其缓存/records/1;2;3净化/records/1/records/2/records/3; 代表410响应/records/1;2;3或其他您认为没有意义的事情。
  3. 此选择是最佳选择,可以RESTful完成。如果您正在创建API,并且希望允许对资源进行大量更改,则可以使用REST来完成,但是对于许多人而言,确切的做法并不是立即显而易见的。一种方法是创建“变更请求”资源(例如,通过发布诸如records=[1,2,3]to 的正文/delete-requests)并轮询创建的资源(由Location响应的标头指定),以了解您的请求是否已被接受,拒绝或处理中或已完成。这对于长时间运行的操作很有用。另一种方法是PATCH请求发送到列表资源/records,其主体包含资源列表以及对这些资源执行的操作(您要支持的任何格式)。这对于快速操作很有用,其中请求的响应代码可以指示操作的结果。

可以在不限制REST约束的情况下实现一切,通常的答案是将“问题”转化为资源,并为其提供URL。
因此,批处理操作(例如在此处删除,将多个项目发布到列表中或对大量资源进行相同的编辑)都可以通过创建“批处理操作”列表并将新操作发布到其中来进行处理。

不要忘记,REST不是解决任何问题的唯一方法。“REST”只是一种建筑风格,你不具备坚持它(但你失去了上网的一些好处,如果你不这样做)。我建议您查看一下此HTTP API体系结构列表,然后选择适合您的一种。只要选择其他体系结构,就应该知道自己会遭受什么损失,然后根据用例做出明智的决定。

对于用于在REST Web服务中处理批处理操作的模式,此问题有一些错误的答案投票太多,但也应该阅读。


2
不必担心您的服务器,它是中介,CDN,缓存代理等。Internet是一个分层系统。这就是它运行良好的原因。Roy确定了该系统成功所需的哪些方面,并将它们命名为REST。如果发出DELETE请求,则被请求者和服务器之间的任何内容都将认为正在删除指定URL上的单个资源。查询字符串是这些设备URL的不透明部分,因此无论如何指定API都无关紧要,它们不了解此知识,因此不能表现不同。
Nicholas Shanks 2014年

3
如果由于URI长度限制而需要删除大量资源,则/ records / 1; 2; 3将无法正常工作
dukethrash 2016年

3
请注意,如果考虑使用DELETE和定义要清除的资源的主体,则某些中介可能不会转发该主体。此外,某些HTTP客户端无法将主体添加到DELETE。参见stackoverflow.com/questions/299628/…–
卢克·普普利特

3
@LukePuplett我只是声明DELETE禁止将请求正文与请求一起传递。不要这样 如果你愿意,我会吃掉你的孩子。名词名词名词
Nicholas Shanks

3
#3参数的问题在于它承担与针对#2的计数器参数相同的惩罚。创建待删除资源并不是上游代理将如何处理的东西-针对方法2提出的相同计数器参数。
LB2

16

如果GET /records?filteringCriteria返回符合条件的所有记录的数组,则DELETE /records?filteringCriteria可以删除所有此类记录。

在这种情况下,您的问题的答案将是DELETE /records?id=1&id=2&id=3


1
我也得出了这样的结论:只需将动词翻转至您想做的事情即可。我不明白GET的用法不等于DELETE的用法。
路加·普普利特

9
GET /records?id=1&id=2&id=3不会意味着“拿到三个记录与ID为1,2&3”,它的意思是“获取与URL路径/记录?ID = 1&ID = 2&ID = 3的单个资源”,这可能是一个萝卜一个画面,一个纯文本包含中文数字“ 42”的文档,或者可能不存在。
Nicholas Shanks 2015年

请考虑以下内容:对/records?id=1/records?id=2发送两个连续的请求,并且它们的响应被某些中介(例如,浏览器或ISP)缓存。如果Internet知道您的应用程序的含义,那么/records?id=1&id=2就可以推断出缓存可以简单地通过合并(以某种方式)合并已经拥有的两个结果而无需请求原始服务器来返回请求。但这是不可能的。/records?id=1&id=2可能无效(每个请求只允许使用1个ID),或者可能返回完全不同的内容(萝卜)。
Nicholas Shanks 2015年

这是一个基本的资源缓存问题。如果我的DBA直接更改了状态,则缓存现在不同步。您提供了一个中间人返回的示例410,但是410是用于永久删除的,删除后,缓存可能会清除该URL的插槽,但是由于它不知道DBA是否会发送410或404,所以它不会发送不仅将资源立即重新放回原处。
卢克·普普利特

4
@NicholasShanks我真的不同意。如果结果被缓存,那是服务器的问题。而且,如果您在谈论API的设计,那么希望您是为服务器编写代码的人。无论您使用查询字符串id[]=1&id[]=2还是id=1&id=2在查询字符串中表示值数组,该查询字符串都确实表示该值。而且我认为让查询字符串代表过滤器是一种非常普遍且良好的做法。此外,如果允许删除和更新,请不要缓存GET请求。如果这样做,客户端将保持陈旧状态。
约瑟夫·尼尔斯

8

我认为Mozilla Storage Service SyncStorage API v1.5是使用REST删除多个记录的好方法。

删除整个集合。

DELETE https://<endpoint-url>/storage/<collection>

通过单个请求从集合中删除多个BSO。

DELETE https://<endpoint-url>/storage/<collection>?ids=<ids>

ids:从集合中删除BSO,其ID在提供的逗号分隔列表中。最多可以提供100个ID。

在给定位置删除BSO。

DELETE https://<endpoint-url>/storage/<collection>/<id>

http://moz-services-docs.readthedocs.io/zh-CN/latest/storage/apis-1.5.html#api-instructions


似乎是一个很好的解决方案。我想如果mozilla认为这是正确的,那一定是吗?唯一的问题就是错误处理。假设它们传递?ids = 1,2,3且id 3不存在,那么您删除1和2,然后用200响应,因为请求者希望3消失并且不存在,所以没关系吗?或如果他们有权删除1而不是2怎么办...您什么都不删除,并以错误答复还是您删除了可以留下的东西...
tempcke

我通常会返回一个成功的响应,因为无论结束状态是相同的。由于它们不再需要处理该错误状态,因此这也简化了客户端上的逻辑。至于授权案例,我只会使整个请求失败...但是实际上,这取决于您的用例。
内森·菲特普拉斯

3

这似乎是REST约定的灰色区域。

是的,到目前为止,我只遇到过一篇提到批处理操作(例如批删除)的REST API设计指南:google api设计指南

本指南提到了创建“自定义”方法的方法,这些方法可以使用冒号通过资源进行关联,例如https://service.name/v1/some/resource/name:customVerb,还明确提到了批处理操作作为用例:

定制方法可以与资源,集合或服务相关联。它可以接受任意请求并返回任意响应,并且还支持流式请求和响应。自定义方法应使用HTTP POST动词,因为它具有最灵活的语义。对于性能至关重要的方法,提供自定义批处理方法以减少每个请求的开销可能很有用。

因此,您可以根据Google的API指南执行以下操作:

POST /api/path/to/your/collection:batchDelete

...删除一堆您的收藏资源。


通过JSON格式的数组传达项目列表的可行解决方案吗?
丹妮(Daniele),

是的,可以。您可以发布有效负载,其中通过json数组发送ID。
B12Toaster

Google API指南If the HTTP verb used for the custom method does not accept an HTTP request body (GET, DELETE), the HTTP configuration of such method must not use the body clause at all,在“自定义方法”一章中说的很有趣。但是 GET accounts.locations.batchGetapi是带有body的GET方法。太奇怪了 developers.google.com/my-business/reference/rest/v4/…–
郑元杰

@郑元杰同意,乍看之下看起来有些奇怪,但如果仔细观察,它实际上是POST所使用的http方法,并且仅命名了custom方法batchGet。我想Google这样做是为了(a)坚持所有自定义方法都必须遵循的规则POST(请参见我的回答),以及(b)使人们更容易在体内放置“过滤器”,因此您不必对查询字符串进行转义或编码。不利的
一面

https://service.name/v1/some/resource/name:customVerb根据定义不是RESTful的。
迪蒙

2

我已经允许批量替换一个集合,例如PUT ~/people/123/shoes,主体是整个集合的表示形式。

这适用于小型的项目子集,客户希望客户查看这些项目并修剪一些项目,然后再添加一些其他项目,然后更新服务器。他们可以输入一个空集合以删除所有集合。

这意味着GET ~/people/123/shoes/9即使PUT删除了它,它仍将保留在缓存中,但这只是一个缓存问题,如果其他人删除了鞋子,这将是一个问题。

我的数据/系统API始终使用ETag,而不是使用到期时间,因此服务器在每次请求时都被命中,因此我需要正确的版本/并发标头才能对数据进行突变。对于只读且视图/报告对齐的API,我确实使用了有效时间来减少原始点击,例如,排行榜可以持续10分钟。

对于更大的集合,例如~/people,我倾向于不需要多次删除,用例往往不会自然出现,因此单个DELETE可以正常工作。

将来,从构建REST API并遇到相同问题和要求(如审核)的经验来看,我倾向于仅使用GET和POST动词并围绕事件进行设计,例如POST更改地址事件,尽管我怀疑会带来一系列问题:)

我还允许前端开发人员构建自己的API,这些API消耗更严格的后端API,因为通常有一些实用,有效的客户端原因,他们为什么不喜欢严格的“ Fielding zealot” REST API设计,并且出于生产力和缓存分层的原因。


我一直喜欢这个答案,直到我读完最后一句话:)我从未见过使用严格的REST会产生净有害影响的用例。当然,可以在两端写更多的代码,但是最终您会得到一个更安全,更干净,耦合更少的系统。
Nicholas Shanks

哈哈。它实际上已成为一种模式!前端的后端称为ThoughtWorks技术雷达。它还允许编写更多的应用程序逻辑,例如JavaScript,这很麻烦,并且显然可以在没有客户端的情况下进行更新,因此对于iOS应用程序来说可以说是更新。
路加·普普利特

略读Google的前四首歌曲,看来这种BFF技术只有在客户控制之下时才能起作用。客户端开发人员开发他们想要的API,将调用映射到真正的后端微服务API。在此图中:samnewman.io/patterns/architectural/bff/#bff我将在BFF框下面放置“ Perimeter”线-每个框只是客户端的一部分。它甚至可能位于容纳微服务的数据中心之外。我也看不到REST如何不适用于两个接口(客户端/ BFF和BFF /微服务)。
Nicholas Shanks

1
是的,这很不错。例如,通常是当您有一个构建微服务的团队和一个开发有角度的应用程序的团队时,而该开发团队是前端类型更多的人,他们不愿意与大量的纯粹的服务打交道。尽管我看不到任何原因,但您不能使用相同的模式来为客户抽象微服务并将其聚合为更可用的外观,以便可以在不影响外观的情况下更改微服务。
路加·普普利特

API端点应该对域和业务的需求进行建模。编写代码以解决这些问题,并避免过度设计以遵守严格且屡屡僵化的规范。无论如何,REST只是准则。
Victorio Berra
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.