学习springboot ,RestTemplate的使用场景非常非常多,比如springcloud中的服务消费。
我以前还自己去写http请求相关的交互,用的比较多的是apache httpcomponents ,后来在学springboot的过程中发现Spring的RestTemplate提供了一些更高级别的方法来满足我们的功能。后来就把项目中原来的http交互都改成了RestTemplate。
下面来说说我在学习中的一些记录和遇到的问题(还是在使用的角度,具体源码剖析,大家可以自己翻看源码):
1.RestTemplate 的引入:
先来看下RestTemplate 的类路径:
org.springframework.web.client.RestTemplate
可以通过上面的路径看出RestTemplate 是web下,项目中只需要加入spring-web的依赖就可以了。我现在使用的spring版本是4.3.9.RELEASE。由于项目是基于springboot 的
在spring-boot-starter-web中已经有了它的依赖。
2.RestTemplate 构造:
RestTemplate有两个构造方法,分别是:
public RestTemplate() {
/**
...初始化过程
*/
}
public RestTemplate(ClientHttpRequestFactory requestFactory) {
this();
setRequestFactory(requestFactory);
}
其中,第一个进行默认初始化,没法进行更多的限制和后续处理。如:设置超时时间...。第二个构造方法中可以传入ClientHttpRequestFactory参数,ClientHttpRequestFactory接口的实现类中存在timeout属性等等
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(1000);
requestFactory.setReadTimeout(1000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
我们可以在springboot的某个自定义的configure类中的restTemplate 构造方法上添加
@Bean
RestTemplate restTemplate(){
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(1000);
requestFactory.setReadTimeout(1000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
将RestTemplate 实例注入spring容器中。
调用时可以通过:
@Autowired
private RestTemplate restTemplate ;
来使用。
3.RestTemplate 对HTTP Method的支持:
大家可以在图中看到,Spring的RestTemplate提供了对这么多HTTP method的支持。一般来说大家对GET,POST的使用场景比较多,因此下面以这两个为例,简单的说下它的使用。
4.RestTemplate 使用实例(简单):
GET:
public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables) throws RestClientException
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> urlVariables) throws RestClientException
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException
使用方法:
String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42", "21");
Map<String, String> vars = Collections.singletonMap("hotel", "42");
String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
POST:
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException
public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException
使用方法:
MultiValueMap<String, String> bodyMap = new LinkedMultiValueMap<String, String>();
bodyMap.setAll(urlVariables);
ResponseClass responseClass = restTemplate.postForObject(CAR_CES_URL, bodyMap, ResponseClass.class);
//更完整的:
HttpHeaders headers = new HttpHeaders();
headers.add("X-Auth-Token", "e348bc22-5efa-4299-9142-529f07a18ac9");
MultiValueMap<String, String> postParameters = new LinkedMultiValueMap<String, String>();
postParameters.add("owner", "11");
postParameters.add("subdomain", "aoa");
postParameters.add("comment", "");
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(postParameters, headers);
ParseResultVo exchange = null;
try {
exchange = restTemplate.postForObject("http://demo", requestEntity, ParseResultVo.class);
logger.info(exchange.toString());
} catch (RestClientException e) {
logger.info("。。。。");
}
5.其他相关-异步调用(AsyncRestTemplate):
在很多场景中我们需要异步调用,我们使用RestTemplate的兄弟类AsyncRestTemplate。 AsyncRestTemplate是在Spring4.0中对RestTemplate进行扩展产生的新类,其为客户端提供了异步http请求处理的一种机制,通过返回ListenableFuture对象生成回调机制,以达到异步非阻塞发送http请求。
public String asyncReq(){
String url = "http://localhost:8080/jsonAsync";
ListenableFuture<ResponseEntity<JSONObject>> future = asyncRestTemplate.getForEntity(url, JSONObject.class);
future.addCallback(new SuccessCallback<ResponseEntity<JSONObject>>() {
public void onSuccess(ResponseEntity<JSONObject> result) {
System.out.println(result.getBody().toJSONString());
}
}, new FailureCallback() {
public void onFailure(Throwable ex) {
System.out.println("onFailure:"+ex);
}
});
return "this is async sample";
}
我这里使用的是futrue,可以带返回参数的。这是java多线程中的一部分内容。如果有时间我会另起一篇简单的说下Java的多线程。
6.请求ssl
参考我的另一篇文章:
RestTemplate设置headers,访问https实现ssl请求
7.遇到的问题:
我在写微信的请求调用的时候出现了问题,微信在文档中说返回的是json数据。但实际返回确是:text/plain。这时直接使用会出现类型转换的错误:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.solar.app.model.weixin.WxBaseUserInfo] and content type [text/plain]
但是由于默认构造的 MappingJackson2HttpMessageConverter(大家可以翻看源码) 中的 supportedMediaTypes 只支持:application/json 的 MediaType。
为此我们必须添加对它的支持:
public class WxMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
public WxMappingJackson2HttpMessageConverter(){
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.TEXT_PLAIN);
setSupportedMediaTypes(mediaTypes);
}
}
我既不推荐把 WxMappingJackson2HttpMessageConverter 实例当作构造 RestTemplate 时的参数来构造 RestTemplate,也不推荐 使用新的 WxMappingJackson2HttpMessageConverter 替换 RestTemplate 默认构造中创建的 MappingJackson2HttpMessageConverter 实例,因为这两种方式都会导致 Content-Type 为 application/json 的 Json 响应没有转换器来反序列化,所以最佳的方式还是“追加”。
@Bean
RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());
return restTemplate;
}
最后贴上一个完整实例:
@Configuration
public class RestTemplateConfig {
@Bean
@ConditionalOnMissingBean({ RestOperations.class, RestTemplate.class })
//Spring Boot的自动配置机制依靠@ConditionalOnMissingBean注解判断是否执行初始化代码,
// 即如果用户已经创建了bean,则相关的初始化代码不再执行。
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
// return new RestTemplate(factory);
RestTemplate restTemplate = new RestTemplate(factory);
// 使用 utf-8 编码集的 conver 替换默认的 conver(默认的 string conver 的编码集为"ISO-8859-1")
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
Iterator<HttpMessageConverter<?>> iterator = messageConverters.iterator();
while (iterator.hasNext()) {
HttpMessageConverter<?> converter = iterator.next();
if (converter instanceof StringHttpMessageConverter) {
iterator.remove();
}
}
messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
//解决微信返回text/plain的解析
restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());
return restTemplate;
}
@Bean
@ConditionalOnMissingBean({ClientHttpRequestFactory.class})
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(15000);// ms
factory.setConnectTimeout(15000);// ms
return factory;
}
}
7.参考资料:
RestTemplate 微信接口 text/plain HttpMessageConverter
基于AsyncRestTemplate异步HTTP请求的一种轻量级技术实现