使用 HATEOAS 时找到 restful 资源?

Finding a restful resource when using HATEOAS?

在阅读有关 HATEOAS/Hypermedia 约束时,我经常看到的一件事是资源应该具有某种 self/href。 对此的论点是客户端不需要知道如何来为特定资源构造URL,例如用于更新资源。

例如

{
     //instead of "id":"123"
     "href":"/api/v1/orders/123",

     order state...
}

我喜欢这个主意。

但是这个概念如何适用于获取数据? 假设我需要获取具有特定订单 ID 的订单,客户将如何处理? 在那种情况下,我仍然需要知道如何为资源构造 URL,对吗?

客户应该如何知道在哪里以及如何寻找资源? 它必须以某种方式了解 API URL?

您使用与在任何 Web 应用程序中使用的完全相同的概念来执行此操作:通过向客户端发送构建下一个请求的配方。在 HTML 中,您将使用 HTML 表单来执行此操作。如果您使用 JSON,您的格式需要具有相同的概念,例如使用 URI 模板,甚至表单,即键值对列表,可能已经填写了一些值。

例如,之前的请求可能 return 是这样的:

{ "order": {
    "link": {
        "template": "https://your-api.com/orders/{id}",
        "method": "GET",
        "type": "application/json"
    }
  }
}

当然,您的代码仍将依赖于某些信息——在本例中,即在模板中调用 id "id" 的事实。但是通过添加这种间接寻址,它并不知道实际的 URI。另请注意,我添加了方法和类型参数作为示例;您是否选择使其可配置取决于您使用的格式。有关更详细的示例,请参阅(或使用)优秀的 collection+json format.

虽然您可以提供 URI Template, I always consider that a bit of a smell when I have to do it. Sometimes it's necessary, but if a major part of my API ends up using that approach, I wouldn't call it HATEOAS or a level 3 RESTful API.

如何对场景建模在很大程度上取决于上下文,这可能就是 Pete 询问用例的原因。

通常,您可以像为人类可用网站建模一样为 RESTful API 建模。例如,您可以拥有一个列出用户所有订单的资源,例如按顺序排序。最近的订单排在第一位:

{
    "orders": [
        {
            "date": "2015-01-25",
            "total": 1234,
            "links": [
                {
                    "rel": "order",
                    "href": "https://follow.the.link"
                }
            ]
        },
        {
            "date": "2015-01-22",
            "total": 1337,
            "links": [
                {
                    "rel": "order",
                    "href": "https://follow.this.other.link"
                }
            ]
        }
    ]
}

我的客户可以在这个列表中寻找它需要的订单,然后,一旦它确定了感兴趣的订单,就可以遵循具有“订单”关系类型的相应link。

作为通用资源,RESTful Web Services Cookbook 是回答此类问题不可或缺的资源。

设计良好的 HATEOAS API 将具有明确的入口点,其余的应用程序行为将从那里被发现。

由于问题是关于 HATEOAS 具体我会说使用 URI 模板会给客户端带来太多责任。相反,您应该为给定当前应用程序状态的资源上的每个有效操作提供明确的 URL。

这不仅仅是风格上的问题。如果服务器提供模板,则客户端开发人员必须编写代码来填充模板,从而在它们之间创建耦合。您现在不能在不破坏与客户的合同的情况下更改服务器端 URL 结构。使用 HATEOAS,您将 URL 与资源上允许的每个操作相关联,客户端只关心该操作。 URL 实际上是一个不透明句柄:"Choose your own adventure",正如 Ian Robinson 所说。

HATEOAS 是关于使用超媒体——包含指向其他媒体的链接的媒体——使客户端能够在应用程序中导航,除了它收到的最后一个响应之外没有其他信息。这意味着每个响应都应该为客户端提供现成的 URLs,代表对当前资源的所有有效操作。

请记住,在线获取的东西只是资源的表示(REST 代表代表性状态传输)。根据您当前的上下文,例如您当前的权限集和当前的应用程序状态,同一资源可能有不同的表示形式。不同的表示可以合法地提供不同的下一步行动。

使用您的示例,订单的所有者可能会看到:

{
  "id": "/api/v1/orders/123", // reference to the current resource
  "rel": {
    "cancel": {
      "url": "/api/v1/orders/cancel?order_id=123",
      "method": "POST",
      // Metadata about what the cancel operation returns...
    },
    "list_orders": {
      "url": "/api/v1/orders",
      "method": "GET",
      // Metadata about what the list_orders operation returns...
    },
    // ...
    // Other operations available to the owner
  },
  // ...
  // Order state
}

这里我定义了一个使用键作为操作名称的映射,或者 HATEOAS 术语中的 relation,尽管我同样可以有一个映射列表,其中的键名为"rel" 以及 "cancel""list_orders" 的值。

另一个角色,比如运输协调员,可能看不到 cancel 操作,因为他们没有取消订单的权限。

让我们看看你的情况。他们的方式我会看看它。您的搜索始终 return 是一个互动(即 URL)。然后客户端总是可以从那里开始导航以获取数据。

关键是不要 return 值,而只 return 表示。

我们正在为此实施使用一个平台。它具有独特的基于交互的语言。当向其发送搜索时,该平台完全符合 HATEOAS,returns URL。

在您的示例中,如果我们发送预订 ID。它 return 的所有订单 url 都具有此预订 ID。然后客户端可以浏览这些 urls。

希望对您的查询有所帮助。