一.简介
Spring HATEOAS的目标是解决两个问题:link creation(创建链接)及representation assembly(集合表述)。
二.创建链接
HATEOAS的核心是链接。链接的存在使得客户端可以动态发现其所能执行的动作。
HATEOAS生成链接的几种方式:
① 静态生成链接
Spring HATEOAS使用 org.springframework.hateoas.Link 类来表示链接。可以继承自 Spring HATEOAS 提供的 org.springframework.hateoas.Resource 类,Resource 类提供了简单的方式来创建链接。
Eg:
Link link1 = new Link("http://localhost:8080/something");
② 动态生成链接
在创建资源的链接时,指向单个资源的链接的href属性值是类似“http://localhost:8080/lists/1”这样的格式。而其中的“/lists”不应该是硬编码的,否则当修改了 ListRestController 类的“@RequestMapping”时,所有相关的生成链接的代码都需要进行修改。Spring HATEOAS 提供了 org.springframework.hateoas.mvc.ControllerLinkBuilder 来解决这个问题,用来根据 Spring MVC 控制器动态生成链接。
Eg:
//通过slash方法找到下一级,生成自身链接
Link link = linkTo(PersonController.class).slash(person.getId()).withSelfRel();
//如果实体类实现Identifiable接口
Link link = linkTo(PersonController.class).slash(person).withSelfRel();
//通过指定类的方法,生成rel为"items"的链接
Link link = linkTo(methodOn(ItemRestController.class).readItems(listId)).withRel("items");
③ 通过实体类创建单个链接
首先需要添加Maven依赖,此外还需要在控制器类中通过“@ExposesResourceFor”注解声明其所暴露的模型类,另外在 Spring 应用的配置类中需要通过“@EnableEntityLinks”注解来启用 EntityLinks 功能。
Eg:
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
<version>1.1.0.RELEASE</version>
</dependency>
@RestController
@ExposesResourceFor(List.class)
@RequestMapping("/lists")
public class ListRestController {
@Autowired
private EntityLinks entityLinks;
entityLinks.linkForSingleResource(List.class, 1)
}
需要注意的是,为了linkForSingleResource方法可以正常工作,控制器类中需要包含访问单个资源的方法,而且其“@RequestMapping”是类似“/{id}”这样的形式。
④Model—>ModelResource
继承ResourceAssemblerSupport类,并根据ModelRestController与ModelResource进行相应配置。
//组装单个资源对象
new ModelResourceAssembler().toResource(model);
//组装资源对象的集合
Resources<ModelResource> resources = new Resources<ModelResource>(new ModelResourceAssembler().toResources(models));
二.集合表述
①Resource资源
继承Resource类,在ModelResource类中可以根据实体参数进行自定义封装,并向ModelResource中添加自定义链接。
Eg:
public class ChartResource extends Resource { public ChartResource(Chart chart) throws Exception { super(chart); Long chartId = chart.getId(); add(linkTo(methodOn(ChartRestController.class).getDimensions(chartId)).withRel("dimensions"));//维度 add(linkTo(methodOn(ChartRestController.class).getConditions(1)).withRel("conditions"));//条件 }}
结果如下:
"id": 1,
"title": "房价监管",
"_links": {
"dimensions": {"href": "http://localhost:8088/charts/1/dimensions"}, "conditions": {"href": "http://localhost:8088/charts/1/conditions"}, "self": {"href": "http://localhost:8088/charts/1"}
}
②_embedded子集合
首先是内嵌资源在_embedded对应的哈希对象中的属性值,该属性值是由 org.springframework.hateoas.RelProvider 接口的实现来提供的。对于应用来说,只需要在内嵌资源对应的模型类中添加 org.springframework.hateoas.core.Relation 注解即可.
@Relation(value = "list", collectionRelation = "lists")
public class List extends AbstractEntity {
}
③ CurieProvider API
使用URL作为链接的关系带来的问题是 URL 作为属性名称来说显得过长,而且不同关系的 URL 的大部分内容是重复的。为了解决这个问题,可以使用 Curie。简单来说,Curie 可以作为链接关系 URL 的模板。链接的关系声明时使用 Curie 的名称作为前缀,不用提供完整的 URL。应用中声明的 Curie 出现在_links 属性中。
@Bean
public CurieProvider curieProvider() {
return new DefaultCurieProvider("todo",
new UriTemplate("http://www.midgetontoes.com/todolist/rels/{rel}"));
}
注意:CurieProvider每个应用程序范围只能定义一个bean
获取链接属性:
String content = "{'_links' : { 'foo' : { 'href' : '/foo/bar' }}}";
LinkDiscoverer discoverer = new HalLinkDiscoverer();
Link link = discoverer.findLinkWithRel("foo", content);
assertThat(link.getRel(), is("foo"));
assertThat(link.getHref(), is("/foo/bar"));