关于REST URI约定:REST URI约定 – 创建资源时的单个或复数名称

REST URI convention - Singular or plural name of resource while creating it

我不熟悉REST,我注意到在一些RESTful服务中,它们使用不同的资源URI进行更新/获取/删除和创建。例如

  • 使用post方法创建-使用/resources(注意复数),在某些地方使用/resource(单数)
  • 更新-使用/resource/123 with put方法
  • get-使用/resource/123和get方法

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


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

1
2
3
4
5
6
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)


我也不认为这样做的意义所在,我认为这不是最佳的URI设计。作为一个RESTful服务的用户,我希望列表资源具有相同的名称,不管我是访问列表还是"列表"中的特定资源。无论是使用列表资源还是特定资源,都应该使用相同的标识符。


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

但是,单个资源在/resource处可用。如果您执行GET /resource,可能会出错,因为此请求没有任何意义,而/resource/123则完全有意义。

使用/resource而不是/resources,与使用文件系统和文件集合时的情况类似,其中/resource是包含单个123456文件的"目录"。

无论是对是错,都要选择你最喜欢的。


复数的

  • 简单-所有URL以相同的前缀开头
  • logical-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时,为什么您希望服务返回一个列表?


单数

方便事物可以有不规则的复数名称。有时他们没有。但是单数的名字总是存在的。

例如,客户服装而不是客户服装

考虑这个相关的资源。

这个/order/12/orderdetail/12/orders/12/orderdetails/4更易读,更合乎逻辑。

数据库表

资源表示类似数据库表的实体。它应该有一个逻辑上的单数名称。这是表名的答案。

类映射

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

阅读有关RESTAPI开发人员困境的更多信息


尽管最普遍的做法是使用复数形式的RESTful API,例如/api/resources/123,但有一种特殊情况是,我发现使用单数形式的名称比复数形式的名称更合适/更具表现力。这是一对一的关系。特别是如果目标项是值对象(在域驱动的设计范式中)。

假设每个资源都有一对一的accessLog,它可以被建模为一个值对象,即不是一个实体,因此没有ID。它可以表示为/api/resources/123/accessLog。通常的动词(post、put、delete、get)会恰当地表达意图,而且事实上这种关系确实是一对一的。


为什么不遵循数据库表名的流行趋势,在这种情况下通常接受单一形式?去过那里,做过——让我们重用。

表命名困境:单数与复数名称


我很惊讶地看到这么多人会跳上复数名词的潮流。在执行单数到复数转换时,您是否注意不规则复数名词?你喜欢疼痛吗?

见http://web2.uvcs.uvic.ca/elc/studyzone/330/grammar/irrplu.htm网站

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

构成复数示例的名词类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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)


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

理想的。。。

  • GET /resources应返回资源列表。
  • GET /resource应返回400级状态代码。
  • GET /resources/id/{resourceId}应该用一个资源返回一个集合。
  • GET /resource/id/{resourceId}应该返回一个资源对象。
  • POST /resources应该批量创建资源。
  • POST /resource应该创建一个资源。
  • PUT /resource应该更新一个资源对象。
  • PATCH /resource应该通过只发布更改的属性来更新资源。
  • PATCH /resources应该批量更新只发布更改属性的资源。
  • DELETE /resources应删除所有资源;开玩笑:400状态码
  • 埃多克斯1〔23〕
  • 这种方法是最灵活和功能丰富的,但也是最耗时的开发方法。所以,如果你赶时间(软件开发总是这样),只需说出你的端点resource或复数形式resources。我更喜欢单数形式,因为它可以让你选择以编程方式反省和评估,因为并非所有复数形式都以"s"结尾。

    尽管如此,无论出于什么原因,最常用的实践开发人员选择的是使用复数形式。这是我最终选择的路径,如果你看看像githubtwitter这样的流行API,这就是它们所做的。

    决定的一些标准可能是:

  • 我的时间限制是什么?
  • 我将允许我的消费者做什么操作?
  • 请求和结果有效负载是什么样子的?
  • 我想在代码中使用反射和解析URI吗?
  • 所以这取决于你。不管你做什么都要始终如一。


    为了简单和一致,我更喜欢使用单数形式。

    例如,考虑以下URL:

    /客户/1

    我将把客户视为客户集合,但为了简单起见,集合部分被移除。

    另一个例子:

    /设备/1

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


    我的2分:那些花费时间从复数变为单数或viceversa的方法是在浪费CPU周期。我可能是个老派,但在我的时代,一切都是一样的。我如何查找与人有关的方法?任何定期的表达都不能覆盖没有不良副作用的人和人。

    英语的复数形式可能非常武断,它们不必要地妨碍了代码。坚持一个命名约定。计算机语言应该是关于数学的清晰,而不是模仿自然语言。


    对于命名约定,通常说"只选一个并坚持它"是安全的,这是有意义的。

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

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

    A directory can contain multiple files and/or sub-directories, and therefore its name should be in plural form.

    我喜欢这样。但是,另一方面,它是您的目录,如果您需要的话,您可以将它命名为"a-resource-or-multiple-resource s"。这并不重要。

    重要的是,如果将名为"123"的文件放在名为"resources"的目录下(导致/resourceS/123的结果),那么就不能期望通过/resource/123访问该文件。

    不要试图让它变得比它必须的更聪明-从复数变为单数取决于你目前正在访问的资源的数量,这对一些人来说可能是美学上的愉悦,但它是无效的,在一个等级系统中也没有意义。

    注:从技术上讲,你可以做"符号链接",这样/resourceS/123也可以通过/resource/123访问,但前者仍然存在!


    (P)我知道大多数人是在决定使用复数还是单数之间。The issue that has not been addressed here is that the client will need to know which one you are using,and they are always like to make a mistake.这是我的建议从哪里来。(p)(P)怎么样的船?And by that,I mean use unique for your whole api and then create routes to forward requests made in the plural form to the individual form.For example:(p)字母名称(P)You get the picture.没有人是错误的,最小的努力,和客户将永远得到它的权利。(p)


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


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

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

    如果您正在进行某种深度链接来表示对关系的查询,那么使用更严格的约定可以帮助开发人员编写API。

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

    假设我们有以下模型。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    interface User {
        <string>id;
        <Friend[]>friends;
        <Manager>user;
    }

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

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

    江户十一〔四〕号

    以下是更多示例:

    • 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上创建新资源。


    (P)How about:(p)(P)字母名称2(p)(P)EDOCX1 2(英文)意思是"It's a folder contains some called"resource",it's a"resource"folder.(p)(P)and also I think the Naming Convention of Database Tables is the same,for example,a table called'user'is a"user table",it contains some called"user".(p)


    对我来说,复数形式操纵集合,而单数形式操纵集合内的项。

    集合允许方法get/post/delete

    项允许方法获取/放置/删除

    例如

    发布/学生将在学校添加新学生。

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

    /student/123上的delete将从学校中删除student 123。

    可能感觉不重要,但有些工程师有时会忘记ID。如果路由始终是复数并执行删除操作,则可能会意外地擦除数据。而在单数上缺少ID将返回一个404路由找不到。

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

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

    选择正确的词有时本身就是一个挑战,但我喜欢为收藏保持多元化。例如,cart_itemscart/items感觉是对的。与删除cart相反,删除购物车对象本身而不是购物车中的项目;)。


    (P)I prefer to use both plural(EDOCX1)and individual(EDOCX1)and individual(EDOCX1)because I think that it more clearly separates the logic between working on the collection of resources and working on a single resource.(p)(P)作为一个重要的方面——这一点的影响,它也可以帮助防止一些人利用投资政策评审错误。For example,consider the case where a user wrongly tries to get a resource by specifying the id as a parameter like this:(p)字母名称(P)In this case,where we use the plural version,the server will most likely ignore the id parameter and return the list of all resources.如果用户不关心,他会认为呼吁是成功的,并在名单中使用第一种资源。(p)(P)On the other hand,when using the unique form:(p)字母名称(P)The server will most likely return an error because the id is not specified in the right way,and the user will have to realize that some thing is wrong.(p)