ChatGPT解决这个技术问题 Extra ChatGPT

使用 REST 删除多条记录

删除多个项目的 REST-ful 方式是什么?

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

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

所有选项都不太理想。

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


N
Nicholas Shanks

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

一切都可以在保持 REST 约束的情况下实现,通常答案是将“问题”变成资源,并给它一个 URL。因此,批量操作,例如在此处删除,或将多个项目发布到列表,或对大量资源进行相同的编辑,都可以通过创建“批量操作”列表并将新操作发布到它来处理。

不要忘记,REST 并不是解决任何问题的唯一方法。 “REST”只是一种架构风格,您不必必须遵守它(但如果不遵守,您将失去互联网的某些好处)。我建议您查看此 HTTP API architectures 列表并选择适合您的列表。如果您选择其他架构,请让自己意识到您会失去什么,并根据您的用例做出明智的决定。

Patterns for handling batch operations in REST web services? 上的这个问题有一些不好的答案,有太多的赞成票,但也应该阅读。


您需要担心的不是您的服务器,而是中介、CDN、缓存代理等。互联网是一个分层系统。这就是它运作良好的原因。 Roy 确定了系统的哪些方面是其成功所必需的,并将它们命名为 REST。如果您发出 DELETE 请求,则被请求者和服务器之间的任何东西都会认为指定 URL 处的单个资源正在被删除。查询字符串是这些设备的 URL 的不透明部分,因此无论您如何指定 API,它们都不了解这些知识,因此不会有不同的行为。
/records/1;2;3 如果由于 URI 长度限制而需要删除大量资源,则 /records/1;2;3 将不起作用
请注意,如果考虑 DELETE 和定义要清除的资源的主体,则某些中介可能不会转发该主体。此外,某些 HTTP 客户端无法将正文添加到 DELETE。请参阅stackoverflow.com/questions/299628/…
@LukePuplett 我只想声明禁止使用 DELETE 请求传递请求正文。不要这样做。如果你这样做,我会吃掉你的孩子。名义名义名义。
#3 的论点的问题在于它与反对 #2 的反论点具有相同的惩罚。创建要删除的资源不是上游代理知道如何处理的事情——与方法 #2 提出的相同反对论点。
M
Martin Ždila

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

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


我也得出了这个结论:只要把动词翻转成你想做的事。我不明白 GET 的用途如何不适用于 DELETE。
GET /records?id=1&id=2&id=3not 是否表示“获取 ID 为 1、2 和3”,意思是“获取URL路径/records?id=1&id=2&id=3的单一资源”,可能是萝卜的图片,中文包含数字“42”的纯文本文档,或者可能不存在。
请考虑以下情况:发送 /records?id=1/records?id=2 的两个连续请求,并且它们的响应由某些中介(例如您的浏览器或 ISP)缓存。如果 Internet 知道您的应用程序的含义,那么可以通过简单地合并(以某种方式)它已经拥有的两个结果,而无需询问原始服务器,就可以通过缓存返回对 /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 请求。如果您这样做,客户端将保持陈旧状态。
b
bootsoon

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

删除整个集合。

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

使用单个请求从集合中删除多个 BSO。

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

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

删除给定位置的 BSO。

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 怎么办...您是否删除任何内容并以错误响应,或者您是否删除了您可以删除的内容并留下其他内容...
我通常会返回一个成功的响应,因为无论如何最终状态都是相同的。这也简化了客户端的逻辑,因为它们不再需要处理该错误状态。至于授权案例,我只会让整个请求失败……但实际上这取决于您的用例。
我同意内森。
F
Felix K.

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

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

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

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

所以你可以根据谷歌的api指南做以下事情:

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

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


项目列表通过 JSON 格式的数组进行通信是一个可行的解决方案吗?
是的,当然。您可以发布一个有效负载,其中 ID 通过 json 数组发送。
有趣的是,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.batchGet api 是带有 body 的 GET 方法。这很奇怪。 developers.google.com/my-business/reference/rest/v4/…
@郑元杰同意,乍一看有点奇怪,但如果仔细观察,它实际上是一个POST使用的http方法,只有自定义方法被命名为batchGet。我猜谷歌这样做是为了(a)坚持他们的规则,即所有自定义方法都必须是 POST(请参阅我的答案)和(b)让人们更容易在正文中放置“过滤器”,所以你这样做不必像查询字符串那样对过滤器进行转义或编码。当然,缺点是这不再是真正可缓存的了……
@deamon - 为什么不呢?在这种情况下,自定义操作是资源。正如 Google API 指南作者指出的那样,你正在向它发布,它具有最灵活的语义。虽然 ReST API 通常看起来像瘦 CRUD 包装器,但这不是必需的。
L
Luke Puplett

我允许对集合进行大规模替换,例如 PUT ~/people/123/shoes,其中 body 是整个集合表示。

这适用于客户想要查看项目并删除一些项目并添加一些其他项目然后更新服务器的小型项目集合。他们可以 PUT 一个空集合来删除所有集合。

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

我的数据/系统 API 始终使用 ETags 而不是到期时间,因此每个请求都会命中服务器,并且我需要正确的版本/并发标头来改变数据。对于只读和视图/报告对齐的 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 我会将“周界”线放置在 BFF 框下方 — 每个框只是客户端的一部分。它甚至可以位于包含微服务的数据中心之外。我也看不出 REST 如何不适用于两个接口(客户端/BFF 和 BFF/微服务)。
是的,这是一个很好的观点。例如,当您有一个构建微服务的团队和一个制作 Angular 应用程序的团队时,通常是这样的,并且该开发团队是更多的前端类型,他们不喜欢与一堆小的纯粹服务打交道。尽管我看不出有任何理由不能使用相同的模式来抽象微服务并聚合成对客户更有用的外观,这样就可以在不影响外观的情况下更改微服务。
API 端点应该模拟领域和业务的需求。解决这些问题的代码并避免过度设计以遵守严格且多次不灵活的规范。无论如何,REST 只不过是指导方针。
L
Lam Duc Trung

您可以发布已删除的资源:)。 URL 为 POST /deleted-records,正文为 {"ids": [1, 2, 3]}