我是REST的新手,我注意到在某些RESTful服务中,它们使用不同的资源URI进行更新/获取/删除和创建。如
- 创建-使用/资源用POST方法(注意复数),使用一些地方/资源(单数)
- 更新-使用/ resource / 123和PUT方法
- 获取-将/ resource / 123与GET方法一起使用
我对此URI命名约定有点困惑。我们应该使用复数还是单数来创建资源?决定的标准是什么?
我是REST的新手,我注意到在某些RESTful服务中,它们使用不同的资源URI进行更新/获取/删除和创建。如
我对此URI命名约定有点困惑。我们应该使用复数还是单数来创建资源?决定的标准是什么?
Answers:
使用的前提/resources
是它代表“所有”资源。如果执行GET /resources
,则可能会返回整个集合。通过发布到/resources
,您将添加到集合中。
但是,单个资源位于/ resource。如果执行GET /resource
,则可能会出错,因为此请求没有任何意义,而/resource/123
完全有道理。
使用/resource
的,而不是/resources
类似于你会怎么做,如果你用,比如说,一个文件系统和文件的收集工作,/resource
是“目录”与个人123
,456
在它的文件。
不管是对还是错,都随心所欲。
对我来说,最好有一个可以直接映射到代码的模式(易于自动化),这主要是因为代码将是两端。
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服务的用户,无论我访问列表还是列表中的特定资源,我都希望列表资源具有相同的名称。无论您要使用列表资源还是特定资源,都应使用相同的标识符。
复数
orders/
获取订单的索引列表。例如:
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
对于一个处理引用一个顺序的对象的单个实例的类来说,它是一个好名字。OrderList
是处理多个Order
实例的类的名称。Orders Table
是许多订单的数据库表的好名字。
便利 事物可以具有不规则的复数名称。有时他们没有一个。但是单数名称始终存在。
例如,CustomerAddress超过CustomerAddresses
考虑此相关资源。
这/order/12/orderdetail/12
比更具可读性和逻辑性/orders/12/orderdetails/4
。
资源代表诸如数据库表之类的实体。它应该具有逻辑上的单数名称。这是表格名称的答案。
类总是单数。ORM工具生成与类名称同名的表。随着越来越多的工具被使用,单数名称已成为一种标准。
阅读有关REST API开发人员困境的更多信息
/clothe/12/trouser/34
:)
clothe
是动词。其余的API在谈论资源时通常使用名词,在描述动作时使用动词。单数形式是clout
,但是过时的,很可能更适合用代替garment
。
尽管最普遍的做法是使用复数形式的RESTful API,例如/api/resources/123
,在一种特殊情况下,我发现使用单数名称比复数名称更合适/更具表达性。一对一的关系就是这种情况。特别是如果目标项目是值对象(在域驱动设计范式中)。
让我们假设每个资源都是一对一的accessLog
,可以将其建模为值对象,即不是实体,因此没有ID。它可以表示为/api/resources/123/accessLog
。常用动词(POST,PUT,DELETE和GET)会适当地表达意图,并且也表明这种关系确实是一对一的。
GET /users/123/location
应该获取用户工作的位置。真的不GET /users/123/locations
误导消费者吗?
accessLog
被建模为属性或值,而不是实体,因此应为单数。如果您过分设计,那么日志条目将是一个实体,而您将拥有/api/accessLogEntries?resource=123
。
为什么不遵循通常接受单数形式的数据库表名称的流行趋势?到那里,做完了-让我们重复使用。
看到如此多的人跳上复数名词带动我感到惊讶。在实现单数到复数转换时,您要注意不规则的复数名词吗?你喜欢痛苦吗?
参见 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)
从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状态码DELETE /resource/id/{resourceId}
这种方法最灵活,功能最丰富,但开发时间也最长。因此,如果您很着急(在软件开发中通常如此),只需命名端点resource
或复数形式即可resources
。我更喜欢单数形式,因为它可以让您选择以编程方式进行内省和评估,因为并非所有复数形式都以“ s”结尾。
综上所述,无论出于何种原因,开发人员选择的最常用的做法是使用复数形式。最终这就是我选择的路线,如果您喜欢github
和一样的流行api twitter
,这就是它们的作用。
决定的一些标准可能是:
因此,取决于您。无论您做什么,都要保持一致。
POST /users
应该创建一个用户,并将其添加到集合中。我不同意。POST /users
应该创建一个用户列表(即使该列表为1),也POST /user
应该创建一个用户。我认为没有理由不能同时存在多个资源端点和单个资源端点。他们描述了不同的行为,不应让任何人感到惊讶。
POST users/<id>
将创建一个新用户。
PUT /users/<id>
代替POST
。POST
解释为“将其添加到集合中,并确定ID作为其中的一部分”。PUT
解释为“使用此ID更新(或添加)此资源”。有关此原理的详细说明,请参见restcookbook.com/HTTP%20Methods/put-vs-post。
我的两分钱:花费时间从复数变为单数或反之亦然的方法是浪费CPU周期。我可能是高中生,但是在我的时代,事物被称为相同。如何查找有关人的方法?没有规律的表达不会覆盖人和人,而不会产生不良副作用。
英文复数可以是非常任意的,它们不必要地妨碍了代码。遵守一个命名约定。计算机语言应该是数学上的清晰度,而不是模仿自然语言。
为了简化和保持一致性,我更喜欢使用单数形式。
例如,考虑以下URL:
/客户/ 1
我会将客户视为客户集合,但为简单起见,删除了集合部分。
另一个例子:
/设备/ 1
在这种情况下,设备不是正确的复数形式。因此,将其视为设备集合并为简单起见删除集合使其与客户案例保持一致。
POST /customer
做的就是-插入一个客户吗?
POST /customer
给我读,好像它是发给the
客户的。不是客户的集合。但是,我会承认“ Plural”或“ Plural”不是首选。只要它们不像其他答案那样混杂。这将令人难以置信。
路由中的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
使用命名约定,通常可以说“只选一个并坚持下去”是很安全的,这是有道理的。
但是,在必须向很多人解释REST之后,将端点表示为文件系统上的路径是最有表现力的方式。
它是无状态的(文件存在或不存在),分层的,简单且熟悉的-您已经知道如何访问静态文件,无论是本地还是通过http。
在这种情况下,语言规则只能使您达到以下目标:
目录可以包含多个文件和/或子目录,因此其名称应为复数形式。
我喜欢那个。
虽然,另一方面,这是您的目录,如果需要的话,可以将其命名为“ a-resource-or-multiple-resources”。那不是真正重要的事情。
重要的是,如果将名为“ 123”的文件放在名为“ resourceS”的目录下(导致/resourceS/123
),则不能期望可以通过进行访问/resource/123
。
不要尝试使其变得比必需的更加聪明-根据您当前正在访问的资源数量,从复数更改为单数可能在美学上令人愉悦,但这是无效的,并且在某种意义上是没有意义的等级制度。
注意:从技术上讲,您可以进行“符号链接”,因此/resources/123
也可以通过进行访问/resource/123
,但是前者仍然必须存在!
请参阅Google的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 |
|--------------------------+---------------+-------------------+---------------+--------------|
如果您正在考虑此主题,则值得阅读。
两种表示都很有用。为了方便起见,我已经使用单数形式了一段时间,所以很难进行变形。根据我在开发严格的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
我知道大多数人都在决定使用复数还是单数之间。此处尚未解决的问题是客户端将需要知道您使用的是哪个客户端,并且他们总是很可能会犯错。这是我的建议的来源。
两者呢?而且,我的意思是对整个API使用单数形式,然后创建路由以将以复数形式提出的请求转发到单数形式。例如:
GET /resources = GET /resource
GET /resources/1 = GET /resource/1
POST /resources/1 = POST /resource/1
...
您得到图片。没有人是错的,付出最小的努力,客户将永远正确。
/resources
并且总是被重定向到/resource
,那么您做错了。如果其他人使用您的API,则他们可以直接使用正确的URL或被重定向(有效但错误),而您打开的方式却是错误的。
我不希望看到{id}
URL 的一部分与子资源重叠,因为从id
理论上讲它可以是任何东西,并且会有歧义。它混合了不同的概念(标识符和子资源名称)。
类似的问题经常出现在enum
常量或文件夹结构,不同的概念混合(例如,当你有文件夹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
s 没有任何限制,从理论上讲,可以使用户具有id new
而不会与(潜在的将来)子资源名称重叠。
使用单数,并利用“业务目录”中的英语约定。
很多东西都是这样阅读的:“书柜”,“狗包”,“美术馆”,“电影节”,“车位”等。
这很方便地从左到右匹配网址路径。项目类型在左侧。在右侧设置类型。
难道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只能对单个请求具有单个响应。路径总是指单数。
对我来说,复数操纵集合,而单数操纵集合中的项目。
集合允许方法GET / POST / DELETE
项目允许方法GET / PUT / DELETE
例如
/ students上的POST 将在学校中添加一个新学生。
删除/学生将删除所有学校的学生。
/ student / 123上的DELETE 会将学生123从学校中删除。
感觉似乎并不重要,但有些工程师有时会忘记ID。如果路由始终为复数并执行了DELETE,则可能会意外擦除数据。而缺少单数形式的ID将返回未找到的404路由。
为了进一步扩展示例,假设该API应该公开多个学校,则类似
在/ school / abc / students上删除将删除学校中的所有学生abc
。
有时候,选择正确的单词本身就是一个挑战,但是我喜欢保持集合的多元性。例如cart_items
或cart/items
感觉正确。相反cart
,删除会删除自身的购物车对象,而不是购物车中的项目;)。
我更喜欢同时使用复数(/resources
)和单数(/resource/{id}
),因为我认为它更清楚地将处理资源集合与处理单个资源之间的逻辑分开。
作为重要的副作用,它还可以帮助防止有人错误使用API。例如,考虑以下情况:用户通过将Id指定为这样的参数来错误地尝试获取资源:
GET /resources?Id=123
在这种情况下,如果我们使用复数版本,则服务器很可能会忽略Id参数并返回所有资源的列表。如果用户不小心,他会认为呼叫成功,并使用列表中的第一个资源。
另一方面,使用单数形式时:
GET /resource?Id=123
服务器很可能会返回错误,因为没有以正确的方式指定Id,并且用户将必须意识到某些错误。