RESTful API - URI 结构建议
Posted
技术标签:
【中文标题】RESTful API - URI 结构建议【英文标题】:RESTful API - URI Structure Advice 【发布时间】:2014-09-03 06:26:56 【问题描述】:我的 REST API URL 结构类似于:
/api/contacts GET Returns an array of contacts
/api/contacts/:id GET Returns the contact with id of :id
/api/contacts POST Adds a new contact and return it with an id added
/api/contacts/:id PUT Updates the contact with id of :id
/api/contacts/:id PATCH Partially updates the contact with id of :id
/api/contacts/:id DELETE Deletes the contact with id of :id
我的问题是:
/api/contacts/:id GET
假设除了通过 ID 获取联系人之外,我还想通过唯一别名获取它。
如果我希望能够通过 ID 或别名获取联系人,那么 URI 结构应该是什么?
【问题讨论】:
【参考方案1】:如果您的别名不是数字,我建议您使用相同的 URI 结构并确定它是您的 ID 还是别名。就像 Facebook 使用用户名和 user_id 一样。 facebook.com/user_id 或 facebook.com/username。
另一种方法是让客户端使用带有一些额外 GET 参数的 GET /contacts 作为过滤器,首先搜索联系人,然后从该响应中查找 ID。
我认为最后一个选项是使用 GET /contacts/alias/:alias 之类的结构。但这有点暗示别名是联系人的子资源。
【讨论】:
好答案。但是,如果无法以这种方式将 ID 与别名区分开来,您会建议什么? 我没有看到任何其他不会破坏 REST 结构的方法,除了可能执行 GET /contacts/alias/:alias 之类的操作。但这有点暗示别名是联系人的子资源 “但这有点暗示别名是联系人的子资源。” - 是的,但谁需要知道呢?我的意思是只有 API 开发人员需要这些信息来为传入消息构建链接和路由器。其他人只会使用链接并通过检查链接关系来决定导航...所以在我看来,IRI 结构是无关紧要的,IRI 不应该成为您文档的一部分。如果他们是 ppl 可以编写容易破坏的客户端,这些客户端正在自己构建 IRI...【参考方案2】:IRI 的路径和查询部分由您决定。路径用于分层数据,如api/version/module/collection/item/property
,查询用于非分层数据,如?display-fields="id,name,etc..."
或?search="brown teddy bear"&offset=125&count=25
等...
您必须牢记的是,您使用的是资源而不是操作。所以 IRI 是资源标识符,如 DELETE /something
,而不是操作标识符,如 POST /something/delete
。您不必遵循 IRI 的任何结构,例如,您可以简单地使用 POST /dashuif328rgfiwa
。服务器会理解,但是为这种 IRI 编写路由器会困难得多,这就是我们使用好的 IRI 的原因。
一个 IRI 始终只属于一个资源,这一点很重要。所以你不能用GET /cats/123
读取cat 属性,用PUT /cats/123
写入dog 属性。人们通常不明白,一个资源可以有多个 IRI,例如/cats/123
、/cats/name:kitty
、/users/123/cats/kitty
、cats/123?fields="id,name"
等...可以属于同一个资源。或者,如果您想为某物(活猫,而不是描述它的文档)提供 IRI,那么您可以使用 /cats/123#thing
或 /users/123#kitty
等...您通常在 RDF 文档中这样做。
如果我想获取联系人,URI 结构应该是什么 通过 ID 或别名?
它可以是/api/contacts/name:name
,例如/api/contacts/name:John
,因为它显然是分层的。或者您可以检查参数是否在/api/contacts/param
中包含数字或字符串。
您也可以使用查询,但我不建议这样做。例如,以下 IRI 可以有 2 个不同的含义:/api/contacts?name="John"
。您想要列出姓名为 John 的每个联系人,或者您想要一个确切的联系人。因此,您必须在服务器端应用程序的路由器中对此类请求做出一些约定。
【讨论】:
谢谢,/api/contacts/name:name 看起来非常适合我想要的。您能否指出任何使用类似方法的 API? ___ 另外,您对“单个资源可以有多个 IRI”的评论,这是一个好习惯吗?我的意思是单个资源具有多个 URI 是完全自然的吗?您提供的示例很有道理,我只是想知道这种方法是否不违反 REST API 的最佳实践。 只是对不同IRI的解释,例如/users
,/users?page=6
,/users?search="Susanne"
。现在这些是 3 种不同的 IRI。那么我们这里有 3 种不同的资源还是只有一个?你可以说,/users
是一个集合资源,/users?page=6
是一个页面资源,/users?search="Susanne"
是一个 mapreduced 集合资源,但这对我来说没有任何意义。从我的角度来看,/users
是一个集合资源,所有的 IRI 都属于它。分页和搜索是查询的典型用法。
通过分页,您可以使用范围标题作为替代方案。所以/users
Content-Range: 26-50/1234
。这应该返回与/users?offset=25&count=25
几乎相同的返回值,除了下一个和上一个链接 ofc。现在不同的页面是否属于不同的资源?我不这么认为。通过范围标头,它们属于同一资源......范围标头和查询分页参数都是 REST 最佳实践。例如通过分页:youtu.be/5WXYw4J4QOU?t=1h6s,但是你可以找到很多关于如何通过 REST 分页的文章。
***.com/questions/1929347/…【参考方案3】:
当您尝试使用别名解析资源时,我会考虑添加“搜索”资源:
GET /api/contacts/:id
和
GET /api/contacts?alias=:alias
或
GET /api/contacts/search?q=:alias
【讨论】:
【参考方案4】:首先,URL 中的“ID”不必是您的数据库生成的数字 ID。您可以在 URL 中使用任何数据(包括别名),只要它是唯一的。当然,如果您在任何地方都使用数字 ID,那么在您的联系人 API 中执行相同操作会更加一致。但是您可以选择使用别名而不是数字 ID(只要它们始终是唯一的)。
另一种方法是,正如Stromgren 建议的那样,在 URL 中同时允许数字 ID 和别名:
/api/contacts/123
/api/contacts/foobar
但是,如果别名可以是数字,这显然会导致问题,因为那样您将无法区分 ID 和(数字)别名。
最后但同样重要的是,您可以实现一种过滤完整集合的方法,正如shlomi33 已经建议的那样。我不会引入 search
资源,因为这不是真正的 RESTful,所以我会选择其他解决方案:
/api/contacts?alias=foobar
这应该返回所有以foobar
为别名的联系人。由于别名应该是唯一的,这将返回 1 或 0 个结果。
【讨论】:
以上是关于RESTful API - URI 结构建议的主要内容,如果未能解决你的问题,请参考以下文章
API 端点是不是可以根据用户凭据 RESTful 和良好的 URI 设计来区分要返回的资源?
架构师之路 — API 经济 — RESTful API 设计规范原则
RESTful API 设计 - 使用资源 URI 与 ID