据我所知,每个单独的资源应该只有一个规范路径。那么在下面的示例中,好的 URL 模式是什么?
以公司的休息表示为例。在这个假设示例中,每个公司拥有 0 个或多个部门,每个部门拥有 0 个或多个员工。
没有关联公司,部门就无法存在。
没有相关部门,员工就无法存在。
现在我会发现资源模式的自然表示。
/companies 公司集合 - 接受新公司的 POST。获取整个系列。
/companies/{companyId} 单个公司。接受 GET、PUT 和 DELETE
/companies/{companyId}/departments 接受新项目的 POST。 (在公司内创建一个部门。)
/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 模式的最佳实践是什么?
我尝试了两种设计策略 - 嵌套和非嵌套端点。我发现:
如果嵌套资源具有主键而您没有其父主键,则嵌套结构需要您获取它,即使系统实际上并不需要它。嵌套端点通常需要冗余端点。换句话说,您通常需要额外的 /employees 端点,以便您可以获取跨部门的员工列表。如果你有 /employees,那么 /companies/departments/employees 到底给你买了什么?嵌套端点没有发展得那么好。例如,您现在可能不需要搜索员工,但以后可能需要,如果您有嵌套结构,则别无选择,只能添加另一个端点。使用非嵌套设计,您只需添加更多参数,这更简单。有时一个资源可能有多种类型的父级。导致多个端点都返回相同的资源。冗余端点使文档更难编写,也使 api 更难学习。
简而言之,非嵌套设计似乎允许更灵活和更简单的端点模式。
你所做的是正确的。一般来说,同一个资源可以有多个 URI - 没有规则说你不应该这样做。
通常,您可能需要直接访问项目或作为其他项目的子集 - 所以您的结构对我来说很有意义。
仅仅因为员工可以在部门下访问:
company/{companyid}/department/{departmentid}/employees
并不意味着它们也不能在公司下访问:
company/{companyid}/employees
这将返回该公司的员工。这取决于您的消费客户需要什么——这就是您应该设计的目标。
但我希望所有 URL 处理程序都使用相同的支持代码来满足请求,这样您就不会重复代码。
/company/3/department/2/employees/1
)。如果 api 提供了获取每个资源的方法,那么可以在客户端库中或作为重用代码的一次性端点来完成每个请求。
/company/*
应该只返回公司资源而根本不改变资源类型。 REST 没有指定这些 - 它通常指定得很差 - 只是个人喜好。
我已经将我所做的事情从问题转移到了更多人可能会看到的答案。
我所做的是在嵌套端点处创建端点,用于修改或查询项目的规范端点不在嵌套资源处。
所以在这个例子中(只列出改变资源的端点)
POST /companies/ 创建一个新公司返回一个链接到创建的公司。
POST /companies/{companyId}/departments 当一个部门被创建时,新部门返回一个链接到 /departments/{departmentId}
PUT /departments/{departmentId} 修改一个部门
POST /departments/{deparmentId}/employees 创建新员工 返回指向 /employees/{employeeId} 的链接
因此,每个集合都有根级资源。然而,创建是在拥有的对象中。
POST
表示 PUT
,否则。
我已经阅读了上述所有答案,但似乎他们没有共同的策略。我找到了一篇关于 best practices in Design API from Microsoft Documents 的好文章。我觉得你应该参考。
在更复杂的系统中,提供 URI 以使客户端能够在多个关系级别中导航可能很诱人,例如 /customers/1/orders/99/products。然而,如果资源之间的关系在未来发生变化,这种复杂程度可能难以维持并且不灵活。相反,尽量保持 URI 相对简单。一旦应用程序引用了资源,就应该可以使用该引用来查找与该资源相关的项目。可以将上述查询替换为 URI /customers/1/orders 以查找客户 1 的所有订单,然后 /orders/99/products 以查找此订单中的产品。
.
提示 避免要求比 collection/item/collection 更复杂的资源 URI。
我不同意这种路径
GET /companies/{companyId}/departments
如果你想获得部门,我认为最好使用 /departments 资源
GET /departments?companyId=123
我想您有一个 companies
表和一个 departments
表,然后用您使用的编程语言进行类映射。我还假设部门可以附加到公司以外的其他实体,因此 /departments 资源很简单,将资源映射到表很方便,而且您不需要那么多端点,因为您可以重用
GET /departments?companyId=123
例如,对于任何类型的搜索
GET /departments?name=xxx
GET /departments?companyId=123&name=xxx
etc.
如果你想创建一个部门,
POST /departments
应使用资源,请求正文应包含公司 ID(如果部门只能链接到一家公司)。
GET /companies/{companyId}?include=departments
,因为这允许在单个 HTTP 请求中获取公司及其部门。 Fractal 在这方面做得非常好。
/departments
端点限制为只能由管理员访问,并让每个公司只能通过 /companies/{companyId}/departments 访问自己的部门
您的 URL 的外观与 REST 无关。什么都行。它实际上是一个“实施细节”。所以就像你如何命名你的变量一样。它们所要做的就是独一无二且经久耐用。
不要在这方面浪费太多时间,只需做出选择并坚持下去/保持一致。例如,如果您使用层次结构,那么您对所有资源都这样做。如果您使用查询参数...等,就像代码中的命名约定一样。
为什么这样 ?据我所知,“RESTful”API 是可浏览的(您知道……“超媒体作为应用程序状态的引擎”),因此 API 客户端并不关心您的 URL 是什么样的,只要它们是有效(没有搜索引擎优化,没有人需要阅读那些“友好的网址”,除了可能用于调试......)
REST API 中的 URL 有多好/可理解只对作为 API 开发人员的您感兴趣,而不是 API 客户端,就像代码中变量的名称一样。
最重要的是您的 API 客户端知道如何解释您的媒体类型。例如它知道:
您的媒体类型具有列出可用/相关链接的链接属性。
每个链接都由关系标识(就像浏览器知道 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
响应:公司的部分列表(在项目下),资源包含相关链接,例如获取下几个公司的链接(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's maturity model”的第 3 级)
Rails 对此提供了解决方案:shallow nesting。
我认为这很好,因为当您直接处理已知资源时,无需使用嵌套路由,正如此处其他答案中所讨论的那样。
根据 django rest 框架文档:
通常,我们建议尽可能使用平面样式的 API 表示,但嵌套 URL 样式在适度使用时也是合理的。
https://www.django-rest-framework.org/api-guide/relations/#example_2
不定期副业成功案例分享