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風格的實現方式。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM