文章目录
- 0. 概述
- 1. RestTemplate配置
- 2. GET请求
- 2.1. getForObject
- 2.1.1. 带参的get请求(restful风格):
- 2.1.2. 带参的get请求(使用占位符号传参):
- 2.2. getForEntity
- 3. POST请求
- 3.1. postForObject
- 3.1.1. 表单请求:
- 3.1.2. 表单请求(传递对象):
- 3.2. postForEntity
- 3.3 postForLocation
- 4. PUT
- 5. DELETE
- 6. EXCHANGE
- 7. 文件上传
- 一些其他设置
0. 概述
RestTemplate
是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。
-
getForObject
:Retrieves a representation viaGET
. -
getForEntity
:Retrieves aResponseEntity
(that is, status, headers, and body) by using GET. -
headForHeaders
:Retrieves all headers for a resource by usingHEAD
. -
postForLocation
:Creates a new resource by usingPOST
and returns theLocation header
from the response. -
postForObject
:Creates a new resource by usingPOST
and returns the representation from the response. -
postForEntity
:Creates a new resource by usingPOST
and returns the representation from the response. -
put
:Creates or updates a resource by usingPUT
. -
delete
:Deletes the resources at the specified URI by usingDELETE
. -
exchange
:More generalized (and less opinionated) version of the preceding methods that provides extra flexibility when needed. It accepts aRequestEntity
(including HTTP method, URL, headers, and body as input) and returns aResponseEntity
. -
execute
:The most generalized way to perform a request, with full control over request preparation and response extraction throughcallback
interfaces.
getForObject - delete:提供常规的 Rest API(GET、POST、DELETE 等)方法调用;
exchange:接收一个 RequestEntity 参数,可以自己设置 HTTP method,URL,headers 和 body,返回 ResponseEntity;
execute:通过 callback 接口,可以对请求和返回做更加全面的自定义控制。
一般情况下,我们使用第一组和第二组方法就够了。
1. RestTemplate配置
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</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<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>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<Header> 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;
}
}
2. GET请求
-
getForObject()
:返回值是 HTTP 协议的响应体
-
getForEntity()
:返回的是ResponseEntity
,ResponseEntity
是对 HTTP 响应的封装,除了包含响应体,还包含HTTP状态码、contentType、contentLength、Header等信息
2.1. getForObject
2.1.1. 带参的get请求(restful风格):
服务端:
@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
2.1.2. 带参的get请求(使用占位符号传参):
服务端:
@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
。
2.2. getForEntity
上面的所有的getForObject
请求传参方法,getForEntity
都可以使用,使用方法上也几乎是一致的,只是在返回结果接收的时候略有差别。
getForEntity
方法的返回值是一个ResponseEntity<T>
,ResponseEntity<T>
是 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 进行响应转换。
3. POST请求
其实 POST 请求方法和 GET 请求方法上大同小异,RestTemplate 的 POST 请求也包含两个主要方法:
-
postForObject()
:返回body
对象 -
postForEntity()
:返回全部的信息
3.1. postForObject
3.1.1. 表单请求:
服务端:
@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<String, String>
,不能使用HashMap<String, Object>
。
3.1.2. 表单请求(传递对象):
服务端:
@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
3.2. postForEntity
上面的所有的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
3.3 postForLocation
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());
}
}
4. PUT
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);
}
}
5. DELETE
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);
}
}
6. EXCHANGE
如果以上方法还不满足你的要求。在RestTemplate工具类里面,还有一个exchange
通用协议请求方法,它可以发送GET、POST、DELETE、PUT、OPTIONS、PATCH等 HTTP 方法请求。
- 允许调用者指定 HTTP 请求的方法(GET,POST,PUT等)
- 可以在请求中增加 body 以及头信息,其内容通过参数
RequestEntity<?> requestEntity
描述 - exchange支持‘含参数的类型’(即泛型类)作为返回类型,该特性通过
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 <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType)
中,RequestEntity<?> requestEntity
包含method,URL,contentType、accept、请求报文体等请求配置,Class<T> responseType
指定响应参数类型。
7. 文件上传
参考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 中类似。