什么是删除多个项目的REST-ful方式?
我的用例是我有一个Backbone Collection,我需要能够一次删除多个项目。 选项似乎是:
发送每条记录的DELETE请求(如果有可能存在数十个项目,这似乎是一个坏主意);
发送一个DELETE,其中要删除的ID在URL中串在一起(即"/ records / 1; 2; 3");
以非REST方式,发送包含标记为删除的ID的自定义JSON对象。
所有选项都不太理想。
这似乎是REST约定的灰色区域。
是一个可行的RESTful选择,但显然有你所描述的限制。
不要这样做。中介机构将其解释为"在/records/1;2;3处删除(单个)资源" - 因此,对此的2xx响应可能会导致它们清除其/records/1;2;3的缓存;不清除/records/1,/records/2或/records/3;代理/records/1;2;3的410响应,或其他从您的角度来看没有意义的事情。
这个选择是最好的,可以RESTful完成。如果您正在创建API并且希望允许对资源进行大量更改,则可以使用REST来执行此操作,但对于许多人而言,确切的方法并不是很明显。一种方法是创建"更改请求"资源(例如,通过将诸如records=[1,2,3]的主体POST到/delete-requests)并轮询创建的资源(由响应的Location标头指定)以查明您的请求已被接受,拒绝,正在进行或已完成。这对于长时间运行的操作很有用。另一种方法是向列表资源/records发送PATCH请求,其主体包含要对这些资源执行的资源和操作列表(以您想要支持的任何格式)。这对于快速操作很有用,其中请求的响应代码可以指示操作的结果。
在保持REST限制的同时,一切都可以实现,通常答案是将"问题"变成资源,并给它一个URL。
因此,批处理操作(例如在此处删除,或将多个项目发布到列表中,或对大量资源进行相同的编辑)都可以通过创建"批处理操作"列表并将新操作发布到其中来处理。
不要忘记,REST不是解决任何问题的唯一方法。"REST"只是一种建筑风格,你不必坚持它(但如果你不这样做,你会失去互联网的某些好处)。我建议你查看这个HTTP API架构列表,然后选择适合你的那个。如果您选择其他架构,只要让自己意识到自己失去了什么,并根据您的用例做出明智的决定。
对于处理REST Web服务中的批处理操作的模式,这个问题有一些不好的答案?有太多的赞成,但也应该被阅读。
-
根据您的网址/查询字符串的格式,您的服务器应该能够确定DELETE /records/1;2;3或者是单个还是多个删除事件。例如DELETE /records?id=1;2;3;4&multi=1?
-
这不是你的服务器,你必须担心,它是中介,CDN,缓存代理等。互联网是一个分层系统。这就是它运作良好的原因。 Roy确定了系统的哪些方面是成功所必需的,并将其命名为REST。如果发出DELETE请求,则被请求者和服务器之间的任何内容都会认为正在删除指定URL处的单个资源。查询字符串是这些设备的URL的不透明部分,因此无论您如何指定API,他们都不了解这些知识,因此不能表现不同。
-
如果由于URI长度限制而有大量要删除的资源,则/ records / 1; 2; 3将不起作用
-
请注意,如果考虑DELETE和定义要清除的资源的主体,那么某些中介可能无法转发正文。此外,某些HTTP客户端无法将正文添加到DELETE。请参阅stackoverflow.com/questions/299628/…
-
@LukePuplett我只是声明禁止传递带有DELETE请求的请求体。不要这样做。如果你这样做,我会吃你的孩子。 Nom nom nom。
-
。哦(他知道我有孩子)
-
stackoverflow.com/a/299696/409976不同意您的"通过带有DELETE请求的请求正文被禁止",@ NicholasShanks
-
@KevinMeredith嗨,谢谢你的评论。根据规格,是的,它是不被禁止的(虽然注意shelly对该答案的评论);但卢克建议OP如何定义他的API。我只是建议我的API会声明禁止向其发送带有正文的DELETE请求。
-
#3参数的问题在于它与#2的反驳参数相同。创建to-delete资源不是上游代理知道如何处理的东西 - 与方法#2相同的计数器参数。
-
@ LB2我知道你来自哪里,而你是对的,中间人不会知道创造这种资源会导致其他资源消失;但其他资源一直在消失。关键是,在这种情况下,中间人不会做错事,即使他们做的事情不是最优的。
-
PATCH旨在更改特定资源 - restcookbook.com/HTTP%20Methods/patch,restful-api-design.readthedocs.io/en/latest/methods.html
-
@ user152807是的,在这种情况下,它正在更改"记录列表"的特定资源。不要忘记集合本身就是一种资源,其内容是其成员列表。请注意,从集合中删除项目不一定要删除引用的资源,但它可以删除。
-
@NicholasShanks抱歉,我没有正确阅读,它说"向列表资源发送一个PATCH请求",用粗体表示。
-
选项3与选项2有相同的问题,不是吗?我的意思是中介不清除/记录/ 1,/ record / 2或/ records / 3。
-
@ user1633272你是对的 - 这是一个妥协,但不违反REST。即使中间人确实清除了该URL或为现在删除的URL返回了缓存结果,除了客户端获取过时数据之外,不会发生任何不良事件(所有数据在到达客户端时都是陈旧的)。
-
链接到"HTTP API架构"不再有效。
-
@Ravik谢谢 - 修复。自从我写这个答案以来,它已经移动了至少3次!
-
我想建议这篇文章:« REST从来都不是关于CRUD» tyk.io/blog/rest-never-crud。它帮助我更好地理解REST。它处理BULK / BATCH资源问题,但也有更多(例如长时间运行的批处理,长时间运行的任务的事务资源等)。试一试
如果GET /records?filteringCriteria返回符合条件的所有记录的数组,则DELETE /records?filteringCriteria可以删除所有此类记录。
在这种情况下,您的问题的答案将是DELETE /records?id=1&id=2&id=3。
-
我也得出了这个结论:只需将动词翻到你想要做的事情上。我不明白为GET做什么不是为了DELETE。
-
GET /records?id=1&id=2&id=3并不意味着"获得ID为1,2和&的3条记录。 3",表示"获取具有URL路径/记录的单一资源?id = 1& id = 2& id = 3",这可能是萝卜图片,包含中文数字"42"的纯文本文档,或者可能不存在。
-
请考虑以下事项:发送对/records?id=1和/records?id=2的两个连续请求,并由某些中介(例如您的浏览器或ISP)缓存其响应。如果互联网知道你的应用程序的含义,那么只需合并(不知何故)它已经拥有的两个结果就可以通过缓存返回/records?id=1&id=2的请求,而不必询问原始服务器。但这是不可能的。 /records?id=1&id=2可能无效(每个请求只允许1个ID)或者可能返回完全不同的东西(萝卜)。
-
这是一个基本的资源缓存问题。如果我的DBA直接改变了状态,那么缓存现在就不同步了。您给出了一个由中间人返回的示例410,但是410用于永久删除,在DELETE时,缓存可能会清除该URL的插槽,但它不会发送410或404,因为它不知道是否有DBA不仅仅是将资源重新放回原点。
-
@NicholasShanks我真的不同意。如果结果被缓存,那就是服务器的错误。如果您正在讨论API的设计,那么您希望是为服务器编写代码的人。无论是在查询字符串中使用id[]=1&id[]=2还是id=1&id=2来表示值数组,该查询字符串都表示这一点。我认为这是非常普遍的&让查询字符串表示过滤器的好习惯。此外,如果允许删除和更新,请不要缓存GET请求。如果这样做,客户将保持陈旧状态。
-
@JosephNields你误解了我,我们的两点并不矛盾。我说资源可以代表服务器想要的任何东西,包括两个项目的集合,但中间人不会理解/?ids = 1,2产生/ 1和/ 2的集合 - 这个知识是内部的原始服务器。如果你正在使用REST的超文本,即使客户也不应该知道这一点。当我在原始评论中说"不代表"时,我指的是"从HTTP的角度来看",而不是来自服务器的。
-
@NicholasShanks,公平地说,这是真的,这是服务器内部的,但不是一个RESTful API只是一个API?我们必须定义行为和客户如何与之交互。 GET api/records/1可以返回萝卜的图片,但是如果我们说它应该返回"记录"对象的JSON表示,那么客户可以期待。带有查询字符串的DELETE请求不必表示删除与过滤器匹配的记录,但这并不意味着它不能/不应该。这种方法在不破坏RESTful约束的情况下实现了预期的结果,因此我认为这是一个很好的解决方案。
-
@JosephNields我同意所有这些:-)。在URL中列出ID列表的另一个缺点是,在实际环境中URI长度比体长更受限制,如果要列出50万个ID,这可能是个问题。
-
@NicholasShanks你是对的,我先处理过这个问题。如果出现此问题,有些人会将服务器配置为接受x-method-override标头,而是POST请求x-www-form-urlencoded。只有几百个ID可能会导致问题!
-
我认为这个问题的主要问题是,在同时对多个记录运行查询时,应该小心处理任何非幂等请求(特别是删除和更新);主要是由于前所未有的边缘情况和错误。
我认为Mozilla Storage Service SyncStorage API v1.5是使用REST删除多个记录的好方法。
删除整个集合。
1
| DELETE https://<endpoint-url>/storage/<collection> |
使用单个请求从集合中删除多个BSO。
1
| DELETE https://<endpoint-url>/storage/<collection>?ids=<ids> |
ids:从集合中删除BSO,其中的ID位于提供的逗号分隔列表中。最多可提供100个ID。
删除给定位置的BSO。
1
| DELETE https://<endpoint-url>/storage/<collection>/<id> |
http://moz-services-docs.readthedocs.io/en/latest/storage/apis-1.5.html#api-instructions
-
这似乎是一个很好的解决方案。我想如果mozilla认为它是正确的那么它一定是吗?唯一的问题是错误处理。假设它们传递?ids = 1,2,3并且id 3不存在你是否删除1和2然后用200响应因为请求者想要3消失而它不存在所以它没关系?或者,如果他们被授权删除1但不是2 ...如果你没有删除任何内容并回复错误,或者你删除了你能做什么而留下其他人......
This seems like a gray area of the REST convention.
是的,到目前为止,我只提到了一个REST API设计指南,其中提到了批处理操作(例如批量删除):google api设计指南。
本指南提到了"自定义"方法的创建,这些方法可以通过使用冒号来通过资源关联,例如https://service.name/v1/some/resource/name:customVerb,它还明确提到批处理操作作为用例:
A custom method can be associated with a resource, a collection, or a service. It may take an arbitrary request and return an arbitrary response, and also supports streaming request and response. [...] Custom methods should use HTTP POST verb since it has the most flexible semantics [...] For performance critical methods, it may be useful to provide custom batch methods to reduce per-request overhead.
所以你可以根据谷歌的api指南做以下事情:
1
| POST /api/path/to/your/collection:batchDelete |
...删除收集资源的一堆项目。
-
是一个可行的解决方案,项目列表是通过JSON格式的数组传递的吗?
-
是的你可以POST一个有效载荷,通过json数组发送id。
我允许批量更换收藏品,例如PUT ~/people/123/shoes其中正文是整个集合表示。
这适用于小型子项集合,其中客户端想要查看项目并删除一些项目并添加其他项目然后更新服务器。他们可以将一个空集合删除所有。
这意味着即使PUT删除它,GET ~/people/123/shoes/9仍然会保留在缓存中,但这只是一个缓存问题,如果其他人删除了鞋子就会出现问题。
我的数据/系统API总是使用ETag而不是到期时间,因此每个请求都会遇到服务器,我需要正确的版本/并发标头来改变数据。对于只读和查看/报告对齐的API,我确实使用到期时间来减少原点命中,例如排行榜可以持续10分钟。
对于更大的集合,如~/people,我倾向于不需要多次删除,用例往往不会自然产生,因此单个DELETE工作正常。
将来,根据构建REST API和遇到相同问题和要求(如审计)的经验,我倾向于仅使用GET和POST动词并围绕事件进行设计,例如: POST一个地址变更事件,虽然我怀疑它会带来一系列问题:)
我还允许前端开发人员构建他们自己的API,这些API使用更严格的后端API,因为他们不喜欢严格的"Fielding zealot"REST API设计,以及生产力和效率,这通常是实际的,有效的客户端原因。缓存分层原因。
-
直到我读完最后一句话,我才喜欢这个答案:)我从未见过使用严格REST会产生净不利影响的用例。当然,它可以在两端编写更多的代码,但最终会得到一个更安全,更清洁,耦合度更低的系统。
-
哈哈。它实际上变成了一种模式!前端后端称为ThoughtWorks技术雷达。它还允许编写更多的应用程序逻辑,比如说JavaScript很麻烦,显然可以在没有客户端的情况下进行更新,因此更新说,对于iOS应用程序。
-
浏览谷歌的前四次点击,似乎这种BFF技术只有在客户受你控制时才能工作。客户端开发人员开发他们想要的API,将调用映射到真正的后端微服务API。在此图中:samnewman.io/patterns/architectural/bff/#bff我将"Perimeter"行放在BFF框下方 - 每个框只是客户端的一部分。它甚至可以存放在容纳微服务的数据中心之外。我也没有看到REST如何不适用于这两个接口(客户端/ BFF和BFF /微服务)。
-
是的,这是一个好点。通常情况下,当你有一个团队建立微服务和一个团队制作一个有角度的应用程序时,开发团队就是更多的前端类型,他们不喜欢与一堆小纯粹主义服务对抗。虽然我没有看到任何理由您无法使用相同的模式将微服务和聚合抽象为更适用于客户的外观,这样可以在不影响外观的情况下更改微服务。
-
API端点应该模拟域和业务的需求。解决这些问题的代码,避免过度工程,以遵守严格和许多时间的不灵活规范。无论如何,REST只不过是指导原则。
如果要在DELETE HTTP方法的RESTAPI中发送多个参数,如POST,GET,PUT,请尝试以下操作:
1 2 3 4 5 6 7 8
| DELETE /app/module/test HTTP/1.1
Host: xxx.xxx.x.xx
ACCESS-TOKEN: xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: text/plain
Cache-Control: no-cache
Postman-Token: 3eabfb52-fdb2-4b05-aefa-b2fb0c53c247
first_name=name1&last_name=name2&mobile_no=2222222222& [email protected] |
每个值分隔
& (and sign)
and Content-Type is text/plain OR text as you wish.
这将出现在向邮递员发送请求中。
我收到了来自服务器的回复
而这些回复来自邮递员看起来像这样的服务器。
1 2 3 4 5 6 7
| Array
(
[first_name] => name1
[last_name] => name2
[mobile_no] => 2222222222
[email_id] => [email protected]
) |