Spring restTemplate
什么是RestTemplate
RestTemplate是Spring提供的用于访问Rest服务的客户端,提供了多种便捷访问远程HTTP服务的方法,能够大大提高客户端的编写效率。
项目中注入RestTemplate
首先在项目中添加依赖:
<!-- Jackson对自动解析JSON和XML格式的支持 --> <dependency> <groupId>com.fasterxml.jackson.jaxrs</groupId> <artifactId>jackson-jaxrs-json-provider</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency> <!-- HttpClient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency>
在注入RestTemplate的bean的时候,可以通过ClientHtppRequestFactory指定RestTemplate发起HTTP请求的底层实现所采用的类库。对此,ClientHttpRequestFactory接口主要提供了以下两种方法:
一种是SimpleClientHttpRequestFactory,使用J2SE提供的方式(即java.net包提供的方式)创建底层的HTTP请求连接。
另一种是使用HttpComponentsClientHttpRequestFactory方式,底层使用HttpClient访问远程的http服务,使用HttpClient可以配置连接池和证书等信息。
以下的两个方法都采用线程安全的单例(懒汉模式)
(1)SimpleClientHttpRequestFactory
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; import org.springframework.stereotype.Component; import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.RestTemplate; import javax.annotation.PostConstruct; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @Component @Lazy(false) public class SimpleRestClient { private static final Logger LOGGER = LoggerFactory.getLogger(SimpleRestClient.class); private static RestTemplate restTemplate; static { SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); requestFactory.setReadTimeout(5000); requestFactory.setConnectTimeout(5000); // 添加转换器 List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8"))); messageConverters.add(new FormHttpMessageConverter()); messageConverters.add(new MappingJackson2XmlHttpMessageConverter()); messageConverters.add(new MappingJackson2HttpMessageConverter()); restTemplate = new RestTemplate(messageConverters); restTemplate.setRequestFactory(requestFactory); restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); LOGGER.info("SimpleRestClient初始化完成"); } private SimpleRestClient() { } @PostConstruct public static RestTemplate getClient() { return restTemplate; } }
(2)HttpComponentsClientHttpRequestFactory(推荐使用)
import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.http.Header; import org.apache.http.client.HttpClient; import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy; import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; import org.springframework.web.client.RestTemplate; @Configuration public class RestTemplateConfig { /** * 返回RestTemplate * @param factory * @return */ @Bean public RestTemplate restTemplate(ClientHttpRequestFactory factory){ //消息转换器,Spring Boot环境可省略,只需要添加相关依赖即可 // List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); // messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8"))); // messageConverters.add(new FormHttpMessageConverter()); // messageConverters.add(new MappingJackson2XmlHttpMessageConverter()); // messageConverters.add(new MappingJackson2HttpMessageConverter()); RestTemplate restTemplate = new RestTemplate(factory); // restTemplate.setMessageConverters(messageConverters); return restTemplate; } /** * ClientHttpRequestFactory接口的另一种实现方式(推荐使用),即: * HttpComponentsClientHttpRequestFactory:底层使用Httpclient连接池的方式创建Http连接请求 * @return */ @Bean public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory(){ //Httpclient连接池,长连接保持30秒 PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS); //设置总连接数 connectionManager.setMaxTotal(1000); //设置同路由的并发数 connectionManager.setDefaultMaxPerRoute(1000); //设置header List<Header> headers = new ArrayList<Header>(); headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.04")); headers.add(new BasicHeader("Accept-Encoding", "gzip, deflate")); headers.add(new BasicHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")); headers.add(new BasicHeader("Connection", "keep-alive")); //创建HttpClient HttpClient httpClient = HttpClientBuilder.create() .setConnectionManager(connectionManager) .setDefaultHeaders(headers) .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) //设置重试次数 .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()) //设置保持长连接 .build(); //创建HttpComponentsClientHttpRequestFactory实例 HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); //设置客户端和服务端建立连接的超时时间 requestFactory.setConnectTimeout(5000); //设置客户端从服务端读取数据的超时时间 requestFactory.setReadTimeout(5000); //设置从连接池获取连接的超时时间,不宜过长 requestFactory.setConnectionRequestTimeout(200); //缓冲请求数据,默认为true。通过POST或者PUT大量发送数据时,建议将此更改为false,以免耗尽内存 requestFactory.setBufferRequestBody(false); return requestFactory; } }
RestTemplate提供了很多方法,可以与HTTP方法对应。
HTTP方法 | RestTemplate方法 | 说明 |
---|---|---|
DELETE | delete() | 在特定的URL上对资源执行HTTP DELETE操作 |
GET |
getForEntity() getForObject() |
getForEntity():发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所映射成的对象。 getForObject():发送一个HTTP GET请求,返回根据响应体映射形成的对象 |
POST |
postForEntity() postForLocation() postForObject() |
postForEntity(): POST数据到一个URL,返回的ResponseEntity包含了响应体所映射成的对象。 postForLocation(): POST数据到一个URL,返回新创建资源的URL。 postForObject(): POST数据到一个URL,返回根据响应体映射形成的对象 |
PUT | put() | PUT资源到特定的URL |
HEAD | headForHeaders() | 发送HTTP HEAD请求,返回包含特定资源URL的HTTP头 |
OPTIONS | optionsForAllow() | 发送HTTP OPTIONS请求,返回对特定URL的Allow头信息 |
PATCH and others |
exchange() execute() |
exchange(): 在URL上执行特定的HTTP方法,返回的ResponseEntity包含了响应体所映射成的对象 execute():在URL上执行特定的HTTP方法,返回一个从响应体映射得到的对象 |
接下来讲一下RestTemplate的这几个方法如何使用。
GET请求方式的两种方法
1. getForEntity()
三个getForEntity()方法的签名:
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException; <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException; <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException;
示例代码:
/** * 测试Get请求返回详细信息,包括:响应正文、响应状态码、响应Header等 */ @Test public void testGetMethod3(){ //第一个参数为要调用的服务的地址,第二个参数为返回值的类型,第三和第四个参数时url中的传参 ResponseEntity<DemoObj> responseEntity = restTemplate.getForEntity("http://127.0.0.1:9090/rest/testJson2?id={1}&name={2}" , DemoObj.class , 1,"Tom"); DemoObj body = responseEntity.getBody(); int statusCodeValue = responseEntity.getStatusCodeValue(); HttpHeaders headers = responseEntity.getHeaders(); System.out.println("responseEntity.getBody():" + body); System.out.println("responseEntity.getStatusCodeValue():" + statusCodeValue); System.out.println("responseEntity.getHeaders():" + headers); }
结果:
responseEntity.getBody():DemoObj [id=2, name=Tom Ret] responseEntity.getStatusCodeValue():200 responseEntity.getHeaders():{Date=[Fri, 09 Feb 2018 06:22:28 GMT], Content-Type=[application/json;charset=utf-8], Transfer-Encoding=[chunked]}
再来看一个Map传参的例子:
@RequestMapping("/sayhello2") public String sayHello2() { Map<String, String> map = new HashMap<>(); map.put("name", "李四"); ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/sayhello?name={name}", String.class, map); return responseEntity.getBody(); }
当然,第一个参数也可以是URI而不是字符串,可以通过Spring中的UriComponents来构建uri即可。
@RequestMapping("/sayhello3") public String sayHello3() { UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://HELLO-SERVICE/sayhello?name={name}").build().expand("王五").encode(); URI uri = uriComponents.toUri(); ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class); return responseEntity.getBody(); }
2.getForObject()
getForObject()实际上是对getForEntity()的进一步封装,用法类似,唯一的区别就是getForObject()方法只返回请求类型的对象,而getForEntity()会返回请求的对象以及响应的Header,响应状态码等额外信息。
@RequestMapping("/book2") public Book book2() { Book book = restTemplate.getForObject("http://HELLO-SERVICE/getbook1", Book.class); return book; }
@Test public void testGetMethod2(){ Map<String, String> uriVariables = new HashMap<String, String>(); uriVariables.put("var_id", "1"); uriVariables.put("var_name", "Tom"); DemoObj obj = restTemplate.getForObject("http://127.0.0.1:9090/rest/testJson2?id={var_id}&name={var_name}" , DemoObj.class , uriVariables); System.out.println(obj); }
POST请求的三种方法
1. postForEntity()
POST请求和GET请求类似,也是三种方法:
<T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables) throws RestClientException; <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException; <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException;
看个示例:
@Test public void testPostMethod2(){ DemoObj request = new DemoObj(1l, "Tim"); ResponseEntity<DemoObj> responseEntity = restTemplate.postForEntity("http://127.0.0.1:9090/rest/testJson1" , request, DemoObj.class); DemoObj body = responseEntity.getBody(); int statusCodeValue = responseEntity.getStatusCodeValue(); HttpHeaders headers = responseEntity.getHeaders(); System.out.println("responseEntity.getBody():" + body); System.out.println("responseEntity.getStatusCodeValue():" + statusCodeValue); System.out.println("responseEntity.getHeaders():" + headers); }
结果:
responseEntity.getBody():DemoObj [id=2, name=Tim Ret] responseEntity.getStatusCodeValue():200 responseEntity.getHeaders():{Date=[Fri, 09 Feb 2018 06:32:02 GMT], Content-Type=[application/json;charset=utf-8], Transfer-Encoding=[chunked]}
2.postForObject()
@Test public void testPostMethod1(){ DemoObj request = new DemoObj(1l, "Tim"); DemoObj obj = restTemplate.postForObject("http://127.0.0.1:9090/rest/testJson1" , request, DemoObj.class); System.out.println(obj); }
3.postForLocation()
postForLocation()也是提交新资源,提交成功之后,返回新资源的URI,postForLocation的参数和前面两种的参数基本一致,只不过该方法的返回值为Uri,这个只需要服务提供者返回一个Uri即可,该Uri表示新资源的位置。
PUT请求
在RestTemplate中,PUT请求可以通过put方法调用,put方法的参数和前面介绍的postForEntity方法的参数基本一致,只是put方法没有返回值而已。
@RequestMapping("/put") public void put() { Book book = new Book(); book.setName("红楼梦"); restTemplate.put("http://HELLO-SERVICE/getbook3/{1}", book, 99); }
DELETE请求
@RequestMapping("/delete") public void delete() { restTemplate.delete("http://HELLO-SERVICE/getbook4/{1}", 100); }
exchange()方法执行指定的HTTP请求
exchange()方法同上面的很多方法不同的是,它可以指定请求的HTTP方式。
@Test public void testExchange(){ //设置header HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "application/x-zifangsky"); //设置参数 String requestBody = "1#Converter"; HttpEntity<String> requestEntity = new HttpEntity<String>(requestBody,headers); ResponseEntity<String> responseEntity = restTemplate.exchange("http://127.0.0.1:9090/convert" , HttpMethod.POST, requestEntity, String.class); System.out.println("responseEntity.getBody():" + responseEntity.getBody()); System.out.println("responseEntity.getHeaders():" + responseEntity.getHeaders()); }
手动指定转换器
调用restful接口传递的数据是json格式的字符串,返回的响应也是json格式的字符串。