REST URI约定 – 创build资源时的单数或复数名称
我是REST的新手,我发现在一些RESTful服务中,他们使用不同的资源URI来进行更新/获取/删除和创build。 如
- 创build – 使用/资源与POST方法(观察复数)在某些地方使用/资源 (单数)
- 更新 – 使用/资源/ 123与PUT方法
- 获取 – 使用/资源/ 123与GET方法
我对这个URI命名约定有点困惑。 我们应该使用复数或单数来创造资源? 在决定的时候应该是什么标准呢?
使用/resources
的前提是它代表“所有”资源。 如果你做一个GET /resources
,你可能会返回整个集合。 通过发布到/resources
,您正在添加到集合。
但是,个人资源在/资源处可用。 如果你做一个GET /resource
,你可能会错误,因为这个请求没有任何意义,而/resource/123
是非常有意义的。
使用/resource
而不是/resources
类似于如果您使用文件系统和文件集合,并且/resource
是其中包含单个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)
我也没有看到这一点,我认为这不是最好的URIdevise。 作为RESTful服务的用户,我希望列表资源具有相同的名称,而不pipe我是否访问列表中的列表或特定资源。 无论您想使用列表资源还是特定资源,都应使用相同的标识符。
尽pipe最stream行的做法是使用复数forms的RESTful /api/resources/123
例如/api/resources/123
,但有一种特殊情况是我使用单数名称比复数名称更适合/expression。 这是一对一的关系的情况。 特别是如果目标项是一个值对象(在域驱动devise范例中)。
让我们假设每个资源都有一个一对一的accessLog
,它可以被模拟为一个值对象,即不是一个实体,因此没有ID。 它可以表示为/api/resources/123/accessLog
。 通常的动词(POST,PUT,DELETE,GET)可以恰当地expression意图,而且事实上这种关系确实是一对一的。
REST URI中资源名称的多元化是广泛采用的标准,其次是绝大多数公共和私有API。
除了“标准方法”之外,这也是有道理的,也是最简单的。
例如:
GET /resources
– 返回资源项目列表
POST /resources
– 创build一个或多个资源项目
PUT /resources
– 更新一个或多个资源项目
修补程序PATCH /resources
– 部分更新一个或多个资源项目
DELETE /resources
– 删除所有资源项目
对于单个资源项目:
GET /resources/:id
– 根据:id
参数返回一个特定的资源项
POST /resources/:id
– 用指定的id创build一个资源项(需要validation)
PUT /resources/:id
– 更新特定资源项目
PATCH /resources/:id
– 部分更新特定的资源项目
DELETE /resources/:id
– 删除特定资源项目
单数
方便东西可以有不规则的复数名称。 有时他们没有一个。 但是奇异的名字总是在那里。
例如CustomerAddress上的CustomerAddress
考虑这个相关的资源。
这个/order/12/orderdetail/12
比/orders/12/orderdetails/4
更具可读性和逻辑性。
数据库表
资源代表一个像数据库表一样的实体。 它应该有一个逻辑单数名称。 这是对表名的回答 。
类映射
类总是单数。 ORM工具生成与类名相同的表。 随着越来越多的工具被使用,单数名称正在成为一种标准。
阅读关于REST API开发者困境的更多信息
为什么不遵循数据库表名称的普遍趋势,在这种情况下,单数forms被普遍接受? 在那里,做到了 – 让我们重复使用。
表命名困境:单数名称与复数名称
从API消费者的angular度来看,端点应该是可预测的
理想的情况下…
-
GET /resources
应该返回一个资源列表。 -
GET /resource
应该返回一个400级的状态码。 -
GET /resources/id/{resourceId}
应该返回一个资源集合。 -
GET /resource/id/{resourceId}
应该返回一个资源对象。 -
POST /resources
应批量创build资源。 -
POST /resource
应该创build一个资源。 -
PUT /resource
应该更新资源对象。 -
PATCH /resource
应通过只发布已更改的属性来更新资源。 - 修补程序
PATCH /resources
应该批量更新仅发布已更改属性的资源。 -
DELETE /resources
应删除所有资源; 只是开玩笑:400状态码 -
DELETE /resource/id/{resourceId}
这种方法是最灵活和function丰富的,也是最耗时的开发。 所以,如果你很急(软件开发总是这样),只需要命名你的端点resource
或复数forms的resources
。 我更喜欢单数forms,因为它让你select反思和评估编程,因为并不是所有的复数forms都以's'结尾。
尽pipe如此,开发人员select最常用的做法是出于任何原因使用复数forms。 这最终是我select的路线,如果你看看像github
和twitter
这样的stream行的apis,这就是他们所做的。
一些决定标准可能是:
- 我有什么时间限制?
- 我会允许消费者做什么操作?
- 请求和结果负载是什么样的?
- 我是否希望能够在代码中使用reflection和parsingURI?
所以这取决于你。 只要你做到一致。
我的两分钱:花费时间从复数变为单数或反之亦然的方法浪费了CPU周期。 我可能是老生常谈,但在我的时间,就像事情被称为一样。 我如何查找关于人的方法? 没有经常的表述将会覆盖人和人,没有不良的副作用。
英文复数可以是非常随意的,它们不必要地阻碍了代码。 坚持一个命名约定。 计算机语言应该是关于math的清晰,而不是模仿自然语言。
按照命名规则,通常可以说“只选一个并坚持下去”,这是有道理的。
但是,在向许多人解释REST之后,将端点表示为文件系统上的path是最有performance力的方式。
它是无状态的(文件存在或不存在),层次结构,简单和熟悉 – 您已经知道如何访问静态文件,无论是本地还是通过http。
在这种情况下,语言规则只能使你达到以下几点:
目录可以包含多个文件和/或子目录,因此其名称应该是复数forms。
我喜欢这个
另一方面,虽然这是你的目录,但是如果你想要的话,你可以把它命名为“a-resource-or-multiple-resources”。 这并不重要。
重要的是,如果你把一个名为“123”的文件放在一个名为“resourceS”的目录下(导致/resourceS/123
),那么你不能期望它可以通过/resource/123
访问。
不要试图让它变得更聪明 – 根据你正在访问的资源的数量,从复数变为单数可能在某些方面是美观的,但是它不是有效的,而且它没有任何意义分层系统。
注意:从技术上讲,你可以做一些“符号链接”,这样/resources/123
也可以通过/resource/123
访问,但前者依然存在!
我更喜欢用简单和一致性的单数forms。
例如,考虑以下url:
/客户/ 1
我将客户视为客户收集,但为了简单起见,收集部分被删除。
另一个例子:
/设备/ 1
在这种情况下,设备不是正确的复数forms。 所以把它作为一个设备收集和清除收集,使其与客户案例一致。
我很惊讶地看到那么多人会跳上复数名词的潮stream。 在执行单数到复数转换时,你在照顾不规则的复数名词吗? 你喜欢痛苦吗?
见http://web2.uvcs.uvic.ca/elc/studyzone/330/grammar/irrplu.htm
不规则复数有许多types,但是这些是最常见的:
名词types形成复数例子
结束-fe将f更改为v然后添加-s刀刀生活中的妻子妻子结束-f将f改为v然后添加-s半个半狼狼群loaf loves loves with -o添加-es马铃薯土豆番茄西红柿火山火山结束随着-us变化-us到-i仙人掌仙人掌primefaces核焦点焦点以-is结尾变化-es到-es分析分析危机危机论文结束语-on变化-on-现象现象标准标准ALL种类改变元音或者换个字或者添加一个不同的结局男人男人脚小孩子小子人人牙齿小老鼠不变单数和复数是一样的羊鹿鱼(有时候)
至less在一个方面使用复数来实现所有方法更为实用:如果您使用Postman(或类似工具)开发和testing资源API,那么在从GET切换到PUT时不需要编辑URI等。
对我来说,复数操作集合 ,而单数操作集合内的项目 。
Collection允许使用GET / POST / DELETE方法
Item允许使用GET / PUT / DELETE方法
例如
DELETE on / cart_items将清除购物车中的所有物品。
DELETE on / cart_item / 123将从购物车中删除123。
这可能会觉得不重要,但有些工程师有时会忘记身份证。 如果路线总是复数,并执行DELETE,则可能会意外擦除数据。 而在单数上缺lessid将返回未find的404路由。
这两种表示都很有用。 为了方便起见,我用了很长时间,变得很难。 我在开发严格独立的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
– 在全局用户集合中创build一个新用户 -
GET /users/{id}
– 从全局用户集合中检索特定的用户 -
GET /users/{id}/manager
– 获取特定用户的pipe理员 -
GET /users/{id}/friends
– 获取用户的朋友列表 -
GET /users/{id}/friends/{friendId}
– 获取用户的特定朋友 -
LINK /users/{id}/friends
– 向该用户添加一个朋友关联 -
UNLINK /users/{id}/friends
– 从该用户移除朋友关联
注意每个级别如何映射到可以采取行动的父级。 为同一个对象使用不同的父母是违反直觉的。 在GET /resource/123
检索资源并不表示创build新资源应该在POST /resources