REST API 子资源,要返回的数据?

Posted

技术标签:

【中文标题】REST API 子资源,要返回的数据?【英文标题】:REST API sub resources, data to return? 【发布时间】:2016-06-30 08:15:18 【问题描述】:

如果我们有 customersorders,我正在寻找正确的 RESTful 方式来获取这些数据:


  "customer": 
    "id": 123,
    "name": "Jim Bloggs"
    "orders": [
      
        "id": 123,
        "item": "Union Jack Keyring",
        "qty": 1
      , 
        "id": 987,
        "item": "London Eye Ticket",
        "qty": 5
      
    ]
  

    GET /customers/123/orders GET /customers/123?inc-orders=1

我是否更正了 URL 的最后一部分/文件夹(不包括查询字符串参数)应该是返回的资源..?

如果是这样,数字 1 应该只返回订单数据而不包括客户数据。而数字 2 直接指向客户 123 并使用查询字符串参数来影响/过滤返回的客户数据,在这种情况下包括订单数据。

这两个调用中哪一个是上述 JSON 的正确 RESTful 调用..? ...或者是否有秘密数字 3 选项..?

【问题讨论】:

【参考方案1】:

你有 3 个我认为可以被认为是 RESTful 的选项。

1) GET /customers/12 但始终包括订单。您是否有客户不想使用订单的情况?或者订单数组可以变得非常大吗?如果是这样,您可能需要其他选择。

2) GET /customers/123,其中可能包含指向他们的订单的链接,如下所示:


  "customer": 
    "id": 123,
    "name": "Jim Bloggs"
    "orders": 
       "href": "<link to you orders go here>"
    
  

这样,您的客户必须发出 2 次请求才能获得客户及其订单。不过,这种方式的好处是您可以轻松实现干净的分页和订单过滤。

3) GET /customers/123?fields=orders 这类似于您的第二种方法。这将允许客户端更有效地使用您的 API,但除非您确实需要限制从服务器返回的字段,否则我不会走这条路。否则,它会给你必须维护的 API 增加不必要的复杂性。

【讨论】:

【参考方案2】:

Resource(由 complete URL 标识)是同一个客户。只有 Representation 不同,有或没有嵌入订单。

使用内容协商为同一资源获取不同的表示。

请求

GET GET /customers/123/
Accept: application/vnd.acme.customer.short+json

回应

200 OK
Content-Type: application/vnd.acm.customer.short+json


  "customer": 
    "id": 123,
    "name": "Jim Bloggs"
  

请求

GET GET /customers/123/
Accept: application/vnd.acme.customer.full+json

回应

200 OK
Content-Type: application/vnd.acme.customer.full+json


  "customer": 
    "id": 123,
    "name": "Jim Bloggs"
    "orders": [
      
        "id": 123,
        "item": "Union Jack Keyring",
        "qty": 1
      , 
        "id": 987,
        "item": "London Eye Ticket",
        "qty": 5
      
    ]
  

【讨论】:

【参考方案3】:

您发布的 JSON 看起来会是什么结果

GET /customers/123

如果 Customer 资源包含一个 Orders 集合作为属性;或者,您可以嵌入它们,或提供指向它们的链接。

后者会导致这样的结果:

GET /customers/123/orders

这会返回类似的东西


    "orders": [
      
        "id": 123,
        "item": "Union Jack Keyring",
        "qty": 1
      , 
      
        "id": 987,
        "item": "London Eye Ticket",
        "qty": 5
      
    ]

【讨论】:

所以,数字1应该只返回orders,数字2应该返回customer数据,有或没有orders数据(查询字符串可以用来提供开关) ..?这正是您所期望的,还是官方 REST 的东西..?【参考方案4】:

我正在寻找正确的 RESTful 方式来获取这些数据

只需对指向产生此数据的资源的 URI 执行 HTTP GET 请求!

TL;DR

REST 不关心 URI 设计,而是关心它的约束! 客户端通过响应中包含的动态标识的超链接通过服务器返回的可能操作执行状态转换。 客户端和服务器可以协商首选的超媒体类型 不要嵌入整个(子)资源,而是考虑只返回指向该资源的链接,以便客户可以在感兴趣时查找它

首先,只要 URI 是唯一的,REST 并不真正关心 URI 设计。当然,一个简单的 URI 设计对人类来说更容易理解,尽管如果与 html 相比,实际链接可以隐藏在更有意义的文本后面,因此对人类来说也不是那么重要,只要他们能够找到链接和可以针对它执行操作。接下来,为什么您认为您的“响应”或 API 是 RESTful 的?要调用 API RESTful,API 应该尊重一对constraints。在这些限制中,有一个非常流行的流行词:超文本作为应用程序状态引擎 (HATEOAS)。

REST 是我们日常使用的 Web 的广义概念。 Web 会话的一个非常常见的任务是客户端请求某些内容,服务器发送包含大量链接和其他资源的 HTML 文档,客户端可以使用这些资源来请求更多页面或流式传输视频(或其他任何内容)。客户端上的用户操作可以使用返回的信息继续进行,请求新页面,向服务器发送信息等。RESTful 应用程序也是如此。这是 REST 简单地定义为 HATEOAS。如果您现在查看您的“响应”并仔细检查 HATEOAS 约束,您可能会发现您的响应不包含任何开始的链接。因此,客户需要领域知识才能进一步进行。

JSON 本身并不是最好的超媒体类型 IMO,因为它只定义了数据的整体语法,但不携带任何语义,类似于纯 XML,虽然可能有一些 DTD 或客户端可以用来验证文档的模式并检查其他地方是否有进一步的语义规则。有几种基于 JSON 的超媒体类型可能更适合 f.e. application/hal+json(可以在此blog post 中找到基于 JSON 的超媒体类型的良好比较)。您当然有权定义自己的超媒体类型,尽管某些客户端可能无法立即理解它。

如果你服用 f.e.看看HAL,您会发现它定义了一个_embedded 元素,您可以在其中放入某些子资源。在您的情况下,这似乎是理想的。根据您的设计,orders 也可以单独作为资源,因此可以通过GET /orders/orderId 本身访问。除了嵌入整个子资源之外,您还可以只包含指向该(子)资源的链接,以便客户可以在感兴趣的情况下查找数据。

如果在某些情况下您只想返回客户数据,而在其他情况下您还想包含订单数据,您可以 f.e.为两者定义不同的超媒体类型(基于 HAL f.e.),一种仅返回客户数据,而另一种还包括 oder 数据。这些类型可以这样命名:application/vnd.yourcompanyname.version.customers.hal+jsonapplication/vnd.yourcompanyname.version.customer_orders.hal+json。虽然与向请求中添加简单的查询参数相比,这肯定是一种开发开销,但语义更清晰,并且文档开销是关于超媒体类型(或表示)而不是 HTTP 操作。

您当然也可以定义某种视图结构,其中一个视图只返回客户数据,而另一个视图返回客户数据,包括类似于我在一个不太不相关的主题上给出的response 的订单。

【讨论】:

以上是关于REST API 子资源,要返回的数据?的主要内容,如果未能解决你的问题,请参考以下文章

REST API 设计 - 最佳实践:链接现有子资源 [关闭]

如果通过rest api发布资源,是不是必须存储资源?

REST API注意事项

Django RESTframework(一):接口与规范

细说REST API安全之访问授权

列表的各个资源上的 REST API 和 ETag