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

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

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


当前回答

复数

简单-所有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 -删除指定的资源项

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

其他回答

复数

简单-所有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 -删除指定的资源项

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

我的观点是:那些把时间从复数变成单数或者反过来的方法是在浪费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处创建新资源

最重要的事情

当你在接口和代码中使用复数时,问问你自己,你的约定是如何处理这些词的:

/pants, /eye-glasses——是单数还是复数? /radii -你知道它的唯一路径是/radius还是/radix吗? /index -你知道它的复数路径是/indexes或/indexes或/indexes吗?

理想情况下,约定的规模应该没有不规则性。英语复数就不会这样,因为

它们也有例外,比如某物被称为复数形式,以及 没有简单的算法可以从一个词的单数中得到一个词的复数,从复数中得到一个词的单数,或者判断一个未知名词是单数还是复数。

这也有缺点。我脑海中最突出的是:

The nouns whose singular and plural forms are the same will force your code to handle the case where the "plural" endpoint and the "singular" endpoint have the same path anyway. Your users/developers have to be proficient with English enough to know the correct singulars and plurals for nouns. In an increasingly internationalized world, this can cause non-negligible frustration and overhead. It singlehandedly turns "I know /foo/{{id}}, what's the path to get all foo?" into a natural language problem instead of a "just drop the last path part" problem.

与此同时,一些人类语言甚至没有名词的单数形式和复数形式。他们还行。你的API也可以。

我不喜欢看到url的{id}部分与子资源重叠,因为id理论上可以是任何东西,而且会有歧义。它混合了不同的概念(标识符和子资源名)。

类似的问题经常出现在枚举常量或文件夹结构中,其中混合了不同的概念(例如,当您有Tigers、Lions和Cheetahs文件夹时,在同一级别上还有一个名为Animals的文件夹——这没有意义,因为其中一个是另一个的子集)。

一般来说,我认为端点的最后一个命名部分如果一次处理单个实体,应该是单数,如果处理一组实体,则应该是复数。

处理单个用户的端点:

GET  /user             -> Not allowed, 400
GET  /user/{id}        -> Returns user with given id
POST /user             -> Creates a new user
PUT  /user/{id}        -> Updates user with given id
DELETE /user/{id}      -> Deletes user with given id

然后有一个单独的资源用于对用户进行查询,通常返回一个列表:

GET /users             -> Lists all users, optionally filtered by way of parameters
GET /users/new?since=x -> Gets all users that are new since a specific time
GET /users/top?max=x   -> Gets top X active users

下面是一些处理特定用户的子资源的例子:

GET /user/{id}/friends -> Returns a list of friends of given user

交个朋友(多对多链接):

PUT /user/{id}/friend/{id}     -> Befriends two users
DELETE /user/{id}/friend/{id}  -> Unfriends two users
GET /user/{id}/friend/{id}     -> Gets status of friendship between two users

永远不会有任何歧义,资源的复数或单数命名都是在暗示用户他们可以期待什么(列表或对象)。对id没有限制,理论上可以让id为new的用户不与(潜在的未来)子资源名重叠。