RestTemplate

img

简介

  • 现如今的 IT 项目,由服务端向外发起网络请求的场景,基本上处处可见!

  • RestTemplate是由Spring框架提供的一个可用于应用中调用rest服务的类它简化了与http服务的通信方式,统一了RESTFul的标准,封装了http连接,我们只需要传入url及其返回值类型即可。相较于之前常用的HttpClientRestTemplate是一种更为优雅的调用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

因为RestTemplateSpirng框架提供的所以只要是一个Springboot项目就不用考虑导包的问题,这些都是提供好的。

但是Spring并没有将其加入SpringBean容器中,需要我们手动加入,因为我们首先创建一个Springboot配置类,再在配置类中将我们的RestTemlate注册到Bean容器中

方法一

使用Springboot提供的RestTemplateBuilder构造类来构造一个RestTemplate,可以自定义一些连接参数,如:连接超时时间,读取超时时间,还有认证信息等

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
public class WebConfiguration {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder){
return builder
//设置连接超时时间
.setConnectTimeout(Duration.ofSeconds(5000))
//设置读取超时时间
.setReadTimeout(Duration.ofSeconds(5000))
//设置认证信息
.basicAuthentication("username","password")
//设置根路径
.rootUri("https://api.test.com/")
//构建
.build();
}
}

添加自定义的拦截器

自定义拦截器示例

  • @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>}
    
    1
    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就读取不到刚刚读过的内容了)

    ![img](https://gitee.com/jacksonzhang1014/blog-image/raw/master/a56513281ea8934d77357484a87f3532.png)

    ------

    #### 方法二

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

    ![img](https://gitee.com/jacksonzhang1014/blog-image/raw/master/91ad85473e90723be1d0d927398bf495.png)

    - 无参构造 全部参数默认

    - 指定

    ClientHttpRequestFactory
    1
    2
    3
    4
    5



    的构造方法可以指定自己实现的

    ClientHttpRequestFactory
    1
    2
    3

    (客户端

    http
    1
    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`

    ![img](https://gitee.com/jacksonzhang1014/blog-image/raw/master/99611aa7f0e2775b470ec35701bf9b90.png)![img](https://gitee.com/jacksonzhang1014/blog-image/raw/master/6ff76f2d0a9f01cb9986069f307b0fcf.png)

    相信大家看到这么多方法,一定很头大,但是我们仔细看上述的方法,我们可以提取出主要的几种方法是(这里只讨论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`、响应消息体等

    ![img](https://gitee.com/jacksonzhang1014/blog-image/raw/master/9ed533f6f639df2da3f34587f44b865a.png)

    通过它继承父类(`HttpEntity<T>`)的`getHeader()`方法我们可以获取`contentType、contentLength`、响应消息体等。比如下面这个例子。

    public void queryWeather() { <span class="token class-name">ResponseEntity</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Object</span><span class="token punctuation">&gt;</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&amp;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嘛 万物皆对象)还可以添加第三个参数,第三个参数为一个可变参数 代表着调用服务时的传参。

    ![img](https://gitee.com/jacksonzhang1014/blog-image/raw/master/35bcf9bd26d4d7ff748139299a99cf0a.png) **第三个参数可以使用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&amp;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>

    方法参数签名与`getForEntity()`基本一致。 1
    1
    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中添加)

    @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()); } 1234567
    1
    2
    3
    4
    5
    6
    7

    ------

    ##### `postForObject()`

    使用方法与`getForObject`类似只是多了一个传入对象参数(传入方式与`postForEntity()`相同)

    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()); } 12345678
    1
    2
    3
    4
    5
    6
    7

    ------

    ##### `postForLocation()`

    `postForLocation`传参用法与前两者一致,只不过返回从实体变成了一个`URL`,因此它不需要指定返回响应内容的类型。

    public void queryWeather() { User user = new User(); user.setName("鲁大师"); URI uri = restTemplate.postForLocation("https://httpbin.org/post", user); System.out.println(uri); } 123456
    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(表单)的形式提交。

    //设置请求头, 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","鲁大师");

12345678

1
2
3
4
5
6
7

------

###### `MultiValueMap<K,V>`

该类是用来封装请求参数的,是以`key-value`的形式封装但是以单个key对应多个value的格式传输(也就是是以单个`key:[value...]`的格式传输的)。

//提交参数设置
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add(“name”,”鲁大师”);
123

1
2
3
4
5
6
7
8
9

**如果像传输单个`key`对应单个`value`使用普通的`Map`传参即可**

------

###### `HttpEntity<T>`

该类是用来封装请求的,主要作用就是将请求头和请求体封装在一起成为一个请求实体 T用来指定用来封装参数的容器的类型。

//组装请求体
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, httpHeaders);
12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

------

###### 测试

通过上述介绍后,我们就可以自己封装一个以form形式提交参数的`POST`请求了。

@Test void contextLoads() { //请求地址 String url = "https://httpbin.org/post"; <span class="token comment">//设置请求头, x-www-form-urlencoded格式的数据</span> <span class="token class-name">HttpHeaders</span> httpHeaders <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HttpHeaders</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> httpHeaders<span class="token punctuation">.</span><span class="token function">setContentType</span><span class="token punctuation">(</span><span class="token class-name">MediaType</span><span class="token punctuation">.</span>APPLICATION_FORM_URLENCODED<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//提交参数设置</span> <span class="token class-name">MultiValueMap</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">String</span><span class="token punctuation">&gt;</span></span> map <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LinkedMultiValueMap</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> map<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">,</span><span class="token string">"鲁大师"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//组装请求体</span> <span class="token class-name">HttpEntity</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">MultiValueMap</span><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">String</span><span class="token punctuation">&gt;</span><span class="token punctuation">&gt;</span></span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HttpEntity</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>map<span class="token punctuation">,</span> httpHeaders<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//发送post请求并打印结果 以String类型接收响应结果JSON字符串</span> <span class="token class-name">String</span> s <span class="token operator">=</span> restTemplate<span class="token punctuation">.</span><span class="token function">postForObject</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> request<span class="token punctuation">,</span> <span class="token class-name">String</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>s<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

![img](https://gitee.com/jacksonzhang1014/blog-image/raw/master/6a5782b6482195903fcd0865b1fd5c17.png)

通过拦截器拦截了请求并对请求头进行拆包,可以发现`ContentType`已经被修改成了`x-www-form-urlencoded`格式了。

#### PUT

`PUT`请求的方法只有一类

> - `void put()`

##### `PUT()`

使用方法与`postForEntity()`参数基本一致,只是`put`方法没有返回值(也就不必去设置响应内容的类型了)。

@Test
void contextLoads() {
//请求地址
String url = “http://httpbin.org/put“;
User user = new User();
user.setName(“鲁大师”);
restTemplate.put(url,user);
}

123456789

1
2
3
4
5
6
7
8
9
10
11
12
13

------

#### DELETE

与`PUT`一样,`DELETE`方法只有一类

> - `void delete()`

##### `delete()`

`delete()`可以指定`url`中的中的参数,但是`RestTemplate`的`delete()`方法是不支持上传`requestBody`的。

void contextLoads() {
//请求地址
String url = “http://httpbin.org/delete“;
restTemplate.delete(url);
}
12345

1
2
3
4
5
6
7
8
9
10
11

------

#### HEADER

`HEADER`也只有一类方法

> - `public HttpHeaders headForHeaders()`

主要用来发送请求获取响应头部信息,但是像`DELETE`、`PUT`这类没有响应的方法,是不能使用该方法的(因为没有响应也就没有响应头了)。

@Test
void contextLoads() {
//请求地址
String url = “http://httpbin.org/get“;
HttpHeaders httpHeaders = restTemplate.headForHeaders(url);
System.out.println(httpHeaders);
}
1234567

1
2
3
4
5
6
7
8
9
10
11

![img](https://gitee.com/jacksonzhang1014/blog-image/raw/master/2920645602cbb64f76f1197d4d0bd373.png)

------

#### OPTIONS

> - `public Set<HttpMethod> optionsForAllow()`

该方法的主要用来判断该服务地址,能够使用那种方法去执行

@Test
void contextLoads() {
    //请求地址
    String url = "http://httpbin.org/get";
    Set<HttpMethod> httpMethods = restTemplate.optionsForAllow(url);
    System.out.println(httpMethods);
}

1234567

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

![img](https://gitee.com/jacksonzhang1014/blog-image/raw/master/2da18056f390295ed8747d0a86770ced.png)

------

#### EXCHANGE

> - `<T> ResponseEntity<T> exchange()`

该接口与其他接口不同

> - 该方法允许用户指定请求的方法(`get,post,put`等)
> - 可以在请求中增加body以及头信息,其内容通过参数`HttpEntity<?> requestEntity`描述
> - `exchange`支持’含参数的类型(即泛型)'作为返回类型,该特性通过`ParameterizedTypeReferenceresponseType` 描述

该方法支持五个参数

> - 第一个是服务地址
> - 第二个是请求方法
> - 第三个是写入的请求实体
> - 第四个是响应内容的类型
> - 第五个是扩展模板的变量或包含`URI`模板变量的映射

@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 userHttpEntity = new HttpEntity<>(user, httpHeaders);
ResponseEntity exchange = restTemplate.exchange(url, HttpMethod.POST, userHttpEntity, Object.class);
System.out.println(exchange);
}
123456789101112

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

上述代码模拟了一个简单的`POST`请求 **可以理解为可以动态的指定请求方法和请求实体的一个方法。**

**响应实体**

![img](https://gitee.com/jacksonzhang1014/blog-image/raw/master/7177290038d64ea6cdfacf4293587685.png)

------

#### EXECUTE

> - `<T> T execute()`

该方法就是执行请求的方法,我们可以发现上述的所有方法的最后执行都是调用的该方法执行,所以他在`RestTemplate`中十分重要

该方法有五个参数

> - 服务地址
> - 请求的方法
> - 准备请求的对象(`requestCallback`)
> - 从响应中提取返回值的对象
> - 扩展模板的变量或包含`URI`模板变量的映射

execute()

1
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

@Override @Nullable public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException { <span class="token class-name">URI</span> expanded <span class="token operator">=</span> <span class="token function">getUriTemplateHandler</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">expand</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> uriVariables<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token function">doExecute</span><span class="token punctuation">(</span>expanded<span class="token punctuation">,</span> method<span class="token punctuation">,</span> requestCallback<span class="token punctuation">,</span> responseExtractor<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>



通过上述源码我们可以发现`execute()`方法只是将我们传入的`String`类型的`URL`转换为了`URL`类型,最后执行请求是由`doExecute()`方法

------

##### `doExecute()`

这里需要了解两个类:`RequestCallback`和`ResPonseExtractor`

`RequestCallback`: **用于操作请求头和body,在请求发出前执行。不需要关心关闭请求或处理错误:这都将由RestTemplate处理。**

该接口有两个实现类:

![img](https://gitee.com/jacksonzhang1014/blog-image/raw/master/da3d7a4bbea491dcd16610590eb5c7c4.png)

`ResPonseExtractor`: **解析HTTP响应的数据,而且不需要担心异常和资源的关闭。**

该接口在`RestTemplate`中同样有两个实现类:

| `HeadersExtractor` | 提取响应`HttpHeaders`的响应提取器。直接提取响应体中的响应头 | |
| :----------------------------------- | :----------------------------------------------------------- | :--- |
| `ResponseEntityResponseExtractor<T>` | `HttpEntity`**的响应提取器。可以获取响应实体里面包括响应头,响应体等。具体请查看**`HttpEntity` | |

@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 userHttpEntity = new HttpEntity<>(user, httpHeaders);
ResponseEntity execute = restTemplate.execute(url, HttpMethod.POST, restTemplate.httpEntityCallback(userHttpEntity), restTemplate.responseEntityExtractor(Object.class));
System.out.println(execute);
}
123456789101112

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

![img](https://gitee.com/jacksonzhang1014/blog-image/raw/master/1bb5a7092ef0c1d609762a780c03825c.png)

------

### 解惑

- 前面我们介绍方法的时候发现有个一个可变参数,那个参数被描述成了**扩展模板的变量或是包含`URI`模板变量的映射**

我们来简单看一下这个参数,我们知道请求传参可以通过`url`拼接参数的方式传参,拼接参数也分为两种:

> - 路径中嵌入占位的格式(`http://httpbin.org/{1}/post`)也叫模板映射
> - 末尾添加`Key-value`格式(`http://httpbin.org/post?name="彭于晏"`)即扩展模板的变量

- 当我们最后一参数传入map时会以`key-value`的格式拼接在`URL`后(通俗的说就是这样设置的变量会跟着`URL`路径后面)

`http://httpbin.org/post?name="彭于晏"`

@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
2
3

- 当我们传入简单的对象如String,Integer时且路径中有嵌入的占位符时就会代替调用URL中占位符

@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


![img](https://gitee.com/jacksonzhang1014/blog-image/raw/master/df2b1e6cddf0f5b7fd07b4061e451aa4.png)
文章作者: JacksonZhang
文章链接: http://jacksonzhang1014.gitee.io/2023/10/20/Spring_RestTemplate%E7%9A%84%E4%BD%BF%E7%94%A8/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Jackson Zhang
赞助
  • 微信
    微信
  • 支付宝
    支付宝

评论
ValineDisqus
avatar
JacksonZhang
生当如夏花般绚烂
follow me
公告
欢迎来到我的博客!!!