restTemplate源码解析(五)处理ClientHttpResponse响应对象

zhujiangtaotaise 2019-10-26

所有文章

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向下有很多实现,如

restTemplate源码解析(五)处理ClientHttpResponse响应对象

我们选择一个简单的实现看看,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风格的实现方式。

相关推荐