RESTful API - 设计子资源

32

我正在设计一个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并遵循一个原则上级资源没问题,但下级资源不行。这导致以下结果:

方法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. 是否认为Approach B符合RESTful?(我认为是)

Q2. 假设提供了文档,是否应考虑使用Approach B的缺陷?

如果您能指出其他遵循Approach B的API,则可获得额外积分。

编辑

Elad Tabak提供了有见地的观点。

我发现一些使用Approach B的API:

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

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

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


这个问题有点太宽泛了,第三和第四个问题基本上是在邀请你把关于 Web 服务设计的书籍全部倒出来。你应该只问一个问题,而不是一份问卷调查。你的核心问题似乎是 Q1 + Q2 的结合体。 - Gimby
谢谢Gimby,同意,这个问题非常广泛,我最关心的是Q1和Q2。 - Rafa
4个回答

10

4
实际上,没有任何一个URI本身就是符合RESTful的!它们返回的内容是否符合RESTful的约束条件才是关键。REST与URI设计的整洁无关,而更多地涉及将客户端与服务器API解耦,因此客户端应使用响应返回的URI来执行其当前状态的进一步操作。不知道为什么每个人都会将URI设计自动混淆为RESTful方法。此外,Fielding在他的博客文章中进一步澄清了RESTful API必须遵循的一些约束条件。链接 - Roman Vottner

9
设计方法会引发一些需要考虑的问题,当你在两种方法中选择时需要考虑以下几点:
- 存在依赖性
A中,当你删除一个公司时,也会删除所有它的子资源-部门和员工,这是非常直观的。在B中,API用户需要思考这样的操作-我需要对所有员工调用删除操作吗?还是只需删除公司就可以了?当然,这可以记录在文档中,但仍然不够直接。 A在这里具有优势,因为它很清楚-删除资源时,您将删除所有子资源。
- 操作端点
另一个问题是如何更新实体?从哪个端点开始?
如果我想删除一个员工,仅更新公司的新员工集合是否足够?还是必须删除该员工?
或者说,如果我想将员工从一家公司转移到另一家公司,在B中,理论上可以通过更新带有公司字段的员工来完成。在A中,没有这样的方式... A在如何执行操作方面非常直接 - 对实体URL进行CRUD操作。而B则使API用户停下来思考他可以在哪个URL上执行哪个操作。
但与此同时,B在更改实体的“父级关系”时更容易(在相关情况下)。
- 验证
A中,您必须验证URL参数是否匹配,因为用户可能会为员工ID提供错误的公司或部门。在B中则不存在这样的问题。

7
  1. REST对URL设计没有具体规定,只要你能想到的任何URL scheme都可以是RESTful的。但你应该问自己这是否是好的设计。第二种方法比第一种更可取。第一种方法会给客户端带来大量噪音,对所有者来说也是一个巨大的维护问题,并且它还限制了未来的灵活性。

  2. 据我所知,只要清楚地记录如何使用端点,就不会有什么显著的缺陷。例如,嵌套端点通常仅返回关联元素,而删除嵌套元素将仅删除关联,而不是元素本身。这种行为需要以某种方式记录下来。

额外加分提示:要求外部资源超出范围。


而且更进一步说,'子资源'这种东西是不存在的。 - mcintyre321

5
这可能听起来有点疯狂,但在HTTP / REST中不存在“子资源”。
在您的领域模型中,没有公司就不存在部门。
现在,在您的API中,您在/companies/{companyId}公开公司的JSON表示形式,并在/companies/{companyId}/departments/{departmentId}公开部门的JSON表示形式。
这两者都是“资源”。在HTTP / REST术语中,资源只是指URL指向的东西。因此,它是“公司的JSON表示形式”,而不是公司本身。
URL设计本身就是一个死胡同 - URL本身可以看起来像任何东西,它们是否可读并不重要。开发人员根据文档中的操作名称选择URL进行请求。尝试为URL本身添加含义会很快变得棘手,并且随着时间的推移可能会增加混淆。
最好花时间编写文档,而不是试图让人们推断出有关域行为的事情。
*或超媒体(例如https://github.com/kevinswiber/siren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接