文档章节

Spring RestTemplate 详解

learn_more
 learn_more
发布于 2016/12/07 18:30
字数 1638
阅读 8787
收藏 13

1、基本概念

Spring RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率,所以很多客户端比如 Android或者第三方服务商都是使用 RestTemplate 请求 restful 服务。

 

2、RestTemplate 调用流程

调用 RestTemplate 的默认构造函数,RestTemplate 对象在底层通过使用 java.net 包下的实现创建 HTTP 请求,可以通过使用 ClientHttpRequestFactory 指定不同的HTTP请求方式。默认使用 SimpleClientHttpRequestFactory,是 ClientHttpRequestFactory 实现类。如下流程:

1)使用默认构造方法new一个实例

RestTemplate template = new RestTemplate();

2)RestTemplate 内部通过调用 doExecute 方法,首先就是获取 ClientHttpRequest

 

3)RestTemplate 实现了抽象类 HttpAccessor ,所以可以调用父类的 createRequest

private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

public ClientHttpRequestFactory getRequestFactory() {

return this.requestFactory;

}

protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {

ClientHttpRequest request = getRequestFactory().createRequest(url, method);

if (logger.isDebugEnabled()) {

logger.debug("Created " + method.name() + " request for \"" + url + "\"");

}

return request;

}

 

4)SimpleClientHttpRequestFactory 实现了 ClientHttpRequest,同时实现方法

注意 bufferRequestBody 是可以在 RestTemplate 设置,是标志是否使用缓存流的形式,默认是 true,缺点是当发送大量数据时,比如put/post的保存和修改,那么可能内存消耗严重。所以这时候可以设置 RestTemplate.setBufferRequestBody(false);

即使用 SimpleStreamingClientHttpRequest 来实现。

 

5)openConnection 没什么文章,而是 prepareConnection 则是大有文章,这里我们分两个版本来说,因为我们一开始使用 4.1.1 的时候不能使用带请求体的delete,可是在 4.3.2 版本则可以使用,所以特别区分了这两个版本的代码,如下:

SimpleClientHttpRequestFactory -- 4.1.1 版本的代码默认

delete connection.setDoOutput = fase

如果设置false,然后后面又去获取输出流时,会发生如下错误 sun 包的 HttpURLConnection

if(!this.doOutput) {

throw new ProtocolException(

"cannot write to a URLConnection if doOutput=false - call setDoOutput(true)"

);

}

 

SimpleClientHttpRequestFactory -- 4.3.2 版本的代码默认

delete connection.setDoOutput = fase

DoOutput 的属性作用是可以使用 conn.getOutputStream().write() ,这样就能发送请求体了

 

6)接着执行 requestCallback.doWithRequest(request);

RequestCallback 封装了请求体和请求头对象,也就是说在该对象里面可以拿到我们需要的请求参数,在执行 doWithRequest 时,有一个非常重要的步骤,他和前面Connection发送请求体有着密切关系,我们知道请求头就是 SimpleBufferingClientHttpRequest.addHeaders 方法,那么请求体 bufferedOutput 是如何赋值的呢?就是在 doWithRequest 里面,如下 StringHttpMessageConverter (其他 MessageConvert 也一样,这里也是经常乱码的原因)

其中 s 就是请求体,HttpOutputMessage 对象就是我们准备的 ClientHttpRequest 对象,也就是上面的 SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttpRequest

这样,先调用父类的流方法,把内容写入流中,然后调用父类的 executeInternal方法在调用自身的该方法 executeInternal ,如下一步

 

7)接着执行 response = request.execute();

然后使用实例 SimpleBufferingClientHttpRequest 封装请求体和请求头

SimpleBufferingClientHttpRequest -- 4.1.1 版本的代码默认

delete 时通过前面设置的 DoOutput 参数和是否可以设置输出流来判断是否需要发送请求体

如果是 delete 请求,那么很明显 DoOutput = false,所以不会有封装请求体的过程,即不执行

FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());

所以服务端无法获取到请求体,会出现 HttpMessageNotReadableException: Required request body is missing

 

SimpleBufferingClientHttpRequest -- 4.3.2 版本的代码默认

delete 时通过请求方式和是否有请求体对象来判断是否需要发送请求体

如果是delete请求,首先设置 DoOutput = true,然后根据是否有请求体数据,然后封装请求体

FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());

 

8)最后解析response

接着就是 response 的解析了,主要还是 Error 的解析。

handleResponseError(method, url, response);

 

3、RestTemplate 的配置项

1)setBufferRequestBody 是否是否缓冲流来存储请求体,默认true

2)setProxy 设置代理对象

3)setChunkSize 设置每次传输字节长度,与 setBufferRequestBody(false) 结合使用

4)setConnectTimeout 设置连接超时时间,默认 -1

5)setReadTimeout 设置读取内容超时时间,默认 -1

6)setOutputStreaming 设置Connection是否设置输出流程

7)setTaskExecutor 设置异步回调执行器

 

4、RestTemplate 设置 RequestFactory

其实任何有连接的地方都会有连接池的概念,比如数据库连接等,这里也不例外,肯定也会有,RestTemplate 默认有两种工厂对象实现方式,都是 ClientHttpRequestFactory 的子类。如下

1)SimpleClientHttpRequestFactory 底层使用 java.net.HttpUrlConnection,可配置证书

2)HttpComponentsClientHttpRequestFactory 底层使用Apache HttpClient访问远程的Http服务,使用HttpClient同样可以配置连接池和证书等信息,而且功能更强大,配置项更多。

 

5、RequestFactory 的配置方式

1)使用XML配置,就是配置JavaBean

2)使用代码配置,就是初始化这个对象

无论上面那种方式配置,都是配置外壳 RestTemplate,真正发送请求的 request 对象其实都是由工厂管理的,所以我们不关心连接池的管理,只是配置连接池初始化的一些参数而已。

这个可以参考:

http://www.open-open.com/lib/view/open1436018677419.html

 

6、请求参数的传递

 

7、关于网上说的无法发送delete请求体

HttpMessageNotReadableException: Required request body is missing

Spring MVC 的 @RequestBody 只支持RestTemplate 的 POST 和 PUT

但是 RestTemplate 的 delete 方法并不支持传入请求体(Request Body)。经测试,通过调用 RestTemplate 类的exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<ResponseResult> responseType, Object... uriVariables)  方法,将 method 指定为 org.springframework.http.HttpMethod.DELETE,并传入 requestEntity(请求体) 对象时,在服务端得到的 Request Body 仍然为 null。可见 RestTemplate 默认并不支持对 DELETE 方法使用请求体。

    通过查阅资料发现 RestTemplate 默认是使用 spring 自身的 SimpleClientHttpRequestFactory 创建请求对象和对其进行相关设置(如请求头、请求体等),它只支持 PUT 和 POST 方法带请求体,RestTemplate 的 DELETE 方法不支持传入请求体是因为 JDK 中 HttpURLConnection 对象的 delete 方法不支持传入请求体(如果对 HttpURLConnection 对象的 delete 方法传入请求体,在运行时会抛出 IOException)。

从代码中也看到了 Spring 对 delete 做了判断,如果是 4.1.1 及以前的版本,确实是会出现上面的问题,但是当我使用 4.3.2 之后的版本,发现完全可以发送请求体,这里面的变化就是前者在代码中把请求体过滤掉了,后者把请求体加上了。至于更细的细节,希望有人能够继续深究下去。

 

© 著作权归作者所有

learn_more
粉丝 93
博文 240
码字总数 210196
作品 0
深圳
程序员
私信 提问
加载中

评论(4)

zhen2
zhen2
对我有用
learn_more
learn_more 博主

引用来自“kedadiannao220”的评论

```
ResponseEntity<MixResponse> exchange = RestfulUtil.getDeleteRestTemplate()
.exchange(url, HttpMethod.DELETE, new HttpEntity(param), MixResponse.class);
```
我现在都是直接使用 4.3 以上的版本了,直接使用 exchange , 不再考虑 delete 无法发送 body 体的问题
kedadiannao220
kedadiannao220
```
ResponseEntity<MixResponse> exchange = RestfulUtil.getDeleteRestTemplate()
.exchange(url, HttpMethod.DELETE, new HttpEntity(param), MixResponse.class);
```
kedadiannao220
kedadiannao220
4.2.7的spring web也是无法传入body信息的;修改resttemplate的requestFactory
public static RestTemplate getDeleteRestTemplate() {
RestTemplate restTemplate = new RestTemplate()
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory() {
@Override
protected HttpUriRequest createHttpUriRequest(HttpMethod httpMethod, URI uri) {
if (HttpMethod.DELETE == httpMethod) {
return new HttpEntityEnclosingDeleteRequest(uri);
}
return super.createHttpUriRequest(httpMethod, uri);
}
});
return restTemplate;
}

public static class HttpEntityEnclosingDeleteRequest extends HttpEntityEnclosingRequestBase {

public HttpEntityEnclosingDeleteRequest(final URI uri) {
super();
setURI(uri);
}
@Override
public String getMethod() {
return "DELETE";
}
}

白话SpringCloud | 第四章:服务消费者(Ribbon+Feign)

前言 上两章节,介绍了下关于注册中心-Eureka的使用及高可用的配置示例,本章节开始,来介绍下服务和服务之间如何进行服务调用的,同时会讲解下几种不同方式的服务调用。 一点知识 在体系中,...

oKong
2018/09/21
0
0
疯狂Spring Cloud连载(9)——RestTemplate的负载均衡原理

本文节选自《疯狂Spring Cloud微服务架构实战》 京东购买地址:https://item.jd.com/12256011.html 当当网购买地址:http://product.dangdang.com/25201393.html Spring Cloud教学视频:htt...

杨大仙的程序空间
2017/10/18
0
2
《Spring Cloud Alibaba基础教程》连载目录

Spring Cloud Alibaba与Spring Boot、Spring Cloud之间不得不说的版本关系 说说我为什么看好Spring Cloud Alibaba Spring Cloud Alibaba到底坑不坑? 注册中心与配置中心:Nacos Spring Clou...

程序猿DD
04/17
0
0
Consul+Spring boot的服务注册和服务注销

一图胜千言 先看一看要做事情,需要在Consul上面实现注册中心的功能,并以2个Spring boot项目分别作为生产者,消费者。 Consul 假设已经完成文章《Consul的开发者模式之Docker版》中的所有的...

亚林瓜子
06/24
0
0
Spring RestTemplate 实践

什么是RestTemplate? RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。 调用RestTemplate的默认构...

单红宇
2016/03/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Python爬虫也能用手机进行抓包?没错!这个技巧我只告诉你

今天要说说怎么在我们的手机抓包 我们知道了 HTTP 的请求方式 以及在 Chrome 中摸清了一些套路 但是 除了对数据进行解析之外 有时候我们想 对请求的数据或者响应的数据进行篡改 怎么做呢? ...

计算机编程
5分钟前
0
0
趣图:听说996工作可以获得巨大成长

听说996工作可以获得巨大成长 。 。 。 这成长也忒快了吧 扩展阅读 趣图:菜鸟程序员的工作状态… 趣图:当计算机可以更新的时候 趣图:什么?需求文档又改了

Java面经
8分钟前
2
0
influxdb 学习

InfluxDB 学习 安装 brew install influxdb 启动 influxd -config /usr/local/etc/influxdb.conf 入门 $ influx -precision rfc3339Connected to http://localhost:8086 version 1.2.xI......

solate
14分钟前
1
0
快速掌握mongoDB(三)——mongoDB的索引详解

  1 mongoDB索引的管理      2 mongoDB中常用的索引类型      1 单键索引      2 复合索引      3 多键索引      4 哈希索引      3 mongoDB中常用的索引属性   ...

SEOwhywhy
15分钟前
0
0
JackJson中自定义JsonSerializer的使用

最近在做一个需求,一个时间字段,数据库类型为timestamp,默认值为'1970-01-01 08:00:01',产品要求这种情况展示为“-1”,实体类中的属性类型为Date,我也不能把Date属性值设置为“-1”,又...

Coder顾
17分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部