4.5日学习打卡----学习Apache HttpClient 5

4.5日学习打卡

目录:

  • 4.5日学习打卡
  • [Apache Commons HttpClient](#Apache Commons HttpClient)
  • [Apache HttpClient 5](#Apache HttpClient 5)
    • 简介
    • 依赖
    • [HttpClient 5 GET 请求](#HttpClient 5 GET 请求)
    • [HttpClient 5 Fluent GET](#HttpClient 5 Fluent GET)
    • [HttpClient5 GET 请求参数](#HttpClient5 GET 请求参数)
    • [HttpClient 5 POST 请求](#HttpClient 5 POST 请求)
    • [HttpClient 5 Fluent POST](#HttpClient 5 Fluent POST)
    • [HttpClient5 POST JSON 参数](#HttpClient5 POST JSON 参数)
    • [HttpClient 5 设置超时](#HttpClient 5 设置超时)
    • [HttpClient 5 异步请求](#HttpClient 5 异步请求)
    • [HttpClient 5 获取 Cookie](#HttpClient 5 获取 Cookie)
    • [HttpClient 5 读取文件内容请求](#HttpClient 5 读取文件内容请求)
    • [HttpClient 5 表单登录](#HttpClient 5 表单登录)
    • [HttpClient 5 Basic Authorization](#HttpClient 5 Basic Authorization)
    • [HttpClient 5 Digest Authorization](#HttpClient 5 Digest Authorization)
    • [HttpClient 5 拦截器](#HttpClient 5 拦截器)

Apache Commons HttpClient

简介

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

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 依赖如下

xml 复制代码
<!-- HttpClient 3 -->
<dependency>
    <groupId>commons-httpclient</groupId>
    <artifactId>commons-httpclient</artifactId>
    <version>3.1</version>
</dependency>

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

xml 复制代码
<!-- HttpClient 4 -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

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

xml 复制代码
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.1</version>
</dependency>

HttpClient 3 早已不在维护,推荐使用最新的HttpClient 5。HttpClient 5 支持(经典API)(异步API)(反应式API)。

下面我将简单介绍下这几个版本 HttpClient 的用法。

  1. 原生API
    我们先来看看如果不使用 HttpClient 而是使用 Java 原生 API,写一个 http 请求的例子
java 复制代码
package com.jjy.httpclient5demo.test;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class JavaApi {
    public static void main(String[] args) {
        HttpsURLConnection conn = null;
        try {
            URL url = new URL("https://httpbin.org/get");
            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);
                System.out.println(sb.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }
}
java 复制代码
package com.jjy.httpclient5demo.test;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpHeaders;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class NativeJavaHttpClient {
    public static String get(String url) {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Accept", "application/json") // 如果需要的话,设置请求头
                .GET()
                .build();

        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            int statusCode = response.statusCode();
            System.out.println("Status Code: " + statusCode);
            System.out.println("Response Headers: " + response.headers());
            return response.body();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        String url = "http://httpbin.org/get";
        String responseContent = NativeJavaHttpClient.get(url);
        System.out.println(responseContent);
    }
}

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

  1. HttpClient 3
java 复制代码
package com.jjy.httpclient5demo.test;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;

import java.io.IOException;

public class HttpClient3 {
    public static void main(String[] args) {
        // httpClient对象是线程安全的,可以单例使用,提升性能
        HttpClient httpClient = new HttpClient();
// 设置连接超时 和 socket超时
        httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(2000);
        httpClient.getHttpConnectionManager().getParams().setSoTimeout(5000); // 响应超时
        HttpMethod getM = new GetMethod("http://httpbin.org/get");
// 设置请求头
        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();
        }
    }
}
  1. HttpClient 4
java 复制代码
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.http.client.utils.URIBuilder;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

public class HttpClient4 {
    public static void main(String[] args) throws URISyntaxException {
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();

        // 创建一个URIBuilder来构建带参数的URL  
        URIBuilder uriBuilder = new URIBuilder("http://httpbin.org/get");
        try {
            // 添加参数  
            uriBuilder.addParameter("name", "zs");
            uriBuilder.addParameter("age", "11");

            // 构建最终的URI  
            URI uri = uriBuilder.build();

            HttpGet httpGet = new HttpGet(uri);

            // 设置请求配置(超时等)  
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(2000) // 连接超时  
                    .setConnectionRequestTimeout(2000) // 请求超时  
                    .setSocketTimeout(2000) // 响应超时  
                    .build();
            httpGet.setConfig(requestConfig);

            // 设置请求头(如果需要)  
            // httpGet.setHeader("Content-Type", "application/json"); // 注意:GET请求通常不需要Content-Type头  

            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == HttpStatus.SC_OK) {
                    HttpEntity entity = response.getEntity();
                    System.out.println(EntityUtils.toString(entity, "UTF-8"));
                } else {
                    System.err.println("请求失败,状态码:" + statusCode);
                }
            } catch (IOException e) {
                System.err.println("请求异常:" + e.getMessage());
            }
        } catch (URISyntaxException e) {
            System.err.println("URI构建异常:" + e.getMessage());
        }
    }
}

Apache HttpClient 5

简介

Apache HttpClient 5 是一个开源的 HTTP 工具包,可以支持最新 HTTP 协议标准,且有丰富的 API 和强大的扩展特性,可以用于构建任何需要进行 HTTP 协议处理的应用程序。

下面将会介绍 Apache HttpClient 5 中最为常见的一些用法:

HttpClient 5 的 Get 请求、Post 请求、如何携带参数、JSON 参数、设置超时、异步请求、操作 Cookie、表单登录、基本认证、Digest 认证以及自定义 HTTP 请求拦截器等

依赖

maven

xml 复制代码
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.client5/httpclient5 -->
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.1.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.client5/httpclient5-fluent -->
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5-fluent</artifactId>
    <version>5.1.3</version>
</dependency>

HttpClient 5 GET 请求

java 复制代码
package com.jjy.httpclient5demo.test;

import org.apache.hc.client5.http.classic.methods.HttpGet; // 导入Apache HttpClient 5的HttpGet类,用于发送HTTP GET请求  
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; // 导入CloseableHttpClient类,表示一个可关闭的HTTP客户端  
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; // 导入CloseableHttpResponse类,表示一个可关闭的HTTP响应  
import org.apache.hc.client5.http.impl.classic.HttpClients; // 导入HttpClients类,用于创建HttpClient实例  
import org.apache.hc.core5.http.HttpEntity; // 导入HttpEntity类,表示HTTP消息体  
import org.apache.hc.core5.http.ParseException; // 导入ParseException类,表示HTTP消息解析异常  
import org.apache.hc.core5.http.io.entity.EntityUtils; // 导入EntityUtils类,提供HTTP实体的工具方法  

// 注意:下面的导入似乎是不相关的,因为该类并不是一个Servlet  
// import javax.servlet.http.HttpServlet;  
// import javax.servlet.http.HttpServletRequest;  

import java.io.IOException; // 导入IO异常类  

public class GetHttp5Client {

    /**
     * 发送GET请求并返回响应内容  
     *
     * @param url 请求的URL  
     * @return 响应内容的字符串形式
     */
    public static String get(String url) {
        String resultContent = null; // 初始化响应内容的字符串为null  
        HttpGet httpGet = new HttpGet(url); // 创建HttpGet对象并设置请求的URL  
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) { // 创建默认的CloseableHttpClient实例  
            try (CloseableHttpResponse response = httpclient.execute(httpGet)) { // 执行GET请求并获取响应  
                // 获取响应的状态信息  
                System.out.println(response.getVersion()); // 打印HTTP协议版本(例如:HTTP/1.1)  
                System.out.println(response.getCode()); // 打印响应的状态码(例如:200)  
                System.out.println(response.getReasonPhrase()); // 打印响应的状态描述(例如:OK)  
                HttpEntity entity = response.getEntity(); // 获取响应实体  
                // 将响应实体转换为字符串  
                resultContent = EntityUtils.toString(entity);
            }
        } catch (IOException | ParseException e) { // 捕获IO异常或HTTP消息解析异常  
            e.printStackTrace(); // 打印异常堆栈信息  
        }
        return resultContent; // 返回响应内容的字符串  
    }

    /**
     * 主函数,程序的入口点  
     *
     * @param args 命令行参数  
     */
    public static void main(String[] args) {
        // 测试GET请求,注意这里的URL已经被注释掉了,实际使用时可以取消注释并替换成需要的URL  
        // String url = "http://localhost:8080/user/loginwithcode";  
        // String url = "https://api.fastgpt.in/api/v1/chat/completions";  
        String url = "http://httpbin.org/get"; // 使用的测试URL  

        String s = GetHttp5Client.get(url); // 调用get方法发送GET请求并获取响应内容  
        System.out.println(s); // 打印响应内容  
    }
}

响应信息:

java 复制代码
HTTP/1.1
200
OK
{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip, x-gzip, deflate", 
    "Host": "httpbin.org", 
    "User-Agent": "Apache-HttpClient/5.1.3 (Java/17)", 
    "X-Amzn-Trace-Id": "Root=1-62bb1891-5ab5e5376ed960471bf32f17"
  }, 
  "origin": "47.251.4.198", 
  "url": "http://httpbin.org/get"
}

HttpClient 5 Fluent GET

使用 Apache HttpClient 5 提供的 Fluent API 可以更便捷的发起 GET 请求,但是可操作的地方较少。

依赖:

xml 复制代码
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.client5/httpclient5-fluent -->
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5-fluent</artifactId>
    <version>5.1.3</version>
</dependency>

示例:

java 复制代码
package com.jjy.httpclient5demo;

import java.io.IOException;

import org.apache.hc.client5.http.fluent.Request; // 导入Apache HttpClient 5的fluent API的Request类,用于构建HTTP请求  
import org.apache.hc.client5.http.fluent.Response; // 导入Apache HttpClient 5的fluent API的Response类,用于处理HTTP响应  

/**
 * 使用Apache HttpClient 5的fluent API发送GET请求并获取响应内容  
 *
 * @author zbxmx
 */
public class HttpClient5GetFluent {

    /**
     * 主函数,程序的入口点  
     *
     * @param args 命令行参数  
     */
    public static void main(String[] args) {
        // 调用get方法发送GET请求,并打印返回的响应内容  
        System.out.println(get("http://httpbin.org/get"));
    }

    /**
     * 发送GET请求并返回响应内容  
     *
     * @param url 请求的URL  
     * @return 响应内容的字符串形式
     */
    public static String get(String url) {
        String result = null; // 初始化响应内容的字符串为null  
        try {
            // 使用fluent API构建GET请求并执行,获取响应对象  
            Response response = Request.get(url).execute();
            // 从响应对象中获取响应内容,并转换为字符串  
            result = response.returnContent().asString();
        } catch (IOException e) {
            // 捕获IO异常,并打印异常堆栈信息  
            e.printStackTrace();
        }
        // 返回响应内容的字符串  
        return result;
    }

}

HttpClient5 GET 请求参数

使用 URIBuilder 的 addParameters() 方法来构建 GET 请求的参数。

java 复制代码
package com.jjy.httpclient5demo.test;


import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.apache.hc.core5.net.URIBuilder;

/**
 * 使用Apache HttpClient 5的经典API发送带有参数的GET请求并获取响应内容
 *
 * @author zbxmx
 */
public class HttpClient5GetParams {

    /**
     * 主函数,程序的入口点
     *
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        // 调用get方法发送带有参数的GET请求,并打印返回的响应内容
        String result = get("http://httpbin.org/get");
        System.out.println(result);
    }

    /**
     * 发送带有参数的GET请求并返回响应内容
     *
     * @param url 请求的URL
     * @return 响应内容的字符串形式
     */
    public static String get(String url) {
        String resultContent = null;

        // 创建HttpGet对象,设置请求的URL
        HttpGet httpGet = new HttpGet(url);

        // 创建存放表单参数的列表
        List<NameValuePair> nvps = new ArrayList<>();

        // 添加GET请求参数
        nvps.add(new BasicNameValuePair("username", "wdbyte.com"));
        nvps.add(new BasicNameValuePair("password", "secret"));

        // 使用URIBuilder构建新的URI,将参数添加到请求URL中
        try {
            URI uri = new URIBuilder(new URI(url))
                    .addParameters(nvps) // 将参数添加到URL中
                    .build(); // 构建完整的URI

            // 设置HttpGet对象的URI
            httpGet.setUri(uri);
        } catch (URISyntaxException e) {
            // 如果URI构建出错,则抛出运行时异常
            throw new RuntimeException(e);
        }

        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            // 创建默认的HttpClient实例
            try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
                // 使用HttpClient执行GET请求,获取响应对象

                // 打印响应的HTTP版本、状态码和原因短语
                System.out.println(response.getVersion()); // HTTP/1.1
                System.out.println(response.getCode()); // 200
                System.out.println(response.getReasonPhrase()); // OK
                 
                HttpEntity entity = response.getEntity();

                // 将响应实体转换为字符串
                resultContent = EntityUtils.toString(entity);
            }
        } catch (IOException | ParseException e) {
            // 捕获IO异常或解析异常,并打印异常堆栈信息
            e.printStackTrace();
        }

        // 返回响应内容的字符串
        return resultContent;
    }
}

输出信息:

java 复制代码
{
  "args": {
    "password": "secret", 
    "username": "wdbyte.com"
  }, 
  "headers": {
    "Accept-Encoding": "gzip, x-gzip, deflate", 
    "Host": "httpbin.org", 
    "User-Agent": "Apache-HttpClient/5.1.3 (Java/1.8.0_151)", 
    "X-Amzn-Trace-Id": "Root=1-62ecc660-69d58a226aefb1b6226541ec"
  }, 
  "origin": "218.26.154.94", 
  "url": "http://httpbin.org/get?username=wdbyte.com&password=secret"
}

HttpClient 5 POST 请求

下面演示发起一个 POST 请求,并携带表单参数。

java 复制代码
package com.jjy.httpclient5demo.test;


import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.message.BasicNameValuePair;
/**
 * 使用Apache HttpClient 5的经典API发送带有表单参数的POST请求并获取响应内容
 *
 * @author zbxmx
 */
public class HttpClient5Post {

    /**
     * 主函数,程序的入口点
     *
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        // 调用post方法发送带有表单参数的POST请求,并打印返回的响应内容
        String result = post("http://httpbin.org/post");
        System.out.println(result);
    }

    /**
     * 发送带有表单参数的POST请求并返回响应内容
     *
     * @param url 请求的URL
     * @return 响应内容的字符串形式
     */
    public static String post(String url) {
        String result = null;

        // 创建HttpPost对象,设置请求的URL
        HttpPost httpPost = new HttpPost(url);

        // 创建存放表单参数的列表
        List<NameValuePair> nvps = new ArrayList<>();

        // 添加POST请求参数
        nvps.add(new BasicNameValuePair("username", "wdbyte.com"));
        nvps.add(new BasicNameValuePair("password", "secret"));

        // 创建UrlEncodedFormEntity,将表单参数添加到POST请求中
        httpPost.setEntity(new UrlEncodedFormEntity(nvps));


        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            // 创建默认的HttpClient实例
            try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
                // 使用HttpClient执行POST请求,获取响应对象

                // 打印响应的HTTP版本、状态码和原因短语
                System.out.println(response.getVersion()); // HTTP/1.1
                System.out.println(response.getCode()); // 200
                System.out.println(response.getReasonPhrase()); // OK

                // 获取响应实体
                HttpEntity entity = response.getEntity();

                // 将响应实体转换为字符串
                result = EntityUtils.toString(entity);

                // 确保响应实体被完全消费,避免资源泄露
                EntityUtils.consume(entity);
            }
        } catch (IOException | ParseException e) {
            // 捕获IO异常,并打印异常堆栈信息
            e.printStackTrace();
        }

        // 返回响应内容的字符串
        return result;
    }
}

输出结果:

java 复制代码
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "password": "secret", 
    "username": "wdbyte.com"
  }, 
  "headers": {
    "Accept-Encoding": "gzip, x-gzip, deflate", 
    "Content-Length": "35", 
    "Content-Type": "application/x-www-form-urlencoded; charset=ISO-8859-1", 
    "Host": "httpbin.org", 
    "User-Agent": "Apache-HttpClient/5.1.3 (Java/17.0.9)", 
    "X-Amzn-Trace-Id": "Root=1-660fe4f3-76d02ec05aa7a6535e833aad"
  }, 
  "json": null, 
  "origin": "218.26.154.94", 
  "url": "http://httpbin.org/post"
}

HttpClient 5 Fluent POST

使用 Apache HttpClient 5 提供的 Fluent API 可以更便捷的发起 POST 请求,但是可操作的地方较少。

java 复制代码
package com.jjy.httpclient5demo.test;

import java.io.IOException;

import org.apache.hc.client5.http.fluent.Request;
import org.apache.hc.core5.http.message.BasicNameValuePair;

/**
 * @author zbzmx
 */
public class HttpClient5PostFluent {

    public static void main(String[] args) {
        String result = post("http://httpbin.org/post");
        System.out.println(result);
    }

    public static String post(String url) {
        String result = null;
        Request request = Request.post(url);
        // POST 请求参数
        request.bodyForm(
                new BasicNameValuePair("username", "wdbyte.com"),
                new BasicNameValuePair("password", "secret"));
        try {
            result = request.execute().returnContent().asString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
}

HttpClient5 POST JSON 参数

java 复制代码
package com.jjy.httpclient5demo.test;



import java.io.IOException;

import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;

/**
 * @author zbxmx
 */
public class HttpClient5PostWithJson {

    public static void main(String[] args) {
        String json = "{"
                + "    \"password\": \"secret\","
                + "    \"username\": \"wdbyte.com\""
                + "}";
        String result = post("http://httpbin.org/post", json);
        System.out.println(result);
    }

    public static String post(String url, String jsonBody) {
        String result = null;
        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(new StringEntity(jsonBody, ContentType.APPLICATION_JSON));

        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
                // 获取响应信息
                result = EntityUtils.toString(response.getEntity());
            }
        } catch (IOException | ParseException e) {
            e.printStackTrace();
        }
        return result;
    }

}

输出结果:

java 复制代码
{
  "args": {}, 
  "data": "{    \"password\": \"secret\",    \"username\": \"wdbyte.com\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip, x-gzip, deflate", 
    "Content-Length": "55", 
    "Content-Type": "application/json; charset=UTF-8", 
    "Host": "httpbin.org", 
    "User-Agent": "Apache-HttpClient/5.1.3 (Java/17.0.9)", 
    "X-Amzn-Trace-Id": "Root=1-660ff565-0afd9ffd3d41ed4417652ca1"
  }, 
  "json": {
    "password": "secret", 
    "username": "wdbyte.com"
  }, 
  "origin": "218.26.154.94", 
  "url": "http://httpbin.org/post"
}

HttpClient 5 设置超时

使用 RequestConfig 对象来配置超时时间。

java 复制代码
package com.jjy.httpclient5demo.test;


import java.io.IOException;

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.util.Timeout;

/**
 * @author zbxmx
 */
public class HttpClient5GetWithTimeout {

    public static void main(String[] args) {
        String result = get("http://httpbin.org/get");
        System.out.println(result);
    }

    public static String get(String url) {
        String resultContent = null;
        // 设置超时时间
        RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(Timeout.ofMilliseconds(5000L))
                .setConnectionRequestTimeout(Timeout.ofMilliseconds(5000L))
                .setResponseTimeout(Timeout.ofMilliseconds(5000L))
                .build();
        // 请求级别的超时
        HttpGet httpGet = new HttpGet(url);
        //httpGet.setConfig(config);
        //try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
        // 客户端级别的超时
        try (CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(config).build()) {
            try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
                // 获取状态码
                System.out.println(response.getVersion()); // HTTP/1.1
                System.out.println(response.getCode()); // 200
                System.out.println(response.getReasonPhrase()); // OK
                HttpEntity entity = response.getEntity();
                // 获取响应信息
                resultContent = EntityUtils.toString(entity);
            }
        } catch (IOException | ParseException e) {
            e.printStackTrace();
        }
        return resultContent;
    }

}

HttpClient 5 异步请求

下面演示三种 HttpClient 5 异步请求方式。

java 复制代码
package com.jjy.httpclient5demo.test;

import java.io.IOException;
import java.nio.CharBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.apache.hc.client5.http.async.methods.AbstractCharResponseConsumer;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.nio.AsyncRequestProducer;
import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder;

/**
 * HttpClient 5 异步请求
 * @author https://www.wdbyte.com
 *
 */
public class HttpClient5Async {

    public static void main(String[] args) {
        getAsync1("http://httpbin.org/get");
        getAsync2("http://httpbin.org/get");
        getAsync3("http://httpbin.org/get");
    }

    /**
     * 异步请求
     *
     * @param url
     * @return
     */
    public static String getAsync1(String url) {
        try (CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault()) {
            // 开始 http clinet
            httpclient.start();
            // 执行请求
            SimpleHttpRequest request1 = SimpleHttpRequests.get(url);
            Future<SimpleHttpResponse> future = httpclient.execute(request1, null);
            // 等待直到返回完毕
            SimpleHttpResponse response1 = future.get();
            System.out.println("getAsync1:" + request1.getRequestUri() + "->" + response1.getCode());
        } catch (IOException | ExecutionException | InterruptedException e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    /**
     * 异步请求,根据响应情况回调
     *
     * @param url
     * @return
     */
    public static String getAsync2(String url) {
        try (CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault()) {
            // 开始 http clinet
            httpclient.start();
            // 根据请求响应情况进行回调操作
            CountDownLatch latch = new CountDownLatch(1);
            SimpleHttpRequest request = SimpleHttpRequests.get(url);
            httpclient.execute(request, new FutureCallback<SimpleHttpResponse>() {
                @Override
                public void completed(SimpleHttpResponse response2) {
                    latch.countDown();
                    System.out.println("getAsync2:" + request.getRequestUri() + "->" + response2.getCode());
                }

                @Override
                public void failed(Exception ex) {
                    latch.countDown();
                    System.out.println("getAsync2:" + request.getRequestUri() + "->" + ex);
                }

                @Override
                public void cancelled() {
                    latch.countDown();
                    System.out.println("getAsync2:" + request.getRequestUri() + " cancelled");
                }

            });
            latch.await();
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    /**
     * 异步请求,对响应流做点什么
     *
     * @param url
     * @return
     */
    public static String getAsync3(String url) {
        try (CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault()) {
            // 开始 http clinet
            httpclient.start();
            // 根据请求响应情况进行回调操作
            SimpleHttpRequest request = SimpleHttpRequests.get(url);

            CountDownLatch latch = new CountDownLatch(1);
            AsyncRequestProducer producer = AsyncRequestBuilder.get("http://httpbin.org/get").build();
            AbstractCharResponseConsumer<HttpResponse> consumer3 = new AbstractCharResponseConsumer<HttpResponse>() {

                HttpResponse response;

                @Override
                protected void start(HttpResponse response, ContentType contentType) throws HttpException, IOException {
                    System.out.println("getAsync3: 开始响应....");
                    this.response = response;
                }

                @Override
                protected int capacityIncrement() {
                    return Integer.MAX_VALUE;
                }

                @Override
                protected void data(CharBuffer data, boolean endOfStream) throws IOException {
                    System.out.println("getAsync3: 收到数据....");
                    // Do something useful
                }

                @Override
                protected HttpResponse buildResult() throws IOException {
                    System.out.println("getAsync3: 接收完毕...");
                    return response;
                }

                @Override
                public void releaseResources() {
                }

            };
            httpclient.execute(producer, consumer3, new FutureCallback<HttpResponse>() {

                @Override
                public void completed(HttpResponse response) {
                    latch.countDown();
                    System.out.println("getAsync3: "+request.getRequestUri() + "->" + response.getCode());
                }

                @Override
                public void failed(Exception ex) {
                    latch.countDown();
                    System.out.println("getAsync3: "+request.getRequestUri() + "->" + ex);
                }

                @Override
                public void cancelled() {
                    latch.countDown();
                    System.out.println("getAsync3: "+request.getRequestUri() + " cancelled");
                }

            });
            latch.await();
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
        return null;

    }
}

输出结果

java 复制代码
getAsync1:/get->200
getAsync2:/get->200
getAsync3: 开始响应....
getAsync3: 收到数据....
getAsync3: 收到数据....
getAsync3: 收到数据....
getAsync3: 接收完毕...
getAsync3: /get->200

请求 http://httpbin.org/cookies/set/cookieName/www.wdbyte.com 的响应中会带有一个Cookie 信息,其中 name 为 cookieName,value 为 www.wdbyte.com,我们以此用作测试。

java 复制代码
package com.jjy.httpclient5demo.test;

import java.util.List;

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.cookie.BasicCookieStore;
import org.apache.hc.client5.http.cookie.Cookie;
import org.apache.hc.client5.http.cookie.CookieStore;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.http.io.entity.EntityUtils;

/**
 * 这个例子演示了使用本地HTTP上下文填充, 自定义属性
 */
public class HttpClient5WithCookie {

    public static void main(final String[] args) throws Exception {
        try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
            // 创建一个本地的 Cookie 存储
            final CookieStore cookieStore = new BasicCookieStore();
            // BasicClientCookie clientCookie = new BasicClientCookie("name", "www.wdbyte.com");
            // clientCookie.setDomain("http://httpbin.org/cookies");
            // 过期时间
            // clientCookie.setExpiryDate(new Date());
            // 添加到本地 Cookie
            // cookieStore.addCookie(clientCookie);

            // 创建本地 HTTP 请求上下文 HttpClientContext
            final HttpClientContext localContext = HttpClientContext.create();
            // 绑定 cookieStore 到 localContext
            localContext.setCookieStore(cookieStore);

            final HttpGet httpget = new HttpGet("http://httpbin.org/cookies/set/cookieName/www.wdbyte.com");
            System.out.println("执行请求 " + httpget.getMethod() + " " + httpget.getUri());

            // 获取 Coolie 信息
            try (final CloseableHttpResponse response = httpclient.execute(httpget, localContext)) {
                System.out.println("----------------------------------------");
                System.out.println(response.getCode() + " " + response.getReasonPhrase());
                final List<Cookie> cookies = cookieStore.getCookies();
                for (int i = 0; i < cookies.size(); i++) {
                    System.out.println("Local cookie: " + cookies.get(i));
                }
                EntityUtils.consume(response.getEntity());
            }
        }
    }

}

输出结果:

java 复制代码
执行请求 GET http://httpbin.org/cookies/set/cookieName/www.wdbyte.com
----------------------------------------
200 OK
Local cookie: [name: cookieName; value: www.wdbyte.com; domain: httpbin.org; path: /; expiry: null]

HttpClient 5 读取文件内容请求

准备一个 JSON 内容格式的文件 params.json。

json 复制代码
{"name":"www.wdbyte.com"}

读取这个文件作为请求参数发起请求。

java 复制代码
package com.jjy.httpclient5demo.test;


import java.io.File;
import java.io.FileInputStream;

import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.FileEntity;
import org.apache.hc.core5.http.io.entity.InputStreamEntity;

/**
 * 加载数据流作为 POST 请求参数
 */
public class HttpClient5ChunkEncodedPost {

    public static void main(final String[] args) throws Exception {
        String params = "/Users/darcy/params.json";

        try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
            final HttpPost httppost = new HttpPost("http://httpbin.org/post");

            final InputStreamEntity reqEntity = new InputStreamEntity(new FileInputStream(params), -1,
                    ContentType.APPLICATION_JSON);
            // 也可以使用 FileEntity 的形式
            // FileEntity reqEntity = new FileEntity(new File(params), ContentType.APPLICATION_JSON);

            httppost.setEntity(reqEntity);

            System.out.println("执行请求 " + httppost.getMethod() + " " + httppost.getUri());
            try (final CloseableHttpResponse response = httpclient.execute(httppost)) {
                System.out.println("----------------------------------------");
                System.out.println(response.getCode() + " " + response.getReasonPhrase());
                System.out.println(EntityUtils.toString(response.getEntity()));
            }
        }
    }
}

输出结果

java 复制代码
执行请求 POST http://httpbin.org/post
----------------------------------------
200 OK
{
  "args": {}, 
  "data": "{\"name\":\"www.wdbyte.com\"}\n", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip, x-gzip, deflate", 
    "Content-Length": "26", 
    "Content-Type": "application/json; charset=UTF-8", 
    "Host": "httpbin.org", 
    "User-Agent": "Apache-HttpClient/5.1.3 (Java/1.8.0_151)", 
    "X-Amzn-Trace-Id": "Root=1-62ee4d95-1f956d4303cea09c52694c86"
  }, 
  "json": {
    "name": "www.wdbyte.com"
  }, 
  "origin": "42.120.74.238", 
  "url": "http://httpbin.org/post"
}

HttpClient 5 表单登录

表单登录可以理解为发起一个携带了认证信息的请求,然后得到响应的 Cookie 的过程。当然这里不仅仅适用于表单登录,也可以是简单的发起一个携带了表单信息的请求。

本应该使用 POST 请求发送表单参数测试,但是在 httpbin.org 中没有对应的接口用于测试,所以这里换成了 GET 请求

java 复制代码
package com.jjy.httpclient5demo.test;



import java.util.ArrayList;
import java.util.List;

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.cookie.BasicCookieStore;
import org.apache.hc.client5.http.cookie.Cookie;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.message.BasicNameValuePair;

/**
 * 演示基于表单的登录
 *
 * @author zbxmx
 */
public class HttpClient5FormLogin {

    public static void main(final String[] args) throws Exception {
        final BasicCookieStore cookieStore = new BasicCookieStore();
        try (final CloseableHttpClient httpclient = HttpClients.custom()
                .setDefaultCookieStore(cookieStore)
                .build()) {

            // 本应该使用 POST 请求发送表单参数,但是在 httpbin.org 中没有对应的接口用于测试,所以这里换成了 GET 请求
            // HttpPost httpPost = new HttpPost("http://httpbin.org/cookies/set/username/wdbyte.com");
            HttpGet httpPost = new HttpGet("http://httpbin.org/cookies/set/username/wdbyte.com");
            // POST 表单请求参数
            List<NameValuePair> nvps = new ArrayList<>();
            nvps.add(new BasicNameValuePair("username", "wdbyte.com"));
            nvps.add(new BasicNameValuePair("password", "secret"));
            httpPost.setEntity(new UrlEncodedFormEntity(nvps));

            try (final CloseableHttpResponse response2 = httpclient.execute(httpPost)) {
                final HttpEntity entity = response2.getEntity();

                System.out.println("Login form get: " + response2.getCode() + " " + response2.getReasonPhrase());
                System.out.println("当前响应信息 "+EntityUtils.toString(entity));;

                System.out.println("Post 登录 Cookie:");
                final List<Cookie> cookies = cookieStore.getCookies();
                if (cookies.isEmpty()) {
                    System.out.println("None");
                } else {
                    for (int i = 0; i < cookies.size(); i++) {
                        System.out.println("- " + cookies.get(i));
                    }
                }
            }
        }
    }
}

输出结果:

java 复制代码
Login form get: 200 OK
当前响应信息 {
  "cookies": {
    "username": "wdbyte.com"
  }
}
 
Post 登录 Cookie:
- [name: username; value: wdbyte.com; domain: httpbin.org; path: /; expiry: null]

HttpClient 5 Basic Authorization

HTTP 基本认证(Basic Authorization)是一种比较简单的认证实现,主要流程如下

  1. 请求一个需要进行基本认证的 HTTP 接口,但是没有携带认证信息。

  2. 此时会响应 401 状态码,并在响应 header 中的 WWW-Authenticate 提示需要进行基本认证。

  3. 用户把需要提交认证信息进行冒号拼接,然后进行 base64 编码,再在得到的字符串开头拼接上 Basic 放入请求头 Authorization 中。

  4. 认证成功,响应成功。

你可以通过浏览器打开下面这个 URL 进行基本认证测试。

http://httpbin.org/basic-auth/admin/123456

java 复制代码
package com.jjy.httpclient5demo.test;


import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.io.entity.EntityUtils;

/**
 * 一个简单的示例,它使用HttpClient执行HTTP请求;
 * 一个需要进行用户身份验证的目标站点。
 */
public class HttpClient5BasicAuthentication {

    public static void main(final String[] args) throws Exception {
        final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(
                new AuthScope("httpbin.org", 80),
                new UsernamePasswordCredentials("admin", "123456".toCharArray()));
        try (final CloseableHttpClient httpclient = HttpClients.custom()
                .setDefaultCredentialsProvider(credsProvider)
                .build()) {
            final HttpGet httpget = new HttpGet("http://httpbin.org/basic-auth/admin/123456");

            System.out.println("执行请求" + httpget.getMethod() + " " + httpget.getUri());
            try (final CloseableHttpResponse response = httpclient.execute(httpget)) {
                System.out.println("----------------------------------------");
                System.out.println(response.getCode() + " " + response.getReasonPhrase());
                System.out.println(EntityUtils.toString(response.getEntity()));
            }
        }
    }
}

输出结果

java 复制代码
执行请求GET http://httpbin.org/basic-auth/user/passwd
----------------------------------------
200 OK
{
  "authenticated": true, 
  "user": "user"
}

HttpClient 5 Digest Authorization

HTTP Basic Authorization 的缺点显而易见,密码通过明文传输存在一定的安全风险,Digest Authorization 认证方式解决了明文传输的问题,这里不过多介绍 Digest 的相关内容,通过一个图简单的示意 Digest 认证方式的流程。

java 复制代码
package com.jjy.httpclient5demo.test;


import org.apache.hc.client5.http.auth.AuthExchange;
import org.apache.hc.client5.http.auth.AuthScheme;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.auth.DigestScheme;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.io.entity.EntityUtils;

/**
 *
 * HttpClient如何验证多个请求的示例
 * 使用相同的摘要方案。在初始请求/响应交换之后
 * 共享相同执行上下文的所有后续请求都可以重用
 * 要向服务器进行身份验证的最后一个摘要nonce值。
 */
public class HttpClient5PreemptiveDigestAuthentication {

    public static void main(final String[] args) throws Exception {
        try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {

            final HttpHost target = new HttpHost("http", "httpbin.org", 80);

            final HttpClientContext localContext = HttpClientContext.create();
            final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(
                    new AuthScope(target),
                    new UsernamePasswordCredentials("admin", "123456".toCharArray()));
            localContext.setCredentialsProvider(credentialsProvider);

            final HttpGet httpget = new HttpGet("http://httpbin.org/digest-auth/auth/admin/123456");

            System.out.println("执行请求 " + httpget.getMethod() + " " + httpget.getUri());
            for (int i = 0; i < 2; i++) {
                try (final CloseableHttpResponse response = httpclient.execute(target, httpget, localContext)) {
                    System.out.println("----------------------------------------");
                    System.out.println(response.getCode() + " " + response.getReasonPhrase());
                    EntityUtils.consume(response.getEntity());

                    final AuthExchange authExchange = localContext.getAuthExchange(target);
                    if (authExchange != null) {
                        final AuthScheme authScheme = authExchange.getAuthScheme();
                        if (authScheme instanceof DigestScheme) {
                            final DigestScheme digestScheme = (DigestScheme) authScheme;
                            System.out.println("Nonce: " + digestScheme.getNonce() +
                                    "; count: " + digestScheme.getNounceCount());
                        }
                    }
                }
            }
        }
    }

}

HttpClient 5 拦截器

HttpClient 5 中的拦截器可以对请求过程的各个阶段进行拦截处理,通过 HttpClientBuilder 中的关于 Interceptor 的方法可以看到可以

HttpClient5 拦截器

下面编写一个示例,发起三次请求,每次请求都在请求头 herader 中增加一个 request-id 参数,然后对 request-id 值为 2 的请求直接响应 404 结束。

java 复制代码
package com.jjy.httpclient5demo.test;


import java.io.IOException;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.hc.client5.http.classic.ExecChain;
import org.apache.hc.client5.http.classic.ExecChain.Scope;
import org.apache.hc.client5.http.classic.ExecChainHandler;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.ChainElement;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.EntityDetails;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpRequestInterceptor;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
import org.apache.hc.core5.http.protocol.HttpContext;

/**
 * 展示如何在请求和响应时进行拦截进行自定义处理。
 */
public class HttpClient5Interceptors {

    public static void main(final String[] args) throws Exception {
        try (final CloseableHttpClient httpclient = HttpClients.custom()
                // 添加一个请求 id 到请求 header
                .addRequestInterceptorFirst(new HttpRequestInterceptor() {
                    private final AtomicLong count = new AtomicLong(0);
                    @Override
                    public void process(
                            final HttpRequest request,
                            final EntityDetails entity,
                            final HttpContext context) throws HttpException, IOException {
                        request.setHeader("request-id", Long.toString(count.incrementAndGet()));
                    }
                })
                .addExecInterceptorAfter(ChainElement.PROTOCOL.name(), "custom", new ExecChainHandler() {
                    // 请求 id 为 2 的,模拟 404 响应,并自定义响应的内容。
                    @Override
                    public ClassicHttpResponse execute(
                            final ClassicHttpRequest request,
                            final Scope scope,
                            final ExecChain chain) throws IOException, HttpException {

                        final Header idHeader = request.getFirstHeader("request-id");
                        if (idHeader != null && "2".equalsIgnoreCase(idHeader.getValue())) {
                            final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_NOT_FOUND,
                                    "Oppsie");
                            response.setEntity(new StringEntity("bad luck", ContentType.TEXT_PLAIN));
                            return response;
                        } else {
                            return chain.proceed(request, scope);
                        }
                    }
                })
                .build()) {

            for (int i = 0; i < 3; i++) {
                final HttpGet httpget = new HttpGet("http://httpbin.org/get");

                try (final CloseableHttpResponse response = httpclient.execute(httpget)) {
                    System.out.println("----------------------------------------");
                    System.out.println("执行请求 " + httpget.getMethod() + " " + httpget.getUri());
                    System.out.println(response.getCode() + " " + response.getReasonPhrase());
                    System.out.println(EntityUtils.toString(response.getEntity()));
                }
            }
        }
    }

}

输出结果

java 复制代码
----------------------------------------
执行请求 GET http://httpbin.org/get
200 OK
{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip, x-gzip, deflate", 
    "Host": "httpbin.org", 
    "Request-Id": "1", 
    "User-Agent": "Apache-HttpClient/5.1.3 (Java/1.8.0_151)", 
    "X-Amzn-Trace-Id": "Root=1-62f615ba-658ccd42182d22534dbba82c"
  }, 
  "origin": "42.120.75.221", 
  "url": "http://httpbin.org/get"
}
 
----------------------------------------
执行请求 GET http://httpbin.org/get
404 Oppsie
bad luck
----------------------------------------
执行请求 GET http://httpbin.org/get
200 OK
{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip, x-gzip, deflate", 
    "Host": "httpbin.org", 
    "Request-Id": "3", 
    "User-Agent": "Apache-HttpClient/5.1.3 (Java/1.8.0_151)", 
    "X-Amzn-Trace-Id": "Root=1-62f615bb-4eb6ba10736ace0e21d0cb8c"
  }, 
  "origin": "42.120.75.221", 
  "url": "http://httpbin.org/get"
}

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力

相关推荐
虾球xz1 小时前
游戏引擎学习第55天
学习·游戏引擎
oneouto1 小时前
selenium学习笔记(二)
笔记·学习·selenium
sealaugh322 小时前
aws(学习笔记第十九课) 使用ECS和Fargate进行容器开发
笔记·学习·aws
炭烤玛卡巴卡2 小时前
学习postman工具使用
学习·测试工具·postman
thesky1234562 小时前
活着就好20241224
学习·算法
蜗牛hb3 小时前
VMware Workstation虚拟机网络模式
开发语言·学习·php
汤姆和杰瑞在瑞士吃糯米粑粑3 小时前
【C++学习篇】AVL树
开发语言·c++·学习
虾球xz3 小时前
游戏引擎学习第58天
学习·游戏引擎
奶香臭豆腐4 小时前
C++ —— 模板类具体化
开发语言·c++·学习
波音彬要多做4 小时前
41 stack类与queue类
开发语言·数据结构·c++·学习·算法