一.簡(jiǎn)介
????????Spring HATEOAS的目標(biāo)是解決兩個(gè)問(wèn)題:link?creation(創(chuàng)建鏈接)及representation assembly(集合表述)。
二.創(chuàng)建鏈接
????????HATEOAS的核心是鏈接。鏈接的存在使得客戶(hù)端可以動(dòng)態(tài)發(fā)現(xiàn)其所能執(zhí)行的動(dòng)作。
HATEOAS生成鏈接的幾種方式:
① 靜態(tài)生成鏈接
????????Spring HATEOAS使用 org.springframework.hateoas.Link 類(lèi)來(lái)表示鏈接??梢岳^承自 Spring HATEOAS 提供的 org.springframework.hateoas.Resource 類(lèi),Resource 類(lèi)提供了簡(jiǎn)單的方式來(lái)創(chuàng)建鏈接。
Eg:
Link link1 = new Link("http://localhost:8080/something");
② 動(dòng)態(tài)生成鏈接
????????在創(chuàng)建資源的鏈接時(shí),指向單個(gè)資源的鏈接的href屬性值是類(lèi)似“http://localhost:8080/lists/1”這樣的格式。而其中的“/lists”不應(yīng)該是硬編碼的,否則當(dāng)修改了 ListRestController 類(lèi)的“@RequestMapping”時(shí),所有相關(guān)的生成鏈接的代碼都需要進(jìn)行修改。Spring HATEOAS 提供了 org.springframework.hateoas.mvc.ControllerLinkBuilder 來(lái)解決這個(gè)問(wèn)題,用來(lái)根據(jù) Spring MVC 控制器動(dòng)態(tài)生成鏈接。
Eg:
//通過(guò)slash方法找到下一級(jí),生成自身鏈接
Link link = linkTo(PersonController.class).slash(person.getId()).withSelfRel();
//如果實(shí)體類(lèi)實(shí)現(xiàn)Identifiable接口
Link link = linkTo(PersonController.class).slash(person).withSelfRel();
//通過(guò)指定類(lèi)的方法,生成rel為"items"的鏈接
Link link = linkTo(methodOn(ItemRestController.class).readItems(listId)).withRel("items");
③ 通過(guò)實(shí)體類(lèi)創(chuàng)建單個(gè)鏈接
????????首先需要添加Maven依賴(lài),此外還需要在控制器類(lèi)中通過(guò)“@ExposesResourceFor”注解聲明其所暴露的模型類(lèi),另外在 Spring 應(yīng)用的配置類(lèi)中需要通過(guò)“@EnableEntityLinks”注解來(lái)啟用 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方法可以正常工作,控制器類(lèi)中需要包含訪問(wèn)單個(gè)資源的方法,而且其“@RequestMapping”是類(lèi)似“/{id}”這樣的形式。
④Model—>ModelResource
????????繼承ResourceAssemblerSupport類(lèi),并根據(jù)ModelRestController與ModelResource進(jìn)行相應(yīng)配置。
//組裝單個(gè)資源對(duì)象
new ModelResourceAssembler().toResource(model);
//組裝資源對(duì)象的集合
Resources<ModelResource> resources = new Resources<ModelResource>(new ModelResourceAssembler().toResources(models));
二.集合表述
①Resource資源
????????繼承Resource類(lèi),在ModelResource類(lèi)中可以根據(jù)實(shí)體參數(shù)進(jìn)行自定義封裝,并向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"));//條件????}}
結(jié)果如下:
"id":?1,
"title":?"房?jī)r(jià)監(jiān)管",
"_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子集合
????????首先是內(nèi)嵌資源在_embedded對(duì)應(yīng)的哈希對(duì)象中的屬性值,該屬性值是由 org.springframework.hateoas.RelProvider 接口的實(shí)現(xiàn)來(lái)提供的。對(duì)于應(yīng)用來(lái)說(shuō),只需要在內(nèi)嵌資源對(duì)應(yīng)的模型類(lèi)中添加 org.springframework.hateoas.core.Relation 注解即可.
@Relation(value = "list", collectionRelation = "lists")
public class List extends AbstractEntity {
}
③ CurieProvider API
????????使用URL作為鏈接的關(guān)系帶來(lái)的問(wèn)題是 URL 作為屬性名稱(chēng)來(lái)說(shuō)顯得過(guò)長(zhǎng),而且不同關(guān)系的 URL 的大部分內(nèi)容是重復(fù)的。為了解決這個(gè)問(wèn)題,可以使用 Curie。簡(jiǎn)單來(lái)說(shuō),Curie 可以作為鏈接關(guān)系 URL 的模板。鏈接的關(guān)系聲明時(shí)使用 Curie 的名稱(chēng)作為前綴,不用提供完整的 URL。應(yīng)用中聲明的 Curie 出現(xiàn)在_links 屬性中。
@Bean
public CurieProvider curieProvider() {
return new DefaultCurieProvider("todo",
new UriTemplate("http://www.midgetontoes.com/todolist/rels/{rel}"));
}
注意:CurieProvider每個(gè)應(yīng)用程序范圍只能定義一個(gè)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"));