Apache HttpClient 5 使用详细教程

SpringBoot项目里,用Apache HttpClient 4.5.6发起GET/POST请求的保姆级教程 本文详细介绍了在SpringBoot项目中使用Apache HttpClient 4.5.6发起GET/POST请求的完整指南,包括基础配置、连接池管理、超时控制、异常处理等生产级特性。通过实战代码示例,帮助开发者快速掌握HTTP客户端的高效使用方法,提升微服务间通信的稳定性和性能。 阅读详情
AI摘要

本文深入解析Apache HttpClient 5的GET、POST请求,参数传递、JSON支持,超时设置,异步请求,Cookie操作,表单登录,基本认证与Digest认证,以及自定义请求拦截器的使用,适合HTTP开发者快速上手。

超文本传输协议(HTTP)可能是当今互联网上最重要的协议之一,Web 服务、微服务以及支持网络的各种设备上的服务几乎都是 HTTP 协议,HTTP 协议已经从 Web 浏览器走向了更广泛的使用场景。

虽然 java.net 包已经提供了 HTTP 访问资源的基本功能,但是它不够灵活,而且不能随心所欲的进行自定义。Apache HttpClient 5 是一个开源的 HTTP 工具包,可以支持最新 HTTP 协议标准,且有丰富的 API 和强大的扩展特性,可以用于构建任何需要进行 HTTP 协议处理的应用程序。

这篇文章介绍 Apache HttpClient 5 中最为常见的一些用法,通过这篇文章可以快速的入门使用 HttpClient 5,主要内容包括 HttpClient 5 的 Get 请求、Post 请求、如何携带参数、JSON 参数、设置超时、异步请求、操作 Cookie、表单登录、基本认证、Digest 认证以及自定义 HTTP 请求拦截器等。

HttpClient 5 依赖

HttpClient 5 Maven 依赖

<!-- 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 Gradle 依赖

implementation 'org.apache.httpcomponents.client5:httpclient5:5.1.3'
implementation 'org.apache.httpcomponents.client5:httpclient5-fluent:5.1.3'

HttpClient 5 GET 请求

package com.wdbyte.httpclient;

import java.io.IOException;

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.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;

/**
 * @author https://www.wdbyte.com
 */
public class HttpClient5Get {

    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;
        HttpGet httpGet = new HttpGet(url);
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            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;
    }

}

响应信息:

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 请求,但是可操作的地方较少。

依赖:

<!-- 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>

示例:

package com.wdbyte.httpclient;

import java.io.IOException;

import org.apache.hc.client5.http.fluent.Request;
import org.apache.hc.client5.http.fluent.Response;

/**
* @author https://www.wdbyte.com
 */
public class HttpClient5GetFluent {

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

    public static String get(String url) {
        String result = null;
        try {
            Response response = Request.get(url).execute();
            result = response.returnContent().asString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

}

输出信息:

{
  "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-62bb190e-1ba46a92645843a04c55da32"
  }, 
  "origin": "47.251.4.198", 
  "url": "http://httpbin.org/get"
}

HttpClient5 GET 请求参数

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

package com.wdbyte.httpclient;

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;

/**
* @author https://www.wdbyte.com
 */
public class HttpClient5GetParams {

    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;
        HttpGet httpGet = new HttpGet(url);
        // 表单参数
        List<NameValuePair> nvps = new ArrayList<>();
        // GET 请求参数
        nvps.add(new BasicNameValuePair("username", "wdbyte.com"));
        nvps.add(new BasicNameValuePair("password", "secret"));
        // 增加到请求 URL 中
        try {
            URI uri = new URIBuilder(new URI(url))
                .addParameters(nvps)
                .build();
            httpGet.setUri(uri);
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }

        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            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;
    }
}

输出信息:

{
  "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": "42.120.75.185", 
  "url": "http://httpbin.org/get?username=wdbyte.com&password=secret"
}

下面是通过抓包得到的请求响应信息格式:

// 请求信息
GET /get?username=wdbyte.com&password=secret HTTP/1.1
Accept-Encoding: gzip, x-gzip, deflate
Host: httpbin.org
Connection: keep-alive
User-Agent: Apache-HttpClient/5.1.3 (Java/1.8.0_151)

// 响应信息
HTTP/1.1 200 OK
Date: Fri, 05 Aug 2022 07:27:30 GMT
Content-Type: application/json
Content-Length: 405
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "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": "42.120.75.185", 
  "url": "http://httpbin.org/get?username=wdbyte.com&password=secret"
}

HttpClient 5 POST 请求

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

参数:username=wdbyte.com&password=secret

package com.wdbyte.httpclient;

import java.io.IOException;
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;

/**
* @author https://www.wdbyte.com
 */
public class HttpClient5Post {

    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;
        HttpPost httpPost = new HttpPost(url);
        // 表单参数
        List<NameValuePair> nvps = new ArrayList<>();
        // POST 请求参数
        nvps.add(new BasicNameValuePair("username", "wdbyte.com"));
        nvps.add(new BasicNameValuePair("password", "secret"));
        httpPost.setEntity(new UrlEncodedFormEntity(nvps));
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
                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) {
            e.printStackTrace();
        }
        return result;
    }

}

输出信息:

HTTP/1.1
200
OK
{
  "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)", 
    "X-Amzn-Trace-Id": "Root=1-62bb1ac8-489b2100728c81d70797a482"
  }, 
  "json": null, 
  "origin": "183.128.136.89", 
  "url": "http://httpbin.org/post"
}

下面是通过 Wireshark 抓包得到的请求信息:

POST /post HTTP/1.1
Accept-Encoding: gzip, x-gzip, deflate
Content-Length: 35
Content-Type: application/x-www-form-urlencoded; charset=ISO-8859-1
Host: httpbin.org
Connection: keep-alive
User-Agent: Apache-HttpClient/5.1.3 (Java/17)

username=wdbyte.com&password=secret

HttpClient 5 Fluent POST

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

一样发送一个简单的表单参数:username=wdbyte.com&password=secret

package com.wdbyte.httpclient;

import java.io.IOException;

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

/**
* @author https://www.wdbyte.com
 */
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;
    }
}

输出信息:

{
  "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)", 
    "X-Amzn-Trace-Id": "Root=1-62bb1c8a-7aee8c004f06919f31a2b533"
  }, 
  "json": null, 
  "origin": "183.128.136.89", 
  "url": "http://httpbin.org/post"
}

HttpClient5 POST JSON 参数

使用 StringEntity 类存入 JSON 参数。

package com.wdbyte.httpclient;

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.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;

/**
* @author https://www.wdbyte.com
 */
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;
    }

}

输出信息:

{
  "args": {}, 
  "data": "{    \"password\": \"secret\",    \"username\": \"wdbyte.com\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip, x-gzip, deflate", 
    "Content-Length": "55", 
    "Content-Type": "text/plain; charset=ISO-8859-1", 
    "Host": "httpbin.org", 
    "User-Agent": "Apache-HttpClient/5.1.3 (Java/17)", 
    "X-Amzn-Trace-Id": "Root=1-62bb1dbb-5a963c1d798b06be3ee1a15e"
  }, 
  "json": {
    "password": "secret", 
    "username": "wdbyte.com"
  }, 
  "origin": "183.128.136.89", 
  "url": "http://httpbin.org/post"
}

下面是通过 Wireshark 抓包得到的请求响应信息:

// 请求信息
POST /post HTTP/1.1
Accept-Encoding: gzip, x-gzip, deflate
Content-Length: 55
Content-Type: application/json; charset=UTF-8
Host: httpbin.org
Connection: keep-alive
User-Agent: Apache-HttpClient/5.1.3 (Java/17)

{    "password": "secret",    "username": "wdbyte.com"}

// 响应信息
HTTP/1.1 200 OK
Date: Tue, 28 Jun 2022 15:30:17 GMT
Content-Type: application/json
Content-Length: 573
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "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)", 
    "X-Amzn-Trace-Id": "Root=1-62bb1e89-64db55730a0361c720232ccd"
  }, 
  "json": {
    "password": "secret", 
    "username": "wdbyte.com"
  }, 
  "origin": "183.128.136.89", 
  "url": "http://httpbin.org/post"
}

HttpClient 5 设置超时

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

package com.wdbyte.httpclient;

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 https://www.wdbyte.com
 */
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 异步请求方式。

package com.wdbyte.httpclient;

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
 * @date 2022/06/25
 */
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;

    }
}

输出结果:

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

HttpClient 5 获取 Cookie

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

Postman 请求测试,可以看到响应了 Cookie 信息。

下面编写 Java 代码进行请求测试

package com.wdbyte.httpclient;

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

}

输出结果:

执行请求 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。

{"name":"www.wdbyte.com"}

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

package com.wdbyte.httpclient;

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

输出结果:

执行请求 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 请求

示例代码:

package com.wdbyte.httpclient;

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 https://www.wdbyte.com
 */
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));
                    }
                }
            }
        }
    }
}

输出结果:

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 进行基本认证测试。

httpbin.org/basic-auth/…

在 Apache HttpClient 5 中的实现方式。

package com.wdbyte.httpclient;

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

输出结果:

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

通过抓包可以看到完整的 HTTP 请求响应过程。

// 请求
GET /basic-auth/user/passwd HTTP/1.1
Accept-Encoding: gzip, x-gzip, deflate
Host: httpbin.org
Connection: keep-alive
User-Agent: Apache-HttpClient/5.1.3 (Java/1.8.0_151)
// 响应
HTTP/1.1 401 UNAUTHORIZED
Date: Sat, 06 Aug 2022 08:25:33 GMT
Content-Length: 0
Connection: keep-alive
Server: gunicorn/19.9.0
WWW-Authenticate: Basic realm="Fake Realm"
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
// 请求
GET /basic-auth/user/passwd HTTP/1.1
Host: httpbin.org
Connection: keep-alive
User-Agent: Apache-HttpClient/5.1.3 (Java/1.8.0_151)
Authorization: Basic dXNlcjpwYXNzd2Q=
// 响应
HTTP/1.1 200 OK
Date: Sat, 06 Aug 2022 08:25:33 GMT
Content-Type: application/json
Content-Length: 47
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "authenticated": true, 
  "user": "user"
}

HttpClient 5 Digest Authorization

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

下面是代码演示。

package com.wdbyte.httpclient;

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

}

通过抓包工具可以清晰的看到 2 次请求的流程,在最后一次请求中,直接共享了认证信息,没有再次的重新认证的流程。

// 1\. 请求
GET /digest-auth/auth/admin/123456 HTTP/1.1
Accept-Encoding: gzip, x-gzip, deflate
Host: httpbin.org
Connection: keep-alive
User-Agent: Apache-HttpClient/5.1.3 (Java/1.8.0_151)
// 2\. 详情,提示认证,给出参数
HTTP/1.1 401 UNAUTHORIZED
Date: Fri, 12 Aug 2022 07:11:06 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: keep-alive
Server: gunicorn/19.9.0
WWW-Authenticate: Digest realm="me@kennethreitz.com", nonce="8dc5e7974a86a6fcc3cf73230b0c4a93", qop="auth", opaque="64b7f68b386c3acc38131f7472aa2079", algorithm=MD5, stale=FALSE
Set-Cookie: stale_after=never; Path=/
Set-Cookie: fake=fake_value; Path=/
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
// 3\. 参数+密码 加密后再次请求
GET /digest-auth/auth/admin/123456 HTTP/1.1
Host: httpbin.org
Connection: keep-alive
User-Agent: Apache-HttpClient/5.1.3 (Java/1.8.0_151)
Cookie: fake=fake_value; stale_after=never
Authorization: Digest username="admin", realm="me@kennethreitz.com", nonce="8dc5e7974a86a6fcc3cf73230b0c4a93", uri="/digest-auth/auth/admin/123456", response="7c6726f8ac54c1ba28e19c71b2fc7338", qop=auth, nc=00000001, cnonce="2fa61501d47a9d39", algorithm=MD5, opaque="64b7f68b386c3acc38131f7472aa2079"
// 4\. 认证成功,响应
HTTP/1.1 200 OK
Date: Fri, 12 Aug 2022 07:11:08 GMT
Content-Type: application/json
Content-Length: 48
Connection: keep-alive
Server: gunicorn/19.9.0
Set-Cookie: fake=fake_value; Path=/
Set-Cookie: stale_after=never; Path=/
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "authenticated": true, 
  "user": "admin"
}
// 5\. 再次请求,共享了登录状态。
GET /digest-auth/auth/admin/123456 HTTP/1.1
Accept-Encoding: gzip, x-gzip, deflate
Host: httpbin.org
Connection: keep-alive
User-Agent: Apache-HttpClient/5.1.3 (Java/1.8.0_151)
Cookie: fake=fake_value; stale_after=never
Authorization: Digest username="admin", realm="me@kennethreitz.com", nonce="8dc5e7974a86a6fcc3cf73230b0c4a93", uri="/digest-auth/auth/admin/123456", response="9955ac79f6a51a876a326449447f549d", qop=auth, nc=00000002, cnonce="2fa61501d47a9d39", algorithm=MD5, opaque="64b7f68b386c3acc38131f7472aa2079"
// 5\. 认证成功,响应
HTTP/1.1 200 OK
Date: Fri, 12 Aug 2022 07:11:09 GMT
Content-Type: application/json
Content-Length: 48
Connection: keep-alive
Server: gunicorn/19.9.0
Set-Cookie: fake=fake_value; Path=/
Set-Cookie: stale_after=never; Path=/
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "authenticated": true, 
  "user": "admin"
}

HttpClient 5 拦截器

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

[图片上传失败...(image-bf15ae-1662340876907)]

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

package com.wdbyte.httpclient;

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

}

输出结果。

----------------------------------------
执行请求 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"
}
SpringBoot项目里,用Apache HttpClient 4.5.6发送GET/POST请求的保姆级教程 本文详细介绍了在SpringBoot项目中使用Apache HttpClient 4.5.6发送GET/POST请求的完整实践指南。从基础配置、连接池优化到高级特性如重试机制、文件传输和性能监控,提供了全面的代码示例和最佳实践,帮助开发者构建稳定高效的HTTP通信模块。 阅读详情

相关推荐

Java使用HttpClient5实现发送HTTP请求

HttpClient5 是 Apache HttpComponents 项目中的一个重要组件,它是一个功能齐全且高度可定制的 HTTP 客户端库,专门用于发送 HTTP 请求、处理 HTTP 响应并支持各种 HTTP 协议特性。内置的连接池机制能提高并发处理性能,减少资源消耗。内置 Cookie 管理功能,能够自动处理服务端返回的 Cookie 并在后续请求中使用,模拟浏览器行为。HttpClient5 的设计高度模块化,用户可以根据需要对连接管理、重定向策略、请求重试策略、代理设置等进行灵活定制。

pan_junbiao的博客 2042

HttpUtils工具类(二)Apache HttpClient 5 使用详细教程

Apache HttpClient 5 是一个功能齐全且高度可定制的 HTTP 客户端库, 其专门用于发送 HTTP 请求、处理 HTTP 响应并支持各种 HTTP 协议特性。特别适合处理复杂的 HTTP 请求需求,如多协议支持、认证、连接池、代理等。它适合中大型项目或需要高级 HTTP 特性的应用开发。

m0_52767002的博客 8838

Apache HttpClient4使用教程

基于HttpClient 4.5.2 执行GET请求 CloseableHttpClient httpClient = HttpClients.custom() .build(); CloseableHttpResponse response = httpClient.execute(new HttpGet(&quot;https://www.baidu.com&quot;)); ...

vwycm的博客 5418

深入学习 HttpClient 5:源码分析、封装工具类和注意点

随着现代应用程序对网络通信需求的增加,以及对更灵活、高性能和可定制的 HTTP 客户端的需求,HttpClient 5 成为了 Java 开发者们的首选工具。它提供了强大的功能和灵活的配置选项,使得发送 HTTP 请求和处理响应变得更加简单和高效。在本篇技术博客中,我们将深入学习 HttpClient 5,并从以下几个方面入手:源码分析、封装工具类和最佳实践与注意点。

BASK2312的博客 2936

5-5 HttpClient api的讲解

HttpClient API HttpClient API是在2018年9月发布的Java 11中引入的。但是,它早在Java 9的前一年就已经可以作为预览功能使用。因为API需要一段时间的打磨才能变得成熟和完善。所以,从Java 11开始,HttpClient API是Java标准库的一部分。这意味着你不需要再向应用程序添加任何外部依赖关系即可使用此API。 HttpClient API替代了在Java标准库中存在了很长时间的HttpURLConnection API。稍后,看我心情要不要说一下为什么需要

weixin_48514431的博客 615

AT32F403A ADC采集实战:如何用V2库精准读取可变电阻电压(附完整代码)

本文详细介绍了基于AT32F403A微控制器和V2库实现高精度ADC采集的工程化方法。内容涵盖从基础的单通道轮询配置到多通道扫描与DMA传输的进阶应用,并重点分享了提升ADC采集精度的关键技巧,包括采样时间优化、软件滤波算法以及硬件布局考量,提供了完整的实战代码,帮助开发者解决数据跳动、稳定性等常见问题。

weixin_29189003的博客 92

HTTP Component (Apache HttpClient) 5.0 + 教程

超文本传输​​协议 (HTTP) 可能是当今 Internet 上使用的最重要的协议。Web 服务、支持网络的设备和网络计算的增长继续将 HTTP 协议的作用扩展到用户驱动的 Web 浏览器之外,同时增加了需要 HTTP 支持的应用程序的数量。尽管 java.net 包提供了通过 HTTP 访问资源的基本功能,但它并没有提供许多应用程序所需的全部灵活性或功能。HttpClient 旨在通过提供高效、最新且功能丰富的包来实现最新 HTTP 标准和建议的客户端来填补这一空白。......

yh4494的博客 2436

【实战解析】RTL8306MB交换芯片驱动开发中的RMII接口调试陷阱

本文通过一个RTL8306MB交换芯片驱动开发的实战案例,深入剖析了RMII接口调试中的经典陷阱。文章详细描述了因TX_EN与CRS_DV信号线交叉连接错误导致的“静默”故障,并分享了使用逻辑分析仪和示波器进行硬件排查的完整流程与核心技巧,为嵌入式网络驱动开发提供了宝贵的避坑指南。

weixin_29267307的博客 421

HTTPCLIENT

http://hc.apache.org/httpcomponents-client-4.5.x/tutorial/html/index.html

qq_40447533的博客 117

BAPI创建委外采购订单

使用BAPI BAPI_PO_CREATE1创建委外采购订单,组件为手工输入时,有几个点需要注意: 1、已通过CS01创建过BOM的物料,创建时会自动带出components。如果要修改当前组件清单,可以通过设置pocomponents-change_id = 'E’来实现 lv_sline = lv_sline + 1. ls_pocomponents-po_item = l_ebelp + 10. ls_pocomponents-ITEM_NO = l

Buffalo_soldier的博客 3262

【异常】‘build.plugins.plugin.version‘ for org.apache.maven.plugins:maven-resources-plugin is missing

maven-compiler-plugin 插件版本信息没有指定导致了错误提示如下字面意思就是没有指定版本信息。

奶爸的编程之路,也就一周冷个三天 4599

【HttpClient4.5中文教程】二.构建开发环境

本系列教程使用maven开发,依赖为:  org.apache.httpcomponents httpclient 4.5.4

要啥自行车 1万+

ATECC508A芯片开发笔记(三):获取508A串号、随机数源码及I2C抓包分析

ATECC508A芯片开发笔记(三):读取508A串号、获取随机数源码及I2C抓包分析 APP层函数编写及源码分析 At508_GetSernum() GetSernum抓包记录 At508_GetRandom( ) GetRandom抓包记录 经过前两章节,CryptoAuthlib库已经成功适配在代码中了,下面简单测试一下API函数,并分析下I2C通讯流程: APP层函数编写及源码分析利用li

HowieXue 薛永浩的博客 2万+

HttpClient-4.5.2官方教程完整翻译

HttpClient 4.5.2 前言 超文本传输协议(HTTP)可能是当今互联网上使用的最重要的协议。 网络服务,支持网络的设备以及网络计算的发展继续扩大了HTTP协议在用户驱动的Web浏览器之外的作用,同时增加了需要HTTP支持的应用程序的数量。 尽管java.net包提供了通过HTTP访问资源的基本功能,但它并不能提供许多应用程序所需的全部灵活性或功能。 HttpClient试图通过提...

zhujianlin1990的专栏 1625

【机器学习-17】数据变换---小波变换特征提取及应用案列介绍

小波变换作为一种前沿的数据分析工具,近年来在信号分析领域崭露头角。小波分析的理论和方法凭借其独特优势,在信号处理、图像处理、语音处理、模式识别以及量子物理等多个领域得到了广泛的应用,堪称近年来在工具及方法上的重大突破。小波变换以其多分辨率的特性,在时域和频域均展现出对信号局部特征的强大表征能力。通过伸缩和平移等运算过程,小波变换能够对信号进行多尺度聚焦分析,为非平稳信号的时频分析提供了一种有效手段。它允许我们由粗及细地逐步观察信号,从而精准地提取出有用信息。

qq_38614074的博客 2万+

从原理到落地:监控 SAP OData bgRFC 队列的全景实践指南

摘要(150字) 本文系统讲解SAP Gateway/Backend场景中bgRFC队列的监控方法。内容涵盖:1)监控必要性——确保异步可靠传输,及时识别失败消息;2)两种服务质量(EO/EOIO)对监控策略的影响;3)通过事务码SBGRFCMON和SPRO路径访问监控界面;4)关键授权S_BGRFC的配置要求;5)详细操作指南包括筛选条件和动作选项;6)标准巡检脚本;7)三个典型案例(认证错误、顺序阻塞、调度压力)的诊断与修复方案。全文提供完整的监控方法论和实战指导。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 288

httpclient via socks crawling the data

httpclient reference: 1.http://hc.apache.org/httpcomponents-client-4.3.x/tutorial/html/connmgmt.html#d5e418 2.socks tutorial:         http://hc.apache.org/httpcomponents-client-ga/httpclient/example

凡事预则立,不预则废 684

LeRobot — 基于 PyTorch 的真实世界机器人学习框架(一)- 核心框架模块解析

LeRobot 是一个开源的机器人学习框架,由 Hugging Face 开发,旨在降低机器人学的准入门槛。该框架提供完整的机器学习流程支持,包括数据采集、训练、评估和部署。核心模块涵盖数据集标准化、策略算法、硬件抽象和强化学习组件,采用模块化设计实现灵活扩展。通过集成 Hugging Face Hub,用户可以轻松共享数据集和预训练模型。LeRobot 特别注重真实世界的机器人应用,支持多种前沿算法如模仿学习、视觉语言动作模型等,并提供统一的硬件接口和仿真环境适配。

熊猫小妖的AI世界 171

java httpclient重定向_HttpClient重定向处理示例

HttpClient自动处理所有类型的重定向,除了HTTP规范明确禁止的那些重定向需要用户干预。 请参阅其他(状态码303)在POST上重定向,并且按照HTTP规范的要求将PUT请求转换为GET请求。 可以使用自定义重定向策略来放宽由HTTP规范施加的对POST方法的自动重定向的限制。 在下面的教程中,我们将使用LaxRedirectStrategy来处理http重定向。Maven依赖关系我们使用...

weixin_31256263的博客 1263

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Apache HttpClient

1.下载apache client项目jar包 http://mirrors.tuna.tsinghua.edu.cn/apache//httpcomponents/httpclient/binary/httpcomponents-client-4.5.3-bin.tar.gz httpcomponents-client-4.5.3\lib 下面的jar包 package...

weixin_30596165的博客 106

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

HTTPClient基本使用

官方网站:https://hc.apache.org/ 使用maven,在xml中引用 <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --> <dependency> <groupId>org.apache.httpcompo...

diecheng0411的博客 292

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

httpClient5.0简单使用实例

maven引入 <dependency> <groupId>org.apache.httpcomponents.client5</groupId> <artifactId>httpclient5</artifactId> <version>5.0.1</version> </dependency> htt

moyefeng198436的专栏 5478

Apache HttpClient 5 详细使用指南

/ 加载自定义证书.build();new String[] { "TLSv1.2" }, // 只允许TLS 1.2null,.build().build();// 可以在这里统一添加认证信息System.out.println("请求拦截器:" + request.getRequestUri());.build();@Override// 对网络IO异常进行重试@Override// 对5xx错误进行重试@Override。

yk_dflkg的博客 2969

Spring Cloud OpenFeign4不支持Apache HttpClient4,推荐使用OkHttpClient或Apache HttpClient 5

Spring Cloud OpenFeign4 使用HTTP客户端配置说明

qq_38662415的博客 2398
上一篇: 浅谈 volatile
下一篇: 整合JVM-SANDBOX与VMTOOL,实现支持OGNL的增强自定义MOCK
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值