• 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风格的实现方式。

  • 相关阅读:
    用vs调试sql存储过程
    Html插入Flash.object.embed.swf各个参数值详解介绍[等比例缩放]
    SQL SERVER分区具体例子详解
    C#身份证识别相关技术
    C#调用Java方法(详细实例)
    Visual Studio各版本工程文件之间的转换
    彻底解决asp.net mvc5.2.2:vs2013 cshtml视图文件报错(当前上下文中不存在名称“model”,ViewBag,Url)
    HTML 5 Web 本地存储
    让WeuiPicker隐藏日期中的日,只保留年月
    javascript获取值
  • 原文地址:https://www.cnblogs.com/lay2017/p/11743088.html
Copyright © 2020-2023  润新知