7.2 使用Traverson导航REST API

7.2 使用Traverson导航REST API

Traverson来源于Spring Data HATEOAS项目,是Spring应用中开箱即用的消费超媒体API的解决方案。这个基于Java的库灵感来源于同名的JavaScript库。

你可能已经发现Traverson的名字有点类似于“traverse on”,这种叫法其实可以很好地描述它的用法。在本节中,我们将会以遍历API关系名的方式来消费API。

要使用Traverson,首先我们要用API的基础URI来实例化一个Traverson对象:

1
2
Traverson traverson = new Traverson(
URI.create("http://localhost:8080/api"), MediaTypes.HAL_JSON);

在这里,我将Traverson指向了Taco Cloud的基础URL(本地运行)。这是需要给Traverson指定的唯一URL。从这里开始,我们就可以根据链接的关系名来遍历API。我们同时还指定了API将会生成JSON格式的响应,并且具有HAL风格的超链接,这样Traverson就能知道怎样解析传入的资源数据了。与RestTemplate类似,你可以选择在使用Traverson对象之前实例化它,也可以将其声明为一个bean并在需要的地方注入进来。

有了Traverson对象之后,我们就可以通过以下链接使用API。例如,假设我们想检索所有配料的列表。从6.3.1小节可以知道,配料链接有一个href属性,它会链接到配料资源。我们需要跟踪这个链接:

1
2
3
4
5
6
7
ParameterizedTypeReference<Resources<Ingredient>> ingredientType =
new ParameterizedTypeReference<Resources<Ingredient>>() {};
Resources<Ingredient> ingredientRes =
traverson
.follow("ingredients")
.toObject(ingredientType);
Collection<Ingredient> ingredients = ingredientRes.getContent();

通过调用Traverson对象的follow()方法,我们就可以导航至链接关系名为ingredients的资源。现在,客户端已经导航至ingredients,我们需要通过调用toObject()来提取资源的内容。

我们需要告诉toObject()方法要将数据读入到哪种对象之中。考虑到我们需要以Resources<Ingredient>对象的形式来读入,而且Java类型擦除使得为泛型提供类型信息变得非常困难,所以这可能会有些棘手。但是,创建ParameterizedTypeReference能够帮助我们解决这个问题。

打个比方,假设这不是REST API,而是Web站点上的主页;这也不是REST客户端代码,而是我们正在浏览器中查看主页。在页面中,我们看到了一个关于Ingredients的链接,点击进入该链接。在进入下一个页面的时候,我们需要读取该页面,类似于Traverson将内容提取为Resources<Ingredient>对象。

现在,我们考虑一个更有趣的场景——假设我们想要获取最新创建的taco。从主资源开始,我们可以按照如下方式导航至最近的taco资源:

1
2
3
4
5
6
7
8
ParameterizedTypeReference<Resources<Taco>> tacoType =
new ParameterizedTypeReference<Resources<Taco>>() {};
Resources<Taco> tacoRes =
traverson
.follow("tacos")
.follow("recents")
.toObject(tacoType);
Collection<Taco> tacos = tacoRes.getContent();

在这里,我们跟踪tacos链接,然后从这里开始,跟踪recents链接。通过这种方式,我们得到了感兴趣的资源,所以基于对应的ParameterizedTypeReference调用toObject()方法,我们就得到了想要的内容。我们可以通过列出关系名称列表的形式来简化“.follow()”方法:

1
2
3
4
Resources<Taco> tacoRes =
traverson
.follow("tacos", "recents")
.toObject(tacoType);

正如我们所看到的,Traverson能够很容易地导航HATEOAS的API并消费其资源。但是,它并没有提供通过这些API写入或删除资源的方法。相反,RestTemplate能够写入和删除资源,但是在导航API方面支持得并不太好。

当你既要导航API又要更新或删除资源时,你需要组合使用RestTemplate和Traverson。Traverson仍然可以导航至创建新资源的链接。然后,可以将这个链接传递给RestTemplate来执行POST、PUT、DELETE或任何其他需要的HTTP请求。

例如,我们想要为Taco Cloud菜单添加一个新的Ingredient,如下的addIngredient ()方法将Traverson和RestTemplate组合起来,向API提交了一个新的Ingredient:

1
2
3
4
5
6
7
8
9
private Ingredient addIngredient(Ingredient ingredient) {
String ingredientsUrl = traverson
.follow("ingredients")
.asLink()
.getHref();
return rest.postForObject(ingredientsUrl,
ingredient,
Ingredient.class);
}

在跟踪完Ingredients之后,我们通过调用asLink()方法得到链接本身。基于该链接,我们调用getHref()得到链接的URL。有了URL之后,我们就具备了使用RestTemplate调用postForObject()并创建新配料所需的一切。