一些基于rest的服务使用不同的资源uri进行更新/获取/删除和创建。如

Create -在某些地方使用/resource(单数)使用POST方法使用/resources(复数) 更新-使用PUT方法使用/resource/123 Get -使用Get方法使用/resource/123

我对这个URI命名约定有点困惑。我们应该用复数还是单数来创建资源?决定的标准应该是什么?


当前回答

如何:

/resource/(不是/resource)

/resource/意味着它是一个包含“resource”的文件夹,它是一个“resource”文件夹。

而且我认为数据库表的命名惯例是相同的,例如,一个叫“user”的表就是一个“user”表,它包含了一个叫“user”的东西。

其他回答

复数

简单-所有url都以相同的前缀开头 逻辑——订单/获取订单的索引列表。 标准——被绝大多数公共和私有api广泛采用的标准。

例如:

GET /resources -返回资源项的列表

POST /resources -创建一个或多个资源项

PUT /resources—更新一个或多个资源项

PATCH /resources—部分更新一个或多个资源项

DELETE /resources -删除所有资源项

对于单个资源项:

GET /resources/:id -根据:id参数返回一个特定的资源项

POST /resources/:id—用指定的id创建一个资源项(需要验证)

PUT /resources/:id -更新一个特定的资源项

PATCH /resources/:id—部分更新特定的资源项

DELETE /resources/:id -删除指定的资源项

对于单数的提倡者,可以这样想:你会向某人要一份订单,并期待一件事,还是一份清单?那么,当您键入/订单时,为什么期望服务返回一列东西呢?

从API使用者的角度来看,端点应该是可预测的

在理想的情况下……

GET /resources should return a list of resources. GET /resource should return a 400 level status code. GET /resources/id/{resourceId} should return a collection with one resource. GET /resource/id/{resourceId} should return a resource object. POST /resources should batch create resources. POST /resource should create a resource. PUT /resource should update a resource object. PATCH /resource should update a resource by posting only the changed attributes. PATCH /resources should batch update resources posting only the changed attributes. DELETE /resources should delete all resources; just kidding: 400 status code DELETE /resource/id/{resourceId}

这种方法最灵活,功能最丰富,但开发起来也最耗时。因此,如果您很着急(软件开发总是这样),只需命名您的端点资源或复数形式的资源。我更喜欢单数形式,因为它让你可以选择内省和编程计算,因为不是所有的复数形式都以's'结尾。

说了这么多,不管出于什么原因,最常用的实践开发人员选择使用复数形式。这是我最终选择的路线,如果你看看流行的api,如github和twitter,这就是他们所做的。

决定的一些标准可以是:

我的时间限制是什么? 我将允许我的消费者做哪些操作? 请求和结果有效负载是什么样子的? 我是否希望能够在代码中使用反射并解析URI ?

所以这取决于你。无论你做什么都要坚持。

这两种表示都很有用。在相当长的一段时间里,我为了方便而使用单数,屈折变化可能会很困难。根据我在开发严格的单一REST api方面的经验,使用端点的开发人员对结果可能是什么形状缺乏确定性。我现在更倾向于使用最能描述反应形式的术语。

如果您的所有资源都是顶级的,那么您可以使用单一表示。避免变化是一个巨大的胜利。

如果您正在使用任何类型的深度链接来表示关系上的查询,那么使用您的API编写程序的开发人员可以通过使用更严格的约定来获得帮助。

我的习惯是,URI中的每一层深度都描述了与父资源的交互,而完整的URI应该隐式地描述正在检索的内容。

假设我们有以下模型。

interface User {
    <string>id;
    <Friend[]>friends;
    <Manager>user;
}

interface Friend {
    <string>id;
    <User>user;
    ...<<friendship specific props>>
}

如果我需要提供一个资源,允许客户端获得特定用户的特定朋友的管理器,它可能看起来像这样:

GET /用户/ {id} /朋友/ {friendId} /经理

下面是更多的例子:

GET /users - list the user resources in the global users collection POST /users - create a new user in the global users collection GET /users/{id} - retrieve a specific user from the global users collection GET /users/{id}/manager - get the manager of a specific user GET /users/{id}/friends - get the list of friends of a user GET /users/{id}/friends/{friendId} - get a specific friend of a user LINK /users/{id}/friends - add a friend association to this user UNLINK /users/{id}/friends - remove a friend association from this user

注意每个级别是如何映射到一个可以被操作的父级的。为同一个对象使用不同的父对象是违反直觉的。在GET /resource/123处检索资源时,没有指示应该在POST /resources处创建新资源

请参阅谷歌的API设计指南:资源名称以了解另一种命名资源的方法。

该指南要求集合以复数形式命名。

|--------------------------+---------------+-------------------+---------------+--------------|
| API Service Name         | Collection ID | Resource ID       | Collection ID | Resource ID  |
|--------------------------+---------------+-------------------+---------------+--------------|
| //mail.googleapis.com    | /users        | /name@example.com | /settings     | /customFrom  |
| //storage.googleapis.com | /buckets      | /bucket-id        | /objects      | /object-id   |
|--------------------------+---------------+-------------------+---------------+--------------|

如果你正在思考这个问题,这本书值得一读。

对我来说,最好有一个模式,你可以直接映射到代码(容易自动化),主要是因为代码是什么将在两端。

GET  /orders          <---> orders 
POST /orders          <---> orders.push(data)
GET  /orders/1        <---> orders[1]
PUT  /orders/1        <---> orders[1] = data
GET  /orders/1/lines  <---> orders[1].lines
POST /orders/1/lines  <---> orders[1].lines.push(data)