程序员的福音 - Apache Commons HttpClient

此文是系列文章第十篇,前几篇请点击链接查看

程序猿的福音 - Apache Commons简介

程序员的福音 - Apache Commons Lang

程序员的福音 - Apache Commons IO

程序员的福音 - Apache Commons Codec

程序员的福音 - Apache Commons Compress

程序员的福音 - Apache Commons Exec

程序员的福音 - Apache Commons Email

程序员的福音 - Apache Commons Net

程序员的福音 - Apache Commons Collections

Apache HttpClient 组件是为扩展而设计的,同时提供对基本HTTP协议的强大支持。

java.net包提供了通过HTTP访问资源的基本功能,但它并没有提供许多应用程序所需的全部灵活性或功能。HttpClient 组件通过提供一个高效、最新、功能丰富的包来填补这一空白,该包实现了最新HTTP标准的客户端。

HttpClient 过去是 Commons 的一部分,现在是 Apache HttpComponents 的一部分。Apache HttpComponents 是 Apache 的顶级项目,负责创建和维护专注于 HTTP 和相关协议的 Java 组件工具集。因此文章后面将不再使用 Commons HttpClient 字样,而是使用 HttpClient 。

HttpClient 目前有三个大版本,他们是不兼容的,可以同时存在。HttpClient 3过去是 Commons 的一部分,所以一般来说看到 Apache HttpClient 3 的说法指的就是 Commons HttpClient,所属包 org.apache.commons.httpclient,maven 依赖如下



    commons-httpclient
    commons-httpclient
    3.1

HttpClient 4 指的是 Apache HttpComponents 下的项目,所属包 org.apache.http,maven 依赖如下



    org.apache.httpcomponents
    httpclient
    4.5.13

HttpClient 5 指的是 Apache HttpComponents 下的最新项目,包结构是 org.apache.hc,依赖如下


    org.apache.httpcomponents.client5
    httpclient5
    5.1

HttpClient 3 早已不在维护,推荐使用最新的HttpClient 5,截止本文发布时间 2021-08-13,HttpClient 5 在前5天还发布了新版本。HttpClient 5 支持(经典API)(异步API)(反应式API)。

HttpClient 目前最新版本是 5.1,最低要求 Java8 以上。下面我将简单介绍下这几个版本 HttpClient 的用法。

01. 原生API

我们先来看看如果不使用 HttpClient 而是使用 Java 原生 API,写一个 http 请求的例子

HttpsURLConnection conn = null;
try {
    URL url = new URL("https://www.baidu.com/");
    conn = (HttpsURLConnection) url.openConnection();
    // https请求需要设置证书,为了简单此处默认信任服务器不做证书校验
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, new TrustManager[]{new X509TrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
        }
        @Override
        public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
        }
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }}, new java.security.SecureRandom());

    conn.setSSLSocketFactory(sc.getSocketFactory());
    conn.setHostnameVerifier((s, sslSession) -> true);
    conn.setRequestMethod("GET");
    conn.setConnectTimeout(5000);
    conn.setReadTimeout(5000);
    conn.setUseCaches(false);
    conn.connect();
    InputStream is = conn.getInputStream();
    try (BufferedReader br = new BufferedReader(
            new InputStreamReader(is))) {
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line).append("\n");
        }
        sb.deleteCharAt(sb.length() - 1);
        println(sb.toString());
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (conn != null) {
        conn.disconnect();
    }
}

我们看到这个例子是一个相对比较简单的 https 的 get请求,没有参数。代码已经比较复杂了,如果是 post 请求,需要传递参数,需要保存cookie(有些请求需求登录,我们还要先模拟登录请求后手动将 cookie 保存下来,下次请求在把 cookie 设置上)等场景代码将更为复杂。并且原生 API 默认不支持异步不支持响应式等,这时候就轮到 HttpClient 大显手身了。

02. HttpClient 3

HttpClient 3 由于早已不在维护,不支持 http2 和异步等特性,此处只做一个最简单的示例给大家了解一下。注意所属包是 org.apache.commons.httpclient

// httpClient对象是线程安全的,可以单例使用,提升性能
HttpClient httpClient = new HttpClient();
// 设置连接超时 和 socket超时
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(2000);
httpClient.getHttpConnectionManager().getParams().setSoTimeout(5000); // 响应超时
HttpMethod getM = new GetMethod("http://test.com/");
// 设置请求头
getM.setRequestHeader("Content-Type", "application/json");
NameValuePair p1 = new NameValuePair("name", "zs");
NameValuePair p2 = new NameValuePair("age", "11");
// 设置查询参数,相当于 ?name=zs&age=11
getM.setQueryString(new NameValuePair[]{p1, p2});
try {
    int code = httpClient.executeMethod(getM);
    if (code == HttpStatus.SC_OK) {
        // 获取结果字符串
        String res = getM.getResponseBodyAsString();
        // InputStream res = getM.getResponseBodyAsStream(); // 也可以转换为流
        System.out.println(res);
    } else {
        System.err.println("请求失败,状态码:" + code);
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    // 释放连接资源
    getM.releaseConnection();
}

03. HttpClient 4

HttpClient 4 也不是最新版本,此处也只做一个最简单的示例给大家了解一下。注意所属包是 org.apache.http

// httpClient对象是线程安全的,可以单例使用,提升性能
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
HttpGet httpGet = new HttpGet("http://test.com/?name=zs&age=11");
httpGet.setHeader("Content-Type", "application/json");
RequestConfig requestConfig = RequestConfig.custom()
        .setConnectTimeout(2000) // 连接超时
        .setConnectionRequestTimeout(2000) // 请求超时
        .setSocketTimeout(2000) // 响应超时
        .build();
httpGet.setConfig(requestConfig);
try (CloseableHttpResponse res = httpClient.execute(httpGet)) {
    int code = res.getStatusLine().getStatusCode();
    if (code == HttpStatus.SC_OK) {
        HttpEntity entity = res.getEntity();
        println(EntityUtils.toString(entity, "UTF-8"));
    } else {
        System.err.println("请求失败,状态码:" + code);
    }
} catch (IOException e) {
    System.err.println("请求异常");
} finally {
    httpGet.reset();
    // httpGet.releaseConnection(); // 等价于reset()
}

04. HttpClient 5

HttpClient 5 是目前最新版本。下面将着重介绍下。注意所属包是 org.apache.hc。HttpClient 5 除了包名和 HttpClient 4 有所区别,API的用法大体类似,只有小部分不一致。

建议将 httpClient 作为单例使用,所有请求共用一个 httpClient 对象,这样可以保存 HTTP 的状态,比如登录状态等

1. post请求

// httpClient对象是线程安全的,可以单例使用,提升性能
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost("https://test.com/");
RequestConfig requestConfig = RequestConfig.custom()
        .setConnectTimeout(Timeout.ofSeconds(2))
        .setConnectionRequestTimeout(Timeout.ofSeconds(2))
        .setResponseTimeout(Timeout.ofSeconds(2))
        .build();
httpPost.setConfig(requestConfig);
httpPost.setVersion(HttpVersion.HTTP_2); // 支持http2
httpPost.setHeader("Content-Type", "application/json");
// 支持多种entity参数,字节数组,流,文件等等
// 此处使用restful的"application/json",所以传递json字符串
httpPost.setEntity(new StringEntity(JSON.toJSONString(paramsObj)));
try (CloseableHttpResponse res = httpClient.execute(httpPost)) {
    if (res.getCode() == HttpStatus.SC_OK) {
        HttpEntity entity = res.getEntity();
        println(EntityUtils.toString(entity));
    } else {
        System.err.println("请求失败,状态码:" + res.getCode());
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    // 释放连接资源
    httpPost.reset();
}

2. 重定向支持

有些服务器在可能会返回一个重定向的响应,状态码为302或301,如果是浏览器则会自动向重定向地址发起http请求,HttpClient 5 同样支持此功能,示例如下

CloseableHttpClient httpClient = HttpClients.custom()
        .disableAutomaticRetries() //关闭自动重试
        .setRedirectStrategy(DefaultRedirectStrategy.INSTANCE)
        .build();
// ... ...

3. 异步支持

异步 API 的好处就不说了,HttpClient 5 异步 API 使用 NIO,可以使用少量的线程支持大量的并发,在并发量较大的情况下依然可以保持服务的稳定性和吞吐量。下面看一个简单的例子

CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault();
SimpleHttpRequest request = SimpleHttpRequest.create(Method.GET.name(), "https://www.baidu.com/");
httpClient.start();
httpClient.execute(request, new FutureCallback() {
    @Override
    public void completed(SimpleHttpResponse result) {
        // 响应成功
        println(result.getBodyText());
        IOUtils.closeQuietly(httpClient);
    }

    @Override
    public void failed(Exception ex) {
        // 响应出错
        ex.printStackTrace();
        IOUtils.closeQuietly(httpClient);
    }

    @Override
    public void cancelled() {
        // 响应取消
        println("cancelled");
        IOUtils.closeQuietly(httpClient);
    }
});
// ... ... 做其他业务处理

05. 总结

HttpClient 作为 Java HTTP 客户端的工具类,API简单易懂,大大简化了Java HTTP 发送程序的复杂度,并且自动支持 Cookie 功能,GZip 解析,重定向支持,异步支持等等,用来代替 Java 自带的 API 是个不错的选择。

后续章节我将继续给大家介绍 commons 中其他好用的工具类库,期待你的关注。

你可能感兴趣的:(程序员的福音 - Apache Commons HttpClient)