RestTemplate
是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。
getForObject
:Retrieves a representation via GET
.getForEntity
:Retrieves a ResponseEntity
(that is, status, headers, and body) by using GET.headForHeaders
:Retrieves all headers for a resource by using HEAD
.postForLocation
:Creates a new resource by using POST
and returns the Location header
from the response.postForObject
:Creates a new resource by using POST
and returns the representation from the response.postForEntity
:Creates a new resource by using POST
and returns the representation from the response.put
:Creates or updates a resource by using PUT
.delete
:Deletes the resources at the specified URI by using DELETE
.exchange
:More generalized (and less opinionated) version of the preceding methods that provides extra flexibility when needed. It accepts a RequestEntity
(including HTTP method, URL, headers, and body as input) and returns a ResponseEntity
.execute
:The most generalized way to perform a request, with full control over request preparation and response extraction through callback
interfaces.getForObject - delete:提供常规的 Rest API(GET、POST、DELETE 等)方法调用;
exchange:接收一个 RequestEntity 参数,可以自己设置 HTTP method,URL,headers 和 body,返回 ResponseEntity;
execute:通过 callback 接口,可以对请求和返回做更加全面的自定义控制。
一般情况下,我们使用第一组和第二组方法就够了。
依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
将RestTemplate
配置初始化为一个Bean
:
@Configuration
public class RestTemplateConfig {
/**
* 没有实例化RestTemplate时,初始化RestTemplate
*/
@ConditionalOnMissingBean(RestTemplate.class)
@Bean
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
}
默认使用了 JDK 自带的HttpURLConnection
作为底层 HTTP 客户端实现,可以使用ClientHttpRequestFactory
指定不同的 HTTP 连接方式。比如,将其改成HttpClient
客户端:
@Configuration
public class RestTemplateConfig {
@ConditionalOnMissingBean(RestTemplate.class)
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory){
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
return restTemplate;
}
/**
* 使用HttpClient作为底层客户端
* @return
*/
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory() {
int timeout = 5000;
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout)
.setConnectionRequestTimeout(timeout)
.setSocketTimeout(timeout)
.build();
CloseableHttpClient client = HttpClientBuilder
.create()
.setDefaultRequestConfig(config)
.build();
return new HttpComponentsClientHttpRequestFactory(client);
}
}
在需要使用RestTemplate的位置,注入并使用即可!
@Autowired
private RestTemplate restTemplate;
从开发人员的反馈,和网上的各种 HTTP 客户端性能以及易用程度评测来看,OkHttp
优于 Apache的HttpClient
、Apache的HttpClient
优于HttpURLConnection
。
因此,我们还可以通过如下方式,将底层的 HTTP 客户端换成OkHttp
:
/**
* 使用OkHttpClient作为底层客户端
* @return
*/
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory() {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.build();
return new OkHttp3ClientHttpRequestFactory(okHttpClient);
}
如果需要配置http连接管理器,完整配置如下:
@Configuration
public class RestTemplateConfig {
/**
* http连接管理器
* @return
*/
@Bean
public HttpClientConnectionManager poolingHttpClientConnectionManager() {
/*// 注册http和https请求
Registry registry = RegistryBuilder.create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(registry);*/
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
// 最大连接数
poolingHttpClientConnectionManager.setMaxTotal(500);
// 同路由并发数(每个主机的并发)
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);
return poolingHttpClientConnectionManager;
}
/**
* HttpClient
* @param poolingHttpClientConnectionManager
* @return
*/
@Bean
public HttpClient httpClient(HttpClientConnectionManager poolingHttpClientConnectionManager) {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
// 设置http连接管理器
httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
/*// 设置重试次数
httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true));*/
// 设置默认请求头
/*List headers = new ArrayList<>();
headers.add(new BasicHeader("Connection", "Keep-Alive"));
httpClientBuilder.setDefaultHeaders(headers);*/
return httpClientBuilder.build();
}
/**
* 请求连接池配置
* @param httpClient
* @return
*/
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
// httpClient创建器
clientHttpRequestFactory.setHttpClient(httpClient);
// 连接超时时间/毫秒(连接上服务器(握手成功)的时间,超出抛出connect timeout)
clientHttpRequestFactory.setConnectTimeout(5 * 1000);
// 数据读取超时时间(socketTimeout)/毫秒(务器返回数据(response)的时间,超过抛出read timeout)
clientHttpRequestFactory.setReadTimeout(10 * 1000);
// 连接池获取请求连接的超时时间,不宜过长,必须设置/毫秒(超时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool)
clientHttpRequestFactory.setConnectionRequestTimeout(10 * 1000);
return clientHttpRequestFactory;
}
/**
* rest模板
* @return
*/
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
// boot中可使用RestTemplateBuilder.build创建
RestTemplate restTemplate = new RestTemplate();
// 配置请求工厂
restTemplate.setRequestFactory(clientHttpRequestFactory);
return restTemplate;
}
}
getForObject()
:返回值是 HTTP 协议的响应体
getForEntity()
:返回的是ResponseEntity
,ResponseEntity
是对 HTTP 响应的封装,除了包含响应体,还包含HTTP状态码、contentType、contentLength、Header等信息服务端:
@RestController
public class HelloController {
@GetMapping("/testGet/{name}/{age}")
public ResponseBean testGet(@PathVariable("name") String name, @PathVariable("age") int age){
System.out.println("name="+name+",age="+age);
ResponseBean response = new ResponseBean();
response.setCode("200");
response.setMsg("请求成功,方法:testGetByRestFul,请求参数id:" + name + "、请求参数name:" + age);
return response;
}
}
响应类:
@Data
public class ResponseBean {
private String code;
private String msg;
}
请求端:
@RestController
public class TestRestTemplateController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/testRestGet1")
public void testRestGet1(){
//请求地址
String url = "http://localhost:8080/testGet/{1}/{2}";
//发起请求,直接返回对象(restful风格)
ResponseBean response = restTemplate.getForObject(url, ResponseBean.class, "lili", 23);
System.out.println(response);
}
}
访问地址:http://192.168.1.166:8080/testRestGet1
服务端:
@RestController
public class HelloController {
@GetMapping("/testGet2")
public ResponseBean testGet2(@RequestParam("name") String name, @RequestParam("age") int age){
System.out.println("name="+name+",age="+age);
ResponseBean response = new ResponseBean();
response.setCode("200");
response.setMsg("请求成功,方法:testGetByParam,请求参数id:" + name + "、请求参数name:" + age);
return response;
}
}
请求端:
@RestController
public class TestRestTemplateController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/testRestGet2")
public void testRestGet2(){
//请求地址
String url = "http://localhost:8080/testGet2?name={name}&age={age}";
//请求参数
HashMap<String, Object> map = new HashMap<>();
map.put("name","lili");
map.put("age",25);
//发起请求,直接返回对象(带参数请求)
ResponseBean responseBean = restTemplate.getForObject(url, ResponseBean.class, map);
System.out.println(responseBean.toString());
}
}
访问地址:http://192.168.1.166:8080/testRestGet2
使用name={name}&age={age}
这种形式,最后一个参数是一个 map,map 的 key 即为前边占位符的名字,map 的 value 为参数值。
如果你只关注返回的消息体的内容,对其他信息都不关注,此时可以使用
getForObject
。
上面的所有的getForObject
请求传参方法,getForEntity
都可以使用,使用方法上也几乎是一致的,只是在返回结果接收的时候略有差别。
getForEntity
方法的返回值是一个ResponseEntity
,ResponseEntity
是 Spring 对 HTTP 请求响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、响应消息体等。responseEntity.getBody()
获取响应体。
请求端:
@RestController
public class TestRestTemplateController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/testRestGet2")
public void testRestGet2(){
//请求地址
String url = "http://localhost:8080/testGet2?name={name}&age={age}";
//请求参数
HashMap<String, Object> map = new HashMap<>();
map.put("name","lili");
map.put("age",25);
//发起请求
ResponseEntity<ResponseBean> response = restTemplate.getForEntity(url, ResponseBean.class, map);
// 获取响应体
System.out.println("HTTP 响应body:[" + response.getBody().toString() + "]");
// 以下是getForEntity比getForObject多出来的内容
HttpStatus statusCode = response.getStatusCode();
int statusCodeValue = response.getStatusCodeValue();
HttpHeaders headers = response.getHeaders();
System.out.println("HTTP 响应状态:[" + statusCode + "]");
System.out.println("HTTP 响应状态码:[" + statusCodeValue + "]");
System.out.println("HTTP Headers信息:[" + headers + "]");
}
}
访问地址:http://192.168.1.166:8080/testRestGet2
注1:restTemplate 会根据 params 的具体类型,调用合适的 HttpMessageConvert 将请求参数写到请求体 body 中,并在请求头中添加合适的 content-type;
注2:也会根据 responseType 的类型(本例返回类类型,默认是 application/json),设置 head 中的 accept 字段,当响应返回的时候再调用合适的 HttpMessageConvert 进行响应转换。
其实 POST 请求方法和 GET 请求方法上大同小异,RestTemplate 的 POST 请求也包含两个主要方法:
postForObject()
:返回body
对象postForEntity()
:返回全部的信息服务端:
@RestController
public class HelloController {
@PostMapping("testPost")
public ResponseBean testPost(@RequestParam("name") String name,
@RequestParam("age") String age){
ResponseBean result = new ResponseBean();
result.setCode("200");
result.setMsg("请求成功,方法:testPostByForm,请求参数name:" + name + ",age:" + age);
return result;
}
}
请求类:
@Data
public class RequestBean {
private String name;
private String age;
}
请求端:
@RestController
public class TestRestTemplateController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/testRestPost1")
public void testRestPost1(){
// 1. 请求地址
String url = "http://localhost:8080/testPost";
// 2.1 请求头设置,x-www-form-urlencoded格式的数据
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// 2.2 提交参数设置
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("name", "唐三藏");
map.add("age", "25");
// 2.3组装请求体
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
// 3. 发起请求
ResponseBean responseBean = restTemplate.postForObject(url, request, ResponseBean.class);
System.out.println(responseBean.toString());
}
}
访问地址:http://192.168.1.166:8080/testRestPost1
POST请求报文体需要使用MultiValueMap
,不能使用HashMap
。
服务端:
@RestController
public class HelloController {
@PostMapping("testPost2")
public ResponseBean testPost2(@RequestBody RequestBean request){
ResponseBean result = new ResponseBean();
result.setCode("200");
result.setMsg("请求成功,方法:testPostByFormAndObj,请求参数:" + request);
return result;
}
}
请求端:
@RestController
public class TestRestTemplateController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/testRestPost2")
public void testRestPost2(){
// 1. 请求地址
String url = "http://localhost:8080/testPost2";
// 2. 入参
RequestBean request = new RequestBean();
request.setName("唐三藏");
request.setAge("23");
// 3. 发起请求
ResponseBean responseBean = restTemplate.postForObject(url, request, ResponseBean.class);
System.out.println(responseBean.toString());
}
}
访问地址:http://192.168.1.166:8080/testRestPost2
上面的所有的postForObject
请求传参方法,postForEntity
都可以使用,使用方法上也几乎是一致的,只是在返回结果接收的时候略有差别。
请求端:
@RestController
public class TestRestTemplateController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/testRestPost2")
public void testRestPost2(){
// 1. 请求地址
String url = "http://localhost:8080/testPost2";
// 2. 入参
RequestBean request = new RequestBean();
request.setName("唐三藏");
request.setAge("23");
// 3. 发起请求
ResponseEntity<ResponseBean> response = restTemplate.postForEntity(url, request, ResponseBean.class);
System.out.println("HTTP 响应状态码:[" + response.getBody() + "]");
System.out.println("HTTP 响应状态:[" + response.getStatusCode() + "]");
System.out.println("HTTP 响应状态码:[" + response.getStatusCodeValue() + "]");
System.out.println("HTTP Headers信息:[" + response.getHeaders() + "]");
}
}
访问地址:http://192.168.1.166:8080/testRestPost2
postForLocation
用于页面重定向
,postForLocation 的参数和前面两种的参数基本一致,只不过该方法的返回值为 URI ,这个只需要服务提供者返回一个 URI 即可,该 URI 表示新资源的位置。
服务端:
@RestController
public class HelloController {
@PostMapping("testPostByLocation")
public String testPostByLocation(@RequestBody RequestBean request){
return "redirect:index.html";
}
}
请求端:
@RestController
public class TestRestTemplateController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/testRestLocation")
public void testRestLocation(){
// 1. 请求地址
String url = "http://localhost:8080/testPostByLocation";
// 2. 入参
RequestBean request = new RequestBean();
request.setName("唐三藏");
request.setAge("23");
// 3. 用于提交完成数据之后的页面跳转,返回跳转url
URI uri = restTemplate.postForLocation(url, request);
System.out.println(uri.toString());
}
}
PUT
方法的参数和前面介绍的 postForEntity 方法的参数基本一致,只是 put 方法没有返回值而已。它指的是修改一个已经存在的资源或者插入资源,该方法会向URL代表的资源发送一个HTTP PUT
方法请求。
服务端:
@RestController
public class HelloController {
/**
* 模拟JSON请求,put方法测试
*/
@PutMapping("testPutByJson")
public void testPutByJson(@RequestBody RequestBean request){
System.out.println("请求成功,方法:testPutByJson,请求参数:" + request);
}
}
请求端:
@RestController
public class TestRestTemplateController {
@Autowired
RestTemplate restTemplate;
/**
* 模拟JSON提交,put请求
*/
@RequestMapping("/testPutByJson")
public void testPutByJson(){
//请求地址
String url = "http://localhost:8080/testPutByJson";
//入参
RequestBean request = new RequestBean();
request.setName("唐三藏");
request.setAge("23");
//模拟JSON提交,put请求
restTemplate.put(url, request);
}
}
delete
方法协议,表示删除一个已经存在的资源,该方法会向URL代表的资源发送一个HTTP DELETE
方法请求。
服务端:
@RestController
public class HelloController {
/**
* 模拟JSON请求,delete方法测试
*/
@DeleteMapping("testDeleteByJson")
public void testDeleteByJson(){
System.out.println("请求成功,方法:testDeleteByJson");
}
}
请求端:
@RestController
public class TestRestTemplateController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/testDeleteByJson")
public void testDeleteByJson(){
//请求地址
String url = "http://localhost:8080/testDeleteByJson";
//模拟JSON提交,delete请求
restTemplate.delete(url);
}
}
如果以上方法还不满足你的要求。在RestTemplate工具类里面,还有一个exchange
通用协议请求方法,它可以发送GET、POST、DELETE、PUT、OPTIONS、PATCH等 HTTP 方法请求。
RequestEntity> requestEntity
描述ParameterizedTypeReferenceresponseType
描述服务端:
@RestController
public class HelloController {
@PostMapping("testExchange")
public ResponseBean testExchange(@RequestBody RequestBean request){
ResponseBean result = new ResponseBean();
result.setCode("200");
result.setMsg("请求成功,方法:testExchange,请求参数:" + request);
return result;
}
}
请求端:
@RestController
public class TestRestTemplateController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/testExchange")
public void testExchange() {
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("http://localhost:8080").
path("/testExchange").build(true);
URI uri = uriComponents.toUri();
RequestBean request = new RequestBean();
request.setName("唐三藏");
request.setAge("23");
RequestEntity<RequestBean> requestEntity = RequestEntity.post(uri).
header(HttpHeaders.COOKIE,"key1=value1").
accept(MediaType.APPLICATION_JSON).
contentType(MediaType.APPLICATION_JSON).
body(request);
ResponseEntity<ResponseBean> responseEntity = restTemplate.exchange(requestEntity, ResponseBean.class);
// 响应结果
ResponseBean response = responseEntity.getBody();
System.out.println("返回报文=" + response);
}
}
访问地址:http://192.168.1.166:8080/testExchange
public
中,RequestEntity> requestEntity
包含method,URL,contentType、accept、请求报文体等请求配置,Class
指定响应参数类型。
参考Spring之RestTemplate详解,懒得写了。
1. 拦截器配置
RestTemplate 也可以设置拦截器做一些统一处理。这个功能感觉和 Spring MVC 的拦截器类似。配置也很简单:
class MyInterceptor implements ClientHttpRequestInterceptor{
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
logger.info("enter interceptor...");
return execution.execute(request,body);
}
}
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
MyInterceptor myInterceptor = new MyInterceptor();
List<ClientHttpRequestInterceptor> list = new ArrayList<>();
list.add(myInterceptor);
restTemplate.setInterceptors(list);
return restTemplate;
}
2. ErrorHandler 配置
3. HttpMessageConverter
配置 RestTemplate 也可以配置 HttpMessageConverter,配置的原理和 Spring MVC 中类似。
引用:
Spring之RestTemplate详解