REST 更新最佳实践:PUT collection/:id 没有 id in body VS PUT collection/ with id included in body

REST Update Best Practice: PUT collection/:id without id in body VS PUT collection/ with id included within body

我正在尝试为我正在处理的项目定义 PUT 请求的约定,但我无法决定这两个选项中哪一个是最合适的,或者这是否是个人偏好的问题。我看过多篇关于 REST 最佳实践的文章,似乎 id 作为 PUT 请求的 url 的一部分总是被暗示为必要的。

目标: 通过 REST 更新资源 API。

选项

  1. PUT collection/{id} - 正文中没有 id
  2. PUT collection/ - URL 中没有 id,仅在正文中

问题

  1. 我应该使用哪个约定?
  2. 为什么?
  3. 每个选项是否有普遍适用的优缺点?

PUT 替换指定 uri 处的资源。

因此,如果您 PUT collection,我希望整个集合都被覆盖。

所以这不是一个真正的意见问题,在这种情况下,这完全是错误的。

确保您理解 PUT

的语义很重要

A successful PUT of a given representation would suggest that a subsequent GET on that same target resource will result in an equivalent representation being sent in a 200 (OK) response.

换句话说

PUT /collection

表示“将 /collection 的表示替换为此消息中包含的表示”。

因此,如果您尝试修改集合资源本身的表示,那么这是正确的请求,如果您尝试修改其他资源的表示,则该请求是错误的。

例如,如果集合的表示是其元素的描述列表,那么您可以使用 PUT 将列表替换为不同的列表。

GET /collection

200 OK

[1, 2, 3]

PUT /collection

[4, 5]

204 No Content

GET /collection

[4, 5]

换句话说,从 REST 的角度来看,“集合”使用与所有其他资源完全相同的消息,具有相同的语义。

如果您要描述的是对某些其他资源的更改,那么您可以使用该资源的标识符

PUT /collection/1

现在,标识符“1”应该出现在表示中吗?这取决于 - 但这是基本规则:消费者(和中间组件)永远不应尝试从 URI 中提取信息,而只能从表示中提取信息。因此,如果您的消费者用例需要那个“1”,那么它需要出现在资源的表示中。

从这个意义上说,PUT 只是 GET 的对偶——如果 GET 需要在响应提供的表示中包含信息,那么 PUT 需要在请求提供的表示中包含相同的信息。

想到文件内容可能会有用——我们把reader需要的信息放到文件中,文件name/path只是告诉reader如何找到它.或者,如果您考虑一个 Hash/Map/Dictionary —— 键用于获取地图内外的值,但信息在值中。

If I am not planning to support complete collection replacement

... 那么 PUT /collection 错误的 ,因为我们都同意(通过 RFC 7231)PUT 表示“替换”。

It's okay to use POST,而不是 PUT,如果您试图以某种非标准化方式操纵 /collection

如果你打算操纵集合的表示,但你想描述插入和删除而不是每次都发送整个表示,你可以使用 PATCH 和某种补丁文档来描述你的改变。

REST 依赖于其传输层的语义,在大多数情况下是 HTTP。 HTTP 的核心只是一个远程文档管理系统,它只负责以无状态方式将文件从一个位置发送到另一个位置。正如 Jim Webber 所指出的,任何业务规则的总结基本上只是实际文档管理的副作用。

因此,您应该首先从常规文档管理的角度来看待您的任务。你把一个文件放在某个地方并替换任何现有的表示。这就是 PUT 的实际含义。因此,只要文档中的 ID 不是实际文档内容的一部分,它就不是最好的主意。

URI 是否包含单独的标识符在 REST 架构中并不重要。更正式地说,整个 URI 是资源本身的标识符,无论您是否将一些进一步的 ID 放入 URI 中。 URI 既不应该告诉客户端有关资源或其语义的任何信息,也不应告诉客户端任何层次结构。内容类型协商和 link 关系就在那里。

菲尔丁甚至说

A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). Servers must have the freedom to control their own namespace. Instead, allow servers to instruct clients on how to construct appropriate URIs, such as is done in HTML forms and URI templates, by defining those instructions within media types and link relations. [Failure here implies that clients are assuming a resource structure due to out-of band information, such as a domain-specific standard, which is the data-oriented equivalent to RPC’s functional coupling]. (Source)

许多开发人员将 ID 放入他们的 URI 的原因基本上是在服务器端他们标记 URI 并使用相应的标记在相应的段上执行 DB 查找。不将 productID 放入 URI 的一个原因是,当 ID 更改时,无论出于何种原因,URI 作为结果事件发生更改,但实际产品仍然完全相同。 IE。想想产品 A000123 突然被称为 P12300001 但没有其他改变。这可能是由于公司合并等原因发生的(过去曾发生过此类事件)。在 REST 架构中,诸如 /products/A000123/products/P12300001 之类的 URI 指向两个不同的资源,因此指向两个不同的产品,即使它们可能 return 相同的内容。如果不再使用产品标识符,而是使用任意 UUID,导致该 UUID 到 productId 的内部映射,URI 可能保持不变,因为基本上产品也保持不变。

到目前为止,我想将您的问题重新定义为:“您的客户真的需要 ID 吗?”。在一个完美的 REST 架构中,服务器会在流程的任何步骤告诉客户端客户端能够和允许做什么。由于 REST 基本上只是 Web 上使用的概念的概括,因此可以将整个设计建模为好像交互应该发生在 Web 上,然后使用相同的概念进行应用程序交互。想一想一个任意的客户端,人类或机器驱动的,它应该从亚马逊订购一些商品,即客户端向亚马逊询问符合某些搜索条件的产品列表,服务器响应一个包含 links 的页面到更多或匹配的产品较少,客户可以进一步深入了解每个产品的详细信息。如果有兴趣,客户可以通过单击 link 将产品添加到其购物车中。它实际上并不关心 URI 的外观,它只是将其作为一些元数据(文本或 link 关系名称)调用它,声明这会将产品添加到购物车。客户在任何时候都不会真正对实际产品编号感兴趣,所以为什么要添加它而不是让客户(人类交互者)有可能在与支持等的电子邮件交互中出于推荐原因使用它。

最后,您是在 URI 中使用 ID 还是在主体负载中使用 ID,是您个人的选择。根据您的用例,甚至可能不需要向客户端公开 ID,因为 REST(和 Web)中使用的整个交互模型仅使用 URI 来检索数据的下一个“页面”以及是否使用该 URI 的提示保存在 URI 附带的元数据中,例如 links 内容的摘要文本,如 HTML links 的情况,或作为 link 关系的一部分由媒体类型定义的名称,由 Web linking extensions (such as Dublin Core) or by a standardization authority.