一、RESTful API
Spring Boot 2 中引入的一批新注解中,除了 @GetMapping ,还有 @PutMapping、@PostMapping、@DeleteMapping 等注解,这些注解极大方便了开发人员显式指定 HTTP 的请求方法。当然,你也可以继续使用原先的 @RequestMapping 实现同样的效果。
业务操作 | 非RESTful | RESTful API |
---|---|---|
获取用户账号 | /account/query/1 | /accounts/1 GET |
新增用户账号 | /account/add | /accounts POST |
更新用户账号 | /account/edit | /accounts PUT |
删除用户账号 | /account/delete | accounts DELETE |
二、使用 RestTemplate 访问 HTTP 端点
RestTemplate 是 Spring 提供的用于访问 RESTful 服务的客户端的模板工具类,它位于 org.springframework.web.client 包下。
在设计上,RestTemplate 完全可以满足 RESTful 架构风格的设计原则。相较传统 Apache 中的 HttpClient 客户端工具类,RestTemplate 在编码的简便性以及异常的处理等方面都做了很多改进。
接下来,我们先来看一下如何创建一个 RestTemplate 对象,并通过该对象所提供的大量工具方法实现对远程 HTTP 端点的高效访问。
创建 RestTemplate
如果我们想创建一个 RestTemplate 对象,最简单且最常见的方法是直接 new 一个该类的实例,如下代码所示:
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
这里我们创建了一个 RestTemplate 实例,并通过 @Bean 注解将其注入 Spring 容器中。
通常,我们会把上述代码放在 Spring Boot 的 Bootstrap 类中,使得我们在代码工程的其他地方也可以引用这个实例。
下面我们查看下 RestTemplate 的无参构造函数,看看创建它的实例时,RestTemplate 都做了哪些事情,如下代码所示:
public RestTemplate() {
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter(false));
this.messageConverters.add(new SourceHttpMessageConverter<>());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
//省略其他添加 HttpMessageConverter 的代码
}
可以看到 RestTemplate 的无参构造函数只做了一件事情,添加了一批用于实现消息转换的 HttpMessageConverter 对象。
我们知道通过 RestTemplate 发送的请求和获取的响应都是以 JSON 作为序列化方式,而我们调用后续将要介绍的 getForObject、exchange 等方法时所传入的参数及获取的结果都是普通的 Java 对象,我们就是通过使用 RestTemplate 中的 HttpMessageConverter 自动帮我们做了这一层转换操作。
这里请注意,其实 RestTemplate 还有另外一个更强大的有参构造函数,如下代码所示:
public RestTemplate(ClientHttpRequestFactory requestFactory) {
this();
setRequestFactory(requestFactory);
}
从以上代码中,我们可以看到这个构造函数一方面调用了前面的无参构造函数,另一方面可以设置一个 ClientHttpRequestFactory 接口。而基于这个 ClientHttpRequestFactory 接口的各种实现类,我们可以对 RestTemplate 中的行为进行精细化控制。
这方面典型的应用场景是设置 HTTP 请求的超时时间等属性,如下代码所示:
@Bean
public RestTemplate customRestTemplate(){
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpRequestFactory.setConnectionRequestTimeout(3000);
httpRequestFactory.setConnectTimeout(3000);
httpRequestFactory.setReadTimeout(3000);
return new RestTemplate(httpRequestFactory);
}
这里我们创建了一个 HttpComponentsClientHttpRequestFactory 工厂类,它是 ClientHttpRequestFactory 接口的一个实现类。通过设置连接请求超时时间 ConnectionRequestTimeout、连接超时时间 ConnectTimeout 等属性,我们对 RestTemplate 的默认行为进行了定制化处理。
除了实现常规的 HTTP 请求,RestTemplate 还有一些高级用法可供我们进行使用,如指定消息转换器、设置拦截器和处理异常等。
指定消息转换器
在 RestTemplate 中,实际上还存在第三个构造函数,如下代码所示:
public RestTemplate(List<HttpMessageConverter<?>> messageConverters) {
Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required");
this.messageConverters.addAll(messageConverters);
}
从以上代码中不难看出,我们可以通过传入一组 HttpMessageConverter 来初始化 RestTemplate,这也为消息转换器的定制提供了途径。
假如,我们希望把支持 Gson 的 GsonHttpMessageConverter 加载到 RestTemplate 中,就可以使用如下所示的代码。
@Bean
public RestTemplate restTemplate() {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.add(new GsonHttpMessageConverter());
RestTemplate restTemplate = new RestTemplate(messageConverters);
return restTemplate;
}
原则上,我们可以根据需要实现各种自定义的 HttpMessageConverter ,并通过以上方法完成对 RestTemplate 的初始化。
设置拦截器
如果我们想对请求做一些通用拦截设置,那么我们可以使用拦截器。不过,使用拦截器之前,首先我们需要实现 ClientHttpRequestInterceptor 接口。
这方面最典型的应用场景是在 Spring Cloud 中通过 @LoadBalanced 注解为 RestTemplate 添加负载均衡机制。我们可以在 LoadBalanceAutoConfiguration 自动配置类中找到如下代码。
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
在上面代码中,我们可以看到这里出现了一个 LoadBalancerInterceptor 类,该类实现了 ClientHttpRequestInterceptor 接口,然后我们通过调用 setInterceptors 方法将这个自定义的 LoadBalancerInterceptor 注入 RestTemplate 的拦截器列表中。
处理异常
请求状态码不是返回 200 时,RestTemplate 在默认情况下会抛出异常,并中断接下来的操作,如果我们不想采用这个处理过程,那么就需要覆盖默认的 ResponseErrorHandler。示例代码结构如下所示:
RestTemplate restTemplate = new RestTemplate();
ResponseErrorHandler responseErrorHandler = new ResponseErrorHandler() {
@Override
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
return true;
}
@Override
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
//添加定制化的异常处理代码
}
};
restTemplate.setErrorHandler(responseErrorHandler);
在上述的 handleError 方法中,我们可以实现任何自己想控制的异常处理代码。