RESTful API - URI结构建议

6

我有一个类似于以下结构的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结构应该是什么?
4个回答

4
如果您的别名不是数字,我建议您使用相同的URI结构,并弄清楚它在您那里是ID还是别名。就像Facebook使用用户名和用户ID一样。facebook.com/user_id或facebook.com/username。
另一个方法是让客户端使用GET /contacts,并带有一些额外的GET参数作为筛选器来首先搜索联系人,然后从该响应中查找ID。
我认为最后一个选择是使用类似于GET /contacts/alias/:alias这样的结构。但这会有点暗示别名是联系人的子资源。

好的回答。但是如果无法通过这种方式区分ID和别名,您会有什么建议? - shlomi33
除了像GET /contacts/alias/:alias这样的方式,我没有看到其他不会破坏REST结构的方法。但是这似乎意味着别名是联系人的子资源。 - Stromgren
“但这也有点意味着别名是联系人的子资源。”- 是的,但谁需要知道呢?我的意思是只有API开发人员需要这些信息来构建链接和路由传入消息。其他所有人都将只使用链接,并通过检查链接关系来决定导航。因此,在我看来,IRI结构是无关紧要的,并且IRI不应成为您文档的一部分。如果它们是,则可能会编写易于中断客户端的人,这些客户端正在自行构建IRI... - inf3rno

3
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读取猫属性并使用PUT /cats/123写入狗属性。人们通常不理解的是,单个资源可以有多个IRI,例如/cats/123/cats/name:kitty/users/123/cats/kittycats/123?fields="id,name"等等...可以属于同一资源。或者,如果您想为一件事物(活着的猫,而不是描述它的文档)提供IRI,则可以使用/cats/123#thing/users/123#kitty等等...您通常在RDF文档中这样做。

如果我想通过ID或别名获取联系人,URI结构应该是什么?

例如,它可以是/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的最佳实践。
- Jatinder Thind
这只是关于不同IRI的解释,例如/users/users?page=6/users?search="Susanne"。现在这些都是三个不同的IRI。那么这里有三个不同的资源还是只有一个?你可以说/users是一个集合资源,/users?page=6是一个页面资源,/users?search="Susanne"是一个映射减少集合资源,但对我来说这并没有什么意义。从我的角度来看,/users是一个集合资源,所有的IRI都属于它。分页和搜索是查询的典型用法。 - inf3rno
通过分页,您可以使用范围标头作为替代方案。因此,/users Content-Range: 26-50/1234。这应该几乎与 /users?offset=25&count=25 相同,除了下一个和上一个链接。现在,不同的页面属于不同的资源吗?我认为不是。通过范围标头,它们属于同一资源... 范围标头和查询分页参数都是 REST 最佳实践。例如,通过分页:http://youtu.be/5WXYw4J4QOU?t=1h6s,但您可以找到很多关于如何通过 REST 进行分页的文章。 - inf3rno
https://dev59.com/znI-5IYBdhLWcg3wSGQB - inf3rno

1
我建议在尝试使用别名解析资源时添加一个“搜索”资源:
GET /api/contacts/:id

并且

GET /api/contacts?alias=:alias

or

GET /api/contacts/search?q=:alias

1
首先,URL中的“ID”不必是数据库生成的数字ID。您可以使用URL中的任何数据片段(包括别名),只要它是唯一的。当然,如果您在所有地方都使用数字ID,则在联系人API中也这样做更加一致。但是,您可以选择使用别名而不是数字ID(只要它们始终是唯一的)。
另一种方法是,正如Stromgren所建议的那样,在URL中允许使用数字ID和别名。
/api/contacts/123
/api/contacts/foobar

但是,如果别名可以是数字,这显然会引起问题,因为您将无法区分ID和(数字)别名。
最后但并非最不重要的是,您可以实现一种过滤完整集合的方法,就像shlomi33已经建议的那样。我不会引入一个“搜索”资源,因为那不是真正的RESTful,所以我会选择另一种解决方案:
/api/contacts?alias=foobar

此操作将返回所有别名为 foobar 的联系人。由于别名应该是唯一的,因此将返回 1 或 0 个结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接