6

在阅读 HATEOAS/超媒体约束时,我经常看到的一件事是资源应该具有某种 self/href。这样做的理由是客户端不需要知道如何为特定资源构建 URL,例如更新资源。

例如

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

     order state...
}

我喜欢这个主意。

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

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

4

4 回答 4

6

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

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

这不仅仅是一个风格点。如果服务器提供模板,则客户端开发人员必须编写代码来填充模板,这会在它们之间产生耦合。您现在无法更改服务器端 URL 结构而不违反与客户端的合同。使用 HATEOAS,您可以将 URL 与资源上允许的每个操作相关联,而客户端只关心该操作。URL 实际上是一个不透明的句柄:“选择你自己的冒险”,正如Ian Robinson所说。

HATEOAS 是关于使用超媒体(包含指向其他媒体的链接的媒体)来使客户端能够在除了收到的最后一个响应之外没有其他知识的情况下浏览应用程序。这意味着每个响应都应该为客户端提供现成的 URL,代表当前资源上的所有有效操作。

请记住,您通过网络获得的东西只是资源的表示(REST 代表 REpresentational State Transfer)。根据您当前的上下文,例如您当前的权限集和当前应用程序状态,同一资源可以有不同的表示形式。不同的表示可以合法地提供不同的下一步操作。

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

{
  "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 术语中的关系,尽管我同样可以拥有一个映射列表,其中的键分别称为and和"rel"值。"cancel""list_orders"

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

于 2015-01-25T19:47:59.470 回答
5

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

例如,之前的请求可能会返回如下内容:

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

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

于 2015-01-25T09:06:02.430 回答
5

虽然您可以提供一个URI Template ,但当我必须这样做时,我总是认为这有点异味。有时这是必要的,但如果我的 API 的主要部分最终使用这种方法,我不会将其称为 HATEOAS 或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"
                }
            ]
        }
    ]
}

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

作为通用资源,RESTful Web Services Cookbook对于回答这类问题是必不可少的。

于 2015-01-25T13:05:48.623 回答
1

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

关键是不返回值而只返回表示。

我们正在使用一个平台来实现这个。它具有独特的基于交互的语言。该平台完全符合 HATEOAS,在向其发送搜索时返回 URL。

在您的示例中,如果我们发送预订 ID。它返回所有具有此预订 id 的订单 url。然后,客户端可以通过这些 url 导航。

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

于 2015-02-26T14:12:25.500 回答