REST嵌套资源的最佳实践是什么?

据我所知,每个人的资源应该只有一条规范的path。 所以在下面的例子中,好的URL模式是什么?

以一个公司的rest表示为例。 在这个假设的例子中,每个公司拥有 0个或更多的部门,每个部门拥有 0个或更多的员工。

没有关联公司就不能生存。

没有相关部门的员工不能存在

现在我会发现资源模式的自然表示。

  • /companies 一系列公司 – 接受投入新公司。 获取整个集合。
  • /companies/{companyId}个别公司。 接受GET,PUT和DELETE
  • /companies/{companyId}/departments接受POST的新项目。 (在公司内部创build一个部门)
  • /companies/{companyId}/departments/{departmentId}/
  • /companies/{companyId}/departments/{departmentId}/employees
  • /companies/{companyId}/departments/{departmentId}/employees/{empId}

鉴于每个部分的限制,我觉得这是有道理的,如果有点深层嵌套。

但是,如果我想列出( GET )所有公司的所有员工,我的困难就来了。

资源模式最贴近/employees (所有员工的集合)

这是否意味着我应该拥有/employees/{empId}也是因为如果有,那么有两个URI来获得相同的资源?

或者,也许整个模式应该是扁平的,但这意味着员工是一个嵌套的顶级对象。

在基本级别/employees/?company={companyId}&department={deptId}返回与嵌套最深的模式完全相同的员工视图。

URL资源被其他资源拥有 ,但应该单独查询的最佳做法是什么?

看到我下面的答案,看看我已经做了什么。

你所做的是正确的。 一般来说,对于同一个资源可能有很多URI–没有规则说你不应该那样做。

一般而言,您可能需要直接访问项目或作为其他项目的子集,所以您的结构对我来说是有意义的。

仅仅因为员工可以在部门中访问:

company/{companyid}/department/{departmentid}/employees

这并不意味着他们也不能在公司之下访问:

company/{companyid}/employees

这将返回该公司的员工。 这取决于你的消费客户需要什么 – 这就是你应该devise的。

但是我希望所有的URL处理程序都使用相同的后台代码来满足请求,以免重复代码。

我已经尝试了两种devise策略 – 嵌套和非嵌套端点。 我发现:

  1. 如果嵌套资源具有主键并且没有其父主键,则嵌套结构需要获取它,即使系统实际上并不需要它。

  2. 嵌套端点通常需要冗余端点。 换句话说,您通常会需要附加/员工端点,以便您可以获得跨部门的员工列表。 如果你有/员工,那么/公司/部门/员工向你购买什么?

  3. 嵌套端点不会很好地发展。 例如,您现在可能不需要search员工,但以后可能需要search,如果您有嵌套结构,则只能添加另一个终结点。 使用非嵌套devise,您只需添加更多的参数,这更简单。

  4. 有时一个资源可能有多种types的父母。 导致多个端点都返回相同的资源。

  5. 冗余端点使文档更难写,也使得api更难学习。

简言之,非嵌套devise似乎允许更灵活和更简单的端点模式。

我已经把我从这个问题所做的一切转移到了更多人可能会看到的答案上。

我所做的是在嵌套端点上创build端点,在非嵌套资源上修改或修改项目的规范端点。

所以在这个例子中(只列出了更改资源的端点)

  • POST /companies/创build一个新公司返回到创build公司的链接。
  • POST /companies/{companyId}/departments放置/companies/{companyId}/departments时创build新部门返回链接到/departments/{departmentId}
  • PUT /departments/{departmentId}修改部门
  • POST /departments/{deparmentId}/employees创build一个新员工返回到/employees/{employeeId}的链接

所以每个集合都有根级别的资源。 然而, 创造是在自己的对象。

您的url外观与REST无关。 什么都可以。 它实际上是一个“实现细节”。 所以就像你如何命名你的variables。 所有他们必须是独特和耐久的。

不要浪费太多的时间在这个,只是做出select,坚持下去/保持一致。 例如,如果你去与层次结构,那么你做它的所有资源。 如果你去查询参数…等就像你的代码中的命名约定。

为什么这样 ? 就我所知,一个“RESTful”API是可浏览的(你知道……“超媒体作为应用程序状态的引擎”),因此API客户端并不关心你的URL是什么样的,只要它们是有效(没有search引擎优化,没有人需要阅读这些“友好的url”,除了可能是为了debugging…)

在REST API中,URL是多么的好/可以理解,只有你作为API开发者而不是API客户端才有意义,你的代码中的variables的名字也是如此。

最重要的是你的API客户端知道如何解释你的媒体types。 例如它知道:

  • 您的媒体types具有列出可用/相关链接的链接属性。
  • 每个链接都由一个关系来标识(就像浏览器知道链接[rel =“stylesheet”]意味着它的样式表或rel = favico是指向favicon的链接…)
  • 并且知道这些关系是什么意思(“公司”是指公司名单,“search”是指在资源列表上search的模板化url,“部门”是指当前资源的部门)

下面是一个HTTP交换的示例(由于它更容易编写,所以实体在yaml中):

请求

 GET / HTTP/1.1 Host: api.acme.io Accept: text/yaml, text/acme-mediatype+yaml 

回应:主要资源链接列表(公司,人员,任何…)

 HTTP/1.1 200 OK Date: Tue, 05 Apr 2016 15:04:00 GMT Last-Modified: Tue, 05 Apr 2016 00:00:00 GMT Content-Type: text/acme-mediatype+yaml # body: this is your API's entrypoint (like a homepage) links: # could be some random path https://api.acme.local/modskmklmkdsml # the only thing the API client cares about is the key (or rel) "companies" companies: https://api.acme.local/companies people: https://api.acme.local/people 

请求:链接到公司(使用之前的回复body.links.com公司)

 GET /companies HTTP/1.1 Host: api.acme.local Accept: text/yaml, text/acme-mediatype+yaml 

回应:部分公司名单(项目下),资源包含相关链接,如链接获取下一个公司(body.links.next)和其他(模板)链接到search(body.links.search)

 HTTP/1.1 200 OK Date: Tue, 05 Apr 2016 15:06:00 GMT Last-Modified: Tue, 05 Apr 2016 00:00:00 GMT Content-Type: text/acme-mediatype+yaml # body: representation of a list of companies links: # link to the next page next: https://api.acme.local/companies?page=2 # templated link for search search: https://api.acme.local/companies?query={query} # you could provide available actions related to this resource actions: add: href: https://api.acme.local/companies method: POST items: - name: company1 links: self: https://api.acme.local/companies/8er13eo # and here is the link to departments # again the client only cares about the key department department: https://api.acme.local/companies/8er13eo/departments - name: company2 links: self: https://api.acme.local/companies/9r13d4l # or could be in some other location ! department: https://api2.acme.local/departments?company=8er13eo 

所以当你看到如果你去链接/关系的方式如何构build你的URL的path部分没有任何价值的API客户端。 而且,如果您将URL的结构作为文档传达给客户,那么您的REST(或者至less不是根据“ Richardson的成熟度模型 ”的级别3)

我不同意这种path

 GET /companies/{companyId}/departments 

如果你想获得部门,我认为最好使用/部门资源

 GET /departments?companyId=123 

我想你有一个companies表和一个departments表,然后是类,用你使用的编程语言来映射它们。 我还假设部门可以连接到除公司之外的其他实体,因此部门资源非常简单,将资源映射到表格也很方便,而且由于您可以重复使用,因此您不需要尽可能多的端点

 GET /departments?companyId=123 

对于任何types的search,例如

 GET /departments?name=xxx GET /departments?companyId=123&name=xxx etc. 

如果你想创build一个部门,

 POST /departments 

应该使用资源,并且请求主体应该包含公司ID(如果部门只能链接到一个公司)。