除了能够或多或少地自由更改URL结构的能力之外,HATEOAS还提供了哪些可发现性和去耦性?


61

最近,我一直在阅读有关作为应用程序状态引擎(HATEOAS)的Hypermedia的信息,据称该约束使Web API成为“真正的RESTful”。它归结为基本上包括链接,这些链接包含对从当前状态可能进行的转换的每个响应。

让我根据自己的理解来说明HATEOAS是什么-如果错过任何事情,请纠正我。

/
    GET: {
        "_links": {
            "child": [
                { "href": "http://myapi.com/articles", "title": "articles" }
            ]
        }
    }

/articles?contains=HATEOAS
    GET: {
        "_items": [
            { "uri": "http://myapi.com/articles/0", "title": "Why Should I Care About HATEOAS?" },
            { "uri": "http://myapi.com/articles/1", "title": "HATEOAS: Problem or Solution?" }
        ],
        "_links": {
            "self": { "href": "http://myapi.com/articles", "title": "articles" },
            "parent": { "href": "http://myapi.com/", "title": "home" }
        }
    }

    POST: {
        "title": "A New Article",
        "body": "Article body",
        "tags": [ "tag1", "tag2" ]
    }

/articles/0
    GET: {
        "title": "Why Should I Care About HATEOAS?",
        "body": "Blah blah blah"
        "tags": [ "REST", "HATEOAS" ],
        "_links": {
            "self": { "href": "http://myapi.com/articles/0", "title": "article" },
            "parent": { "href": "http://myapi.com/articles", "title": "articles" }
        }
    }

据说HATEOAS具有两个主要优点:

  1. 从根URI开始就可以发现整个服务,不再需要文档。

  2. 客户端与服务器解耦,服务器现在可以自由更改URI结构。这消除了API版本控制的需要。

但是在我看来,服务不仅仅是其URI结构。为了有效地使用它,您还需要了解:

  • 您可以使用哪些查询参数及其可能的值
  • JSON / XML /您需要在POST / PATCH / etc请求中发送的所有文档的结构
  • 服务器发送的响应的结构
  • 可能发生的错误
  • ...

基于上述内容,HATEOAS仅解决了极小的可发现性和耦合问题。您仍然需要记录以上四个方面,并且由于它们,客户端仍将与服务器紧密耦合。为了避免破坏客户端,您仍然需要对API进行版本控制。

它提供的唯一好处是您可以自由地或多或少地更改URL结构(顺便说一下,“ Cool URIs not change”原理是怎么回事?)。我的理解正确吗?

Answers:


46

我认为您的直觉在很大程度上是正确的;这些宣称的好处确实不是那么伟大,因为对于任何非平凡的Web应用程序,客户端将不得不关心他们正在做的语义以及语法。

但这并不意味着您不应该使您的应用程序遵循HATEOAS的原则!

HATEOAS到底是什么意思?这意味着对应用程序进行结构化,使其在原则上像网站一样,并且无需下载某些复杂的架构即可发现您可能要执行的所有操作。(复杂的WSDL模式可以覆盖所有内容,但是到那时,它们已经超越了几乎每个程序员都能理解的能力,更不用说编写了!您可以将HATEOAS视为对这种复杂性的反应。)

HATEOAS不仅意味着丰富的链接。这意味着使用HTTP标准的错误机制来更准确地指出出了什么问题。您不必只回答“哇!否”,而是可以提供描述实际错误以及客户可能对此采取的措施的文档。它还意味着支持诸如OPTIONS请求(允许客户端找出他们可以使用的HTTP方法的标准方式)和内容类型协商之类的事情,以便响应的格式可以适应客户端可以处理的形式。这意味着要输入解释性文字(或更可能的是,链接到它),以便客户在不了解的情况下可以查找如何使用该系统;说明性文字可能是人类可读的,也可能是机器可读的(并且可以根据需要复杂)。最后,这意味着客户端不合成链接(查询参数除外);如果您告诉客户,则客户只会使用链接。

您必须考虑让用户浏览该网站(该用户可以读取JSON或XML而不是HTML,因此有点怪异),并且具有很大的链接存储空间和HTTP标准的百科全书知识,但是对于这些内容一无所知。做。

当然,您可以使用内容类型协商来提供HTML(5)/ JS客户端,如果他们的浏览器准备接受的话,它们将允许他们使用您的应用程序。毕竟,如果您的RESTful API有什么好处,那么在其之上实现应该是“琐碎的”?


6

事实是,HATEOAS必须附带第二个支柱来定义RESTful API是什么:标准化媒体类型。罗伊·菲尔丁说

REST API应该花费几乎所有的描述性精力来定义用于表示资源的媒体类型。”

使用显式定义过渡的标准化媒体类型和将资源相互指向的超文本,您可以创建可以采用任何形式而不破坏任何客户端的资源图。就像网络工作一样,实际上:您在文档之间具有链接,并且文档以HTML编写,用于定义如何遵循这些链接。<a href>是GET,<form>是GET还是POST(并定义了GET时使用的url模板),<link type="text/css">是GET ...等。这就是浏览器如何浏览任意结构化HTML页面和Web的方式。

您提出的所有观点

  • 您可以使用哪些查询参数及其可能的值
  • JSON / XML /您需要在POST / PATCH / etc请求中发送的所有文档的结构
  • 服务器发送的响应的结构
  • 可能发生的错误

标准化媒体类型的定义应解决的问题。当然,这要困难得多,这并不是大多数人在定义“ REST” API时会想到的东西。您不仅可以将业务实体带入一个JSON文档中以拥有RESTful API,还可以将它们的属性推送到JSON文档中。

当然,发生的事情是REST被某种程度地稀释以表示“使用HTTP代替复杂的SOAPy”。仅使用HTTP和HyperText不足以实现RESTful,这是大多数人都会犯的错误。

并不是说这是一件坏事:REST牺牲了性能和易于开发,以换取长期的可维护性和发展性。它是为大型企业应用程序集成而设计的。您可能需要一个带有JSON硬编码结构的小型Web API。只是别称它为REST,它是一个特殊的Web API,仅此而已。但这并不意味着它很烂,只是意味着它没有尝试遵循REST的约束。

进一步阅读

希望这个帮助可以澄清一下:)


2

有一些Hypermedia格式致力于提供更丰富的响应,其中包括有关发送哪种请求的更多信息,没有什么可以阻止您使用更多信息来丰富响应。

这是一个Siren文档示例:

{
  "class": [ "order" ],
  "properties": { 
      "orderNumber": 42, 
      "itemCount": 3,
      "status": "pending"
  },
  "entities": [
    {
      "class": [ "info", "customer" ],
      "rel": [ "http://x.io/rels/customer" ], 
      "properties": { 
        "customerId": "pj123",
        "name": "Peter Joseph"
      },
      "links": [
        { "rel": [ "self" ], "href": "http://api.x.io/customers/pj123" }
      ]
    }
  ],
  "actions": [
    {
      "name": "add-item",
      "title": "Add Item",
      "method": "POST",
      "href": "http://api.x.io/orders/42/items",
      "type": "application/x-www-form-urlencoded",
      "fields": [
        { "name": "orderNumber", "type": "hidden", "value": "42" },
        { "name": "productCode", "type": "text" },
        { "name": "quantity", "type": "number" }
      ]
    }
  ],
  "links": [
    { "rel": [ "self" ], "href": "http://api.x.io/orders/42" },
    { "rel": [ "previous" ], "href": "http://api.x.io/orders/41" },
    { "rel": [ "next" ], "href": "http://api.x.io/orders/43" }
  ]
}

如您所见,消息中提供了有关如何调用相关actions信息的信息,然后通过解释此信息,客户端将变得更加抵制更改。

如果rels是可以查找的URI,而不是从固定的词汇表中查找,则它将变得特别强大。


0

您在哪里读到HATEAOS服务的“不再需要文档”?如您所说,您仍然需要记录链接的语义。但是,使用HATEOAS,您无需记录大多数URI的结构,因此可以永久保留。

HATEOAS允许服务实现者在不更改客户端依赖的一小部分URI的情况下,显着,高效地修改和扩展实现。保持少量入口点比保留大量入口点更容易。因此,与非HATEOAS服务相比,减少服务的公共入口点数量并动态提供指向子资源(HATEOAS)的链接实际上更好地支持“ Cool URI不变”。


罗伊·菲尔丁(Roy Fielding)的论文就是其中一个人可以读到的“不再需要文档”的地方。
meriton-罢工

1
我只是在Fielding的论文中搜索了“文档”的使用,却没有发现类似于“不再需要文档”的说法。您能否指出在Fielding的论文中您发现此主张的位置?
乔纳森·吉迪

0

(HATEOAS),它是使Web API“真正RESTful”的约束

使之成为真正的REST API的唯一方法是满足所有约束,而不仅仅是一个约束。

但是在我看来,服务不仅仅是其URI结构。为了有效地使用它,您还需要知道:...

这就是为什么我们需要其他约束,自我描述性消息等的原因。

为了避免破坏客户端,您仍然需要对API进行版本控制。

无论您如何尝试,都需要对API进行版本控制。在REST客户端中,您仍然需要知道如何进入要执行操作的页面,要遵循的链接以及需要根据描述消息的RDF词汇收集哪些属性。如果您需要从该vocab上更换或移除某些东西,那么它可能会破坏您所有的客户,并且您需要一个新版本。因此,我认为您不应该过早发布REST(并在不断更改API的同时弄清楚模型),否则您将拥有许多版本。首先需要稳定的域模型,然后才能在...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.