一些基于rest的服务使用不同的资源uri进行更新/获取/删除和创建。如
Create -在某些地方使用/resource(单数)使用POST方法使用/resources(复数) 更新-使用PUT方法使用/resource/123 Get -使用Get方法使用/resource/123
我对这个URI命名约定有点困惑。我们应该用复数还是单数来创建资源?决定的标准应该是什么?
一些基于rest的服务使用不同的资源uri进行更新/获取/删除和创建。如
Create -在某些地方使用/resource(单数)使用POST方法使用/resources(复数) 更新-使用PUT方法使用/resource/123 Get -使用Get方法使用/resource/123
我对这个URI命名约定有点困惑。我们应该用复数还是单数来创建资源?决定的标准应该是什么?
当前回答
尽管最流行的做法是使用复数的RESTful api,例如/api/resources/123,但有一个特殊的情况,我发现使用单数名称比使用复数名称更合适/更具表现力。这是一对一关系的例子。特别是如果目标项是一个值对象(在领域驱动设计范例中)。
让我们假设每个资源都有一个一对一的accessLog,它可以被建模为一个值对象,即不是实体,因此没有ID。它可以表示为/api/resources/123/accessLog。通常的动词(POST、PUT、DELETE、GET)可以恰当地表达意图,以及关系确实是一对一的事实。
其他回答
复数
简单-所有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 -删除指定的资源项
对于单数的提倡者,可以这样想:你会向某人要一份订单,并期待一件事,还是一份清单?那么,当您键入/订单时,为什么期望服务返回一列东西呢?
尽管最流行的做法是使用复数的RESTful api,例如/api/resources/123,但有一个特殊的情况,我发现使用单数名称比使用复数名称更合适/更具表现力。这是一对一关系的例子。特别是如果目标项是一个值对象(在领域驱动设计范例中)。
让我们假设每个资源都有一个一对一的accessLog,它可以被建模为一个值对象,即不是实体,因此没有ID。它可以表示为/api/resources/123/accessLog。通常的动词(POST、PUT、DELETE、GET)可以恰当地表达意图,以及关系确实是一对一的事实。
我的观点是:那些把时间从复数变成单数或者反过来的方法是在浪费CPU周期。我可能是个老派,但在我那个时代,类似的东西都是一样的。如何查找有关人员的方法?任何正则表达式都不能同时覆盖person和people而没有不良的副作用。
英语复数可以是非常随意的,它们不必要地妨碍代码。坚持一个命名约定。计算机语言应该是关于数学的清晰性,而不是关于模仿自然语言。
这两种表示都很有用。在相当长的一段时间里,我为了方便而使用单数,屈折变化可能会很困难。根据我在开发严格的单一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处创建新资源
我更喜欢同时使用复数(/resources)和单数(/resource/{id}),因为我认为它更清楚地区分了处理资源集合和处理单个资源之间的逻辑。
作为一个重要的副作用,它还可以帮助防止某些人错误地使用API。例如,考虑这样一种情况,用户错误地试图通过将Id指定为如下参数来获取资源:
GET /resources?Id=123
在本例中,我们使用复数形式,服务器很可能会忽略Id参数并返回所有资源的列表。如果用户不小心,他会认为调用成功,并使用列表中的第一个资源。
另一方面,当使用单数形式时:
GET /resource?Id=123
服务器很可能会返回一个错误,因为Id没有以正确的方式指定,并且用户将不得不意识到某些事情是错误的。