REST URI约定 - 创建资源时的单数或复数名称

时间:2011-07-27 14:05:38

标签: rest resources naming-conventions uri

我是REST的新手,我发现在一些RESTful服务中,他们使用不同的资源URI进行更新/获取/删除和创建。如

  • 使用 / resource 在某些地方使用 / resources 和POST方法(观察复数形式)进行创建(单数)
  • 更新 - 使用 / resource / 123 和PUT方法
  • 获取 - 使用GET方法 / resource / 123

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

23 个答案:

答案 0 :(得分:528)

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

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) 

答案 1 :(得分:250)

我也没有看到这样做的重点,我认为这不是最好的URI设计。作为RESTful服务的用户,我希望列表资源具有相同的名称,无论我是否访问列表中的列表或特定资源。无论您是要使用列表资源还是特定资源,都应使用相同的标识符。

答案 2 :(得分:233)

使用/resources的前提是它代表“所有”资源。如果您执行GET /resources,则可能会返回整个集合。通过POST到/resources,您将添加到集合中。

但是,个人资源位于/ resource。如果您执行GET /resource,则可能会出错,因为此请求没有任何意义,而/resource/123则非常有意义。

使用/resource代替/resources类似于如果您使用文件系统和文件集合并且/resource是“目录”的方式“其中包含个人123456个文件。

无论是对还是错,都选择你最喜欢的方式。

答案 3 :(得分:56)

<强>复数

  • 简单 - 所有网址都以相同的前缀
  • 开头
  • 逻辑 - orders/获取订单的索引列表。
  • 标准 - 最广泛采用的标准,其次是绝大多数公共和私有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 - 删除特定资源项

对于单数的拥护者,可以这样想:你会问一个人order并期待一件事或一系列事情吗?那么,当您键入/order

时,为什么您希望服务返回一系列内容?

答案 4 :(得分:33)

奇异

<强>便利 事情可能有不规则的复数名称。有时他们没有。 但奇异的名字总是在那里。

e.g。 CustomerAddresses上的CustomerAddress

考虑这个相关资源。

/order/12/orderdetail/12/orders/12/orderdetails/4更具可读性和逻辑性。

数据库表

资源表示像数据库表这样的实体。 它应该具有逻辑上的单数名称。 这是answer表格名称。

类映射

类总是单数。 ORM工具生成与类名相同的表。随着越来越多的工具的使用,奇异的名称正在成为一种标准。

详细了解A REST API Developer's Dilemma

答案 5 :(得分:28)

而最流行的做法是RESTful apis,其中使用复数,例如/api/resources/123,有一个特殊情况,我发现使用比复数名称更恰当/富有表现力的单数名称。这是一对一关系的情况。特别是如果目标项是值对象(在Domain-driven-design范例中)。

让我们假设每个资源都有一对一accessLog,它可以被建模为一个值对象,即不是一个实体,因此没有ID。它可以表示为/api/resources/123/accessLog。通常的动词(POST,PUT,DELETE,GET)会恰当地表达意图以及这种关系确实是一对一的事实。

答案 6 :(得分:20)

为什么不遵循数据库表名的普遍趋势,通常接受单数形式?在那里,完成了 - 让我们重用。

Table Naming Dilemma: Singular vs. Plural Names

答案 7 :(得分:13)

从API消费者的角度来看,端点应该是可预测的,所以

...理想地

  1. GET /resources应返回资源列表。
  2. GET /resource应返回400级状态代码。
  3. GET /resources/id/{resourceId}应返回包含一个资源的集合。
  4. GET /resource/id/{resourceId}应该返回一个资源对象。
  5. POST /resources应该批量创建资源。
  6. POST /resource应该创建一个资源。
  7. PUT /resource应该更新资源对象。
  8. PATCH /resource应通过仅发布已更改的属性来更新资源。
  9. PATCH /resources应批量更新仅发布已更改属性的资源。
  10. DELETE /resources应删除所有资源;开玩笑:400状态代码
  11. DELETE /resource/id/{resourceId}
  12. 这种方法最灵活,功能最丰富,但也是最耗时的开发方法。因此,如果您匆忙(软件开发总是这样),只需将端点resource或复数形式resources命名即可。我更喜欢单数形式,因为它为您提供了以编程方式进行内省和评估的选项,因为并非所有复数形式都以“#”形式结束。

    说了这么多,无论出于何种原因,最常用的练习开发者选择的是使用复数形式。这最终是我选择的路线,如果你看一下像githubtwitter这样的流行api,这就是他们所做的。

    决定的一些标准可能是:

    1. 我的时间限制是什么?
    2. 我允许消费者做什么操作?
    3. 请求和结果有效负载是什么样的?
    4. 我是否希望能够使用反射并在我的代码中解析URI?
    5. 所以它取决于你。无论你做什么都是一贯的。

答案 8 :(得分:13)

我很惊讶地看到很多人会跳上复数名词的潮流。在实现单数转换为多数转换时,您是否正在处理不规则复数名词?你喜欢疼吗?

请参阅 http://web2.uvcs.uvic.ca/elc/studyzone/330/grammar/irrplu.htm

有许多类型的不规则复数,但这些是最常见的:

名词类型形成复数示例

Ends with -fe   Change f to v then Add -s   
    knife   knives 
    life   lives 
    wife   wives
Ends with -f    Change f to v then Add -es  
    half   halves 
    wolf   wolves
    loaf   loaves
Ends with -o    Add -es 
    potato   potatoes
    tomato   tomatoes
    volcano   volcanoes
Ends with -us   Change -us to -i    
    cactus   cacti
    nucleus   nuclei
    focus   foci
Ends with -is   Change -is to -es   
    analysis   analyses
    crisis   crises
    thesis   theses
Ends with -on   Change -on to -a    
    phenomenon   phenomena
    criterion   criteria
ALL KINDS   Change the vowel or Change the word or Add a different ending   
     man   men
     foot   feet
     child   children
     person   people
     tooth   teeth
     mouse   mice
 Unchanging Singular and plural are the same    
     sheep deer fish (sometimes)

答案 9 :(得分:5)

我更喜欢使用单数形式来实现简单性和一致性。

例如,考虑以下网址:

/客户/ 1

我会将客户视为客户收集,但为简单起见,我们会删除收集部分。

另一个例子:

/设备/ 1

在这种情况下,设备不是正确的复数形式。因此,将其视为设备集合并简化集合,使其与客户案例保持一致。

答案 10 :(得分:5)

我的两分钱:花时间从复数变为单数或反复的方法是浪费CPU周期。我可能是老派,但在我的时间里,事情被称为相同。我如何查找有关人的方法?没有正常的表达将涵盖人和人,没有不良副作用。

英语复数可以是非常随意的,它们会不必要地阻碍代码。坚持一个命名惯例。计算机语言应该是关于数学清晰度的,而不是模仿自然语言。

答案 11 :(得分:3)

使用命名约定,通常可以安全地说&#34;只需选择一个并坚持使用#34;这是有道理的。

但是,在向许多人解释REST之后,将端点表示为文件系统上的路径是最有表现力的方式。
它是无状态的(文件存在或不存在),分层,简单和熟悉 - 您已经知道如何访问静态文件,无论是本地还是通过http。

在这种情况下,语言规则只能让你达到以下目的:

  

目录可以包含多个文件和/或子目录,因此其名称​​应为复数形式。

我喜欢那样 虽然,另一方面 - 它是您的目录,但您可以将其命名为资源或多资源&#34;如果这就是你想要的。这并不重要。

重要的是,如果你把一个名为&#34; 123&#34;在名为&#34; resourceS&#34;的目录下(产生/resourceS/123),您无法通过/resource/123访问它。

不要试图让它变得比它更聪明 - 根据您当前访问的资源数量从复数变为单数可能在某些方面可能在美学上令人愉悦,但它没有效果,在分层系统中没有意义。

注意:从技术上讲,您可以制作&#34;符号链接&#34;,以便/resources/123也可以通过/resource/123访问,但前者仍然必须存在!< / em>的

答案 12 :(得分:3)

使用单数形式,并利用例如“企业目录”。

很多东西都是这样写的:“书柜”,“狗包”,“美术馆”,“电影节”,“车位”等。

这方便地从左到右匹配网址路径。项目类型在左侧。在右侧设置类型。

GET /users真的可以获取一组用户吗?通常不行。它获取一组包含密钥和用户名的存根。因此,它实际上并不是/users。它是用户的索引,如果可以的话,也可以是“用户索引”。为什么不这样称呼呢?这是/user/index。由于我们已经命名了集合类型,因此我们可以使用多种类型来显示用户的不同投射,而无需借助查询参数,例如user/phone-list/user/mailing-list

用户300呢?仍然是/user/300

GET /user/index
GET /user/{id}

POST /user
PUT /user/{id}

DELETE /user/{id}

最后,HTTP只能对单个请求具有单个响应。路径总是指单个事物。

答案 13 :(得分:2)

我知道大多数人在决定是使用复数还是单数。这里没有解决的问题是客户需要知道您正在使用哪一个,并且他们总是可能犯错误。这是我的建议来自的地方。

两者兼而有之?就此而言,我的意思是对您的整个API使用单数,然后创建路由以将复数形式的请求转发为单数形式。例如:

GET  /resources     =     GET  /resource
GET  /resources/1   =     GET  /resource/1
POST /resources/1   =     POST /resource/1
...

你得到了照片。没有人是错的,只需要很少的努力,而且客户永远都会做对。

答案 14 :(得分:1)

至少在一个方面,对所有方法使用复数更实用: 如果您使用Postman(或类似工具)开发和测试资源API,则在从GET切换到PUT到POST等时,您不需要编辑URI。

答案 15 :(得分:1)

两种表示都很有用。为了方便我已经使用了单数很长一段时间,变形可能很困难。我在开发严格单一的REST API方面的经验,使用端点的开发人员对结果的形状缺乏确定性。我现在更喜欢使用最能描述响应形状的术语。

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

如果您正在进行任何类型的深层链接来表示关系的查询,那么可以通过更严格的约定来帮助开发人员编写API。

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

假设我们有以下模型。

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

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

如果我需要提供允许客户端获取特定用户的特定朋友的经理的资源,它可能看起来像:

GET /users/{id}/friends/{friendId}/manager

以下是一些例子:

  • GET /users - 列出全局用户集合中的用户资源
  • POST /users - 在全局用户集合中创建新用户
  • GET /users/{id} - 从全局用户集合中检索特定用户
  • GET /users/{id}/manager - 获取特定用户的经理
  • GET /users/{id}/friends - 获取用户的朋友列表
  • GET /users/{id}/friends/{friendId} - 获取用户的特定朋友
  • LINK /users/{id}/friends - 向此用户添加好友关联
  • UNLINK /users/{id}/friends - 从此用户中删除朋友关联

注意每个级别如何映射到可以对其执行操作的父级。对同一个对象使用不同的父对象是违反直觉的。在GET /resource/123检索资源并未表明应在POST /resources

处创建新资源

答案 16 :(得分:1)

应该将路由中的ID视为与列表的索引相同,并且命名应相应进行。

numbers = [1, 2, 3]

numbers            GET /numbers
numbers[1]         GET /numbers/1
numbers.push(4)    POST /numbers
numbers[1] = 23    UPDATE /numbers/1

但是某些资源在其路由中不使用id,因为要么只有一个,要么一个用户永远无法访问多个id,因此这些不是列表:

GET /dashboard
DELETE /session
POST /login
GET /users/{:id}/profile
UPDATE /users/{:id}/profile

答案 17 :(得分:1)

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

类似的问题通常出现在enum常量或文件夹结构中,其中混合了不同的概念(例如,当您拥有文件夹TigersLionsCheetahs时,然后在同一级别上还有一个名为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的用户与(可能的将来)子资源名称不重叠。

答案 18 :(得分:0)

怎么样:

/resource/(不是/resource

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

我也认为数据库表的命名约定是相同的,例如,一个名为“ user”的表是一个“ user table”,其中包含一个名为“ user”的东西。

答案 19 :(得分:0)

结帐 Google API Design Guide: Resource Names,以获取其他命名资源的信息。

简而言之:

  • 集合以复数命名。
  • 单个资源用字符串命名。
|--------------------------+---------------+-------------------+---------------+--------------|
| 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   |
|--------------------------+---------------+-------------------+---------------+--------------|

答案 20 :(得分:0)

最重要的事情

/pants, /eye-glasses - 那些是单数还是复数路径?

/radii - 你是否知道它的单一路径是 /radius 还是 /radix

/index - 如果复数路径是 /indexes/indeces/indices,您是否一头雾水?

每当您在接口和代码中使用复数形式时,问问自己,您的约定如何处理上述单词?

理想情况下,公约应该在没有违规的情况下进行扩展。英语复数不这样做,因为

  1. 他们有例外,例如复数形式所称的事物之一,以及
  2. 没有简单的算法可以从单数中获取单词的复数,从复数中获取单数,或者判断未知名词是单数还是复数。

这有缺点。我头顶最突出的:

  1. 单复数形式相同的名词将强制您的代码处理“复数”端点和“单数”端点无论如何具有相同路径的情况。
  2. 您的用户/开发人员必须精通英语,才能知道名词的正确单数和复数。在日益国际化的世界中,这可能会导致不可忽视的挫败感和开销。
  3. 它单独变成了“我知道 /foo/{{id}},获得所有 foo 的途径是什么?”变成自然语言问题,而不是“只删除最后一个路径部分”的问题。

与此同时,一些人类语言甚至没有名词的单复数形式。他们管理得很好。您的 API 也可以。

答案 21 :(得分:-1)

对我来说,复数操纵集合,而单身操纵该集合中的

集合允许方法 GET / POST / DELETE

项目允许方法 GET / PUT / DELETE

例如

/ students 上的POST将在学校中添加一名新学生。

删除 /学生将删除学校中的所有学生。

删除 / student / 123 会删除学生123。

它可能感觉不重要但有些工程师有时会忘记id。如果路径始终为复数并执行DELETE,则可能会意外擦除数据。如果缺少单数上的id将返回未找到的404路径。

如果API应该公开多个学校,那么为了进一步扩展这个例子,那么

删除 / school / abc / students 将删除学校中的所有学生abc

选择正确的单词有时候本身就是一个挑战,但我喜欢为集合保留多元化。例如。 cart_itemscart/items感觉正确。相反,删除cart,删除自己的购物车对象,而不删除购物车中的商品;)。

答案 22 :(得分:-3)

我更喜欢使用复数(/resources)和单数(/resource/{id}),因为我认为它更清楚地区分了处理资源集合和处理单一资源之间的逻辑。 / p>

作为一个重要的副作用,它还可以帮助防止有人错误地使用API​​。例如,考虑用户通过将Id指定为如下参数来错误地尝试获取资源的情况:

GET /resources?Id=123

在这种情况下,我们使用复数版本,服务器很可能会忽略Id参数并返回所有资源的列表。如果用户不小心,他会认为呼叫成功并使用列表中的第一个资源。

另一方面,使用单数形式时:

GET /resource?Id=123

服务器很可能会返回错误,因为没有以正确的方式指定Id,并且用户必须意识到出了问题。