在构建 REST 接口时,单纯的数据返回已经无法满足复杂前端对资源导航的需求。Spring HATEOAS 是 Spring 生态中专门处理超媒体驱动的模块,而 EnableHAL 相关支持能让 Spring Boot 自动将领域对象序列化为 HAL(Hypertext Application Language)规范格式。HAL 通过在响应体中嵌入 _links 节点来描述资源自身及关联资源的 URL,客户端不再需要硬编码接口路径,而是根据返回链接进行下一步请求。

EnableHAL 的底层开启方式
Spring Boot 本身并不会默认开启 HAL 超媒体支持,需要显式通过注解引入。核心注解是 @EnableHypermediaSupport,它位于 org.springframework.hateoas.config 包下,其 type 属性可指定 HATEOAS 的媒体类型,其中 HypermediaType.HAL 就对应我们要用的 HAL 格式。在配置类上添加该注解后,Spring 会注册一系列特定的 HttpMessageConverter,替换掉原本单纯使用 Jackson 的序列化逻辑。
从原理上看,当请求头 Accept 包含 application/hal+json 时,框架会把控制器返回的实体对象或 RepresentationModel 子类,交给 HAL 专用的序列化器处理。该序列化器会扫描对象中的 Link 属性,并将其放置到 _links 字段中。如果没有开启 EnableHAL,即使你手动创建了 Link,也只会被当成普通字段序列化,无法形成标准 HAL 结构。下面是一段典型的开启配置:
import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableHypermediaSupport(type = HypermediaType.HAL)
public class HalConfig {
// 无需额外 Bean,注解已导入必要组件
}
值得注意的是,从 Spring HATEOAS 1.x 开始,很多旧版教程中提到的 @EnableHal 已经废弃,统一收敛到 @EnableHypermediaSupport 中。如果项目中引入了 spring-boot-starter-hateoas,其实也可以依靠自动配置,但显式声明能让团队明确技术选型,也方便在单元测试中精准控制配置。
实体与控制器如何改造为 HAL 资源
要让接口真正输出 HAL,实体类不应直接返回 POJO,而应继承 RepresentationModel 或 EntityModel。以用户资源为例,我们定义一个 UserModel 继承 RepresentationModel,在构造时通过 add(Link) 方法补充 self 链接。控制器层则使用 EntityModel.of() 包装数据,或返回自定义的 RepresentationModel 子类,由框架完成链接渲染。
对比未使用 HAL 的写法,传统 Controller 直接返回 User 对象,前端拿到的是纯属性 JSON;改造后,同样的接口在 Accept 为 hal+json 时,会多出 _links 节点。如下代码展示控制器如何结合 WebMvcLinkBuilder 生成指向自身的链接,并附加一个关联订单资源的链接:
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@GetMapping("/users/{id}")
public EntityModel<User> getUser(@PathVariable Long id) {
User user = new User(id, "张三");
Link selfLink = WebMvcLinkBuilder.linkTo(
WebMvcLinkBuilder.methodOn(UserController.class).getUser(id))
.withSelfRel();
Link ordersLink = WebMvcLinkBuilder.linkTo(
WebMvcLinkBuilder.methodOn(OrderController.class).getByUser(id))
.withRel("orders");
return EntityModel.of(user, selfLink, ordersLink);
}
}
上述写法把链接构建逻辑与控制器方法绑定,重构接口路径时链接会自动跟随变化,避免手动拼接字符串带来的断裂风险。若团队采用 Spring Data REST,则 Repository 自动暴露的端点本身就内置了 HAL,但自定义业务接口仍需按上述方式处理。此外,HAL 格式支持 _embedded 节点来内联子资源,当我们需要在一份响应中同时返回用户及其最近订单时,可以使用 CollectionModel 或 EntityModel 的 embed 方法,提升客户端首屏效率。
HAL 响应的验证与常见误区
开启 EnableHAL 后,必须通过请求头验证输出。使用 curl 或 Postman 发送 GET 请求,并设置 Accept: application/hal+json,观察响应体是否包含 _links.self.href。许多开发者在浏览器直接访问接口,由于浏览器默认 Accept 为 text/html 或 */*,Spring 可能回退到普通 JSON 视图,从而误以为 HAL 没有生效。此时应在测试类中使用 MockMvc 明确设定媒体类型。
另一个典型误区是混淆 HTML 标签与 HATEOAS 的 link 概念。在正文讨论中,我们提及的 <a> 标签是超文本标记语言里的锚点元素,而 HAL 中的链接是 JSON 里的数据字段,二者并不相同。如下测试代码演示如何断言 HAL 结构,注意这里使用的 andExpect 来自 MockMvcResultMatchers,与页面中的 <link> 标签毫无关系:
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
mockMvc.perform(get("/users/1").accept("application/hal+json"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links.self.href").exists());
在生产环境,还可以借助 HAL Explorer 或 Spring REST Docs 生成接口文档,让前端直观看到链接关系。如果系统需要同时支持普通 JSON 与 HAL,可以通过内容协商让同一接口依据 Accept 头返回不同视图,而 EnableHAL 只是打开了 HAL 这一路的渲染能力,并不影响其他消息转换器。正确整合后,服务的可进化性明显增强,客户端可随服务端链接变更自适应,减少版本化 API 的维护成本。
Spring_BootSpring_HATEOASEnableHAL修改时间:2026-08-16 06:00:14