restTemplate源码解析(五)处理ClientHttpResponse响应对象
所有文章
https://www.cnblogs.com/lay2017/p/11740855.html
正文
上一篇文章中,我们执行了ClientHttpRequest与服务端进行交互。并返回了一个ClientHttpResponse的实例对象。
本文将继续最后一个部分,处理请求后的响应。
同样的,我们再次回顾一下restTemplate核心逻辑代码
- protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
- @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
- ClientHttpResponse response = null;
- try {
- // 生成请求
- ClientHttpRequest request = createRequest(url, method);
- if (requestCallback != null) {
- // 设置header
- requestCallback.doWithRequest(request);
- }
- // 执行请求,获取响应
- response = request.execute();
- // 处理响应
- handleResponse(url, method, response);
- // 获取响应体对象
- return (responseExtractor != null ? responseExtractor.extractData(response) : null);
- }
- catch (IOException ex) {
- // ... 抛出异常
- }
- finally {
- if (response != null) {
- // 关闭响应流
- response.close();
- }
- }
- }
我们的关注重点在于execute之后做了哪些事情
handleResponse
先跟进handleResponse方法
- protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
- ResponseErrorHandler errorHandler = getErrorHandler();
- boolean hasError = errorHandler.hasError(response);
- if (logger.isDebugEnabled()) {
- try {
- int code = response.getRawStatusCode();
- HttpStatus status = HttpStatus.resolve(code);
- logger.debug("Response " + (status != null ? status : code));
- }
- catch (IOException ex) {
- // ignore
- }
- }
- if (hasError) {
- errorHandler.handleError(url, method, response);
- }
- }
getErrorHandler获取了一个错误处理器,如果Response的状态码是错误的,那么就调用handleError处理错误并抛出异常。
extractData生成响应对象
跟进extractData方法
- public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException {
- if (this.delegate != null) {
- T body = this.delegate.extractData(response);
- return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(body);
- }
- else {
- return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).build();
- }
- }
这里主要是将状态码和相依你个对象包装成一个ResponseEntity,然后返回。
我们看看body的生成。
先看一下delegate这个代理类是怎么来的
- @Nullable
- private final HttpMessageConverterExtractor<T> delegate;
- public ResponseEntityResponseExtractor(@Nullable Type responseType) {
- if (responseType != null && Void.class != responseType) {
- this.delegate = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
- }
- else {
- this.delegate = null;
- }
- }
其实就是把RestTemplate构造方法中添加的HttpMessageConverter给包装了一下
跟进delegate看看它的extractData方法吧
- public T extractData(ClientHttpResponse response) throws IOException {
- MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
- if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
- return null;
- }
- MediaType contentType = getContentType(responseWrapper);
- try {
- for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
- if (messageConverter instanceof GenericHttpMessageConverter) {
- GenericHttpMessageConverter<?> genericMessageConverter = (GenericHttpMessageConverter<?>) messageConverter;
- // 判断是否能够读取
- if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
- // 读取
- return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
- }
- }
- if (this.responseClass != null) {
- // 判断是否能够读取
- if (messageConverter.canRead(this.responseClass, contentType)) {
- // 读取
- return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
- }
- }
- }
- }
- // ...
- }
核心逻辑就是遍历HttpMessageConveter,如果能够读取数据,那么就调用read方法读取数据。
那么,我们看看HttpMessageConverter的直接实现类AbstractHttpMessageConverter吧,阅读一下它的read方法
- public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
- throws IOException, HttpMessageNotReadableException {
- return readInternal(clazz, inputMessage);
- }
再跟进readInternal方法,readInternal向下有很多实现,如
我们选择一个简单的实现看看,StringHttpMessageConverter类的readInternal方法
- @Override
- protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
- Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
- return StreamUtils.copyToString(inputMessage.getBody(), charset);
- }
String的转换非常简单,getBody方法将获取到ClientHttpResponse中的输入流。copyToString将从输入流中读取数据,并返回字符串结果。
打开copyToString看看
- public static String copyToString(@Nullable InputStream in, Charset charset) throws IOException {
- if (in == null) {
- return "";
- }
- StringBuilder out = new StringBuilder();
- InputStreamReader reader = new InputStreamReader(in, charset);
- char[] buffer = new char[BUFFER_SIZE];
- int bytesRead = -1;
- while ((bytesRead = reader.read(buffer)) != -1) {
- out.append(buffer, 0, bytesRead);
- }
- return out.toString();
- }
到这里,我们就使用HttpMessageConverter把响应实体对象生成了。正如前面说到的,会把状态码和响应实体对象包装成ResponseEntity返回给用户。用户只需要通过get方法就可以获取statusCode或者body了。
关闭输入流
restTemplate还有最后一步操作需要去做,就是在finally块中关闭输入流
- finally {
- if (response != null) {
- response.close();
- }
- }
ClientHttpResponse的close方法将包含输入流的关闭
- @Override
- public void close() {
- try {
- if (this.responseStream == null) {
- getBody();
- }
- StreamUtils.drain(this.responseStream);
- this.responseStream.close();
- }
- catch (Exception ex) {
- // ignore
- }
- }
总结
到这里,restTemplate的源码解读文章就全部结束了。总的来说是一个基于Http请求响应模型的,采用了restful风格的实现方式。
restTemplate源码解析(五)处理ClientHttpResponse响应对象的更多相关文章
- Celery 源码解析五: 远程控制管理
今天要聊的话题可能被大家关注得不过,但是对于 Celery 来说确实很有用的功能,曾经我在工作中遇到这类情况,就是我们将所有的任务都放在同一个队列里面,然后有一天突然某个同学的代码写得不对,导致大量的 ...
- dubbo源码解析五 --- 集群容错架构设计与原理分析
欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...
- restTemplate源码解析(目录)
restTemplate是spring实现的,基于restful风格的http请求模板.使用restTemplate可以简化请求操作的复杂性,同时规范了代码风格.本系列文章,将根据以下目录顺序,从源码 ...
- iOS即时通讯之CocoaAsyncSocket源码解析五
接上篇:iOS即时通讯之CocoaAsyncSocket源码解析四 原文 前言: 本文为CocoaAsyncSocket Read篇终,将重点涉及该框架是如何利用缓冲区对数据进行读取. ...
- ReactiveCocoa源码解析(五) SignalProtocol的observe()、Map、Filter延展实现
上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...
- ReactiveSwift源码解析(五) SignalProtocol的observe()、Map、Filter延展实现
上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...
- Spring 源码解析之DispatcherServlet源码解析(五)
spring的整个请求流程都是围绕着DispatcherServlet进行的 类结构图 根据类的结构来说DispatcherServlet本身也是继承了HttpServlet的,所有的请求都是根据这一 ...
- restTemplate源码解析(二)restTemplate的核心逻辑
所有文章 https://www.cnblogs.com/lay2017/p/11740855.html 正文 上一篇文章中,我们构造了一个RestTemplate的Bean实例对象.本文将主要了解一 ...
- jquery源码解析:jQuery静态属性对象support详解
jQuery.support是用功能检测的方法来检测浏览器是否支持某些功能.针对jQuery内部使用. 我们先来看一些源码: jQuery.support = (function( support ) ...
随机推荐
- mysql排序自段为字符串类型问题解决
677 000.000.000.000 2018-01-09 22:20:58 编辑 删除 锁定 199 666/777/888套餐标配 000.000.000.000 2018-01 ...
- nginx+lua 设置跨域
nginx 配置: header_filter_by_lua_file cros.lua; access_by_lua ' if ngx.var.request_method == "OPT ...
- Ubuntu + Apache2 环境下用C编写 一个简单的cgi脚本
我只学习过c语言,没有学习过prel,网上很多教程都是针对prel的,很少有针对c的.自己在Ubuntu下鼓捣了一下午,也总算是用c成功编写了一个helloworld的cgi,算是cgi入门的第一步. ...
- Inventor2018专业版软件安装激活教程
如果你安装的是Autodesk Inventor Professional 2018,那么序列号为:666-69696969,产品密钥为:797J1, 如果你安装的是Autodesk Inventor ...
- 34 Flutter仿京东商城项目 用户注册 注册流程 POST发送验证码 倒计时功能 验证验证码
加群452892873 下载对应34课文件,运行方法,建好项目,直接替换lib目录 以下列出的是本课涉及的文件. RegisterFirst.dart import 'package:flutter/ ...
- java的servlet执行过程是怎么样的?
java的servlet执行过程是怎么样 答: Servlet执行过程:程序第一次访问,会调用servlet的init()方法初始化(只执行一次),每次程序执行都会根据请求调用doGet()或者d ...
- 在Mac 搭建robotframework 环境 遇到ride.py 打不开的方法(没试过,先记录在此)
折腾来一下午,遇到了好多坑 坑 1.不要用pip 下载wxpython 2.不要用mac自带的python 3.不要自己下载wxpython 步骤: 1. 安装homebrew, /usr/bin/r ...
- JS 时间处理(GMT转换,超24小时加一天,时间差计算)
计算天数,加小时,加分数 Date.prototype.Format = function (fmt) { // author: meizz var o = { "M+": thi ...
- CentOS 7部署 Ceph分布式存储架构
一.概述 随着OpenStack日渐成为开源云计算的标准软件栈,Ceph也已经成为OpenStack的首选后端存储.Ceph是一种为优秀的性能.可靠性和可扩展性而设计的统一的.分布式文件系统. cep ...
- 123457123457#0#-----com.cym.YuErBaiKe02--前拼后广--育儿百科
com.cym.YuErBaiKe02--前拼后广--育儿百科