Spring RestTemplate

Wu Jun 2019-12-25 15:59:03
Categories: > Tags:

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

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

1 组成

RestTemplate 包含以下几个部分:

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
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)
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
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)
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;
}
@Slf4j
@Configuration
public class RestClientConfig {

    @Value("${httpClient.maxTotal:400}")
    private Integer maxTotal;
    @Value("${httpClient.defaultMaxPerRoute:400}")
    private Integer defaultMaxPerRoute;
    @Value("${httpClient.timeout:20000}")
    private Integer timeout;

    @Value("${proxy.enabled}")
    Boolean proxyEnable;

    @Bean
    public RestTemplate restTemplate() {
        ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
        return new RestTemplate(requestFactory);
    }

    @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(maxTotal);
        connectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);

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

        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(connectionManager)
                .setConnectionManagerShared(true);

        log.info("proxyEnable: " + proxyEnable);
        if (proxyEnable) {
            //设置代理
            HttpHost proxy = new HttpHost("web-proxy.oa.com", 8080);
            httpClientBuilder.setProxy(proxy);
        }

        return httpClientBuilder.build();
    }

}

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);