Spring_RestTemplate的使用
RestTemplate
简介
现如今的 IT 项目,由服务端向外发起网络请求的场景,基本上处处可见!
RestTemplate
是由Spring
框架提供的一个可用于应用中调用rest
服务的类它简化了与http
服务的通信方式,统一了RESTFul
的标准,封装了http
连接,我们只需要传入url
及其返回值类型即可。相较于之前常用的HttpClient
,RestTemplate
是一种更为优雅的调用RESTFul
服务的方式。在
Spring
应用程序中访问第三方REST服务
与使用Spring RestTemplate
类有关。RestTemplate
类的设计原则与许多其他Spring
的模板类(例如JdbcTemplate
)相同,为执行复杂任务提供了一种具有默认行为的简化方法。RestTemplate
默认依赖JDK提供了http
连接的能力(HttpURLConnection
),如果有需要的话也可以通过setRequestFactory
方法替换为例如Apache HttpCompoent、Netty或OKHttp
等其他Http libaray
。考虑到了
RestTemplate
类是为了调用REST服务而设计的,因此它的主要方法与REST
的基础紧密相连就不足为奇了,后者时HTTP
协议的方法:HEAD、GET、POST、PUT、DELETE、OPTIONS
例如,RestTemplate
类具有headForHeaders()、getForObject()、putForObject(),put()和delete()
等方法。
创建RestTemplate
因为RestTemplate
是Spirng
框架提供的所以只要是一个Springboot
项目就不用考虑导包的问题,这些都是提供好的。
但是Spring
并没有将其加入SpringBean
容器中,需要我们手动加入,因为我们首先创建一个Springboot
配置类,再在配置类中将我们的RestTemlate
注册到Bean
容器中
方法一
使用Springboot
提供的RestTemplateBuilder
构造类来构造一个RestTemplate
,可以自定义一些连接参数,如:连接超时时间,读取超时时间,还有认证信息等
1 | @Configuration |
添加自定义的拦截器
自定义拦截器示例
-
@Slf4j public class CustomClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { <span class="token comment">//打印请求明细</span> <span class="token function">logRequestDetails</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span>body<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">ClientHttpResponse</span> response <span class="token operator">=</span> execution<span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> body<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//打印响应明细</span> <span class="token function">logResponseDetails</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> response<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">logRequestDetails</span><span class="token punctuation">(</span><span class="token class-name">HttpRequest</span> request<span class="token punctuation">,</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> body<span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span> log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"Headers:{}"</span><span class="token punctuation">,</span>request<span class="token punctuation">.</span><span class="token function">getHeaders</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"body:{}"</span><span class="token punctuation">,</span><span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span>body<span class="token punctuation">,</span> <span class="token class-name">StandardCharsets</span><span class="token punctuation">.</span>UTF_8<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"{}:{}"</span><span class="token punctuation">,</span>request<span class="token punctuation">.</span><span class="token function">getMethod</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>request<span class="token punctuation">.</span><span class="token function">getMethodValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">logResponseDetails</span><span class="token punctuation">(</span><span class="token class-name">ClientHttpResponse</span> response<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{<!-- --></span> log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"Status code : {}"</span><span class="token punctuation">,</span>response<span class="token punctuation">.</span><span class="token function">getStatusCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"Status text : {}"</span><span class="token punctuation">,</span>response<span class="token punctuation">.</span><span class="token function">getStatusText</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"Headers : {}"</span><span class="token punctuation">,</span>response<span class="token punctuation">.</span><span class="token function">getHeaders</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> log<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"Response body: {}"</span><span class="token punctuation">,</span> <span class="token class-name">StreamUtils</span><span class="token punctuation">.</span><span class="token function">copyToString</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span><span class="token function">getBody</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token class-name">StandardCharsets</span><span class="token punctuation">.</span>UTF_8<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>}
ClientHttpRequestFactory1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
使用`RestTemplateBuilder`构造类,添加自定义拦截器,构造带有自定义拦截器的`RestTemplate`实例
@Configuration public class WebConfiguration { <span class="token annotation punctuation">@Bean</span> <span class="token keyword">public</span> <span class="token class-name">RestTemplate</span> <span class="token function">restTemplate</span><span class="token punctuation">(</span><span class="token class-name">RestTemplateBuilder</span> builder<span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> builder <span class="token punctuation">.</span><span class="token function">additionalInterceptors</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">CustomClientHttpRequestInterceptor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">//构建</span> <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> }
- 11
测试请求确实经过了拦截器,注册成功(注意请求和响应的流只会被读取一次,这里我们读取了response后返回的response就读取不到刚刚读过的内容了)

------
#### 方法二
使用`RestTemplate`构造方法构造一个`RestTemlate`,虽然不能像`RestTemplate`构造类那样更详细、更多样的配置参数,但是`RestTemplate`构造方法在一般情况是够用的。

- 无参构造 全部参数默认
- 指定ClientHttpRequestFactory1
2
3
4
5
的构造方法可以指定自己实现的http1
2
3
(客户端方法参数签名与`getForEntity()`基本一致。 11
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
请求工厂)其他的与无参构造相同。
- `ClientHttpRequestFactory`
- 指定`List<HttpMessageConverter<?>>`的构造方法可以指定自己是实现的`HttpMessageConverter`(`Http`消息转换器)传入其他与无参构造相同。
@Configuration public class WebConfiguration { <span class="token annotation punctuation">@Bean</span> <span class="token keyword">public</span> <span class="token class-name">RestTemplate</span> <span class="token function">restTemplate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">RestTemplate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>}
------
两者方法都可使用,前者提供了多样的自定义参数的选择,可以将`RestTemplate`配置的更为完善,后者则简化了配置虽然配置多样性不如前者,但是日常使用调用些`API`还是足以使用
### `RestTemplate API`使用
在使用`RestTemplate`前先让我们看看`RestTemplate`有哪些`API`

相信大家看到这么多方法,一定很头大,但是我们仔细看上述的方法,我们可以提取出主要的几种方法是(这里只讨论Http请求的):
- **`GET`**
- **`POST`**
- **`PUT`**
- **`DELETE`**
- **`HEAD`**
- **`OPTIONS`**
- **`EXCHANGE`**
- **`EXECUTE`**
**这里我给大家安利一个一个网站,它提供免费的RESTFul api的样例测试。**[httpbin A simple HTTP Request & Response Service.](http://httpbin.org/)
------
#### GET
通过上图我们可以发现`RestTemlate`发送`GET`请求的方法有两种
> - `public <T> T getForObject(...)`
> - `public <T> ResponseEntity<T> getForEntity(...)`
##### `getForEntity()`
后缀带有`Entity`的方法都代表返回一个`ResponseEntity<T>`,`ResponseEntity<T>`是Spring对`HTTP`请求响应的封装,包括了几个重要的元素,如响应码,`contentType、contentLength`、响应消息体等

通过它继承父类(`HttpEntity<T>`)的`getHeader()`方法我们可以获取`contentType、contentLength`、响应消息体等。比如下面这个例子。
public void queryWeather() { <span class="token class-name">ResponseEntity</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Object</span><span class="token punctuation">></span></span> forEntity <span class="token operator">=</span> restTemplate<span class="token punctuation">.</span><span class="token function">getForEntity</span><span class="token punctuation">(</span><span class="token string">"https://restapi.amap.com/v3/weather/weatherInfo?city=510100&key=e7a5fa943f706602033b6b329c49fbc6"</span><span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"状态码:"</span><span class="token operator">+</span>forEntity<span class="token punctuation">.</span><span class="token function">getStatusCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"状态码内容:"</span><span class="token operator">+</span>forEntity<span class="token punctuation">.</span><span class="token function">getStatusCodeValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">HttpHeaders</span> headers <span class="token operator">=</span> forEntity<span class="token punctuation">.</span><span class="token function">getHeaders</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"响应头:"</span><span class="token operator">+</span>headers<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Object</span> body <span class="token operator">=</span> forEntity<span class="token punctuation">.</span><span class="token function">getBody</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"响应内容:"</span><span class="token operator">+</span>body<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
-
该例子中`getForEntity()`方法的第一个参数为我要调用服务的`URL`,第二个参数则为响应内容的类的类型(Java嘛 万物皆对象)还可以添加第三个参数,第三个参数为一个可变参数 代表着调用服务时的传参。
 **第三个参数可以使用key-value的map来传入参数**
**get请求也可通过向在url上添加查询参数来发送带有请求的参数**
------
##### `getForObject()`
相比于前者`getForEntity()`该方法则是,更偏向于直接获取响应内容的,因为他直接返回响应实体的`body`(响应内容),。比如下面这个例子
public void queryWeather() { <span class="token class-name">Object</span> body <span class="token operator">=</span> restTemplate<span class="token punctuation">.</span><span class="token function">getForObject</span><span class="token punctuation">(</span><span class="token string">"https://restapi.amap.com/v3/weather/weatherInfo?city=510100&key=e7a5fa943f706602033b6b329c49fbc6"</span><span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>body<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>@Override public void queryWeather() { User user = new User(); user.setName("鲁大师"); ResponseEntity<Object> objectResponseEntity = restTemplate.postForEntity("https://restapi.amap.com/v3/weather/weatherInfo?city=510100&key=e7a5fa943f706602033b6b329c49fbc6", user, Object.class); System.out.println("消息响应内容:"+objectResponseEntity.getBody()); } 12345671
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
------
当你只需要返回的响应内容时,使用`getForObject()`是一个很好的选择,但当你需要获得更详细的响应信息,如响应头中的信息,你就只能选择`getForEntity()`了。
------
#### POST
`POST`请求有如下三种方法
> - `public URI postForLocation(...)`
> - `public <T> T postForObject(...)`
> - `public <T> ResponseEntity<T> postForEntity(...)`
后两种用法与`GET`基本一致不做详细介绍,这里着重介绍`postForLocation()`
------
##### `postForEntity()`
该方法有三个参数,第一个为调用服务的地址(URL)
第二个参数表示上传的参数(json格式提交)
第三个表示返回响应内容的具体类型
第四个参数也用于指定参数(在URL中添加)public void queryWeather() { User user = new User(); user.setName("鲁大师"); ResponseEntity<Object> objectResponseEntity = restTemplate.postForEntity("https://httpbin.org/post", user, Object.class); MediaType contentType = objectResponseEntity.getHeaders().getContentType(); System.out.println(contentType); System.out.println("消息响应内容:"+objectResponseEntity.getBody()); } 123456781
2
3
4
5
6
7
------
##### `postForObject()`
使用方法与`getForObject`类似只是多了一个传入对象参数(传入方式与`postForEntity()`相同)public void queryWeather() { User user = new User(); user.setName("鲁大师"); URI uri = restTemplate.postForLocation("https://httpbin.org/post", user); System.out.println(uri); } 1234561
2
3
4
5
6
7
------
##### `postForLocation()`
`postForLocation`传参用法与前两者一致,只不过返回从实体变成了一个`URL`,因此它不需要指定返回响应内容的类型。//设置请求头, x-www-form-urlencoded格式的数据 HttpHeaders httpHeaders = new HttpHeaders(); //这里指定参数以UTF-8编码格式传输 MediaType mediaType = new MediaType(MediaType.APPLICATION_FORM_URLENCODED, UTF_8); httpHeaders.setContentType(mediaType); //提交参数设置 MultiValueMap<String, String> map = new LinkedMultiValueMap<>(); map.add("name","鲁大师");1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
这个只需要服务提供者返回一个 URI 即可,该`URI`返回值体现的是:用于提交完成数据之后的页面跳转,或数据提交完成之后的下一步数据操作`URI`。
##### 使用POST以表单方式提交
这里我们着重说一下,如何自己封装一个请求体。
我们需要用到如下几个类
> - `HttpHeaders`
> - `MultiValueMap<K,V>`
> - `HttpEntity<T>`
###### `HttpHeaders`
故名思意,就是用来封装Http请求的请求头的,这里我们要设置他的`ContentType`为**`MediaType.APPLICATION_FORM_URLENCODED`**以使得我们提交的参数是以Form(表单)的形式提交。
12345678
1 |
|
//提交参数设置
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add(“name”,”鲁大师”);
123
1 |
|
//组装请求体
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, httpHeaders);
12
1 |
|
@Test
void contextLoads() {
//请求地址
String url = “http://httpbin.org/put“;
User user = new User();
user.setName(“鲁大师”);
restTemplate.put(url,user);
}
123456789
1 |
|
void contextLoads() {
//请求地址
String url = “http://httpbin.org/delete“;
restTemplate.delete(url);
}
12345
1 |
|
@Test
void contextLoads() {
//请求地址
String url = “http://httpbin.org/get“;
HttpHeaders httpHeaders = restTemplate.headForHeaders(url);
System.out.println(httpHeaders);
}
1234567
1 |
|
@Test
void contextLoads() {
//请求地址
String url = "http://httpbin.org/get";
Set<HttpMethod> httpMethods = restTemplate.optionsForAllow(url);
System.out.println(httpMethods);
}
1234567
1 |
|
@Test
void contextLoads() {
//请求地址
String url = “http://httpbin.org/post“;
User user = new User();
user.setName(“彭于晏”);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity
ResponseEntity
1 |
|
execute()
1 |
|
@Test
void contextLoads() {
//请求地址
String url = “http://httpbin.org/post“;
User user = new User();
user.setName(“彭于晏”);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity
ResponseEntity
1 |
|
@Test
void contextLoads() {
//请求地址
String url = “http://httpbin.org/get“;
HashMap<String, String> map = new HashMap<>();
map.put(“name”,”彭于晏”);
Object forObject = restTemplate.getForObject(url, Object.class, map);
System.out.println(forObject);
}
123456789
1 |
|
@Test
void contextLoads() {
//请求地址
String url = “http://httpbin.org/{2}/get“;
HashMap<String, String> map = new HashMap<>();
Object forObject = restTemplate.getForObject(url, Object.class, 99);
System.out.println(forObject);
}
12345678
