2016-10-13 137 views
6

我正在设计一个RESTful API,并提出了与子资源相关的问题。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} 

正如你可以看到,有很多网址,它做同样的事情。例如: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我应该考虑,假设还提供了文档?

奖励积分,如果你指向其他API以下方法B

编辑

Elad Tabak呈现良好的见解。

我喜欢一些API使用B方法

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

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

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

+0

这样做可能有些过于宽泛,Q3和4基本上邀请转储书WebService的设计。你应该问一个问题,而不是问卷。你的核心问题似乎是Q1 + Q2的结合。 – Gimby

+0

感谢Gimby,同意,它非常广泛,我关心Q1和Q2最多。 – Rafa

回答

3

这两种方法都可以被认为是基于REST的,只要你不打破罗伊·托马斯Fielding的论文的chapter 5定义的REST约束:

我看不到常见的陷阱在这两种方法,但我宁愿B方法解决方法A:网址更短,更容易记住,而不是需要很多参数。


加分点:SpotifyFacebook的API采用这种做法。当然还有其他的API,但是这些是我想到的。

+1

其实没有一个URI本身就是RESTful!它所返回的内容是否符合RESTful约束条件。 REST与干净的URI设计无关,而更多的是将客户端与服务器API分离,因此客户端应该使用响应返回的URI来对其当前状态执行进一步的操作。不知道为什么每个人都会使用RESTful方法自动将URI设计混淆。除此之外,Fielding还澄清了RESTful API在其[博客文章](http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven)中需要进一步遵守的一些限制条件。 –

3
  1. REST只字未提URL设计。您提出的任何URL方案都是RESTful。你应该问问它是否是好的设计。是的,第二种方法比第一种更可取。首先是客户的一大堆噪音,以及业主的巨大维修问题。它也限制了未来的灵活性。

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

奖励要点:要求外部资源超出范围。

+0

此外,没有这样的东西作为'子资源' – mcintyre321

2

的设计方法提高你需要考虑之间进行选择时的几个问题之二:

存在依赖

一个,这是非常直观的,当你删除一个公司,你也删除它的所有子资源 - 部门和员工。 在B中,API用户需要考虑一下这样的行为 - 我是否需要调用所有员工的删除,还是删除公司?当然,这可以记录下来,但仍然不是直截了当的。

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

操作端点

另一个问题是它提出了 - 我该如何更新实体?从哪个终点?

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

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

A有一个优势,它非常简单,如何做一个动作 - 对实体URL的CRUD。 B使API用户停止并想知道他可以在哪个URL上执行哪个操作。

但是与此同时,B有一个优势,即更改实体的“父母”(parenting)更容易(如果相关)。

验证

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

1

所以这可能听起来有点疯狂,但在HTTP/REST中没有这样的'subresource'。

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

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

这些都是'资源'。 HTTP/REST术语中的资源只意味着URL指向的内容。所以这是'公司的json代表'而不是公司本身。

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

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

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