聊聊HttpClient的ResponseHandler

本文主要研究一下HttpClient的ResponseHandler

ResponseHandler

org/apache/http/client/ResponseHandler.java

复制代码
public interface ResponseHandler<T> {

    /**
     * Processes an {@link HttpResponse} and returns some value
     * corresponding to that response.
     *
     * @param response The response to process
     * @return A value determined by the response
     *
     * @throws ClientProtocolException in case of an http protocol error
     * @throws IOException in case of a problem or the connection was aborted
     */
    T handleResponse(HttpResponse response) throws ClientProtocolException, IOException;

}

ResponseHandler定义了handleResponse方法,用于解析HttpResponse到泛型T

AbstractResponseHandler

org/apache/http/impl/client/AbstractResponseHandler.java

复制代码
@Contract(threading = ThreadingBehavior.IMMUTABLE)
public abstract class AbstractResponseHandler<T> implements ResponseHandler<T> {

    /**
     * Read the entity from the response body and pass it to the entity handler
     * method if the response was successful (a 2xx status code). If no response
     * body exists, this returns null. If the response was unsuccessful (&gt;= 300
     * status code), throws an {@link HttpResponseException}.
     */
    @Override
    public T handleResponse(final HttpResponse response)
            throws HttpResponseException, IOException {
        final StatusLine statusLine = response.getStatusLine();
        final HttpEntity entity = response.getEntity();
        if (statusLine.getStatusCode() >= 300) {
            EntityUtils.consume(entity);
            throw new HttpResponseException(statusLine.getStatusCode(),
                    statusLine.getReasonPhrase());
        }
        return entity == null ? null : handleEntity(entity);
    }

    /**
     * Handle the response entity and transform it into the actual response
     * object.
     */
    public abstract T handleEntity(HttpEntity entity) throws IOException;

}

AbstractResponseHandler声明实现ResponseHandler接口,其handleResponse方法针对statusCode大于等于300的抛出HttpResponseException,对于entity不为null的执行handleEntity方法

BasicResponseHandler

org/apache/http/impl/client/BasicResponseHandler.java

复制代码
@Contract(threading = ThreadingBehavior.IMMUTABLE)
public class BasicResponseHandler extends AbstractResponseHandler<String> {

    /**
     * Returns the entity as a body as a String.
     */
    @Override
    public String handleEntity(final HttpEntity entity) throws IOException {
        return EntityUtils.toString(entity);
    }

    @Override
    public String handleResponse(
            final HttpResponse response) throws HttpResponseException, IOException {
        return super.handleResponse(response);
    }

}

BasicResponseHandler继承了AbstractResponseHandler,它将entity转为String,使用的是EntityUtils.toString(entity)方法

EntityUtils.toString

org/apache/http/util/EntityUtils.java

复制代码
    public static String toString(final HttpEntity entity) throws IOException, ParseException {
        Args.notNull(entity, "Entity");
        return toString(entity, ContentType.get(entity));
    }

    private static String toString(
            final HttpEntity entity,
            final ContentType contentType) throws IOException {
        final InputStream inStream = entity.getContent();
        if (inStream == null) {
            return null;
        }
        try {
            Args.check(entity.getContentLength() <= Integer.MAX_VALUE,
                    "HTTP entity too large to be buffered in memory");
            int capacity = (int)entity.getContentLength();
            if (capacity < 0) {
                capacity = DEFAULT_BUFFER_SIZE;
            }
            Charset charset = null;
            if (contentType != null) {
                charset = contentType.getCharset();
                if (charset == null) {
                    final ContentType defaultContentType = ContentType.getByMimeType(contentType.getMimeType());
                    charset = defaultContentType != null ? defaultContentType.getCharset() : null;
                }
            }
            if (charset == null) {
                charset = HTTP.DEF_CONTENT_CHARSET;
            }
            final Reader reader = new InputStreamReader(inStream, charset);
            final CharArrayBuffer buffer = new CharArrayBuffer(capacity);
            final char[] tmp = new char[1024];
            int l;
            while((l = reader.read(tmp)) != -1) {
                buffer.append(tmp, 0, l);
            }
            return buffer.toString();
        } finally {
            inStream.close();
        }
    }

EntityUtils.toString方法通过entity.getContent()获取InputStream,之后将InputStream读取到CharArrayBuffer,最后关闭InputStream

execute

org/apache/http/impl/client/CloseableHttpClient.java

复制代码
    @Override
    public <T> T execute(final HttpHost target, final HttpRequest request,
            final ResponseHandler<? extends T> responseHandler, final HttpContext context)
            throws IOException, ClientProtocolException {
        Args.notNull(responseHandler, "Response handler");

        final CloseableHttpResponse response = execute(target, request, context);
        try {
            final T result = responseHandler.handleResponse(response);
            final HttpEntity entity = response.getEntity();
            EntityUtils.consume(entity);
            return result;
        } catch (final ClientProtocolException t) {
            // Try to salvage the underlying connection in case of a protocol exception
            final HttpEntity entity = response.getEntity();
            try {
                EntityUtils.consume(entity);
            } catch (final Exception t2) {
                // Log this exception. The original exception is more
                // important and will be thrown to the caller.
                this.log.warn("Error consuming content after an exception.", t2);
            }
            throw t;
        } finally {
            response.close();
        }
    }

CloseableHttpClient的execute方法先执行execute,然后try catch执行responseHandler.handleResponse,然后执行EntityUtils.consume(entity),在ClientProtocolException的时候也会执行EntityUtils.consume(entity),最后执行response.close()

小结

HttpClient提供了ResponseHandler接口,它有一个实现类是BasicResponseHandler,将entity的content转为string;相应的CloseableHttpClient也提供了支持ResponseHandler参数的execute方法,它先执行无handler的execute方法,然后try catch执行responseHandler.handleResponse,然后执行EntityUtils.consume(entity),在ClientProtocolException的时候也会执行EntityUtils.consume(entity),最后执行response.close()。

相关推荐
风铃儿~26 分钟前
Spring AI 入门:Java 开发者的生成式 AI 实践之路
java·人工智能·spring
斯普信专业组31 分钟前
Tomcat全方位监控实施方案指南
java·tomcat
忆雾屿42 分钟前
云原生时代 Kafka 深度实践:06原理剖析与源码解读
java·后端·云原生·kafka
武昌库里写JAVA1 小时前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
gaoliheng0061 小时前
Redis看门狗机制
java·数据库·redis
我是唐青枫1 小时前
.NET AOT 详解
java·服务器·.net
Su米苏1 小时前
Axios请求超时重发机制
java
本郡主是喵3 小时前
并发编程 - go版
java·服务器·开发语言
南风lof3 小时前
源码赏析:Java线程池中的那些细节
java·源码阅读