Java中的HTTP客戶端工具——HttpClient

客戶端http協(xié)議傳輸類庫(kù)。HttpClient被用來(lái)發(fā)送和接受Http消息。HttpClient不會(huì)處理Http消息的內(nèi)容,不會(huì)進(jìn)行Javascript解析,不會(huì)關(guān)心ContentType,如果沒(méi)有明確設(shè)置,HttpClient也不會(huì)對(duì)請(qǐng)求進(jìn)行格式化、重定向url,或者其他任何和http消息傳輸相關(guān)的功能。

HttpClient核心接口

org.apache.http.HttpMessage:HTTP 消息由客戶端到服務(wù)器的請(qǐng)求和服務(wù)器到客戶端的響應(yīng)組成。
org.apache.http.HttpRequest:從客戶端到服務(wù)器的請(qǐng)求消息在該消息的第一行中包括要應(yīng)用于資源的方法、資源的標(biāo)識(shí)符和正在使用的協(xié)議版本。
org.apache.http.client.HttpClient:這個(gè)接口只代表了 HTTP 請(qǐng)求執(zhí)行的最基本的合約。它對(duì)請(qǐng)求執(zhí)行過(guò)程沒(méi)有施加任何限制或特定細(xì)節(jié),并將狀態(tài)管理、身份驗(yàn)證和重定向處理的細(xì)節(jié)留給單獨(dú)的實(shí)現(xiàn)。
org.apache.http.HttpResponse:在接收并解釋請(qǐng)求消息后,服務(wù)器以 HTTP 響應(yīng)消息進(jìn)行響應(yīng)。

核心依賴

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>

HttpClient入?yún)?/h3>

實(shí)體入?yún)⒌恼?qǐng)求需要繼承HttpEntityEnclosingRequestBase,官方HttpDelete/HttpPatch/HttpPost/HttpPut均支持實(shí)體入?yún)?/strong>

public abstract class HttpEntityEnclosingRequestBase
    extends HttpRequestBase implements HttpEntityEnclosingRequest {

    private HttpEntity entity;

    public HttpEntityEnclosingRequestBase() {
        super();
    }

    @Override
    public HttpEntity getEntity() {
        return this.entity;
    }

    @Override
    public void setEntity(final HttpEntity entity) {
        this.entity = entity;
    }

    @Override
    public boolean expectContinue() {
        final Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE);
        return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue());
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        final HttpEntityEnclosingRequestBase clone =
            (HttpEntityEnclosingRequestBase) super.clone();
        if (this.entity != null) {
            clone.entity = CloneUtils.cloneObject(this.entity);
        }
        return clone;
    }

}

無(wú)需實(shí)體入?yún)⒌恼?qǐng)求可以直接繼承HttpRequestBase,官方HttpGet/HttpHead/HttpOptions/HttpTrace均不支持實(shí)體入?yún)ⅲ瑢?shí)在有需要可以自行重寫(xiě),直接繼承HttpEntityEnclosingRequestBase即可

public abstract class HttpRequestBase extends AbstractExecutionAwareRequest
    implements HttpUriRequest, Configurable {

    private ProtocolVersion version;
    private URI uri;
    private RequestConfig config;

    @Override
    public abstract String getMethod();

    /**
     * @since 4.3
     */
    public void setProtocolVersion(final ProtocolVersion version) {
        this.version = version;
    }

    @Override
    public ProtocolVersion getProtocolVersion() {
        return version != null ? version : HttpProtocolParams.getVersion(getParams());
    }

    @Override
    public URI getURI() {
        return this.uri;
    }

    @Override
    public RequestLine getRequestLine() {
        final String method = getMethod();
        final ProtocolVersion ver = getProtocolVersion();
        final URI uriCopy = getURI(); // avoids possible window where URI could be changed
        String uritext = null;
        if (uriCopy != null) {
            uritext = uriCopy.toASCIIString();
        }
        if (uritext == null || uritext.isEmpty()) {
            uritext = "/";
        }
        return new BasicRequestLine(method, uritext, ver);
    }


    @Override
    public RequestConfig getConfig() {
        return config;
    }

    public void setConfig(final RequestConfig config) {
        this.config = config;
    }

    public void setURI(final URI uri) {
        this.uri = uri;
    }

    public void started() {
    }

    public void releaseConnection() {
        reset();
    }

    @Override
    public String toString() {
        return getMethod() + " " + getURI() + " " + getProtocolVersion();
    }

}
HttpEntityEnclosingRequestBase繼承關(guān)系圖

HttpEntity:HTTP 消息發(fā)送或接收的實(shí)體,HttpEntityEnclosingRequestBase中的屬性

public interface HttpEntity {
    //實(shí)體是否能夠多次生成其數(shù)據(jù)。可重復(fù)實(shí)體的 getContent() 和 writeTo(OutputStream) 方法可以多次調(diào)用,而不可重復(fù)實(shí)體則不能。
    boolean isRepeatable();
    //實(shí)體的分塊編碼。HTTP1.0不支持分塊。此方法的主要目的是指示在發(fā)送實(shí)體時(shí)是否應(yīng)使用分塊編碼。對(duì)于接收到的實(shí)體,它還可以指示是否使用分塊編碼接收到實(shí)體。
    boolean isChunked();
    //內(nèi)容的長(zhǎng)度
    long getContentLength();
    //獲取 Content-Type 標(biāo)頭。這是發(fā)送實(shí)體時(shí)應(yīng)使用的標(biāo)頭,或者與實(shí)體一起接收的標(biāo)頭。它可以包含一個(gè)字符集屬性。
    Header getContentType();
    //獲取 Content-Encoding 標(biāo)頭。這是發(fā)送實(shí)體時(shí)應(yīng)使用的標(biāo)頭,或者與實(shí)體一起接收的標(biāo)頭。修改內(nèi)容編碼的包裝實(shí)體應(yīng)相應(yīng)地調(diào)整此標(biāo)頭。
    Header getContentEncoding();
    //實(shí)體的內(nèi)容流??蒖epeatable實(shí)體應(yīng)為每次調(diào)用此方法創(chuàng)建一個(gè)新的InputStream實(shí)例,因此可以多次使用。不可repeatable的實(shí)體應(yīng)返回相同的InputStream實(shí)例,因此不得多次使用。
    InputStream getContent() throws IOException, UnsupportedOperationException;
    //實(shí)體內(nèi)容寫(xiě)入輸出流。重要提示:請(qǐng)注意,所有實(shí)體實(shí)現(xiàn)必須確保在此方法返回時(shí)正確釋放所有分配的資源。
    void writeTo(OutputStream outStream) throws IOException;
    //實(shí)體是否依賴于基礎(chǔ)流。直接從套接字讀取數(shù)據(jù)的流式實(shí)體應(yīng)返回true 。自包含實(shí)體應(yīng)返回false 。包裝實(shí)體應(yīng)將此調(diào)用委托給被包裝實(shí)體。
    boolean isStreaming(); // don't expect an exception here
    //自 4.1 版起已棄用。調(diào)用該方法表示不再需要該實(shí)體的內(nèi)容。由于此方法調(diào)用,所有實(shí)體實(shí)現(xiàn)都應(yīng)釋放所有分配的資源。
    @Deprecated
    void consumeContent() throws IOException;
}
AbstractHttpEntity:實(shí)體的抽象基類。為HttpEntity的流式和自包含實(shí)現(xiàn)提供常用屬性。
public abstract class AbstractHttpEntity implements HttpEntity {
    protected static final int OUTPUT_BUFFER_SIZE = 4096;
    protected Header contentType;
    protected Header contentEncoding;
    protected boolean chunked;

    protected AbstractHttpEntity() {
        super();
    }

    @Override
    public Header getContentType() {
        return this.contentType;
    }

    @Override
    public Header getContentEncoding() {
        return this.contentEncoding;
    }

    @Override
    public boolean isChunked() {
        return this.chunked;
    }

    public void setContentType(final Header contentType) {
        this.contentType = contentType;
    }

    public void setContentType(final String ctString) {
        Header h = null;
        if (ctString != null) {
            h = new BasicHeader(HTTP.CONTENT_TYPE, ctString);
        }
        setContentType(h);
    }

    public void setContentEncoding(final Header contentEncoding) {
        this.contentEncoding = contentEncoding;
    }

    public void setContentEncoding(final String ceString) {
        Header h = null;
        if (ceString != null) {
            h = new BasicHeader(HTTP.CONTENT_ENCODING, ceString);
        }
        setContentEncoding(h);
    }

    public void setChunked(final boolean b) {
        this.chunked = b;
    }

    @Override
    @Deprecated
    public void consumeContent() throws IOException {
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append('[');
        if (contentType != null) {
            sb.append("Content-Type: ");
            sb.append(contentType.getValue());
            sb.append(',');
        }
        if (contentEncoding != null) {
            sb.append("Content-Encoding: ");
            sb.append(contentEncoding.getValue());
            sb.append(',');
        }
        final long len = getContentLength();
        if (len >= 0) {
            sb.append("Content-Length: ");
            sb.append(len);
            sb.append(',');
        }
        sb.append("Chunked: ");
        sb.append(chunked);
        sb.append(']');
        return sb.toString();
    }

}

AbstractHttpEntity下常用實(shí)體
BasicHttpEntity:從InputStream獲取其內(nèi)容的通用流式、不可重復(fù)實(shí)體
ByteArrayEntity:一個(gè)自包含的、可重復(fù)的實(shí)體,它從字節(jié)數(shù)組中獲取其內(nèi)容。
EntityTemplate:將內(nèi)容生成過(guò)程委托給ContentProducer的實(shí)體。
FileEntity:從文件中獲取其內(nèi)容的自包含、可重復(fù)的實(shí)體。
InputStreamEntity:從InputStream獲取其內(nèi)容的流式、不可重復(fù)的實(shí)體。
SerializableEntity:從Serializable獲取其內(nèi)容的流式實(shí)體。從Serializable實(shí)例獲得的內(nèi)容可以選擇緩沖在字節(jié)數(shù)組中,以使實(shí)體自包含和可重復(fù)。
StringEntity:一個(gè)自包含、可重復(fù)的實(shí)體,從String獲取其內(nèi)容。
UrlEncodedFormEntity:由 url 編碼對(duì)列表組成的實(shí)體。這在發(fā)送 HTTP POST 請(qǐng)求時(shí)通常很有用。

HttpEntityWrapper:用于包裝實(shí)體的基類。保留一個(gè)wrappedEntity并將所有調(diào)用委托給它。包裝實(shí)體的實(shí)現(xiàn)可以從此類派生,并且只需要覆蓋那些不應(yīng)委托給包裝實(shí)體的方法。
public class HttpEntityWrapper implements HttpEntity {
    protected HttpEntity wrappedEntity;

    public HttpEntityWrapper(final HttpEntity wrappedEntity) {
        super();
        this.wrappedEntity = Args.notNull(wrappedEntity, "Wrapped entity");
    }

    @Override
    public boolean isRepeatable() {
        return wrappedEntity.isRepeatable();
    }

    @Override
    public boolean isChunked() {
        return wrappedEntity.isChunked();
    }

    @Override
    public long getContentLength() {
        return wrappedEntity.getContentLength();
    }

    @Override
    public Header getContentType() {
        return wrappedEntity.getContentType();
    }

    @Override
    public Header getContentEncoding() {
        return wrappedEntity.getContentEncoding();
    }

    @Override
    public InputStream getContent()
        throws IOException {
        return wrappedEntity.getContent();
    }

    @Override
    public void writeTo(final OutputStream outStream)
        throws IOException {
        wrappedEntity.writeTo(outStream);
    }

    @Override
    public boolean isStreaming() {
        return wrappedEntity.isStreaming();
    }

    @Override
    @Deprecated
    public void consumeContent() throws IOException {
        wrappedEntity.consumeContent();
    }

}

HttpEntityWrapper下常用實(shí)體
BufferedHttpEntity:必要時(shí)緩沖其內(nèi)容的包裝實(shí)體。緩沖實(shí)體始終是可重復(fù)的。如果被包裝的實(shí)體本身是可重復(fù)的,則調(diào)用被傳遞。如果包裝的實(shí)體不可重復(fù),則將內(nèi)容一次讀入緩沖區(qū)并根據(jù)需要從那里提供。
DecompressingEntity:用于解壓HttpEntity實(shí)現(xiàn)的通用基類,其子類有支持Deflate的DeflateDecompressingEntity和支持GZip的GzipDecompressingEntity
GzipCompressingEntity:writing時(shí)壓縮內(nèi)容的包裝實(shí)體。
ResponseEntityProxy:包含在響應(yīng)消息中的HttpEntity的包裝器類。

HttpClient發(fā)起請(qǐng)求

HTTP 請(qǐng)求執(zhí)行的基本類HttpClient
目前最常用的實(shí)現(xiàn)類的是CloseableHttpClient,以前的DefaultHttpClient在自4.3版本之后作廢了。

@Contract(threading = ThreadingBehavior.SAFE)
public abstract class CloseableHttpClient implements HttpClient, Closeable {

    private final Log log = LogFactory.getLog(getClass());

    protected abstract CloseableHttpResponse doExecute(HttpHost target, HttpRequest request,
            HttpContext context) throws IOException, ClientProtocolException;
    //使用默認(rèn)上下文執(zhí)行 HTTP 請(qǐng)求。
    @Override
    public CloseableHttpResponse execute(
            final HttpHost target,
            final HttpRequest request,
            final HttpContext context) throws IOException, ClientProtocolException {
        return doExecute(target, request, context);
    }

    @Override
    public CloseableHttpResponse execute(
            final HttpUriRequest request,
            final HttpContext context) throws IOException, ClientProtocolException {
        Args.notNull(request, "HTTP request");
        return doExecute(determineTarget(request), request, context);
    }

    private static HttpHost determineTarget(final HttpUriRequest request) throws ClientProtocolException {
        HttpHost target = null;

        final URI requestURI = request.getURI();
        if (requestURI.isAbsolute()) {
            target = URIUtils.extractHost(requestURI);
            if (target == null) {
                throw new ClientProtocolException("URI does not specify a valid host name: "
                        + requestURI);
            }
        }
        return target;
    }

    @Override
    public CloseableHttpResponse execute(
            final HttpUriRequest request) throws IOException, ClientProtocolException {
        return execute(request, (HttpContext) null);
    }

    @Override
    public CloseableHttpResponse execute(
            final HttpHost target,
            final HttpRequest request) throws IOException, ClientProtocolException {
        return doExecute(target, request, null);
    }

    @Override
    public <T> T execute(final HttpUriRequest request,
            final ResponseHandler<? extends T> responseHandler) throws IOException,
            ClientProtocolException {
        return execute(request, responseHandler, null);
    }

    @Override
    public <T> T execute(final HttpUriRequest request,
            final ResponseHandler<? extends T> responseHandler, final HttpContext context)
            throws IOException, ClientProtocolException {
        final HttpHost target = determineTarget(request);
        return execute(target, request, responseHandler, context);
    }

    @Override
    public <T> T execute(final HttpHost target, final HttpRequest request,
            final ResponseHandler<? extends T> responseHandler) throws IOException,
            ClientProtocolException {
        return execute(target, request, responseHandler, null);
    }

    @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();
        }
    }

}

target(HttpHost):請(qǐng)求的目標(biāo)主機(jī)。如果實(shí)現(xiàn)仍然可以確定路由,例如到默認(rèn)目標(biāo)或通過(guò)檢查請(qǐng)求,則可以接受null 。
request:要執(zhí)行的請(qǐng)求,支持HttpRequestHttpUriRequest
responseHandler(ResponseHandler):響應(yīng)處理程序
context(HttpContext):用于執(zhí)行的上下文,或null使用默認(rèn)上下文

HttpClientsCloseableHttpClient實(shí)例的工廠方法。
public class HttpClients {

    private HttpClients() {
        super();
    }
    //創(chuàng)建構(gòu)建器對(duì)象以構(gòu)建自定義CloseableHttpClient實(shí)例。
    public static HttpClientBuilder custom() {
        return HttpClientBuilder.create();
    }
    //使用默認(rèn)配置創(chuàng)建CloseableHttpClient實(shí)例。
    public static CloseableHttpClient createDefault() {
        return HttpClientBuilder.create().build();
    }
    //基于系統(tǒng)屬性創(chuàng)建具有默認(rèn)配置的CloseableHttpClient實(shí)例。
    public static CloseableHttpClient createSystem() {
        return HttpClientBuilder.create().useSystemProperties().build();
    }
    //創(chuàng)建實(shí)現(xiàn)最基本 HTTP 協(xié)議支持的CloseableHttpClient實(shí)例。
    public static CloseableHttpClient createMinimal() {
        return new MinimalHttpClient(new PoolingHttpClientConnectionManager());
    }
    //創(chuàng)建實(shí)現(xiàn)最基本 HTTP 協(xié)議支持的CloseableHttpClient實(shí)例。
    public static CloseableHttpClient createMinimal(final HttpClientConnectionManager connManager) {
        return new MinimalHttpClient(connManager);
    }

}

HttpClient響應(yīng)

目前最常用的是CloseableHttpResponse

public interface CloseableHttpResponse extends HttpResponse, Closeable {
}

HttpResponse

public interface HttpResponse extends HttpMessage {
    //獲取此響應(yīng)的狀態(tài)行??梢允褂胹etStatusLine方法之一設(shè)置狀態(tài)行,也可以在構(gòu)造函數(shù)中對(duì)其進(jìn)行初始化。
    StatusLine getStatusLine();
    //設(shè)置此響應(yīng)的狀態(tài)行。
    void setStatusLine(StatusLine statusline);
    //設(shè)置此響應(yīng)的狀態(tài)行。原因短語(yǔ)將根據(jù)當(dāng)前l(fā)ocale確定。
    void setStatusLine(ProtocolVersion ver, int code);
    void setStatusLine(ProtocolVersion ver, int code, String reason);
    //使用新的狀態(tài)代碼更新此響應(yīng)的狀態(tài)行。
    void setStatusCode(int code)
        throws IllegalStateException;
    //使用新的原因短語(yǔ)更新此響應(yīng)的狀態(tài)行。
    void setReasonPhrase(String reason)
        throws IllegalStateException;
    //獲取此響應(yīng)的消息實(shí)體(如果有)。該實(shí)體是通過(guò)調(diào)用setEntity來(lái)提供的。
    HttpEntity getEntity();
    //將響應(yīng)實(shí)體與此響應(yīng)相關(guān)聯(lián)。
    void setEntity(HttpEntity entity);
    //更改此響應(yīng)的語(yǔ)言環(huán)境。
    void setLocale(Locale loc);
}
CloseableHttpResponse繼承關(guān)系圖

HttpClient進(jìn)行POST請(qǐng)求例子:

    private static String sendPOST(String url) throws IOException {
        String result = "";
        HttpPost post = new HttpPost(url);
        List<NameValuePair> urlParameters = new ArrayList<>();
        urlParameters.add(new BasicNameValuePair("username", "abc"));
        urlParameters.add(new BasicNameValuePair("password", "123"));
        post.setEntity(new UrlEncodedFormEntity(urlParameters));
        try (CloseableHttpClient httpClient = HttpClients.createDefault();
             CloseableHttpResponse response = httpClient.execute(post)){
            result = EntityUtils.toString(response.getEntity());
        }
        return result;
    }
HttpURLConnection相關(guān)博客推薦

上傳文件:https://lsqingfeng.blog.csdn.net/article/details/90611686
下載文件:https://blog.csdn.net/WxQ92222/article/details/79896489

調(diào)用鏈路源碼分析可以參考:
https://blog.csdn.net/u012504392/article/details/109432686

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容