RESTful API - 设计子资源

Posted

技术标签:

【中文标题】RESTful API - 设计子资源【英文标题】:RESTful API - Designing sub-resources 【发布时间】:2016-10-13 14:25:06 【问题描述】:

我正在设计一个 RESTful API,但遇到了与子资源相关的问题。

我看到其他 API 使用完整 URL 来操作子资源。以Company has DepartmentsDepartment has Employees为例。

一开始我想实现所有可能的 URL。结果如下:

方法 A

01. ### COMPANY URLS ###
02. DELETE /companies/companyId
03. GET    /companies/companyId
04. POST   /companies
05. PUT    /companies/companyId
06. 
07. ### DEPARTMENT URLS ###
08. DELETE /companies/companyId/departments/departmentId
09. GET    /companies/companyId/departments/departmentId
10. POST   /companies/companyId/departments
11. PUT    /companies/companyId/departments/departmentId
12. DELETE /departments/departmentId
13. GET    /departments/departmentId
14. PUT    /departments/departmentId
15. 
16. ### EMPLOYEE URLS ###
17. DELETE /companies/companyId/departments/departmentId/employees/employeeId
18. GET    /companies/companyId/departments/departmentId/employees/employeeId
19. POST   /companies/companyId/departments/departmentId/employees
20. PUT    /companies/companyId/departments/departmentId/employees/employeeId
21. DELETE /departments/departmentId/employees/employeeId
22. GET    /departments/departmentId/employees/employeeId
23. POST   /departments/departmentId/employees
24. PUT    /departments/departmentId/employees/employeeId
25. DELETE /employees/employeeId
26. GET    /employees/employeeId
27. PUT    /employees/employeeId

如您所见,有许多 URL 可以做同样的事情。示例:08 与 12 重复; 09 与 13 重复; 17 与 21 和 25 重复...

我想删除重复但保持一致性。因此,重新设计 API 时要牢记原则sup-resources are fine but sub-sub-resources are not。结果如下:

方法 B

01. ### COMPANY URLS ###
02. DELETE /companies/companyId
03. GET    /companies/companyId
04. POST   /companies
05. PUT    /companies/companyId
06. 
07. ### DEPARTMENT URLS ###
08. DELETE /departments/departmentId
09. GET    /departments/departmentId
10. GET    /companies/companyId/departments
11. POST   /companies/companyId/departments
12. PUT    /departments/departmentId
13. 
14. ### EMPLOYEE URLS ###
15. DELETE /employees/employeeId
16. GET    /employees/employeeId
17. GET    /departments/departmentId/employees
18. POST   /departments/departmentId/employees
19. PUT    /employees/employeeId

我的问题

第一季度。 方法 B 是否被视为 RESTful? (我假设是的)

第二季度。假设还提供了文档,我应该考虑方法 B 的陷阱吗?

如果您按照方法 B 指向其他 API,则会获得奖励积分。

编辑

Elad Tabak 提出了很好的见解。

我喜欢使用方法 B 的一些 API:

https://developers.google.com/youtube/v3/docs/

https://developer.github.com/guides/getting-started/

https://dev.twitter.com/rest/public

【问题讨论】:

这有点太宽泛了,Q3 和 4 基本上是邀请转储 Web 服务设计的书。你应该问一个问题,而不是问卷。您的核心问题似乎是 Q1 + Q2 的组合。 感谢 Gimby,同意,它非常广泛,我最关心 Q1 和 Q2。 【参考方案1】:

只要您不违反 Roy Thomas Fielding 的论文 chapter 5 中定义的 REST 约束,这两种方法都可以被视为 RESTful:

Client-server Stateless Cache Uniform interface Layered system Code-on-demand

我看不出这两种方法的主要缺陷,但我更喜欢 方法 B 而不是 方法 A:URL 更短、更容易记住且参数不多是必需的。


奖励积分:Spotify 和 Facebook API 遵循这种方法。当然还有其他 API,但这些是我想到的。

【讨论】:

实际上没有一个 URI 本身是 RESTful 的!他们返回的内容是否满足 RESTful 约束。 REST 与干净的 URI 设计无关,而更多地与将客户端与服务器 API 分离,因此客户端应使用响应返回的 URI 来对其当前状态执行进一步的操作。不知道为什么每个人都将 URI 设计与 RESTful 方法混淆。除此之外,Fielding 在他的blog post 中阐明了 RESTful API 必须进一步遵循的一些限制。【参考方案2】:

设计方法提出了在两者之间进行选择时需要考虑的几个问题:

存在依赖

A中,很直观,当你删除一个公司时,你也删除了它的所有子资源——部门和员工。 在 B 中,API 用户需要考虑一下这样的操作 - 我需要对所有员工调用 delete,还是删除公司就足够了?当然,这可以记录在案,但仍然不是直截了当的。

A这里有一个优势,因为很清楚——删除一个资源时,你删除了它所有的子资源。

操作端点

它提出的另一个问题 - 我如何更新实体?从哪个端点?

如果我想删除一个员工,用一组新员工更新公司是否足够?还是我必须删除该员工?

或者说我想将员工从一家公司换到另一家公司。在B中,理论上我可以用公司字段更新员工,并完成它。在A中没有这样的方式......

A 有一个优势,即如何执行操作非常简单——实体 URL 上的 CRUD。 B 让 API 用户停下来想知道他可以对哪个 URL 执行哪些操作。

但与此同时,B 的优势在于更容易更改实体的“父级”(在相关的情况下)。

验证

A 中,您必须验证 URL 参数是否匹配,因为用户可以提供带有错误公司或部门的员工 ID。 B没有这样的问题。

【讨论】:

【参考方案3】:

    REST 没有提及 URL 设计。您提出的任何 URL 方案都是 RESTful。你应该问它是否是好的设计。是的,第二种方法比第一种方法更可取。首先是给客户带来大量噪音,给业主带来巨大的维护问题。这也限制了未来的灵活性。

    只要您清楚地记录如何使用端点,我就知​​道没有重大缺陷。例如,嵌套端点通常只返回关联元素,而删除嵌套元素只会删除关联,不会删除元素本身。这种行为需要以一种或另一种方式记录下来。

加分:请求外部资源超出范围。

【讨论】:

更进一步,没有“子资源”这样的东西【参考方案4】:

所以这听起来可能有点疯狂,但在 HTTP/REST 中没有“子资源”之类的东西。

在您的领域模型中,没有公司就无法存在部门。

现在,在您的 API 中,您可以在 /companies/companyId 公开一个公司的 json 表示,在 /companies/companyId/departments/departmentId 公开一个部门的 json 表示。

这些都是“资源”。在 HTTP/REST 术语中,资源仅表示 URL 指向的事物。所以它是“公司的 json 表示”而不是公司本身。

URL 设计本身就是一条死胡同——URL 本身可以看起来像任何东西,不管它们是否可读。开发人员提出请求,根据文档中的操作名称选择 URL*。尝试为 URL 本身添加含义很快就会变得棘手,并且随着时间的推移实际上会增加混乱。

最好花时间在文档上,而不是试图让人们推断有关领域行为的事情。

*或超媒体(例如https://github.com/kevinswiber/siren)

【讨论】:

以上是关于RESTful API - 设计子资源的主要内容,如果未能解决你的问题,请参考以下文章

带有批处理请求的 RESTful API

在过滤的 API Restful 资源中添加子类别

在restful api 设计中,如果要获得一个资源,一定要用GET方法么

Spring Boot构建RESTful API

多对多资源映射restful api设计

RESTful API 设计 - 使用资源 URI 与 ID