PAYATHON 2026

Spring HATEOAS 如何在自定义属性旁生成 HAL _embedded?

数据小陈

结论

Spring HATEOAS 的 HalModelBuilder 可以组合根级状态、_links 和 _embedded。把根级属性放在普通对象中,传给 halModelOf(...),再用 embed(collection, relation) 添加嵌入集合。若将 CollectionModel 作为另一个模型的普通属性,它通常会按属性名序列化,不会自动成为根级 _embedded。

Spring HATEOAS 从 1.1 起提供 HalModelBuilder,用于通过 HAL 风格的 API 构建表示。它可以接受任意对象作为主体,并添加嵌入文档和链接。Spring HATEOAS HAL 表示模型文档

示例

下面用一个对象保存示例中的两个根级状态字段,并将订单集合以 ea:order 关系嵌入:

import java.util.List;

import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.LinkRelation;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.mediatype.hal.HalModelBuilder;

record OrdersState(int currentlyProcessing, int shippedToday) {}

List<EntityModel<Order>> orderResources = List.of(
    EntityModel.of(firstOrder)
        .add(Link.of("/orders/123").withSelfRel()),
    EntityModel.of(secondOrder)
        .add(Link.of("/orders/124").withSelfRel())
);

RepresentationModel<?> model = HalModelBuilder
    .halModelOf(new OrdersState(14, 20))
    .link(Link.of("/orders").withSelfRel())
    .embed(orderResources, LinkRelation.of("ea:order"))
    .build();

Order、firstOrder 和 secondOrder 分别代表应用自己的订单类型和数据。示例中的 EntityModel 为每个嵌入的订单附加了 self 链接。实际使用时,也可以继续添加 ea:basket、ea:customer 等链接。若运行环境不支持 Java record,可以将 OrdersState 换成带有可序列化属性的普通 Java 类。

HalModelBuilder 的 embed(Collection<?>, LinkRelation) 重载可以显式指定集合在 _embedded 中的关系名。即使集合为空,也能通过这个重载保留关系名。HalModelBuilder API

需要留意的地方

  • LinkRelation.of("ea:order") 指定关系名。要让带 ea: 前缀的关系与 CURIE 配合使用,还需在 HAL 配置中提供对应的 CURIE。问题中的示例暂时排除了 CURIES,因此这里不展开配置。
  • 直接嵌入普通 Order 对象时,对象是否包含各自的链接,取决于对象类型和序列化方式。如果需要让每个订单都显示 _links,传入带链接的 EntityModel<Order> 会更明确。
  • 如果集合为空,但仍要输出 ea:order: [],应使用带显式关系名的 embed 重载。仅根据集合元素推导关系名时,空集合没有元素可供推导。

要生成根级 _embedded,可以用 HalModelBuilder 设置根级主体、链接和嵌入项;业务字段则由根级状态对象承载。

备注:内容仅供参考。