可以将 REST API OPTIONS 用作 HATEOAS 唯一请求吗?

Could REST API OPTIONS be used as the HATEOAS only request?

据我了解,REST 必须使用 HATEOAS 约束才能正确实施。我对 HATEOAS 的理解是,基本上每个资源都应该共享有关其具有哪些通信选项以及消费者如何使用这些选项来实现最终目标的信息。

我的问题是 HTTP OPTIONS 方法是否可以用作导航 REST API 的方式。基本上,OPTIONS 请求的响应将包括对资源采取的可能操作,这使得在不知道端点的情况下使用 API 成为可能。

例如 API

的初始请求
HTTP OPTIONS /api

能否return所有可供消费的资源及其关系。就像一棵大树一样消耗 API 及其所提供的一切。这个想法也没有忽视在其他响应上实施 HATEOAS,但是 OPTIONS 请求将允许导航而无需 returning 消费者可能实际上不想使用的数据。

这是一个非常糟糕的主意吗?或者它是普遍实施的东西。我目前正在尝试实现 REST API,但如果没有实际请求您可能不一定需要的数据就无法导航 API,我很难理解 HATEOAS 的好处消耗某些端点时。我假设 HATEOAS 旨在让客户通过他们的关系消耗资源,而不是实际硬编码终点?

希望对这个想法提出一些意见。


TL;DR

HTTP OPTIONS 请求能否作为一种导航 REST API 的方式,通过 return 为请求的资源提供哪些通信选项而不实际 return 资源?

Brgds

根据RFC 7231

The OPTIONS HTTP method requests information about the communication options available for the target resource, at either the origin server or an intervening intermediary. This method allows a client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action.

...

A server generating a successful response to OPTIONS SHOULD send any header fields that might indicate optional features implemented by the server and applicable to the target resource (e.g., Allow), including potential extensions not defined by this specification. The response payload, if any, might also describe the communication options in a machine or human-readable representation. A standard format for such a representation is not defined by this specification, but might be defined by future extensions to HTTP. A server MUST generate a Content-Length field with a value of "0" if no payload body is to be sent in the response.

因此,基本上,对 OPTIONS 请求的响应将告诉您的客户端可以对特定资源执行哪些 HTTP 操作。还可以将整个服务器定位为使用 * 而不是特定的资源 URI。

对 OPTIONS 请求的响应可能如下所示:

HTTP/1.1 204 No Content
Allow: OPTIONS, GET, HEAD, POST
Cache-Control: max-age=604800
Date: Thu, 13 Oct 2016 11:45:00 GMT
Expires: Thu, 20 Oct 2016 11:45:00 GMT
Server: EOS (lax004/2813)
x-ec-custom-error: 1

表示某个资源支持响应的 Allow header 中提到的操作。通过 Cache-Control header 客户端知道它在默认情况下可以缓存安全请求(GET 和 HEAD)的响应长达 7 天(值以秒为单位)。 x-ec-custom-error header 指定特定于特定软件的 non-standard header,在该特定情况下特定于 ECS 服务器 。根据这个 Q & A 含义没有公开记录,因此特定于应用程序。

关于从请求 OPTIONS 操作的给定资源中 return 生成可遍历资源树,从技术上讲这是可能的,但是,某些系统可能会生成几乎 never-ending 列表的 URI。因此,这样的设计对于更大的系统来说是有问题的。

My understanding of HATEOAS is that basically every resource should share information about what communication options it has and how the consumer can use those options to achieve their end goal.

超文本作为应用程序状态引擎 (HATEOAS) 基本上只是成功使用 Web 上数十年来使用的交互模型并为应用程序提供相同功能的要求。这使应用程序能够像我们人类一样在网上冲浪

很好,但它是如何工作的?

在 Web 上,我们一直使用 links 和 Web forms。通过 Web 表单,服务器能够基本上告知客户端特定资源支持或期望的属性。但这还不是全部!相同的表单还告诉您的客户端将请求发送到哪里(目标 URI)、要使用的 HTTP 方法以及通常隐式给出的在将请求发送到服务器时有效负载需要序列化的媒体类型。从本质上讲,这使得 out-of-band API 文档变得不必要,因为服务器已经提供了客户端发出有效请求所需的所有信息。

在典型的网站上,您可能有 table 个条目,这些条目提供了添加新条目、更新或删除现有条目的选项。通常这样的 link 隐藏在花哨的图像后面,即用于删除条目的垃圾箱和用于编辑现有条目的铅笔等,其中 image 表示可供性。 affordance of certain elements make it clear what you should do with it or what's the purpose of that element. A button on a page wants to be pushed while a slider widget wants to be changed while a text field waits for user input. As applications aren't that eager to work on images a further concept is used instead. Link relation names exactly serve this purpose. I.e. if you have a pageable collection consisting of multiple page à 25 entries i.e. you might be familiar with a widget containing arrows to page through that collection. A link here should usually be annotated with link relation names such as self, next, prev, first or last. The purpose of such links is quite clear, some others like prefetch, that indicates that a resource can be loaded in the background early as it is very likely that the next action may request it, might be less intuitive at first. Such link relation names should be standardized or at least follow the Web Linking extension mechanism.

通过 link-relation names 的帮助,客户端知道寻找用 next 注释的 URI,即如果服务器决定更改其 URI 方案,它仍然可以工作,因为它对待 URI 相当不透明.

当然,客户端和服务器都需要支持相同的媒体类型,而且能够表示此类功能。 Plain application/json 即无法提供此类支持。 HAL JSON or JSON Hyper-Schema at least add support for links and link relation names to JSON based documents, while hal-forms, halo+json (halform) and ion 可用于教导客户如何创建请求。这里的问题不应该是支持哪种媒体类型,而是你想支持多少种不同的媒体类型,因为你的 API 能够处理的媒体类型越多,就越有可能与不在你的控制。

这些概念基本上允许您使用服务器响应中提供的控件来“推动您的工作流程”向前发展。从本质上讲,作为 API 设计师,您应该做的是设计客户与您的 API 的交互,以便它遵循特定的,正如 Jim Webber 所说的那样,domain application protocol or state machine Asbjørn Ulsberg 说基本上是指导客户完成任务,即从您的商店订购 API.

因此,简而言之,HATEOAS 通过使用命名的 link 关系和 form-like 媒体类型表示,可让您仅对从一个服务器,而不必将来自某些参考文档页面(Swagger、OpenAPI 等)的外部知识烘焙到您的应用程序中。


But how does HATEOAS benefit the consumer in practice then?

首先,除了当前的媒体类型规范外,它不需要参考任何外部文档,尽管通常对 well-known 媒体类型的支持已经支持到流行的框架中,或者至少允许通过添加支持插件或其他库。一旦理解并支持媒体类型,就可以与也支持相同媒体类型的所有服务进行交互,而不管它们的域如何。这允许重用相同的客户端实现来开箱即用地与服务 A 和服务 B 进行交互。在 RPC-like 系统中,您需要首先集成服务 A 的 API,如果您想与服务 B 交互,您还需要单独集成这些 API。这些 API 很可能是不兼容的,因此不允许重复使用相同的 class。

Without knowing the URL for a resource, is the idea that the consumer can discover it by browsing the API, but they will still have a hard dependency on the actual URL? Or is HATEOAS purpose to leverage actions on a certain resource, i.e. the consumer knows the users end-point but he does not need to know the end-points for actions to take on the users resource cause those are provided by the API?

客户端通常不关心URI本身,它关心URI可能提供的内容。将此与您的典型浏览行为进行比较。您更喜欢总结 link 内容的有意义的文本,以便您可以决定是否请求该资源,还是更喜欢解析和剖析 URI 以了解它可能做什么?不过,在后一种情况下,缩小或混淆 URI 对您没有好处。

另一个危险来自客户端赋予意义的 URI 和资源。草率的开发人员会解释这样的 URIs/resources 并实施一个小 hack 来与该服务交互,假设 URI/resource 将保持静态。 IE。将 URI /api/users/1 考虑为 return 一些用户相关数据并根据响应格式写入一个微小的 Java class 并期望接收一个字段用户名和一个用于电子邮件,即如果服务器现在决定添加其他数据或重命名其字段,客户端突然将无法与该服务进一步交互。请放心,在实践中,尤其是在 EDI 领域,您将不得不与不打算与 Web 交互的客户端进行交互,或者在程序员实现他们自己的 JSON 框架的地方进行交互,该框架无法适应不断变化的订单元素或无法处理其他可选字段,即使 spec 包含有关这些问题的注释。菲尔丁声称

A REST API should never have “typed” resources that are significant to the client. Specification authors may use resource types for describing server implementation behind the interface, but those types must be irrelevant and invisible to the client. The only types that are significant to a client are the current representation’s media type and standardized relation names. [ditto] (Source)

而不是 typed resources 应该使用内容类型协商来支持网络中不同堆栈持有者的互操作性。

因此,URI本身只是一种资源的标识符,主要用于了解向何处发送请求。通过有意义的 link 关系名称的帮助,客户端应该知道它感兴趣,即 http:/www.acme.com/rel/orders 如果它想向服务发送订单并且只查找用该 Web 注释的 URI链接扩展名或附加有 URI 的名称。 link 关系名称只是一个注解(即 URI 元素上的另一个属性)还是附加到 link-relation 名称的 URI(即作为 link 关系名称)取决于实际的媒体类型。这样,如果服务器决定更改其 URI 方案或移动资源,无论出于何种原因,客户端仍将能够以这种方式找到 URI,并且它不会关心 URI 中是否存在字符.它只是将 URI 视为不透明的东西。这里的好处是,一个 URI 可以同时用多个 link 关系名称进行注释,这允许服务器向支持不同 link-relation 名称的客户端“提供”该 URI。在表单的情况下,将请求发送到的 URI 可能包含在 action attribute of the form element 等中。

正如您所希望看到的那样,使用 HATEOAS 不需要对 URI 的硬依赖,如果是这样的话,可能会依赖 link-relation 名称。它仍然需要 URI 来了解将请求发送到哪里,但是通过其附带的 link 关系名称查找 URI,您可以使 URI 的处理更加动态,因为它允许服务器在需要时随时更改 URI到或不得不。