据我所知,每个单独的资源应该只有一个规范路径。那么在下面的例子中,好的URL模式是什么呢?

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

没有关联公司,一个部门就不能存在。

一个员工的存在离不开一个相关的部门。

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

公司的集合-接受新公司的POST。获取整个集合。 /companies/{companyId}单个公司。接受GET, PUT和DELETE /companies/{companyId}/departments接受新项的POST。(在公司内部创建一个部门。) /公司/ {companyId} / {departmentId} / /部门 /公司/ / {departmentId} / {companyId} /部门员工 /公司/ {companyId} /部门/ {departmentId} /员工/ {empId}

考虑到每个部分的限制,我认为这是有意义的,如果嵌套有点深的话。

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

它的资源模式将最紧密地映射到/employees(所有员工的集合)

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

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

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

对于资源属于其他资源,但应该可以单独查询的URL模式,最佳实践是什么?


当前回答

Rails为此提供了一个解决方案:浅嵌套。

我认为这很好,因为当您直接处理已知资源时,不需要使用嵌套路由,正如在这里的其他回答中所讨论的那样。

其他回答

url的外观与REST无关。任何事情都可能发生。它实际上是一个“实现细节”。就像给变量命名一样。它们所需要的是独特和耐用。

不要在这上面浪费太多时间,只要做出选择并坚持下去。例如,如果你采用层次结构,那么你对所有资源都这样做。如果你使用查询参数…等等,就像代码中的命名约定一样。

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

REST API中的URL有多好/易懂,只对作为API开发人员的你感兴趣,而不是API客户端,就像代码中的变量名一样。

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

您的媒体类型有一个links属性,它列出了可用的/相关的链接。 每个链接都由一个关系来标识(就像浏览器知道link[rel="stylesheet"]意味着它是一个样式表,或者rel=favico是一个指向favicon的链接…) 它知道这些关系的含义(“公司”指的是公司列表,“搜索”指的是在资源列表上进行搜索的模板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.companies)

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

Response:一个公司的部分列表(在项目下),资源包含相关链接,如链接得到下一对公司(body.links.next)的另一个(模板)链接搜索(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

你可以看到,如果你用链接/关系的方式构造url的路径部分对API客户端没有任何价值。如果你把你的url结构作为文档传达给你的客户端,那么你就不是在使用REST(或者至少不是按照“Richardson的成熟度模型”的第三级)。

根据django rest框架文档:

一般来说,我们建议尽可能使用平面样式的API表示,但如果适度使用嵌套URL样式也可以是合理的。

https://www.django-rest-framework.org/api-guide/relations/#example_2

Rails为此提供了一个解决方案:浅嵌套。

我认为这很好,因为当您直接处理已知资源时,不需要使用嵌套路由,正如在这里的其他回答中所讨论的那样。

你所做的是正确的。一般来说,同一个资源可以有多个uri——没有规则说你不应该这样做。

通常,您可能需要直接访问项目或作为其他内容的子集-所以您的结构对我来说是有意义的。

仅仅因为员工在部门下面是可访问的:

公司/ / {departmentid} / {companyid} /部门员工

这并不意味着他们不能在陪伴下访问:

公司/ {companyid} /员工

这将为该公司带回员工。这取决于你的消费客户需要什么——这就是你应该为之设计的。

但我希望所有url处理程序都使用相同的支持代码来满足请求,这样就不会重复代码。

我已经把我所做的事情从问题转移到更多人可能看到的答案。

我所做的是在嵌套端点上创建端点,用于修改或查询项的规范端点不在嵌套资源上。

在这个例子中(只列出改变资源的端点)

POST /companies/创建一个新公司返回一个到已创建公司的链接。 POST /companies/{companyId}/departments当一个部门被放置时,会创建一个新的部门返回到/departments/{departmentId}的链接 PUT /departments/{departmentId}用于修改部门 POST /departments/{deparmentId}/employees创建一个新的员工,返回到/employees/{employeeId}的链接。

因此,每个集合都有根级资源。但是,create是在所属对象中。