REST批量删除多个项目

Posted

技术标签:

【中文标题】REST批量删除多个项目【英文标题】:REST delete multiple items in the batch 【发布时间】:2019-08-26 03:50:43 【问题描述】:

我需要在批处理中按 id 删除多个项目,但是 HTTP DELETE 不支持正文负载。

解决选项:

1. @DELETE /path/abc?itemId=1&itemId=2&itemId=3 on the server side it will be parsed as List of ids and DELETE operation will be performed on each item.

2. @POST /path/abc including JSON payload containing all ids.  ids: [1, 2, 3] 

这有多糟糕,哪个选项更可取?有什么选择吗?

更新:请注意,性能是这里的关键,它不是对每个单独的 id 执行删除操作的选项。

【问题讨论】:

两者都是不可取的,因为批处理中的部分批处理失败的http方法在返回结果http状态代码时会出现问题。另一种方法是让客户负责批处理操作。 @Mr.Wrong 客户如何负责批处理?批处理的全部意义在于优化一个流程,而不是一个一个地执行请求。 除了错误先生所说的之外,您的两个建议将防止(n 个中间)缓存使调用的 URI 的任何存储响应表示无效,这基本上是一个缓存键,包括任何路径、矩阵或查询参数。因此,对GET /path/abc?itemId=1 的请求可能仍然由缓存提供服务,而不是由实际服务器提供服务,即使实际资源可能已经通过批处理删除。 @RomanVottner 不确定,这与进行任何其他 MUTATION 操作相同。如果您将多个项目添加到类别“A”缓存中,则必须刷新类别“A”。与删除相同。如果您遵循您的逻辑,则意味着您根本无法使用 REST 进行批量操作。 @WildGoat @WildGoat RFC 7234 谈到如果执行变异操作会使任何缓存信息无效,但缓存使用effective request URI 来确定目标资源。通常通过POST /path/to/collections 向集合中添加新项目,而通过GET path/to/collections/item 检索特定项目,这是与用于存储新项目的键不同的键。更新或删除该特定项目将使缓存无效,但 OOTB。 【参考方案1】:

多年来,许多人对此表示怀疑,正如我们在此处的相关问题中看到的那样。似乎接受的答案范围从“一定要这样做”到“它明显滥用协议”。由于许多问题是多年前提出的,让我们深入研究 2014 年 6 月 (RFC 7231) 以来的 HTTP 1.1 规范,以便更好地了解明显不鼓励或不鼓励的内容。

第一个建议的解决方法:

首先,关于 Section 2 上的资源和 URI 本身:

HTTP 请求的目标称为“资源”。 HTTP 不限制资源的性质;它仅仅定义了一个可以用来与资源交互的接口。每个资源都由统一资源标识符 (URI) 标识。

基于此,有些人可能会争辩说,由于 HTTP 不限制资源的性质,所以一个包含多个 id 的 URI 是可能的。我个人认为这是一个解释问题。

关于您提出的第一个解决方法 (DELETE '/path/abc?itemId=1&itemId=2&itemId=3'),我们可以得出结论,如果您将资源视为实体集合中的单个文档,则不鼓励这样做,而将资源视为实体集合本身则很好.

第二种建议的解决方法:

关于您提出的第二个解决方法 (POST '/path/abc' with body: ids: [1, 2, 3] ),使用 POST 删除方法可能会产生误导。 Section 4.3.3 部分提到了POST

POST 方法请求目标资源根据资源自己的特定语义处理请求中包含的表示。例如,POST 用于以下功能(以及其他功能): 向数据处理进程提供数据块,例如输入到 html 表单中的字段;将消息发布到公告板、新闻组、邮件列表、博客或类似的文章组;创建尚未被源服务器识别的新资源;并将数据附加到资源的现有表示中。

虽然对于 POST 的“以及其他”函数有一些解释空间,但它显然与我们有用于资源删除的方法 DELETE 的事实相冲突,正如我们在 Section 4.1 中看到的那样:

DELETE 方法删除目标资源的所有当前表示。

所以我个人强烈反对使用POST删除资源。

另一种解决方法:

受您的第二个解决方法的启发,我们建议另外一种解决方法:

DELETE '/path/abc' with body:  ids: [1, 2, 3] 

这与解决方法二中的建议几乎相同,但使用正确的 HTTP 方法进行删除。在这里,我们遇到了关于在DELETE 请求中使用实体body 的困惑。有很多人说它是无效的,但让我们坚持规范的Section 4.3.5:

删除请求消息中的有效负载没有定义的语义;在 DELETE 请求上发送有效负载正文可能会导致某些现有实现拒绝该请求。

因此,我们可以得出结论,规范不会阻止 DELETE 拥有 body 有效负载。不幸的是,一些现有的实现可能会拒绝该请求……但这对我们今天有何影响?

很难 100% 确定,但使用 fetch 提出的现代请求不允许 body 用于 GETHEAD。这就是 Fetch Standard 在第 34 项的 Section 5.3 处声明的内容:

如果body存在且非null或inputBody非null,并且请求的方法是GET或HEAD,则抛出TypeError。

我们可以确认它以与fetch pollyfill 相同的方式实现line 342。

最后的想法:

由于 HTTP 规范允许使用 DELETEbody 有效负载的替代解决方法,并且所有使用 fetch 的现代浏览器都支持,并且由于 IE10 使用了 polyfill,我建议使用这种方式进行批量删除以有效且完整的工作方式。

【讨论】:

陪审团并不完全决定如何处理 DELETE 和请求正文,但 rfc 目前绝对不意味着暗示 DELETE 上的请求正文具有语义含义完全没有,尽管这是一个常见的混淆。这可能会改变,但目前的意图是DELETE 上的请求正文可能会出现,但没有任何意义。 @Evert 但是,这仅适用于也使用该新协议版本的 HTTP 客户端/服务器。许多服务器/客户端可能在未来几年内都不会支持这种功能。除此之外,尚不清楚批量删除应如何通知中间缓存以使在批量请求中被删除的那些项目的存储响应无效。在其当前形式中,一些忽略有效负载的服务器可能会删除整个集合,而不仅仅是那些提到的少数项目。最终可能需要一个定义明确的新操作 (BATCH-DELETE)?! 我认为这个答案值得一票,因为每个人都在批评批量方法,但没有人提供解决方案。显然是需要的! @WildGoat 我通常保持数据库连接池化并打开。我猜您正在使用一种语言/框架,而这不是一个选项?一种想法可能是将所有内容发送到队列中并在主 HTTP 线程之外进行批处理。 伙计们,我非常愿意遵循正确的 HTTP 语义,但是这些方法在性能或开发工作方面都不够可行。该模块是现有架构的插件,实际上我们不能一个接一个地调用 api 1000 次,显然我们没有人力资源来实现“创造性”的解决方案,我们只需要通过批量执行来完成工作.【参考方案2】:

重要的是要了解 HTTP 方法在“通过网络传输文档”的域中运行,而不是在您自己的自定义域中。

您的资源模型不是您的域模型,也不是您的数据模型。

替代拼写:REST API 是一种外观,可以让您的域看起来像一个网站

在门面后面,实现可以为所欲为,前提是如果实现不符合消息所描述的语义,那么它(而不是客户端)负责由差异。

DELETE /path/abc?itemId=1&itemId=2&itemId=3

因此 HTTP 请求明确表示“将删除语义应用于/path/abc?itemId=1&itemId=2&itemId=3 描述的文档”。该文档是持久存储中三个不同项目的组合,每个项目都需要独立删除,这一事实是一个实现细节。 REST 的部分观点是客户端与此类知识完全隔离。

但是,我觉得这是很多人迷失的地方,删除请求的响应返回的元数据告诉客户端没有关于具有不同标识符的资源。

就客户端而言,/path/abc 是与/path/abc?itemId=1&itemId=2&itemId=3 不同的标识符。因此,如果客户端执行了 /path/abc 的 GET,并收到包含 itemIds 1、2、3 的表示;然后提交你描述的删除,删除成功后,它自己的缓存中仍然会有包含/path/abc的表示。

这可能是您想要的,也可能不是。如果您正在执行 REST(通过 HTTP),那么您应该在设计中考虑这种事情。

POST /path/abc

some-useful-payload

此方法告诉客户端我们正在对/path/abc 进行一些(可能是不安全的)更改,如果成功,则需要使先前的表示无效。客户端应重复其较早的GET /path/abc 请求以刷新其先前的表示,而不是使用任何较早的无效副本。

但和以前一样,它不会影响其他资源的缓存副本

/path/abc/1
/path/abc/2
/path/abc/3

所有这些仍将保留在缓存中,即使它们已被“删除”。

公平地说,很多人不在乎,因为他们没有考虑客户端缓存他们从 Web 服务器获得的数据。并且您可以将元数据添加到 Web 服务器发送的响应中,以与客户端(和中间组件)进行通信,即表示不支持缓存,或者可以缓存结果,但必须在每次使用时重新验证。

再次重申:您的资源模型不是您的域模型,也不是您的数据模型。 REST API 是一种思考正在发生的事情的不同方式,并且 REST 架构风格针对解决特定问题进行了调整,因此可能不适合您尝试解决的更简单的问题。

这并不意味着我认为每个人都应该按照 REST 架构风格设计自己的系统。 REST 旨在用于跨多个组织的基于网络的长期应用程序。如果您认为不需要约束,请不要使用它们。只要您不将结果称为 REST API,我就可以。我对忠于自己的架构风格的系统没有任何问题。 -- Fielding, 2008

【讨论】:

以上是关于REST批量删除多个项目的主要内容,如果未能解决你的问题,请参考以下文章

在 Rest API 中批量更新

从数据库项目中批量删除数据库对象

如何在spring boot jdbc中从具有批量大小的多个表中执行批量删除

linux批量删除文件

vue项目中表格中批量删除

Azure CosmosDB:批量删除期间出错