Spring RestTemplate

RestTemplate 属于 Spring-Web,是 Spring 的同步客户端HTTP访问的中心类。简化了与 HTTP 服务器的通信,并应用了 RESTful 原则。

RestTemplate 默认依赖 JDK 的 HttpURLConnection 来建立 HTTP 连接。 可切换到使用不同的 HTTP 库,例如 Apache HttpComponents,Netty 和 OkHttp。

1 组成

RestTemplate 包含以下几个部分:

  • HttpMessageConverter:对象转换器
  • ClientHttpRequestFactory:客户端连接器,默认是 JDK 的 HttpURLConnection
  • ResponseErrorHandler:异常处理
  • ClientHttpRequestInterceptor:请求拦截器

img

2 初始化

初始化时,可以传入 ClientHttpRequestFactory,自定义参数。

@Bean
RestTemplate restTemplate(){
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
//设置超时时间
requestFactory.setConnectTimeout(1000);
requestFactory.setReadTimeout(1000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}

注入

@Autowired
private RestTemplate restTemplate;

3 访问服务

3.1 HTTP 方法

使用 java.net.URI 代替 String 形式的 URI,不会被 URL 编码两次
以 get 和 post 为例,更多见 官网api

1)GET
  • getForObject()
public <T> T getForObject(URI url, Class<T> responseType)
public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables)
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> urlVariables)
  • getForEntity()
public <T> ResponseEntity<T> getForEntity(URI url,Class<T> responseType)
public <T> ResponseEntity<T> getForEntity(String url,Class<T> responseType,Object... uriVariables)
public <T> ResponseEntity<T> getForEntity(String url,Class<T> responseType,Map<String,?> uriVariables)
2)POST
  • postForObject()
public <T> T postForObject(URI url,Object request,Class<T> responseType)
public <T> T postForObject(String url,Object request,Class<T> responseType,Object... uriVariables)
public <T> T postForObject(String url,Object request,Class<T> responseType,Map<String,?> uriVariables)
  • postForEntity()
public <T> ResponseEntity<T> postForEntity(String url,@NullableObject request,Class<T> responseType,Object... uriVariables)
public <T> ResponseEntity<T> postForEntity(String url,Object request,Class<T> responseType,Map<String,?> uriVariables)
public <T> ResponseEntity<T> postForEntity(URI url,Object request,Class<T> responseType)
3)实例
HttpHeaders headers = new HttpHeaders();
headers.add("X-Auth-Token", "");

MultiValueMap<String, String> postParameters = new LinkedMultiValueMap<String, String>();
postParameters.add("parameter1", "111");
postParameters.add("parameter2", "222");
postParameters.add("parameter3", "333");

HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(postParameters, headers);

Object result = null;

try {
result = restTemplate.postForObject("http://demo", requestEntity, ParseResultVo.class);
} catch (RestClientException e) {
}

4 异常处理

1)捕获 HttpServerErrorException
int retryCount = 0;  
while (true) {
try {
responseEntity = restTemplate.exchange(requestEntity, String.class);
break;
} catch (HttpServerErrorException e) {
if (retryCount == 3) {
throw e;
}
retryCount++;
}
}
2)自定义异常处理
public class CustomErrorHandler extends DefaultResponseErrorHandler {  

@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return true;
}

@Override
public void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
if(statusCode.isError()){
switch (statusCode.series()) {
case CLIENT_ERROR:
throw new HttpClientErrorException(statusCode, response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
case SERVER_ERROR:
throw new HttpServerErrorException(statusCode, response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
default:
throw new UnknownHttpStatusCodeException(statusCode.value(), response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
}
}
}

}

@Configuration
public class RestClientConfig {

@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new CustomErrorHandler());
return restTemplate;
}

}

5 设置连接池

@Configuration  
public class RestClientConfig {

@Bean
public ClientHttpRequestFactory httpRequestFactory() {
return new HttpComponentsClientHttpRequestFactory(httpClient());
}

@Bean
public RestTemplate restTemplate() {
return new RestTemplate(httpRequestFactory());
}

@Bean
public HttpClient httpClient() {
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(20);

RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(8000)
.setConnectTimeout(8000)
.setConnectionRequestTimeout(8000)
.build();

return HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(connectionManager)
.setConnectionManagerShared(true)//设置共享连接池
.build();
}

}

6 处理文件

6.1 发送文件

MultiValueMap<String, Object> multiPartBody = new LinkedMultiValueMap<>();  
multiPartBody.add("file", new ClassPathResource("/tmp/user.txt"));
RequestEntity<MultiValueMap<String, Object>> requestEntity = RequestEntity
.post(uri)
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(multiPartBody);

6.2 下载文件

// 小文件  
RequestEntity requestEntity = RequestEntity.get(uri).build();
ResponseEntity<byte[]> responseEntity = restTemplate.exchange(requestEntity, byte[].class);
byte[] downloadContent = responseEntity.getBody();

// 大文件
ResponseExtractor<ResponseEntity<File>> responseExtractor = new ResponseExtractor<ResponseEntity<File>>() {
@Override
public ResponseEntity<File> extractData(ClientHttpResponse response) throws IOException {
File rcvFile = File.createTempFile("rcvFile", "zip");
FileCopyUtils.copy(response.getBody(), new FileOutputStream(rcvFile));
return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders()).body(rcvFile);
}
};
File getFile = this.restTemplate.execute(targetUri, HttpMethod.GET, null, responseExtractor);

7 Spring Boot

RestTemplateBuilder

@Component  
public class CustomRestTemplateCustomizer implements RestTemplateCustomizer {
@Override
public void customize(RestTemplate restTemplate) {
new RestTemplateBuilder()
.detectRequestFactory(false)
.basicAuthorization("username", "password")
.uriTemplateHandler(new OkHttp3ClientHttpRequestFactory())
.errorHandler(new CustomResponseErrorHandler())
.configure(restTemplate);
}
}

单独设置

@Service  
public class MyRestClientService {

private RestTemplate restTemplate;

public MyRestClientService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder
.basicAuthorization("username", "password")
.setConnectTimeout(3000)
.setReadTimeout(5000)
.rootUri("http://api.example.com/")
.errorHandler(new CustomResponseErrorHandler())
.additionalMessageConverters(new CustomHttpMessageConverter())
.uriTemplateHandler(new OkHttp3ClientHttpRequestFactory())
.build();
}

public String site() {
return this.restTemplate.getForObject("http://rensanning.iteye.com/", String.class);
}

}

8 参数设置

8.1 指定转换器

RestTemplate 默认注册了一组 HttpMessageConverter 用来处理一些不同的 contentType 的请求。

StringHttpMessageConverter 来处理 text/plain;

MappingJackson2HttpMessageConverter 来处理 application/json;

MappingJackson2XmlHttpMessageConverter 来处理 application/xml。

可实现 org.springframework.http.converter.HttpMessageConverter 接口自己写一个转换器。

替换例子:

RestTemplate restTemplate = new RestTemplate();

//获取RestTemplate默认配置好的所有转换器
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();

//默认的MappingJackson2HttpMessageConverter在第7个 先把它移除掉
messageConverters.remove(6);
//添加上GSON的转换器
messageConverters.add(6, new GsonHttpMessageConverter());

8.2 设置底层连接方式

通过构造参数设置,以切换 HttpClient 为例

//生成一个设置了连接超时时间、请求超时时间、异常最大重试次数的 httpClient
RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(10000).setConnectTimeout(10000).setSocketTimeout(30000).build();

HttpClientBuilder builder = HttpClientBuilder.create().setDefaultRequestConfig(config).setRetryHandler(new DefaultHttpRequestRetryHandler(5, false));

HttpClient httpClient = builder.build();

//使用httpClient创建一个 ClientHttpRequestFactory 的实现
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);

//ClientHttpRequestFactory作为参数构造一个使用作为底层的 RestTemplate
RestTemplate restTemplate = new RestTemplate(requestFactory);

8.3 设置拦截器

拦截器需要我们实现 org.springframework.http.client.ClientHttpRequestInterceptor 接口。

public class TokenInterceptor implements ClientHttpRequestInterceptor{ 
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
//请求地址
String checkTokenUrl = request.getURI().getPath();
//请求方法名 POST、GET等
String methodName = request.getMethod().name();
//请求内容
String requestBody = new String(body);
//……
return execution.execute(request, body);
}
}

创建 RestTemplate 实例的时候,添加拦截器

RestTemplate restTemplate = new RestTemplate();

//向restTemplate中添加自定义的拦截器
restTemplate.getInterceptors().add(new TokenInterceptor());

8.4 使用 Proxy

RestTemplate
@Bean
RestTemplate restTemplate(){
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("your.proxy.server", 8080));
requestFactory.setProxy(proxy);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
System properties
Properties props = System.getProperties();
props.put("https.proxyHost", "your.proxy.server");
props.put("https.proxyPort", "8080");
props.put("http.proxyHost", "your.proxy.server");
props.put("http.proxyPort", "8080");

RestTemplate restTemplate = new RestTemplate();
String tt = restTemplate.getForObject("https://baike.baidu.com/",String.class);
System.out.println(tt);
Author: iMine
Link: https://imine141.github.io/2020/07/21/Spring/Web/Spring%20RestTemplate/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.