1. 概述

本文将介绍 HAL 是什么以及它的价值所在,并引入 HAL 浏览器(HAL Browser)

我们会使用 Spring 构建一个简单的 REST 接口,包含几个典型的 CRUD 操作,并初始化一些测试数据。

最后,通过 HAL 浏览器来探索这个 API,直观地理解如何遍历和发现资源之间的关系。


2. HAL 与 HAL 浏览器

JSON Hypertext Application Language(简称 HAL)是一种轻量级的 JSON 格式,它为 REST API 中资源之间的超链接提供了统一且简单的方式。使用 HAL 后,API 更具可探索性,甚至可以做到“自我描述”——无需额外文档也能被轻松理解。

其核心原理是:返回的 JSON 数据不仅包含资源本身,还附带了相关链接和其他上下文信息。

HAL 模型围绕两个核心概念展开:

  • 资源(Resource)

    • 包含指向其他 URI 的链接(Links)
    • 可嵌入子资源(Embedded Resources)
    • 资源自身的状态数据(State)
  • 链接(Link)

    • 目标 URI(href)
    • 关系标识(rel),表示该链接的语义含义
    • 其他可选属性,如是否已弃用、内容协商支持等

⚠️ 注意:这里的 “rel” 是关键,它是客户端识别链接用途的依据。

HAL 浏览器由 HAL 规范的提出者开发,提供了一个基于浏览器的图形界面,让你像浏览网页一样可视化地探索 REST API 的结构和资源跳转路径

接下来我们就动手搭建一个支持 HAL 的 Spring REST 项目,接入 HAL 浏览器并实战体验。


3. 依赖配置

要让 Spring 项目集成 HAL 浏览器,只需添加一个关键依赖。完整依赖可参考 GitHub 示例代码

Maven 项目

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-rest-hal-explorer</artifactId>
    <version>3.4.1.RELEASE</version>
</dependency>

Gradle 项目

compile group: 'org.springframework.data', name: 'spring-data-rest-hal-explorer', version: '3.4.1.RELEASE'

✅ 添加后无需任何配置,Spring Boot 会自动启用 HAL 浏览器,访问 http://localhost:8080 即可进入。


4. 构建简单的 REST API

4.1. 定义数据模型

我们以图书管理系统为例,创建一个 Book 实体类,用于演示资源暴露和链接生成。

@Entity
public class Book {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  @NotNull
  @Column(columnDefinition = "VARCHAR", length = 100)
  private String title;

  @NotNull
  @Column(columnDefinition = "VARCHAR", length = 100)
  private String author;

  @Column(columnDefinition = "VARCHAR", length = 1000)
  private String blurb;

  private int pages;

  // standard getters, setters and constructors
}

这个实体很简单,但足以支撑后续的分页、查询和资源嵌套功能。


4.2. 创建 CRUD 接口

Spring Data JPA 提供了强大的仓库抽象,我们只需继承 PagingAndSortingRepository,就能自动获得分页、排序和基本 CRUD 接口。

@Repository
public interface BookRepository extends PagingAndSortingRepository<Book, Long> {

    @RestResource(rel = "title-contains", path = "title-contains")
    Page<Book> findByTitleContaining(@Param("query") String query, Pageable page);

    @RestResource(rel = "author-contains", path = "author-contains", exported = false)
    Page<Book> findByAuthorContaining(@Param("query") String query, Pageable page);
}

关键点说明:

  • findByTitleContaining:暴露为 /books/search/title-contains,允许按标题模糊查询
  • findByAuthorContaining:设置了 exported = false不会出现在 HAL 浏览器中
  • 🔧 @RestResource 注解用于自定义 rel 和路径,避免暴露原始方法名

💡 小贴士:exported = false 是个实用技巧,适合隐藏内部或敏感查询接口,防止被外部发现。

应用启动时,我们通过实现 ApplicationRunner 接口自动插入测试数据。具体实现见 GitHub 示例


5. 启用 HAL 浏览器

如果你用了上面的依赖,恭喜你——什么都不用做!

Spring Data REST 会自动配置 HAL 浏览器,默认路径为:

http://localhost:8080/

启动应用后直接访问该地址,即可看到 HAL 浏览器界面。是不是简单粗暴?


6. 使用 HAL 浏览器探索 API

HAL 浏览器分为左右两大区域:

  • 左侧:Explorer(探索区) —— 用于导航和触发请求
  • 右侧:Inspector(检查区) —— 显示原始响应头和响应体

下面我们逐一拆解。


6.1. HAL Explorer(探索区)

Explorer 是你的主操作面板,主要包含:

  • 搜索栏:快速跳转到某个 endpoint
  • 自定义请求头输入框
  • 当前资源的属性展示
  • _links 列表(可点击跳转)
  • _embedded 资源列表(内联展示)

你可以像浏览网页一样,通过点击链接层层深入资源。


6.2. 链接(Links)的使用

访问 /books 接口后,你会看到类似如下的链接结构:

Links-1

这些链接来自响应中的 _links 字段:

"_links": {
    "first": {
      "href": "http://localhost:8080/books?page=0&size=20"
    },
    "self": {
      "href": "http://localhost:8080/books{?page,size,sort}",
      "templated": true
    },
    "next": {
      "href": "http://localhost:8080/books?page=1&size=20"
    },
    "last": {
      "href": "http://localhost:8080/books?page=4&size=20"
    },
    "profile": {
      "href": "http://localhost:8080/profile/books"
    },
    "search": {
      "href": "http://localhost:8080/books/search"
    }
}

每个 link 的 rel 表示其语义:

  • self:当前资源
  • next / last:分页导航
  • search:跳转到查询入口

再进入 /books/search,可以看到我们自定义的查询接口:

{
  "_links": {
    "title-contains": {
      "href": "http://localhost:8080/books/search/title-contains{?query,page,size,sort}",
      "templated": true
    },
    "self": {
      "href": "http://localhost:8080/books/search"
    }
  }
}

title-contains 出现了,说明已成功暴露
author-contains 没出现,因为设置了 exported = false

这就是 HAL 的魅力:链接即文档,结构即契约


6.3. 查看嵌入资源(Embedded Resources)

/books 响应中,除了 _links,还有 _embedded.books 数组,里面是具体的图书列表:

embed-2

每个嵌入的 book 资源都包含:

  • 自身属性(title, author, pages...)
  • 独立的 _links(如 self, delete, update 等)
  • 可进一步点击进入详情

这种“资源+链接”的组合,构成了真正的 HATEOAS(Hypermedia as the Engine of Application State)风格 API。


6.4. 使用表单进行查询

注意链接列表中某些条目右侧有个 ❓ 图标,表示这是一个模板化链接(templated link),需要传参。

比如点击 title-contains 后的问号,会弹出表单:

The HAL browser selection form

输入 Java 并提交,浏览器会自动构造请求:

GET /books/search/title-contains?query=Java&page=0&size=20

返回标题包含 “Java” 的前 20 条记录。整个过程无需写任何前端代码,调试起来非常高效。


6.5. HAL Inspector(检查区)

右侧的 Inspector 显示的是原始 HTTP 响应:

  • Response Headers:响应头信息
  • Response Body:完整的 JSON 内容,包括 _links_embedded

🔍 正是这些 HAL 格式的数据,驱动了左侧 Explorer 的链接生成和资源渲染。

你可以在这里验证返回结构是否符合预期,排查 content-type、分页参数等问题。


7. 总结

本文我们完成了以下内容:

  • ✅ 理解了 HAL 的基本概念及其在构建自描述 REST API 中的价值
  • ✅ 使用 Spring Data REST 快速暴露了带分页和自定义查询的接口
  • ✅ 通过 exported = false 控制某些接口不被 HAL 浏览器发现
  • ✅ 实践了 HAL 浏览器的使用,掌握了 Explorer 和 Inspector 的核心功能
  • ✅ 体验了 HATEOAS 风格 API 的可探索性和友好性

HAL 浏览器虽然不算生产必备工具,但在开发、联调和 API 演示阶段非常实用。它把枯燥的 JSON 接口变成了可交互的资源图谱,极大提升了开发效率。

💡 推荐场景:内部系统调试、API 文档预览、新人快速上手项目结构。

完整代码已托管至 GitHub:https://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-data-rest-2


原始标题:Spring REST and HAL Browser | Baeldung

» 下一篇: JMapper 使用指南