ChatGPT解决这个技术问题 Extra ChatGPT

REST:使用一个请求更新多个资源 - 它是标准的还是应该避免的?

一个简单的 REST API:

GET: items/{id} - 返回具有给定 id 的项目的描述

PUT: items/{id} - 更新或创建具有给定 id 的项目

DELETE: items/{id} - 删除具有给定 id 的项目

现在有问题的 API 扩展:

GET: items?filter - 返回与过滤器匹配的所有项目 ID

PUT: items - 更新或创建 JSON 有效负载所描述的一组项目

[[DELETE: items - 删除 JSON 有效负载描述的项目列表]] <- 不正确

我现在对可以通过 PUT/DELETE items/{id} 轻松访问的 DELETE 和 PUT 操作回收功能感兴趣。

问题:提供这样的 API 是否常见?

替代方案:在单连接多请求时代,发出多个请求很便宜,并且会更加原子化,因为更改要么成功要么失败,但在 NOSQL 数据库时代,即使请求处理终止,列表中的更改也可能已经发生内部服务器或任何原因。

[更新]

在考虑 White House Web StandardsWikipedia: REST Examples 之后,现在使用以下示例 API:

一个简单的 REST API:

GET: items/{id} - 返回具有给定 id 的项目的描述

PUT: items/{id} - 更新或创建具有给定 id 的项目

DELETE: items/{id} - 删除具有给定 id 的项目

顶级资源 API:

GET: items?filter - 返回与过滤器匹配的所有项目 ID

POST: items - 更新或创建 JSON 有效负载所描述的一组项目

不支持和禁止对 /items 进行 PUT 和 DELETE。

使用 POST 似乎可以解决问题,因为它可以在封闭资源中创建新项目,而不是替换而是附加。

HTTP Semantics POST 内容:

通过追加操作扩展数据库

PUT 方法需要替换完整集合以返回 HTTP Semantics PUT 引用的等效表示:

给定表示的成功 PUT 将表明对同一目标资源的后续 GET 将导致在 200(OK)响应中返回等效表示。

[更新2]

对于多个对象的更新方面似乎更加一致的替代方法似乎是 PATCH 方法。 PUT 和 PATCH 之间的区别在 Draft RFC 5789 中描述为:

PUT 和 PATCH 请求之间的区别体现在服务器处理封闭实体以修改由 Request-URI 标识的资源的方式上。在 PUT 请求中,包含的实体被认为是存储在源服务器上的资源的修改版本,并且客户端请求替换存储的版本。然而,对于 PATCH,封闭的实体包含一组指令,描述如何修改当前驻留在源服务器上的资源以生成新版本。 PATCH 方法会影响 Request-URI 标识的资源,也可能对其他资源产生副作用;即,可以通过应用 PATCH 创建新资源或修改现有资源。

因此,与 POST 相比,PATCH 可能也是一个更好的主意,因为 PATCH 允许更新,而 POST 只允许附加一些有意义的内容而没有修改的机会。

所以这里的 POST 似乎是错误的,我们需要将我们提议的 API 更改为:

一个简单的 REST API:

GET: items/{id} - 返回具有给定 id 的项目的描述

PUT: items/{id} - 更新或创建具有给定 id 的项目

DELETE: items/{id} - 删除具有给定 id 的项目

顶级资源 API:

GET: items?filter - 返回与过滤器匹配的所有项目 ID

POST: items - 创建一个或多个由 JSON 有效负载描述的项目

PATCH: items - 创建或更新 JSON 有效负载所描述的一个或多个项目

可能有帮助:github.com/WhiteHouse/api-standards#http-verbs。顺便说一句,DELETE 请求没有定义的正文语义 (stackoverflow.com/a/5928241/1225328)。

m
mahemoff

您可以修补集合,例如

PATCH /items
[ { id: 1, name: 'foo' }, { id: 2, name: 'bar' } ]

从技术上讲,PATCH 将识别 URL 中的记录(即 PATCH /items/1 而不是请求正文,但这似乎是一个很好的实用解决方案。

为了支持在单个调用中删除、创建和更新,标准 REST 约定并不真正支持。一种可能性是一种特殊的“批处理”服务,它可以让您将调用组合在一起:

POST /batch
[
  { method: 'POST', path: '/items', body: { title: 'foo' } },
  { method: 'DELETE', path: '/items/bar' }
]

它为每个嵌入式请求返回带有状态代码的响应:

[ 200, 403 ]

不是很标准,但我已经做到了并且它有效。


批处理的想法很有趣。你是如何实施的?它是真正的调度还是只是在内部处理?当考虑到今天只需要一个连接时,发出多个请求而不是单个请求会导致这样的性能损失(延迟、带宽)吗?
最初它是在内部处理并与单个查询分开处理,主要是出于性能原因。 (您可以进行优化,例如执行单个 SQL 查询而不是 N 个查询。)但是由于一些令人困惑的不一致,它最终被重构,以便在批处理命令和“真正的”原子命令之间有效地共享相同的代码。它对性能的影响更大,但如果有必要,您可以通过一些缓存和优化来减轻这种影响。
如果您可以使用 HTTP/2,则该方案可能是不必要的,因为多次调用的性能开销可能非常低。
我考虑过通过提供一个通用方法来提供单个批次(代理/调度程序),该方法接受一组定义的请求,只是将这些请求重新发送到同一个服务器实例或数据中心内部并收集结果。这可能会变得很方便,特别是在将事物组合在一起或提供更好的压缩、重复参数和引用的共享以及各种方面。所以基本上它将代表原始海报作为真实请求发送。
同意。这更像是一个任务调度器或一个工作流处理器。但是您需要指定批处理的哪些部分可以并行处理,哪些形成一个序列,这可以通过添加一个抽象层来轻松完成,例如“tasks”:[“A”:[request,request],“B “:[...]]。对于某些类型的验收测试也很好,因为只需要编写 JSON 命令和 JSON 中的期望。非常适合 REST API 测试。我牢记这一点。
C
Community

使用一个请求更新多个资源 - 它是标准的还是应该避免的?

好吧,有时您只需要执行不符合简单 REST API 的典型方案的原子批处理操作或其他与资源相关的操作,但如果您需要它,则无法避免。

是标准的吗?

没有普遍接受的 REST API 标准,所以这个问题很难回答。但是通过查看一些常用的api设计指南,例如jsonapi.orgrestfulapi.netmicrosoft api design guideIBM's REST API Conventions,这些都没有提到批处理操作,你可以推断出这样的操作并不被普遍理解为REST API 的标准特性。

也就是说,google api 设计指南是一个例外,它mentions创建了可以使用冒号通过资源关联的“自定义”方法,例如 https://service.name/v1/some/resource/name:customVerb,它还明确提到批处理操作作为用例:

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

因此,在您提供的示例案例中,您可以根据 google 的 api 指南执行以下操作:

POST /api/items:batchUpdate

此外,一些公共 API 决定提供中央 /batch 端点,例如 google's gmail API

此外,与 restfulapi.net 上的 mentioned 一样,还有资源“存储”的概念,您可以在其中通过 PUT 一次存储和检索整个项目列表 - 但是,此概念不适用于服务器管理的资源收藏:

存储是客户端管理的资源存储库。存储资源允许 API 客户端将资源放入、取出并决定何时删除它们。商店永远不会生成新的 URI。相反,每个存储的资源都有一个 URI,该 URI 在最初放入存储时由客户端选择。

回答了您最初的问题后,这是尚未提及的另一种解决您的问题的方法。请注意,这种方法有点不合常规,看起来不像典型的 REST API 端点命名方案那么漂亮。我个人没有遵循这种方法,但我仍然觉得应该考虑一下:)

这个想法是,您可以通过端点路径命名方案来区分资源上的 CRUD 操作和其他与资源相关的操作(例如批处理操作)。

例如,考虑一个 RESTful API,它允许您对“公司”资源执行 CRUD 操作,并且您还希望执行一些与“公司”相关的操作,这些操作不适合通常与 RESTful api 关联的面向资源的 CRUD 方案——比如你提到的批量操作。

现在,您可以区分以下各项,而不是直接在 /api/companies 下方(例如 /api/companies/22)公开您的资源:

/api/companies/items – 即公司资源的集合

/api/companies/ops – 即与公司资源相关的操作

对于 items,应用通常的 RESTful api http-methods 和 resource-url 命名方案(例如,如所讨论的 herehere

POST    /api/companies/items
GET     /api/companies/items
GET     /api/companies/items/{id}
DELETE  /api/companies/items/{id}
PUT     /api/companies/items/{id}

现在,对于与公司相关的操作,您可以使用 /api/companies/ops/ 路由前缀并通过 POST 调用操作。

POST    /api/companies/ops/batch-update
POST    /api/companies/ops/batch-delete
POST    /api/companies/ops/garbage-collect-old-companies
POST    /api/companies/ops/increase-some-timestamps-just-for-fun
POST    /api/companies/ops/perform-some-other-action-on-companies-collection

由于 POST 请求不一定会导致创建资源,因此 POST 是在这里使用的正确方法:

POST 方法执行的操作可能不会产生可由 URI 标识的资源。 https://www.rfc-editor.org/rfc/rfc2616#section-9.5


从美学的角度来看,我发现 Google 冒号惯例有点令人讨厌。发明一个新动词时,更常见的是附加一个斜杠并在其上 POST 一些东西 - POST /api/items/batch-update。我完全承认有一个缺点,那就是现在你有一个不能用于子关联的保留字,如果需要子关联,但我会为更清晰的 URL 进行权衡.
@mahemoff 好点。你的建议是我上面提到的技术的一种快捷版本。在我的回答中,我将操作与资源分开以(a)避免动词和资源之间的任何命名空间冲突,以及(b)遵守在 REST url 中“路径段”仅包含“相同”类型的资源的想法。但是考虑到生成的 ID 通常是 UUID 或整数,可以很容易地以编程方式与自定义“动词”(/api/users/3/api/users/batch-update)区分开来,因此命名空间不会重叠,您的方法也是合理的。
There is no universally accepted REST API standard 这就是为什么它是一个糟糕的标准。不支持原子批处理操作意味着这个“标准”还没有为黄金时段做好准备。我构建财务应用程序,原子批处理操作(带有乐观并发检查)是最低要求。我的客户想要 REST,但老实说,这是为了爱好。
@Quarkly REST 更像是一个“概念”而不是“标准”。它只对如何传输资源施加了很少的限制,并没有详细规定应该如何设计 REST API,从而将如何处理事情留给开发人员。由您的团队/公司来定义您可以操作 REST API 的规则。存在一些“好”的设计指南和一些“坏”的设计指南,但是,说它是一个“糟糕的标准”并不能真正公正地评价 REST 作为一个概念/哲学,而且有点误导。
K
Kris

据我了解 REST 概念,它涵盖了通过一个请求更新多个资源。实际上这里的技巧是假设围绕这些多个资源的容器并将其作为一个资源。例如,您可以假设 ID 列表标识包含其他几个资源的资源。

在那些examples in Wikipedia中,他们还谈到了复数形式的资源。


在 Wikipedia 示例中,他们指出 PUT 和 POST 的处理方式不同,应该使用 POST 来编写一个或多个新实体。