点赞再看,动力无限。 微信搜「程序猿阿朗 」。

本文 Github.com/niumoo/JavaNotes未读代码博客 已经收录,有很多知识点和系列文章。

超文本传输协议(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 依赖

  1. <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.client5/httpclient5 -->
  2. <dependency>
  3. <groupId>org.apache.httpcomponents.client5</groupId>
  4. <artifactId>httpclient5</artifactId>
  5. <version>5.1.3</version>
  6. </dependency>
  7. <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.client5/httpclient5-fluent -->
  8. <dependency>
  9. <groupId>org.apache.httpcomponents.client5</groupId>
  10. <artifactId>httpclient5-fluent</artifactId>
  11. <version>5.1.3</version>
  12. </dependency>

HttpClient 5 Gradle 依赖

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

HttpClient 5 GET 请求

  1. package com.wdbyte.httpclient;
  2. import java.io.IOException;
  3. import org.apache.hc.client5.http.classic.methods.HttpGet;
  4. import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
  5. import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
  6. import org.apache.hc.client5.http.impl.classic.HttpClients;
  7. import org.apache.hc.core5.http.HttpEntity;
  8. import org.apache.hc.core5.http.ParseException;
  9. import org.apache.hc.core5.http.io.entity.EntityUtils;
  10. /**
  11. * @author https://www.wdbyte.com
  12. */
  13. public class HttpClient5Get {
  14. public static void main(String[] args) {
  15. String result = get("http://httpbin.org/get");
  16. System.out.println(result);
  17. }
  18. public static String get(String url) {
  19. String resultContent = null;
  20. HttpGet httpGet = new HttpGet(url);
  21. try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
  22. try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
  23. // 获取状态码
  24. System.out.println(response.getVersion()); // HTTP/1.1
  25. System.out.println(response.getCode()); // 200
  26. System.out.println(response.getReasonPhrase()); // OK
  27. HttpEntity entity = response.getEntity();
  28. // 获取响应信息
  29. resultContent = EntityUtils.toString(entity);
  30. }
  31. } catch (IOException | ParseException e) {
  32. e.printStackTrace();
  33. }
  34. return resultContent;
  35. }
  36. }

响应信息:

  1. HTTP/1.1
  2. 200
  3. OK
  4. {
  5. "args": {},
  6. "headers": {
  7. "Accept-Encoding": "gzip, x-gzip, deflate",
  8. "Host": "httpbin.org",
  9. "User-Agent": "Apache-HttpClient/5.1.3 (Java/17)",
  10. "X-Amzn-Trace-Id": "Root=1-62bb1891-5ab5e5376ed960471bf32f17"
  11. },
  12. "origin": "47.251.4.198",
  13. "url": "http://httpbin.org/get"
  14. }

HttpClient 5 Fluent GET

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

依赖:

  1. <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.client5/httpclient5-fluent -->
  2. <dependency>
  3. <groupId>org.apache.httpcomponents.client5</groupId>
  4. <artifactId>httpclient5-fluent</artifactId>
  5. <version>5.1.3</version>
  6. </dependency>

示例:

  1. package com.wdbyte.httpclient;
  2. import java.io.IOException;
  3. import org.apache.hc.client5.http.fluent.Request;
  4. import org.apache.hc.client5.http.fluent.Response;
  5. /**
  6. * @author https://www.wdbyte.com
  7. */
  8. public class HttpClient5GetFluent {
  9. public static void main(String[] args) {
  10. System.out.println(get("http://httpbin.org/get"));
  11. }
  12. public static String get(String url) {
  13. String result = null;
  14. try {
  15. Response response = Request.get(url).execute();
  16. result = response.returnContent().asString();
  17. } catch (IOException e) {
  18. e.printStackTrace();
  19. }
  20. return result;
  21. }
  22. }

输出信息:

  1. {
  2. "args": {},
  3. "headers": {
  4. "Accept-Encoding": "gzip, x-gzip, deflate",
  5. "Host": "httpbin.org",
  6. "User-Agent": "Apache-HttpClient/5.1.3 (Java/17)",
  7. "X-Amzn-Trace-Id": "Root=1-62bb190e-1ba46a92645843a04c55da32"
  8. },
  9. "origin": "47.251.4.198",
  10. "url": "http://httpbin.org/get"
  11. }

HttpClient5 GET 请求参数

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

  1. package com.wdbyte.httpclient;
  2. import java.io.IOException;
  3. import java.net.URI;
  4. import java.net.URISyntaxException;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. import org.apache.hc.client5.http.classic.methods.HttpGet;
  8. import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
  9. import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
  10. import org.apache.hc.client5.http.impl.classic.HttpClients;
  11. import org.apache.hc.core5.http.HttpEntity;
  12. import org.apache.hc.core5.http.NameValuePair;
  13. import org.apache.hc.core5.http.ParseException;
  14. import org.apache.hc.core5.http.io.entity.EntityUtils;
  15. import org.apache.hc.core5.http.message.BasicNameValuePair;
  16. import org.apache.hc.core5.net.URIBuilder;
  17. /**
  18. * @author https://www.wdbyte.com
  19. */
  20. public class HttpClient5GetParams {
  21. public static void main(String[] args) {
  22. String result = get("http://httpbin.org/get");
  23. System.out.println(result);
  24. }
  25. public static String get(String url) {
  26. String resultContent = null;
  27. HttpGet httpGet = new HttpGet(url);
  28. // 表单参数
  29. List<NameValuePair> nvps = new ArrayList<>();
  30. // GET 请求参数
  31. nvps.add(new BasicNameValuePair("username", "wdbyte.com"));
  32. nvps.add(new BasicNameValuePair("password", "secret"));
  33. // 增加到请求 URL 中
  34. try {
  35. URI uri = new URIBuilder(new URI(url))
  36. .addParameters(nvps)
  37. .build();
  38. httpGet.setUri(uri);
  39. } catch (URISyntaxException e) {
  40. throw new RuntimeException(e);
  41. }
  42. try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
  43. try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
  44. // 获取状态码
  45. System.out.println(response.getVersion()); // HTTP/1.1
  46. System.out.println(response.getCode()); // 200
  47. System.out.println(response.getReasonPhrase()); // OK
  48. HttpEntity entity = response.getEntity();
  49. // 获取响应信息
  50. resultContent = EntityUtils.toString(entity);
  51. }
  52. } catch (IOException | ParseException e) {
  53. e.printStackTrace();
  54. }
  55. return resultContent;
  56. }
  57. }

输出信息:

  1. {
  2. "args": {
  3. "password": "secret",
  4. "username": "wdbyte.com"
  5. },
  6. "headers": {
  7. "Accept-Encoding": "gzip, x-gzip, deflate",
  8. "Host": "httpbin.org",
  9. "User-Agent": "Apache-HttpClient/5.1.3 (Java/1.8.0_151)",
  10. "X-Amzn-Trace-Id": "Root=1-62ecc660-69d58a226aefb1b6226541ec"
  11. },
  12. "origin": "42.120.75.185",
  13. "url": "http://httpbin.org/get?username=wdbyte.com&password=secret"
  14. }

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

  1. // 请求信息
  2. GET /get?username=wdbyte.com&password=secret HTTP/1.1
  3. Accept-Encoding: gzip, x-gzip, deflate
  4. Host: httpbin.org
  5. Connection: keep-alive
  6. User-Agent: Apache-HttpClient/5.1.3 (Java/1.8.0_151)
  7. // 响应信息
  8. HTTP/1.1 200 OK
  9. Date: Fri, 05 Aug 2022 07:27:30 GMT
  10. Content-Type: application/json
  11. Content-Length: 405
  12. Connection: keep-alive
  13. Server: gunicorn/19.9.0
  14. Access-Control-Allow-Origin: *
  15. Access-Control-Allow-Credentials: true
  16. {
  17. "args": {
  18. "password": "secret",
  19. "username": "wdbyte.com"
  20. },
  21. "headers": {
  22. "Accept-Encoding": "gzip, x-gzip, deflate",
  23. "Host": "httpbin.org",
  24. "User-Agent": "Apache-HttpClient/5.1.3 (Java/1.8.0_151)",
  25. "X-Amzn-Trace-Id": "Root=1-62ecc660-69d58a226aefb1b6226541ec"
  26. },
  27. "origin": "42.120.75.185",
  28. "url": "http://httpbin.org/get?username=wdbyte.com&password=secret"
  29. }

HttpClient 5 POST 请求

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

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

  1. package com.wdbyte.httpclient;
  2. import java.io.IOException;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import org.apache.hc.client5.http.classic.methods.HttpPost;
  6. import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
  7. import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
  8. import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
  9. import org.apache.hc.client5.http.impl.classic.HttpClients;
  10. import org.apache.hc.core5.http.HttpEntity;
  11. import org.apache.hc.core5.http.NameValuePair;
  12. import org.apache.hc.core5.http.ParseException;
  13. import org.apache.hc.core5.http.io.entity.EntityUtils;
  14. import org.apache.hc.core5.http.message.BasicNameValuePair;
  15. /**
  16. * @author https://www.wdbyte.com
  17. */
  18. public class HttpClient5Post {
  19. public static void main(String[] args) {
  20. String result = post("http://httpbin.org/post");
  21. System.out.println(result);
  22. }
  23. public static String post(String url) {
  24. String result = null;
  25. HttpPost httpPost = new HttpPost(url);
  26. // 表单参数
  27. List<NameValuePair> nvps = new ArrayList<>();
  28. // POST 请求参数
  29. nvps.add(new BasicNameValuePair("username", "wdbyte.com"));
  30. nvps.add(new BasicNameValuePair("password", "secret"));
  31. httpPost.setEntity(new UrlEncodedFormEntity(nvps));
  32. try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
  33. try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
  34. System.out.println(response.getVersion()); // HTTP/1.1
  35. System.out.println(response.getCode()); // 200
  36. System.out.println(response.getReasonPhrase()); // OK
  37. HttpEntity entity = response.getEntity();
  38. // 获取响应信息
  39. result = EntityUtils.toString(entity);
  40. // 确保流被完全消费
  41. EntityUtils.consume(entity);
  42. }
  43. } catch (IOException | ParseException e) {
  44. e.printStackTrace();
  45. }
  46. return result;
  47. }
  48. }

输出信息:

  1. HTTP/1.1
  2. 200
  3. OK
  4. {
  5. "args": {},
  6. "data": "",
  7. "files": {},
  8. "form": {
  9. "password": "secret",
  10. "username": "wdbyte.com"
  11. },
  12. "headers": {
  13. "Accept-Encoding": "gzip, x-gzip, deflate",
  14. "Content-Length": "35",
  15. "Content-Type": "application/x-www-form-urlencoded; charset=ISO-8859-1",
  16. "Host": "httpbin.org",
  17. "User-Agent": "Apache-HttpClient/5.1.3 (Java/17)",
  18. "X-Amzn-Trace-Id": "Root=1-62bb1ac8-489b2100728c81d70797a482"
  19. },
  20. "json": null,
  21. "origin": "183.128.136.89",
  22. "url": "http://httpbin.org/post"
  23. }

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

  1. POST /post HTTP/1.1
  2. Accept-Encoding: gzip, x-gzip, deflate
  3. Content-Length: 35
  4. Content-Type: application/x-www-form-urlencoded; charset=ISO-8859-1
  5. Host: httpbin.org
  6. Connection: keep-alive
  7. User-Agent: Apache-HttpClient/5.1.3 (Java/17)
  8. username=wdbyte.com&password=secret

HttpClient 5 Fluent POST

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

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

  1. package com.wdbyte.httpclient;
  2. import java.io.IOException;
  3. import org.apache.hc.client5.http.fluent.Request;
  4. import org.apache.hc.core5.http.message.BasicNameValuePair;
  5. /**
  6. * @author https://www.wdbyte.com
  7. */
  8. public class HttpClient5PostFluent {
  9. public static void main(String[] args) {
  10. String result = post("http://httpbin.org/post");
  11. System.out.println(result);
  12. }
  13. public static String post(String url) {
  14. String result = null;
  15. Request request = Request.post(url);
  16. // POST 请求参数
  17. request.bodyForm(
  18. new BasicNameValuePair("username", "wdbyte.com"),
  19. new BasicNameValuePair("password", "secret"));
  20. try {
  21. result = request.execute().returnContent().asString();
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. }
  25. return result;
  26. }
  27. }

输出信息:

  1. {
  2. "args": {},
  3. "data": "",
  4. "files": {},
  5. "form": {
  6. "password": "secret",
  7. "username": "wdbyte.com"
  8. },
  9. "headers": {
  10. "Accept-Encoding": "gzip, x-gzip, deflate",
  11. "Content-Length": "35",
  12. "Content-Type": "application/x-www-form-urlencoded; charset=ISO-8859-1",
  13. "Host": "httpbin.org",
  14. "User-Agent": "Apache-HttpClient/5.1.3 (Java/17)",
  15. "X-Amzn-Trace-Id": "Root=1-62bb1c8a-7aee8c004f06919f31a2b533"
  16. },
  17. "json": null,
  18. "origin": "183.128.136.89",
  19. "url": "http://httpbin.org/post"
  20. }

HttpClient5 POST JSON 参数

使用 StringEntity 类存入 JSON 参数。

  1. package com.wdbyte.httpclient;
  2. import java.io.IOException;
  3. import org.apache.hc.client5.http.classic.methods.HttpPost;
  4. import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
  5. import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
  6. import org.apache.hc.client5.http.impl.classic.HttpClients;
  7. import org.apache.hc.core5.http.ParseException;
  8. import org.apache.hc.core5.http.io.entity.EntityUtils;
  9. import org.apache.hc.core5.http.io.entity.StringEntity;
  10. /**
  11. * @author https://www.wdbyte.com
  12. */
  13. public class HttpClient5PostWithJson {
  14. public static void main(String[] args) {
  15. String json = "{"
  16. + " \"password\": \"secret\","
  17. + " \"username\": \"wdbyte.com\""
  18. + "}";
  19. String result = post("http://httpbin.org/post", json);
  20. System.out.println(result);
  21. }
  22. public static String post(String url, String jsonBody) {
  23. String result = null;
  24. HttpPost httpPost = new HttpPost(url);
  25. httpPost.setEntity(new StringEntity(jsonBody, ContentType.APPLICATION_JSON));
  26. try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
  27. try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
  28. // 获取响应信息
  29. result = EntityUtils.toString(response.getEntity());
  30. }
  31. } catch (IOException | ParseException e) {
  32. e.printStackTrace();
  33. }
  34. return result;
  35. }
  36. }

输出信息:

  1. {
  2. "args": {},
  3. "data": "{ \"password\": \"secret\", \"username\": \"wdbyte.com\"}",
  4. "files": {},
  5. "form": {},
  6. "headers": {
  7. "Accept-Encoding": "gzip, x-gzip, deflate",
  8. "Content-Length": "55",
  9. "Content-Type": "text/plain; charset=ISO-8859-1",
  10. "Host": "httpbin.org",
  11. "User-Agent": "Apache-HttpClient/5.1.3 (Java/17)",
  12. "X-Amzn-Trace-Id": "Root=1-62bb1dbb-5a963c1d798b06be3ee1a15e"
  13. },
  14. "json": {
  15. "password": "secret",
  16. "username": "wdbyte.com"
  17. },
  18. "origin": "183.128.136.89",
  19. "url": "http://httpbin.org/post"
  20. }

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

  1. // 请求信息
  2. POST /post HTTP/1.1
  3. Accept-Encoding: gzip, x-gzip, deflate
  4. Content-Length: 55
  5. Content-Type: application/json; charset=UTF-8
  6. Host: httpbin.org
  7. Connection: keep-alive
  8. User-Agent: Apache-HttpClient/5.1.3 (Java/17)
  9. { "password": "secret", "username": "wdbyte.com"}
  10. // 响应信息
  11. HTTP/1.1 200 OK
  12. Date: Tue, 28 Jun 2022 15:30:17 GMT
  13. Content-Type: application/json
  14. Content-Length: 573
  15. Connection: keep-alive
  16. Server: gunicorn/19.9.0
  17. Access-Control-Allow-Origin: *
  18. Access-Control-Allow-Credentials: true
  19. {
  20. "args": {},
  21. "data": "{ \"password\": \"secret\", \"username\": \"wdbyte.com\"}",
  22. "files": {},
  23. "form": {},
  24. "headers": {
  25. "Accept-Encoding": "gzip, x-gzip, deflate",
  26. "Content-Length": "55",
  27. "Content-Type": "application/json; charset=UTF-8",
  28. "Host": "httpbin.org",
  29. "User-Agent": "Apache-HttpClient/5.1.3 (Java/17)",
  30. "X-Amzn-Trace-Id": "Root=1-62bb1e89-64db55730a0361c720232ccd"
  31. },
  32. "json": {
  33. "password": "secret",
  34. "username": "wdbyte.com"
  35. },
  36. "origin": "183.128.136.89",
  37. "url": "http://httpbin.org/post"
  38. }

HttpClient 5 设置超时

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

  1. package com.wdbyte.httpclient;
  2. import java.io.IOException;
  3. import org.apache.hc.client5.http.classic.methods.HttpGet;
  4. import org.apache.hc.client5.http.config.RequestConfig;
  5. import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
  6. import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
  7. import org.apache.hc.client5.http.impl.classic.HttpClients;
  8. import org.apache.hc.core5.http.HttpEntity;
  9. import org.apache.hc.core5.http.ParseException;
  10. import org.apache.hc.core5.http.io.entity.EntityUtils;
  11. import org.apache.hc.core5.util.Timeout;
  12. /**
  13. * @author https://www.wdbyte.com
  14. */
  15. public class HttpClient5GetWithTimeout {
  16. public static void main(String[] args) {
  17. String result = get("http://httpbin.org/get");
  18. System.out.println(result);
  19. }
  20. public static String get(String url) {
  21. String resultContent = null;
  22. // 设置超时时间
  23. RequestConfig config = RequestConfig.custom()
  24. .setConnectTimeout(Timeout.ofMilliseconds(5000L))
  25. .setConnectionRequestTimeout(Timeout.ofMilliseconds(5000L))
  26. .setResponseTimeout(Timeout.ofMilliseconds(5000L))
  27. .build();
  28. // 请求级别的超时
  29. HttpGet httpGet = new HttpGet(url);
  30. //httpGet.setConfig(config);
  31. //try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
  32. // 客户端级别的超时
  33. try (CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(config).build()) {
  34. try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
  35. // 获取状态码
  36. System.out.println(response.getVersion()); // HTTP/1.1
  37. System.out.println(response.getCode()); // 200
  38. System.out.println(response.getReasonPhrase()); // OK
  39. HttpEntity entity = response.getEntity();
  40. // 获取响应信息
  41. resultContent = EntityUtils.toString(entity);
  42. }
  43. } catch (IOException | ParseException e) {
  44. e.printStackTrace();
  45. }
  46. return resultContent;
  47. }
  48. }

HttpClient 5 异步请求

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

  1. package com.wdbyte.httpclient;
  2. import java.io.IOException;
  3. import java.nio.CharBuffer;
  4. import java.util.concurrent.CountDownLatch;
  5. import java.util.concurrent.ExecutionException;
  6. import java.util.concurrent.Future;
  7. import org.apache.hc.client5.http.async.methods.AbstractCharResponseConsumer;
  8. import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
  9. import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
  10. import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
  11. import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
  12. import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
  13. import org.apache.hc.core5.concurrent.FutureCallback;
  14. import org.apache.hc.core5.http.ContentType;
  15. import org.apache.hc.core5.http.HttpException;
  16. import org.apache.hc.core5.http.HttpResponse;
  17. import org.apache.hc.core5.http.nio.AsyncRequestProducer;
  18. import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder;
  19. /**
  20. * HttpClient 5 异步请求
  21. * @author https://www.wdbyte.com
  22. * @date 2022/06/25
  23. */
  24. public class HttpClient5Async {
  25. public static void main(String[] args) {
  26. getAsync1("http://httpbin.org/get");
  27. getAsync2("http://httpbin.org/get");
  28. getAsync3("http://httpbin.org/get");
  29. }
  30. /**
  31. * 异步请求
  32. *
  33. * @param url
  34. * @return
  35. */
  36. public static String getAsync1(String url) {
  37. try (CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault()) {
  38. // 开始 http clinet
  39. httpclient.start();
  40. // 执行请求
  41. SimpleHttpRequest request1 = SimpleHttpRequests.get(url);
  42. Future<SimpleHttpResponse> future = httpclient.execute(request1, null);
  43. // 等待直到返回完毕
  44. SimpleHttpResponse response1 = future.get();
  45. System.out.println("getAsync1:" + request1.getRequestUri() + "->" + response1.getCode());
  46. } catch (IOException | ExecutionException | InterruptedException e) {
  47. throw new RuntimeException(e);
  48. }
  49. return null;
  50. }
  51. /**
  52. * 异步请求,根据响应情况回调
  53. *
  54. * @param url
  55. * @return
  56. */
  57. public static String getAsync2(String url) {
  58. try (CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault()) {
  59. // 开始 http clinet
  60. httpclient.start();
  61. // 根据请求响应情况进行回调操作
  62. CountDownLatch latch = new CountDownLatch(1);
  63. SimpleHttpRequest request = SimpleHttpRequests.get(url);
  64. httpclient.execute(request, new FutureCallback<SimpleHttpResponse>() {
  65. @Override
  66. public void completed(SimpleHttpResponse response2) {
  67. latch.countDown();
  68. System.out.println("getAsync2:" + request.getRequestUri() + "->" + response2.getCode());
  69. }
  70. @Override
  71. public void failed(Exception ex) {
  72. latch.countDown();
  73. System.out.println("getAsync2:" + request.getRequestUri() + "->" + ex);
  74. }
  75. @Override
  76. public void cancelled() {
  77. latch.countDown();
  78. System.out.println("getAsync2:" + request.getRequestUri() + " cancelled");
  79. }
  80. });
  81. latch.await();
  82. } catch (IOException | InterruptedException e) {
  83. throw new RuntimeException(e);
  84. }
  85. return null;
  86. }
  87. /**
  88. * 异步请求,对响应流做点什么
  89. *
  90. * @param url
  91. * @return
  92. */
  93. public static String getAsync3(String url) {
  94. try (CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault()) {
  95. // 开始 http clinet
  96. httpclient.start();
  97. // 根据请求响应情况进行回调操作
  98. SimpleHttpRequest request = SimpleHttpRequests.get(url);
  99. CountDownLatch latch = new CountDownLatch(1);
  100. AsyncRequestProducer producer = AsyncRequestBuilder.get("http://httpbin.org/get").build();
  101. AbstractCharResponseConsumer<HttpResponse> consumer3 = new AbstractCharResponseConsumer<HttpResponse>() {
  102. HttpResponse response;
  103. @Override
  104. protected void start(HttpResponse response, ContentType contentType) throws HttpException, IOException {
  105. System.out.println("getAsync3: 开始响应....");
  106. this.response = response;
  107. }
  108. @Override
  109. protected int capacityIncrement() {
  110. return Integer.MAX_VALUE;
  111. }
  112. @Override
  113. protected void data(CharBuffer data, boolean endOfStream) throws IOException {
  114. System.out.println("getAsync3: 收到数据....");
  115. // Do something useful
  116. }
  117. @Override
  118. protected HttpResponse buildResult() throws IOException {
  119. System.out.println("getAsync3: 接收完毕...");
  120. return response;
  121. }
  122. @Override
  123. public void releaseResources() {
  124. }
  125. };
  126. httpclient.execute(producer, consumer3, new FutureCallback<HttpResponse>() {
  127. @Override
  128. public void completed(HttpResponse response) {
  129. latch.countDown();
  130. System.out.println("getAsync3: "+request.getRequestUri() + "->" + response.getCode());
  131. }
  132. @Override
  133. public void failed(Exception ex) {
  134. latch.countDown();
  135. System.out.println("getAsync3: "+request.getRequestUri() + "->" + ex);
  136. }
  137. @Override
  138. public void cancelled() {
  139. latch.countDown();
  140. System.out.println("getAsync3: "+request.getRequestUri() + " cancelled");
  141. }
  142. });
  143. latch.await();
  144. } catch (IOException | InterruptedException e) {
  145. throw new RuntimeException(e);
  146. }
  147. return null;
  148. }
  149. }

输出结果:

  1. getAsync1:/get->200
  2. getAsync2:/get->200
  3. getAsync3: 开始响应....
  4. getAsync3: 收到数据....
  5. getAsync3: 收到数据....
  6. getAsync3: 收到数据....
  7. getAsync3: 接收完毕...
  8. 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 代码进行请求测试

  1. package com.wdbyte.httpclient;
  2. import java.util.List;
  3. import org.apache.hc.client5.http.classic.methods.HttpGet;
  4. import org.apache.hc.client5.http.cookie.BasicCookieStore;
  5. import org.apache.hc.client5.http.cookie.Cookie;
  6. import org.apache.hc.client5.http.cookie.CookieStore;
  7. import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
  8. import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
  9. import org.apache.hc.client5.http.impl.classic.HttpClients;
  10. import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;
  11. import org.apache.hc.client5.http.protocol.HttpClientContext;
  12. import org.apache.hc.core5.http.io.entity.EntityUtils;
  13. /**
  14. * 这个例子演示了使用本地HTTP上下文填充, 自定义属性
  15. */
  16. public class HttpClient5WithCookie {
  17. public static void main(final String[] args) throws Exception {
  18. try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
  19. // 创建一个本地的 Cookie 存储
  20. final CookieStore cookieStore = new BasicCookieStore();
  21. // BasicClientCookie clientCookie = new BasicClientCookie("name", "www.wdbyte.com");
  22. // clientCookie.setDomain("http://httpbin.org/cookies");
  23. // 过期时间
  24. // clientCookie.setExpiryDate(new Date());
  25. // 添加到本地 Cookie
  26. // cookieStore.addCookie(clientCookie);
  27. // 创建本地 HTTP 请求上下文 HttpClientContext
  28. final HttpClientContext localContext = HttpClientContext.create();
  29. // 绑定 cookieStore 到 localContext
  30. localContext.setCookieStore(cookieStore);
  31. final HttpGet httpget = new HttpGet("http://httpbin.org/cookies/set/cookieName/www.wdbyte.com");
  32. System.out.println("执行请求 " + httpget.getMethod() + " " + httpget.getUri());
  33. // 获取 Coolie 信息
  34. try (final CloseableHttpResponse response = httpclient.execute(httpget, localContext)) {
  35. System.out.println("----------------------------------------");
  36. System.out.println(response.getCode() + " " + response.getReasonPhrase());
  37. final List<Cookie> cookies = cookieStore.getCookies();
  38. for (int i = 0; i < cookies.size(); i++) {
  39. System.out.println("Local cookie: " + cookies.get(i));
  40. }
  41. EntityUtils.consume(response.getEntity());
  42. }
  43. }
  44. }
  45. }

输出结果:

  1. 执行请求 GET http://httpbin.org/cookies/set/cookieName/www.wdbyte.com
  2. ----------------------------------------
  3. 200 OK
  4. Local cookie: [name: cookieName; value: www.wdbyte.com; domain: httpbin.org; path: /; expiry: null]

HttpClient 5 读取文件内容请求

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

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

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

  1. package com.wdbyte.httpclient;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import org.apache.hc.client5.http.classic.methods.HttpPost;
  5. import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
  6. import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
  7. import org.apache.hc.client5.http.impl.classic.HttpClients;
  8. import org.apache.hc.core5.http.ContentType;
  9. import org.apache.hc.core5.http.io.entity.EntityUtils;
  10. import org.apache.hc.core5.http.io.entity.FileEntity;
  11. import org.apache.hc.core5.http.io.entity.InputStreamEntity;
  12. /**
  13. * 加载数据流作为 POST 请求参数
  14. */
  15. public class HttpClient5ChunkEncodedPost {
  16. public static void main(final String[] args) throws Exception {
  17. String params = "/Users/darcy/params.json";
  18. try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
  19. final HttpPost httppost = new HttpPost("http://httpbin.org/post");
  20. final InputStreamEntity reqEntity = new InputStreamEntity(new FileInputStream(params), -1,
  21. ContentType.APPLICATION_JSON);
  22. // 也可以使用 FileEntity 的形式
  23. // FileEntity reqEntity = new FileEntity(new File(params), ContentType.APPLICATION_JSON);
  24. httppost.setEntity(reqEntity);
  25. System.out.println("执行请求 " + httppost.getMethod() + " " + httppost.getUri());
  26. try (final CloseableHttpResponse response = httpclient.execute(httppost)) {
  27. System.out.println("----------------------------------------");
  28. System.out.println(response.getCode() + " " + response.getReasonPhrase());
  29. System.out.println(EntityUtils.toString(response.getEntity()));
  30. }
  31. }
  32. }
  33. }

输出结果:

  1. 执行请求 POST http://httpbin.org/post
  2. ----------------------------------------
  3. 200 OK
  4. {
  5. "args": {},
  6. "data": "{\"name\":\"www.wdbyte.com\"}\n",
  7. "files": {},
  8. "form": {},
  9. "headers": {
  10. "Accept-Encoding": "gzip, x-gzip, deflate",
  11. "Content-Length": "26",
  12. "Content-Type": "application/json; charset=UTF-8",
  13. "Host": "httpbin.org",
  14. "User-Agent": "Apache-HttpClient/5.1.3 (Java/1.8.0_151)",
  15. "X-Amzn-Trace-Id": "Root=1-62ee4d95-1f956d4303cea09c52694c86"
  16. },
  17. "json": {
  18. "name": "www.wdbyte.com"
  19. },
  20. "origin": "42.120.74.238",
  21. "url": "http://httpbin.org/post"
  22. }

HttpClient 5 表单登录

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

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

示例代码:

  1. package com.wdbyte.httpclient;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import org.apache.hc.client5.http.classic.methods.HttpGet;
  5. import org.apache.hc.client5.http.cookie.BasicCookieStore;
  6. import org.apache.hc.client5.http.cookie.Cookie;
  7. import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
  8. import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
  9. import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
  10. import org.apache.hc.client5.http.impl.classic.HttpClients;
  11. import org.apache.hc.core5.http.HttpEntity;
  12. import org.apache.hc.core5.http.NameValuePair;
  13. import org.apache.hc.core5.http.io.entity.EntityUtils;
  14. import org.apache.hc.core5.http.message.BasicNameValuePair;
  15. /**
  16. * 演示基于表单的登录
  17. *
  18. * @author https://www.wdbyte.com
  19. */
  20. public class HttpClient5FormLogin {
  21. public static void main(final String[] args) throws Exception {
  22. final BasicCookieStore cookieStore = new BasicCookieStore();
  23. try (final CloseableHttpClient httpclient = HttpClients.custom()
  24. .setDefaultCookieStore(cookieStore)
  25. .build()) {
  26. // 本应该使用 POST 请求发送表单参数,但是在 httpbin.org 中没有对应的接口用于测试,所以这里换成了 GET 请求
  27. // HttpPost httpPost = new HttpPost("http://httpbin.org/cookies/set/username/wdbyte.com");
  28. HttpGet httpPost = new HttpGet("http://httpbin.org/cookies/set/username/wdbyte.com");
  29. // POST 表单请求参数
  30. List<NameValuePair> nvps = new ArrayList<>();
  31. nvps.add(new BasicNameValuePair("username", "wdbyte.com"));
  32. nvps.add(new BasicNameValuePair("password", "secret"));
  33. httpPost.setEntity(new UrlEncodedFormEntity(nvps));
  34. try (final CloseableHttpResponse response2 = httpclient.execute(httpPost)) {
  35. final HttpEntity entity = response2.getEntity();
  36. System.out.println("Login form get: " + response2.getCode() + " " + response2.getReasonPhrase());
  37. System.out.println("当前响应信息 "+EntityUtils.toString(entity));;
  38. System.out.println("Post 登录 Cookie:");
  39. final List<Cookie> cookies = cookieStore.getCookies();
  40. if (cookies.isEmpty()) {
  41. System.out.println("None");
  42. } else {
  43. for (int i = 0; i < cookies.size(); i++) {
  44. System.out.println("- " + cookies.get(i));
  45. }
  46. }
  47. }
  48. }
  49. }
  50. }

输出结果:

  1. Login form get: 200 OK
  2. 当前响应信息 {
  3. "cookies": {
  4. "username": "wdbyte.com"
  5. }
  6. }
  7. Post 登录 Cookie:
  8. - [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

在 Apache HttpClient 5 中的实现方式。

  1. package com.wdbyte.httpclient;
  2. import org.apache.hc.client5.http.auth.AuthScope;
  3. import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
  4. import org.apache.hc.client5.http.classic.methods.HttpGet;
  5. import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
  6. import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
  7. import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
  8. import org.apache.hc.client5.http.impl.classic.HttpClients;
  9. import org.apache.hc.core5.http.io.entity.EntityUtils;
  10. /**
  11. * 一个简单的示例,它使用HttpClient执行HTTP请求;
  12. * 一个需要进行用户身份验证的目标站点。
  13. */
  14. public class HttpClient5BasicAuthentication {
  15. public static void main(final String[] args) throws Exception {
  16. final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
  17. credsProvider.setCredentials(
  18. new AuthScope("httpbin.org", 80),
  19. new UsernamePasswordCredentials("admin", "123456".toCharArray()));
  20. try (final CloseableHttpClient httpclient = HttpClients.custom()
  21. .setDefaultCredentialsProvider(credsProvider)
  22. .build()) {
  23. final HttpGet httpget = new HttpGet("http://httpbin.org/basic-auth/admin/123456");
  24. System.out.println("执行请求" + httpget.getMethod() + " " + httpget.getUri());
  25. try (final CloseableHttpResponse response = httpclient.execute(httpget)) {
  26. System.out.println("----------------------------------------");
  27. System.out.println(response.getCode() + " " + response.getReasonPhrase());
  28. System.out.println(EntityUtils.toString(response.getEntity()));
  29. }
  30. }
  31. }
  32. }

输出结果:

  1. 执行请求GET http://httpbin.org/basic-auth/user/passwd
  2. ----------------------------------------
  3. 200 OK
  4. {
  5. "authenticated": true,
  6. "user": "user"
  7. }

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

  1. // 请求
  2. GET /basic-auth/user/passwd HTTP/1.1
  3. Accept-Encoding: gzip, x-gzip, deflate
  4. Host: httpbin.org
  5. Connection: keep-alive
  6. User-Agent: Apache-HttpClient/5.1.3 (Java/1.8.0_151)
  7. // 响应
  8. HTTP/1.1 401 UNAUTHORIZED
  9. Date: Sat, 06 Aug 2022 08:25:33 GMT
  10. Content-Length: 0
  11. Connection: keep-alive
  12. Server: gunicorn/19.9.0
  13. WWW-Authenticate: Basic realm="Fake Realm"
  14. Access-Control-Allow-Origin: *
  15. Access-Control-Allow-Credentials: true
  16. // 请求
  17. GET /basic-auth/user/passwd HTTP/1.1
  18. Host: httpbin.org
  19. Connection: keep-alive
  20. User-Agent: Apache-HttpClient/5.1.3 (Java/1.8.0_151)
  21. Authorization: Basic dXNlcjpwYXNzd2Q=
  22. // 响应
  23. HTTP/1.1 200 OK
  24. Date: Sat, 06 Aug 2022 08:25:33 GMT
  25. Content-Type: application/json
  26. Content-Length: 47
  27. Connection: keep-alive
  28. Server: gunicorn/19.9.0
  29. Access-Control-Allow-Origin: *
  30. Access-Control-Allow-Credentials: true
  31. {
  32. "authenticated": true,
  33. "user": "user"
  34. }

HttpClient 5 Digest Authorization

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

下面是代码演示。

  1. package com.wdbyte.httpclient;
  2. import org.apache.hc.client5.http.auth.AuthExchange;
  3. import org.apache.hc.client5.http.auth.AuthScheme;
  4. import org.apache.hc.client5.http.auth.AuthScope;
  5. import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
  6. import org.apache.hc.client5.http.classic.methods.HttpGet;
  7. import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
  8. import org.apache.hc.client5.http.impl.auth.DigestScheme;
  9. import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
  10. import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
  11. import org.apache.hc.client5.http.impl.classic.HttpClients;
  12. import org.apache.hc.client5.http.protocol.HttpClientContext;
  13. import org.apache.hc.core5.http.HttpHost;
  14. import org.apache.hc.core5.http.io.entity.EntityUtils;
  15. /**
  16. *
  17. * HttpClient如何验证多个请求的示例
  18. * 使用相同的摘要方案。在初始请求/响应交换之后
  19. * 共享相同执行上下文的所有后续请求都可以重用
  20. * 要向服务器进行身份验证的最后一个摘要nonce值。
  21. */
  22. public class HttpClient5PreemptiveDigestAuthentication {
  23. public static void main(final String[] args) throws Exception {
  24. try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
  25. final HttpHost target = new HttpHost("http", "httpbin.org", 80);
  26. final HttpClientContext localContext = HttpClientContext.create();
  27. final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
  28. credentialsProvider.setCredentials(
  29. new AuthScope(target),
  30. new UsernamePasswordCredentials("admin", "123456".toCharArray()));
  31. localContext.setCredentialsProvider(credentialsProvider);
  32. final HttpGet httpget = new HttpGet("http://httpbin.org/digest-auth/auth/admin/123456");
  33. System.out.println("执行请求 " + httpget.getMethod() + " " + httpget.getUri());
  34. for (int i = 0; i < 2; i++) {
  35. try (final CloseableHttpResponse response = httpclient.execute(target, httpget, localContext)) {
  36. System.out.println("----------------------------------------");
  37. System.out.println(response.getCode() + " " + response.getReasonPhrase());
  38. EntityUtils.consume(response.getEntity());
  39. final AuthExchange authExchange = localContext.getAuthExchange(target);
  40. if (authExchange != null) {
  41. final AuthScheme authScheme = authExchange.getAuthScheme();
  42. if (authScheme instanceof DigestScheme) {
  43. final DigestScheme digestScheme = (DigestScheme) authScheme;
  44. System.out.println("Nonce: " + digestScheme.getNonce() +
  45. "; count: " + digestScheme.getNounceCount());
  46. }
  47. }
  48. }
  49. }
  50. }
  51. }
  52. }

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

  1. // 1. 请求
  2. GET /digest-auth/auth/admin/123456 HTTP/1.1
  3. Accept-Encoding: gzip, x-gzip, deflate
  4. Host: httpbin.org
  5. Connection: keep-alive
  6. User-Agent: Apache-HttpClient/5.1.3 (Java/1.8.0_151)
  7. // 2. 详情,提示认证,给出参数
  8. HTTP/1.1 401 UNAUTHORIZED
  9. Date: Fri, 12 Aug 2022 07:11:06 GMT
  10. Content-Type: text/html; charset=utf-8
  11. Content-Length: 0
  12. Connection: keep-alive
  13. Server: gunicorn/19.9.0
  14. WWW-Authenticate: Digest realm="me@kennethreitz.com", nonce="8dc5e7974a86a6fcc3cf73230b0c4a93", qop="auth", opaque="64b7f68b386c3acc38131f7472aa2079", algorithm=MD5, stale=FALSE
  15. Set-Cookie: stale_after=never; Path=/
  16. Set-Cookie: fake=fake_value; Path=/
  17. Access-Control-Allow-Origin: *
  18. Access-Control-Allow-Credentials: true
  19. // 3. 参数+密码 加密后再次请求
  20. GET /digest-auth/auth/admin/123456 HTTP/1.1
  21. Host: httpbin.org
  22. Connection: keep-alive
  23. User-Agent: Apache-HttpClient/5.1.3 (Java/1.8.0_151)
  24. Cookie: fake=fake_value; stale_after=never
  25. 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"
  26. // 4. 认证成功,响应
  27. HTTP/1.1 200 OK
  28. Date: Fri, 12 Aug 2022 07:11:08 GMT
  29. Content-Type: application/json
  30. Content-Length: 48
  31. Connection: keep-alive
  32. Server: gunicorn/19.9.0
  33. Set-Cookie: fake=fake_value; Path=/
  34. Set-Cookie: stale_after=never; Path=/
  35. Access-Control-Allow-Origin: *
  36. Access-Control-Allow-Credentials: true
  37. {
  38. "authenticated": true,
  39. "user": "admin"
  40. }
  41. // 5. 再次请求,共享了登录状态。
  42. GET /digest-auth/auth/admin/123456 HTTP/1.1
  43. Accept-Encoding: gzip, x-gzip, deflate
  44. Host: httpbin.org
  45. Connection: keep-alive
  46. User-Agent: Apache-HttpClient/5.1.3 (Java/1.8.0_151)
  47. Cookie: fake=fake_value; stale_after=never
  48. 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"
  49. // 5. 认证成功,响应
  50. HTTP/1.1 200 OK
  51. Date: Fri, 12 Aug 2022 07:11:09 GMT
  52. Content-Type: application/json
  53. Content-Length: 48
  54. Connection: keep-alive
  55. Server: gunicorn/19.9.0
  56. Set-Cookie: fake=fake_value; Path=/
  57. Set-Cookie: stale_after=never; Path=/
  58. Access-Control-Allow-Origin: *
  59. Access-Control-Allow-Credentials: true
  60. {
  61. "authenticated": true,
  62. "user": "admin"
  63. }

HttpClient 5 拦截器

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

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

  1. package com.wdbyte.httpclient;
  2. import java.io.IOException;
  3. import java.util.concurrent.atomic.AtomicLong;
  4. import org.apache.hc.client5.http.classic.ExecChain;
  5. import org.apache.hc.client5.http.classic.ExecChain.Scope;
  6. import org.apache.hc.client5.http.classic.ExecChainHandler;
  7. import org.apache.hc.client5.http.classic.methods.HttpGet;
  8. import org.apache.hc.client5.http.impl.ChainElement;
  9. import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
  10. import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
  11. import org.apache.hc.client5.http.impl.classic.HttpClients;
  12. import org.apache.hc.core5.http.ClassicHttpRequest;
  13. import org.apache.hc.core5.http.ClassicHttpResponse;
  14. import org.apache.hc.core5.http.ContentType;
  15. import org.apache.hc.core5.http.EntityDetails;
  16. import org.apache.hc.core5.http.Header;
  17. import org.apache.hc.core5.http.HttpEntity;
  18. import org.apache.hc.core5.http.HttpException;
  19. import org.apache.hc.core5.http.HttpRequest;
  20. import org.apache.hc.core5.http.HttpRequestInterceptor;
  21. import org.apache.hc.core5.http.HttpStatus;
  22. import org.apache.hc.core5.http.io.entity.EntityUtils;
  23. import org.apache.hc.core5.http.io.entity.StringEntity;
  24. import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
  25. import org.apache.hc.core5.http.protocol.HttpContext;
  26. /**
  27. * 展示如何在请求和响应时进行拦截进行自定义处理。
  28. */
  29. public class HttpClient5Interceptors {
  30. public static void main(final String[] args) throws Exception {
  31. try (final CloseableHttpClient httpclient = HttpClients.custom()
  32. // 添加一个请求 id 到请求 header
  33. .addRequestInterceptorFirst(new HttpRequestInterceptor() {
  34. private final AtomicLong count = new AtomicLong(0);
  35. @Override
  36. public void process(
  37. final HttpRequest request,
  38. final EntityDetails entity,
  39. final HttpContext context) throws HttpException, IOException {
  40. request.setHeader("request-id", Long.toString(count.incrementAndGet()));
  41. }
  42. })
  43. .addExecInterceptorAfter(ChainElement.PROTOCOL.name(), "custom", new ExecChainHandler() {
  44. // 请求 id 为 2 的,模拟 404 响应,并自定义响应的内容。
  45. @Override
  46. public ClassicHttpResponse execute(
  47. final ClassicHttpRequest request,
  48. final Scope scope,
  49. final ExecChain chain) throws IOException, HttpException {
  50. final Header idHeader = request.getFirstHeader("request-id");
  51. if (idHeader != null && "2".equalsIgnoreCase(idHeader.getValue())) {
  52. final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_NOT_FOUND,
  53. "Oppsie");
  54. response.setEntity(new StringEntity("bad luck", ContentType.TEXT_PLAIN));
  55. return response;
  56. } else {
  57. return chain.proceed(request, scope);
  58. }
  59. }
  60. })
  61. .build()) {
  62. for (int i = 0; i < 3; i++) {
  63. final HttpGet httpget = new HttpGet("http://httpbin.org/get");
  64. try (final CloseableHttpResponse response = httpclient.execute(httpget)) {
  65. System.out.println("----------------------------------------");
  66. System.out.println("执行请求 " + httpget.getMethod() + " " + httpget.getUri());
  67. System.out.println(response.getCode() + " " + response.getReasonPhrase());
  68. System.out.println(EntityUtils.toString(response.getEntity()));
  69. }
  70. }
  71. }
  72. }
  73. }

输出结果。

  1. ----------------------------------------
  2. 执行请求 GET http://httpbin.org/get
  3. 200 OK
  4. {
  5. "args": {},
  6. "headers": {
  7. "Accept-Encoding": "gzip, x-gzip, deflate",
  8. "Host": "httpbin.org",
  9. "Request-Id": "1",
  10. "User-Agent": "Apache-HttpClient/5.1.3 (Java/1.8.0_151)",
  11. "X-Amzn-Trace-Id": "Root=1-62f615ba-658ccd42182d22534dbba82c"
  12. },
  13. "origin": "42.120.75.221",
  14. "url": "http://httpbin.org/get"
  15. }
  16. ----------------------------------------
  17. 执行请求 GET http://httpbin.org/get
  18. 404 Oppsie
  19. bad luck
  20. ----------------------------------------
  21. 执行请求 GET http://httpbin.org/get
  22. 200 OK
  23. {
  24. "args": {},
  25. "headers": {
  26. "Accept-Encoding": "gzip, x-gzip, deflate",
  27. "Host": "httpbin.org",
  28. "Request-Id": "3",
  29. "User-Agent": "Apache-HttpClient/5.1.3 (Java/1.8.0_151)",
  30. "X-Amzn-Trace-Id": "Root=1-62f615bb-4eb6ba10736ace0e21d0cb8c"
  31. },
  32. "origin": "42.120.75.221",
  33. "url": "http://httpbin.org/get"
  34. }

一如既往,文章代码都存放在 Github.com/niumoo/javaNotes.

<完>

文章持续更新,可以微信搜一搜「 程序猿阿朗 」或访问「程序猿阿朗博客 」第一时间阅读。本文 Github.com/niumoo/JavaNotes 已经收录,有很多知识点和系列文章,欢迎Star。

Apache HttpClient 5 使用详细教程的更多相关文章

  1. (转)OS X Mountain Lion 系统配置 Apache+Mysql+PHP 详细教程

    如果你是一名 Web 开发者,很多时候都需要在本地搭建服务器测试环境,比如 Apache+Mysql+PHP 这样的环境.事实上 Mac OS X 中想要搭建这样的环境很简单,本文我们就会将详细的教程 ...

  2. Ubuntu 搭建Web服务器(MySQL+PHP+Apache)详细教程

    Ubuntu 搭建Web服务器(MySQL+PHP+Apache)详细教程 看了好多人的博客,有的不全 or 有问题,整理了一下,适合小白 新手先整理几个小问题 1.为啥使用 Linux 搭建服务器? ...

  3. 小白搭建WAMP详细教程---apache安装与设置

    一.apache官网下载Apache HTTP Server服务器 1.打开apache官网http://www.apache.org/,拉到最底下,找到HTTP Server,点击进去下载.也可以直 ...

  4. 如何在Apache HttpClient中设置TLS版本

    1.简介 Apache HttpClient是一个底层.轻量级的客户端HTTP库,用于与HTTP服务器进行通信. 在本教程中,我们将学习如何在使用HttpClient时配置支持的传输层安全(TLS)版 ...

  5. 安装WordPress详细教程指南

    最近准备自己建一个个人博客,以便分享一些自己工作生活中的一些观点及经验,建博客当然选wordpress,毕竟wordpress是为博客而生的嘛.下边记录一下自己安装WordPress的详细过程指南,亦 ...

  6. android 中对apache httpclient及httpurlconnection的选择

    在官方blog中,android工程师谈到了如何去选择apache client和httpurlconnection的问题: 原文见http://android-developers.blogspot ...

  7. Struts2+Spring4+Hibernate4整合超详细教程

    Struts2.Spring4.Hibernate4整合 超详细教程 Struts2.Spring4.Hibernate4整合实例-下载 项目目的: 整合使用最新版本的三大框架(即Struts2.Sp ...

  8. spring入门详细教程(五)

    前言 本篇紧接着spring入门详细教程(三),建议阅读本篇前,先阅读第一篇,第二篇以及第三篇.链接如下: Spring入门详细教程(一) https://www.cnblogs.com/jichi/ ...

  9. Spring入门详细教程(四)

    前言 本篇紧接着spring入门详细教程(三),建议阅读本篇前,先阅读第一篇,第二篇以及第三篇.链接如下: Spring入门详细教程(一) https://www.cnblogs.com/jichi/ ...

随机推荐

  1. 关于Vue移动端框架(Muse-UI)的使用(说明书,针对不愿看文档的童鞋)

    一.安装 1.npm安装 npm i muse-ui -S 或者 CDN安装 <link rel="stylesheet" href="https://unpkg. ...

  2. MongoDB 的内存使用限制

    本文将简述一下MongoDB的内存限制问题 1. 使用Docker限制 当我们使用docker创建mongo 容器时,可通过使用以下参数,对mongo可以使用的资源进行限制 内存限制 参数 简介 -m ...

  3. linux shell的配置文件执行顺序

    shell配置文件的作用:初始化环境变量.设置命令提示符.指定系统命令路径等 shell配置文件分类: (1)系统级别配置文件: /etc下,比如/etc/profile./etc/bashrc (2 ...

  4. docker compose 部署 minio

    1.docker-compose.yaml 文件如下: version: '3' services: minio: image: minio/minio:latest # 原镜像`minio/mini ...

  5. Eclipse拷贝动态的web工程

    1.选中需要拷贝的工程,CTRL+C,然后CTRL+V 2.在web动态工程中,还需要选中新拷贝工程,右键选中properties,然后搜索web,--->Web Project Setttin ...

  6. JDBCToolsV3 :DAO

    编写文件和步骤 ①,bean模块:数据类Course,包含数据的class,封装数据类型; ②,DAO:1)定义对数据的操作接口,及规定标准(包含怎样的操作).例如:CourseDAO数据库操作的接口 ...

  7. javascript基本属性访问对象的属性和方法

    var myName = "Shelley"; //字符串基本类型 alert(myName.length);  //隐式创建String对象,数值与myName相同,并执行len ...

  8. SpringBoot之MongoDB附件操作

    前言 近期自己针对附件上传进一步学习,为了弥足项目中文件上传的漏洞,保证文件上传功能的健壮性和可用性,现在我将自己在这一块的心得总结如下: 一.pom.xml依赖的引入 <dependency& ...

  9. C# MVCapi跨域问题

     he 'Access-Control-Allow-Origin' header contains multiple values ', *', but only one is allowed. Or ...

  10. APISpace 未来7天生活指数API接口 免费好用

    随着经济的发展,我们的生活水平在不断的提高,生活指数在我们的生活中也越来越受到关注,根据当天的生活指数,我们就可以知道在今天我们可以干什么比较好.   未来7天生活指数API,支持国内3400+个城市 ...