据我所知,每个单独的资源应该只有一个规范路径。那么在下面的例子中,好的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模式,最佳实践是什么?


当前回答

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

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

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

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

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

其他回答

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的成熟度模型”的第三级)。

我尝试了两种设计策略——嵌套端点和非嵌套端点。我发现:

if the nested resource has a primary key and you don't have its parent primary key, the nested structure requires you to get it, even though the system doesn't actually require it. nested endpoints typically require redundant endpoints. In other words, you will more often than not, need the additional /employees endpoint so you can get a list of employees across departments. If you have /employees, what exactly does /companies/departments/employees buy you? nesting endpoints don't evolve as nicely. E.g. you might not need to search for employees now but you might later and if you have a nested structure, you have no choice but to add another endpoint. With a non-nested design, you just add more parameters, which is simpler. sometimes a resource could have multiple types of parents. Resulting in multiple endpoints all returning the same resource. redundant endpoints makes the docs harder to write and also makes the api harder to learn.

简而言之,非嵌套设计似乎允许更灵活、更简单的端点模式。

根据django rest框架文档:

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

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

我读了上面所有的答案,但似乎他们没有共同的策略。我从Microsoft Documents找到了一篇关于Design API最佳实践的好文章。我认为你应该推荐。

In more complex systems, it can be tempting to provide URIs that enable a client to navigate through several levels of relationships, such as /customers/1/orders/99/products. However, this level of complexity can be difficult to maintain and is inflexible if the relationships between resources change in the future. Instead, try to keep URIs relatively simple. Once an application has a reference to a resource, it should be possible to use this reference to find items related to that resource. The preceding query can be replaced with the URI /customers/1/orders to find all the orders for customer 1, and then /orders/99/products to find the products in this order.

.

提示 避免要求资源uri比 收集/项目/集合。

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

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