良好的 REST API 资源集操作设计

Good REST API design for operations on resource sets

使用 REST 很清楚如何对资源进行操作,例如

PUT /users/{userId} - updates the user with userId
GET /users/{userId} - reads the user with userId

资源集也是如此

POST /users - creates a new user
GET /users/{userId}/books - reads list of books from a user
GET /users/{userId}/books?filter=x - reads list of books from a user with specific filter

如果我想在 资源集 上开发更精细的操作怎么办,例如

  1. 使用请求正文,将图书列表添加到现有列表并接受重复项(基本上是串联列表)
POST /users/{userId}/books 
or PUT /users/{userId}/books 
or PATCH?
or POST /users/{userId}/books/concatenate
  1. 在请求正文中,将图书列表添加到现有列表中,但不要重复(基本上是合并列表)
POST /users/{userId}/books 
or PUT /users/{userId}/books 
or PATCH?
or POST /users/{userId}/books/merge
  1. 也用于删除部分资源集: 使用请求正文,从现有列表中删除具有特定 属性
  2. 的图书列表
POST /users/{userId}/books/delete?category=x 
or DELETE /users/{userId}/books?category=x
  1. 或删除资源集中的所有资源:
POST /users/{userId}/books/delete_all 
or DELETE /users/{userId}/books

感谢您提供一些提示或指南

“资源集”,从 REST 的角度来看,是虚构的。只有资源。就通用 HTTP 组件而言,以下 URI 隐含了 _no 关系:

/users
/users/{userId}
/users/{userId}/books
/users/{userId}/books?filter=x
/users/{userId}/books/concatenate

它们完全相互独立;例如,DELETE /users 并不意味着 任何关于其他资源的东西

我们人类倾向于以有意义的模式分配标识符,但机器不在乎。

with the request body, add a list of books to the existing list and accepting duplicates (basically concatenating the list)

PUT 和 PATCH 具有远程创作语义;如果您尝试编辑服务器上的文件副本,它们的行为就像您期望的那样。您 GET 资源当前表示的副本,对您的本地副本进行编辑,然后请求服务器更改其副本以匹配您的副本。使用 PUT,您发送资源表示的完整副本;使用 PATCH,您可以发送一个补丁文档来描述您所做的更改。

It's okay to use POST; HTML 只使用 GET 和 POST 相处得很好,网络接管了世界。

您不需要 POST 的单独资源;如果你喜欢,你可以使用一个,但没有必要这样做。

with the request body, add a list of books to the existing list but no duplicates (basically merging the list)

没什么不同;我们在 HTTP 中达成一致的是请求和响应消息的语义。服务器选择做什么是一个实现问题。参见 Fielding 2002

因此,如果我向您发送包含重复条目的列表表示,而您删除重复条目,那“很好”;您只需要谨慎对待您的回复,以确保您不会暗示您按原样接受所请求的陈述。

对于 PATCH,它有点模糊,因为 RFC 描述了 all or nothing 语义,但根据所使用的语言,可以合理地推断实施也受到限制。

also for deleting parts of resource sets: with the request body, delete a list of books from the existing list that have a certain property

仔细阅读 RFC 7231:DELETE 并不完全代表您的示例所暗示的意思。 DELETE 打破键(目标 uri)和值(资源表示)之间的关联,但这并不一定意味着“并且还垃圾收集表示”。

同样的想法用另一种方式表达——假设我从服务器GET /list-of-books,returned表示是三本书的列表。在我希望该资源代替 return 表示 列表的情况下,DELETE 是错误的工具。 DELETE 告诉服务器我希望将来调用 GET /list-of-books 到 return 404 Not Found 或者可能 410 Gone。如果我真正想要的是 200 OK 和一个空列表,那么我需要 PUT/PATCH/POST/etc。资源。

deleting all resources in a resource set

和以前一样的问题。

With REST it is pretty clear how to operate on resources

这就是问题所在 - 不清楚如何对资源进行操作。网络上充斥着对其进行完整散列的文献(我们使用 REST 来获取破坏 REST 教训的文档——非常讽刺)。

REST 包括一个统一的接口作为约束。在 HTTP 中,该接口实际上是一个文档存储。 PUT 和 PATCH 只是编辑文档内容 - 如果您的域是贫血的或声明性的,这将非常令人满意。对于我们没有标准化语义的任何其他内容,我们使用 POST.

参见 Jim Webber,2011 年:“您必须学习如何使用 HTTP 来触发业务 activity 作为在网络中移动文档的副作用。”