一个简单的 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 Standards 和 Wikipedia: 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 似乎可以解决问题,因为它可以在封闭资源中创建新项目,而不是替换而是附加。
通过追加操作扩展数据库
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 有效负载所描述的一个或多个项目
您可以修补集合,例如
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 ]
不是很标准,但我已经做到了并且它有效。
使用一个请求更新多个资源 - 它是标准的还是应该避免的?
好吧,有时您只需要执行不符合简单 REST API 的典型方案的原子批处理操作或其他与资源相关的操作,但如果您需要它,则无法避免。
是标准的吗?
没有普遍接受的 REST API 标准,所以这个问题很难回答。但是通过查看一些常用的api设计指南,例如jsonapi.org、restfulapi.net、microsoft api design guide或IBM'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 命名方案(例如,如所讨论的 here 或 here)
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
POST /api/items/batch-update
。我完全承认有一个缺点,那就是现在你有一个不能用于子关联的保留字,如果需要子关联,但我会为更清晰的 URL 进行权衡.
/api/users/3
与 /api/users/batch-update
)区分开来,因此命名空间不会重叠,您的方法也是合理的。
There is no universally accepted REST API standard
这就是为什么它是一个糟糕的标准。不支持原子批处理操作意味着这个“标准”还没有为黄金时段做好准备。我构建财务应用程序,原子批处理操作(带有乐观并发检查)是最低要求。我的客户想要 REST,但老实说,这是为了爱好。
据我了解 REST 概念,它涵盖了通过一个请求更新多个资源。实际上这里的技巧是假设围绕这些多个资源的容器并将其作为一个资源。例如,您可以假设 ID 列表标识包含其他几个资源的资源。
在那些examples in Wikipedia中,他们还谈到了复数形式的资源。