1. 概述

随着REST(Representational State Transfer)架构的兴起,Web API迅速发展。基于REST的API让开发者能构建模块化、可扩展且松耦合的Web应用。但RESTful API常缺乏可发现性和易用性这两个关键要素。

这时HATEOAS(Hypermedia as the Engine of Application State)和HAL(Hypertext Application Language)就能发挥重要作用。没有HATEOAS的RESTful API会与服务器紧密耦合,客户端需要硬编码接口地址。

本文将深入探讨HATEOAS和HAL的概念、关系及核心区别。

2. 理解HATEOAS

HATEOAS全称是"超媒体作为应用状态的引擎"。要理解这个概念,先要搞清楚什么是超媒体。超媒体是超文本的扩展,不仅包含文本链接,还包括图像、音频、视频等其他媒体类型,让用户能无缝导航相关资源。

HATEOAS通过在响应中嵌入超媒体链接来增强API交互。客户端无需预先了解API结构,就能动态导航和操作资源。这促进了松耦合,使API能独立演进。

核心思想:客户端与服务器的交互应完全依赖服务器响应中的超媒体。每个响应都包含引导客户端发现后续操作和资源的链接。

用购物车示例理解HATEOAS:

当客户端请求包含商品的购物车详情时,服务器会返回可能的操作链接:

GET /cart/12345 HTTP/1.1

HTTP/1.1 200 OK
{
  "cartId": 12345,
  "items": [
    {
      "id": 001,
      "name": "TV",
      "qty": 1,
      "amount": 4750
    }
  ],
  "totalAmount": 4750,
  "links": [
    {
      "rel": "self",
      "href": "/cart/12345"
    },
    {
      "rel": "addItem",
      "href": "/cart/12345/add"
    },
    {
      "rel": "removeItem",
      "href": "/cart/12345/remove"
    },
    {
      "rel": "checkout",
      "href": "/cart/12345/checkout"
    },
    {
      "rel": "clear",
      "href": "/cart/12345/clear"
    }
  ]
}

在这个示例中,客户端可以添加/删除商品、结账或清空购物车。所有操作都基于购物车当前状态。links字段指示了可用操作。再看空购物车的例子:

GET /cart/987 HTTP/1.1

HTTP/1.1 200 OK
{
  "cartId": 987,
  "items": [],
  "totalAmount": 0.0,
  "links": [
    {
      "rel": "self",
      "href": "/cart/987"
    },
    {
      "rel": "addItem",
      "href": "/cart/987/add"
    },
    {
      "rel": "checkout",
      "href": "/cart/987/checkout"
    },
  ]
}

现在客户端的操作受限了:只能添加商品或结账,不能清空或删除(因为购物车已经是空的)。这种超文本直观地展示了哪些操作被允许。

3. 理解HAL

HAL是一种简单格式,帮助开发者创建符合HATEOAS原则的RESTful API超媒体表示。它定义了标准化格式,使REST API中的资源超链接变得简单一致。

3.1. 链接

HAL允许在资源表示中包含超媒体链接。_links属性列出与资源相关的链接。每个链接包含rel(关系类型)和href,供客户端与API交互。_links还包含自链接(self),让客户端能直接访问当前资源。

3.2. 嵌入资源

顾名思义,嵌入资源表示其他资源被包含在给定REST资源中。_embedded属性可持有相关资源,让客户端无需额外请求就能获取关联信息。

3.3. 状态

HAL使用JSON或XML编码资源数据和相关链接。

看个非HAL的示例,API响应通常是这样:

{
  "cartId": 12345,
  "items": [
    {
      "id": 001,
      "name": "TV",
      "qty": 1,
      "amount": 4750
    }
  ]
}

这个示例没提供任何相关资源链接。现在添加HAL链接使其符合HAL规范:

{
  "_embedded": {
    "items": [
      {
        "id": 001,
        "name": "TV",
        "qty": 1,
        "amount": 4750,
        "_links": {
          "self": { "href": "/items/001" },
          "update": { "href": "/items/001/update" }
        }
      }
    ] 
  },
  {
    "_links": {
      "self": { "href": "/carts/12345" },
      "addItem": { "href": "/cart/12345/item" },
      "checkout": { "href": "/cart/12345/checkout" }
    }
  }
}

在这个示例中,主数据位于_embedded属性下。主数据包含购物车中的商品列表,每个商品都有自己的_links

4. HATEOAS与HAL的关系

在RESTful API设计中,HATEOAS和HAL是紧密相关的概念。HATEOAS是REST原则,鼓励使用超媒体链接让客户端动态探索API;而HAL是具体格式,通过标准化资源和链接的表示方式来实现这一原则。

再看之前的购物车示例。当客户端请求包含商品的购物车详情时,服务器返回HAL表示:

GET /cart/12345 HTTP 1.1

HTTP/1.1 200 OK

{
  "cartId": 12345,
  "items": [{
      "id": 001,
      "name": "TV",
      "qty": 1,
      "amount": 4500
    }],
  "totalAmount": 4500,
  "_links": {
      "self": { "href": "/cart/12345", "rel": "self" },
      "addItem": { "href": "/cart/12345/add", "rel": "addItem"},
      "checkout": { "href": "/cart/12345/checkout", "rel": "checkout" },
      "clear": { "href": "/cart/12345/clear" , "rel": "clearCart" }
  },
  "_embedded": {
      "offer": {
        "code": "DISCOUNT10",
        "discount": 10
      }
  }
}

在这个示例中:

  • _links部分提供超媒体链接,描述基于当前资源状态客户端可执行的操作
  • rel字段至关重要,它定义了链接代表的操作(如self, addItem, checkout等)
  • _embedded部分包含直接嵌入主资源的相关资源,减少网络请求
  • 资源状态通过响应中的实际数据捕获(如商品和购物车总金额)

5. HATEOAS与HAL的核心区别

维度 HATEOAS HAL
概念/格式 REST架构原则,引导客户端通过超媒体链接发现可用操作 支持HATEOAS的特定资源表示格式
目的 使API自解释且易于导航,减少客户端对API结构的预知需求 提供清晰简洁的资源表示方式,简化客户端链接解析
实现 可通过JSON、XML或HAL等多种格式实现 专门使用JSON/XML结构化资源表示,强调链接

简单总结:

  • ✅ HATEOAS是设计原则,HAL是实现工具
  • ❌ HATEOAS不是格式,HAL才是具体格式
  • ⚠️ 使用HAL能轻松实现HATEOAS,但HATEOAS不依赖HAL

6. 结论

本文深入探讨了HATEOAS和HAL。HATEOAS定义了RESTful API应遵循的原则,而HAL提供了简化API超媒体控制的具体实现。 使用HAL,开发者能轻松创建符合HATEOAS的API,简化客户端的资源发现和交互。

结合HATEOAS和HAL,API变得自描述且可发现。这实现了客户端与服务器的更好解耦,使API能随时间独立演进——这才是REST架构的精髓所在。


原始标题:Relationship and Difference Between HAL and HATEOAS | Baeldung