Spring应用除了提供对外API之外,也可以对另外一个应用的API发送请求。
使用 RestTemplate消费REST端点
从Spring Framework 5开始,除了WebFlux之外,Spring还引入了一个名为WebClient的新HTTP客户端。WebClient是RestTemplate的现代替代HTTP客户端。它不仅提供传统的同步 API,还支持高效的非阻塞和异步方法。也就是说,如果我们正在开发新应用程序或迁移旧应用程序,那么使用WebClient是一个好主意。展望未来,RestTemplate 将在将来的版本中弃用。
Rest提供了与REST资源交互的方法。
RestTemplate中12个独立的操作
方法 | 描述 |
---|---|
delete() | 在特定的URL上对资源执行HTTP DELETE操作 |
exchange() | 在URL上执行特定的HTTP方法,返回包含对象的ResponseEntity,这个对象是从响应体中映射的得到的 |
execute() | 在URL上执行特定的HTTP方法,返回一个从响应体映射得到的对象 |
getForEntity() | 发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所映射成的对象 |
getForObject() | 发送一个HTTP GET请求,返回的请求体将映射为一个对象 |
headForHeaders() | 发送HTTP HEAD请求,返回包含特定资源URL的HTTP头信息 |
optionsForAllow() | 发送HTTP OPTIONS请求,返回特定URL的Allow头信息 |
patchForObject() | 发送HTTP PATCH请求,返回一个从响应体映射得到的对象 |
postForEntity() | POST数据到一个URL,返回包含一个对象的ResponseEntity,这个对象是从响应体中映射得到的 |
postForLocation() | POST数据到一个URL,返回新创建资源的URL |
postForObject() | POST数据到一个URL,返回根据响应体匹配形成的对象 |
put() | PUT资源到特定的URL |
上述大多数操作都以三种方法的形式进行了重载。
- 使用String 作为URL格式,并使用可变参数列表指明URL参数。
- 使用String作为URL格式,并使用Map<String ,String>指明URL参数
- 使用Java.net.URL作为URL格式,不支持参数化URL
要使用RestTemplate,可以在需要的地方创建实例,也可以声明为一个bean主要到需要的地方
RestTemplate restTemplate = new RestTemplate();
@Bean
public RestTamplate restTemplate() {
return new RestTemplate() ;
}
GET资源
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
RestTemplate restTemplate = new RestTemplate();
Ingredient ingredient = restTemplate.getForObject("http://localhost:8080/test",Ingredient.class);
System.out.println(ingredient);
}
}
可变参数Id
Ingredient ingredient = restTemplate.getForObject("http://localhost:8080/ingredients/{id}",Ingredient.class,ingredientId);
Map指定URL变量
public Ingredient get(String Id) {
Map<String,String> urlVariables = new HashMap<>;
urlVariables.put("id",Id);
return restTempalate.getForObject("http://localhost:8080/ingredients/{id}",Ingredient.class,urlVariables);
}
使用URL参数要稍微复杂一点,这种方式需要我们在调用getForObject之前构建一个URL对象。
public Ingredient get(String Id) {
Map<String,String> urlVariables = new HashMap<>;
urlVariables.put("id",Id);
URL url = UriComponentsBuider
.fromHttpUrl("http://localhost:8080/ingredients/{id}")
.build(urlVariables);
return restTempalate.getForObject(url,Ingredient.class);
}
其他put,delete,post方法与上述get方法非常相似。
使用Traverson导航REST API
Traverson来源于Spring Data HATEOAS项目,是Spring应用中开箱即用的消费超媒体API的解决方案。
Traverson traverson = new Traverson(
URI.create("http://localhost:8080/api"), MediaTypes.HAL_JSON);
ParameterizedTypeReference<CollectionModel<Ingredient>> ingredientType =
new ParameterizedTypeReference<CollectionModel<Ingredient>>() {};
CollectionModel<Ingredient> ingredientRes =
traverson.follow("ingredients").toObject(ingredientType);
System.out.println(ingredientRes)
记住重点是MediaTypes.HAL_JSON
要使用Traverson,首先我们要用基础URL实例化一个Traverson对象,在这里我们将URL指向了基础URL。从这里开始我们就可以根据链接的关系名来遍历API、我们同时还指定了API会生成JSON格式的响应,并且具有HAL风格的超链接,这样Traverson就知道怎么解析传入的数据了。与RestTemplate相似,可以实例化也可声明为Bean使用。
通过follow方法我们可以导航至链接关系名为ingredients的资源。我们需要调用toObject来提取资源的内容。
我们需要告诉toObject方法要将数据读取到哪种对象之中,考虑到我们需要以CollectionModel对象的形式读入,而且Java类型擦除使得为泛型提供类型信息变得非常困难,但是,我们通过创建 ParameterizedTypeReference能够解决这个问题。
我们可以轻松跟踪taco链接,找到我们想要的内容。所以基于ParameterizedTypeReference调用toObject方法,我们就得到了我们想要的内容。
ParameterizeTypeReference<CollectionModel<Taco>> tacoType =
new ParameterizedTypeReference<CollectionModel<Taco>>() {};
CollectionModel<Taco> tacoRes =
traverson.follow("tacos").follow("recents").toObject(tacoType);
Collection<Taco> tacos = tacoRes.getContent();
我们可以通过列出关系名称来简化follow方法:
Collection<Taco> tacoRes =
traverson.follow("tacos", "recents").toObject(tacoType);
结合
Traverson 可以轻松地引导启用了 HATEOAS 的 API 并调用其资源。但有一件事它没有提供任何方法来编写或删除这些 API。相比之下,RestTemplate 可以编写和删除资源,但不便于引导 API。
当需要同时引导 API 和更新或删除资源时,需要同时使用 RestTemplate 和 Traverson。Traverson 仍然可以用于引导到将创建新资源的链接。然后可以给 RestTemplate 一个链接来执行 POST、PUT、DELETE 或任何其他 HTTP 请求。
private Ingredient addIngredient(Ingredient ingredient) {
String ingredientsUrl = traverson.follow("ingredients")
.asLink().getHref();
return rest.postForObject(ingredientsUrl,
ingredient,
Ingredient.class);
}
在 follow Ingredient 链接之后,通过调用 asLink() 得到链接本身。在该链接中,通过调用 getHref() 得到链接的 URL。有了 URL,就有了在 RestTemplate 实例上调用 postForObject() 并保存新 Ingredient 所需的一切。