RESTful API - 设计子资源

RESTful API - Designing sub-resources

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

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

为例

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

方法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 的重复...

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

方法 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}

我的问题

Q1。 方法 B 是否被认为是 RESTful? (我假设是)

Q2。假设还提供了文档,是否存在陷阱方法 B 我应该考虑?

如果您在方法 B 之后指向其他 API,则可获得加分。

编辑

Elad Tabak 提出了很好的见解。

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

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

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

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

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

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

奖励积分:请求外部资源超出范围。

两种方法都可以考虑 RESTful,前提是您不破坏 Roy Thomas Fielding 论文 chapter 5 中定义的 REST 约束:

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


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

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

存在依赖性

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

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

操作端点

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

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

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

A 有一个优势,它可以非常直接地执行操作 - 实体 URL 上的 CRUD。 B 让 API 用户停下来想知道他可以对 URL.

执行哪个操作

但与此同时,B 有一个优势,即更改实体的 "parenting" 更容易(在相关的情况下)。

验证

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

所以这听起来有点疯狂,但是 HTTP/REST 中没有 'subresource' 这样的东西。

在您的领域模型中,部门不能没有公司。

现在,在您的 API 中,您公开了 /companies/{companyId} 公司的 json 代表和 /companies/{companyId}/departments/{departmentId} 部门的 json 代表。

它们都是'resources'。在 HTTP/REST 术语中,资源仅表示 URL 指向的事物。所以这是 'json representation of a company' 而不是公司本身。

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

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

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