RESTful API 中识别单个资源的多种方法

Multiple ways to identify a single resource in a RESTful API

想象一个 HTTP REST API 用于检索 Git 存储库的提交列表:

/:repository/:branch/commits

我想扩展 API 以允许通过标识符 (SHA) 或其他一些标准获取有关 单个 提交的信息,例如最后一次提交的时间。这可以通过使用路径或查询参数以两种不同的方式实现。

主要资源标识符通常使用 URL 参数:

/:repository/:branch/commits/:sha

现在最新提交的 URL 可以是

/:repository/:branch/commits/latest

这将导致最后一个 URL 组件被视为特殊标识符或提交 SHA。如果“最新”不是有效的提交标识符,但感觉不对,这将起作用:相同的 URL 组件根据其值具有不同的角色。

另一种方法是使用查询参数:

/:repository/:branch/commits?latest 或 (?latest=true)

在这种情况下,通过 SHA 或不同的标准来识别提交是完全分开的。 /commits API 端点变得不对称但是:响应具有不同的结构,具体取决于存在的查询参数,如果请求最新提交则为单个对象,否则为数组(例如,没有过滤或作者过滤)。为最新提交返回单个对象数组也不合适。

两种方法中的

None 结果一致 API。还有另一种没有相同缺点的方法吗?是否有一些问题我没有考虑过让一种设计优于另一种设计?

Multiple ways to identify a single resource in a RESTful API

我认为,在 资源 上查看来源 material 是明智的,请参阅 Fielding, 2000

A resource is a conceptual mapping to a set of entities....

For example, the "authors' preferred version" of an academic paper is a mapping whose value changes over time, whereas a mapping to "the paper published in the proceedings of conference X" is static. These are two distinct resources, even if they both map to the same value at some point in time. The distinction is necessary so that both resources can be identified and referenced independently. A similar example from software engineering is the separate identification of a version-controlled source code file when referring to the "latest revision", "revision number 1.2.7", or "revision included with the Orange release."

从概念上讲,同一资源有多个标识符并没有错

GET /f8b0440e-1d65-4800-9e79-ef01183062da
GET /80871fe0-c414-4ec0-b1b3-2c3f0521e2ab

通用组件无法知道哪些标识符引用公共资源,哪些引用不同资源。因此,从客户端的角度来看,每个唯一标识符都意味着一个唯一的资源。

例如,使使用一个标识符存储的缓存表示无效不会使使用其他标识符的表示无效。


This will lead to the last URL component being treated either as a special identifier or as a commit SHA. This will work, if "latest" is not a valid commit identifier, but doesn't feel right: the same URL component has different role based on its value.

同一个 URL 组件根据其值具有不同的作用这一事实并没有错。您只需添加逻辑来区分角色——从根本上说,此逻辑与您已经用来将请求发送到正确处理程序的逻辑没有任何不同。我们解析 URI,并使用我们找到的数据分支到正确的处理程序。

另一方面,信息模棱两可的事实意味着我们要努力确保 return 正确表示会很痛苦。

答案当然是给URI加上提示,让意思不再有歧义。您可以通过路径段或根据需要修改查询来执行此操作。

In this case identifying a commit by a SHA or by a different criteria is cleanly separated. /commits API endpoint becomes asymmetrical however: the response has different structure depending on query parameters present, a single object if the latest commit is requested, an array otherwise (e.g. without filtering or filtering by an author).

是的,那又怎样? client 并不关心——它只是要求用给定的标识符来表示资源;你如何实现它取决于你。生成表示的代码完全不受影响。您需要添加的唯一部分是一些逻辑来确定您的端点如何委托要完成的工作。

但是,例如,如果您的目标是选择资源标识符,以便逻辑复杂性可以隐藏在您的通用路由代码中,您可能会考虑类似

/:repository/:branch/commits
/:repository/:branch/commits/:sha
/:repository/:branch/latest

或类似

/:repository/:branch/commits
/:repository/:branch/commits/sha?:sha
/:repository/:branch/commits/latest

/:repository/:branch/commits
/:repository/:branch/commits/sha=:sha
/:repository/:branch/commits/latest

/:repository/:branch/commits
/:repository/:branch/commits/sha/:sha
/:repository/:branch/commits/latest

这些都很好;您选择最适合您的上下文约束的拼写(例如,如果您希望使用 HTML 形式来访问您的资源,您将对在查询中使用键值对的设计更感兴趣部分)。

我会选择以下

用于收集资源(收集提交)

/:repository/:branch/commits

对于特定资源(特定提交哈希)

/:repository/:branch/commits/:sha

由于“latest”不是特定的(结果根据请求的时间而变化),实际上类似于“search”或“filter”,为此我会使用查询参数:

/:repository/:branch/commits?latest=true