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
字段:
"_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
数组,里面是具体的图书列表:
每个嵌入的 book 资源都包含:
- 自身属性(title, author, pages...)
- 独立的
_links
(如 self, delete, update 等) - 可进一步点击进入详情
这种“资源+链接”的组合,构成了真正的 HATEOAS(Hypermedia as the Engine of Application State)风格 API。
6.4. 使用表单进行查询
注意链接列表中某些条目右侧有个 ❓ 图标,表示这是一个模板化链接(templated link),需要传参。
比如点击 title-contains
后的问号,会弹出表单:
输入 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