Java 中使用 HttpClinet 工具详解 · 小豆丁个人博客
!版权声明:本博客内容均均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。
http://www.mydlq.club/article/68/
目录[-]
系统环境:
- JDK 版本:1.8
- HttpClient 版本:5.0
参考地址:
- Apache Httpclient 官方文档
- 博文示例项目 Github 地址: https://github.com/my-dlq/blog-example/tree/master/java/java-httpclient-example
一、HttpClient 简介
超文本传输协议(HTTP)可能是当今 Internet
上使用的最重要的协议。 Web
服务,支持网络的设备和网络计算的增长继续将 HTTP
协议的作用扩展到用户驱动的Web浏览器之外,同时增加了需要 HTTP
支持的应用程序的数量。
尽管 java.net
软件包提供了用于通过 HTTP
访问资源的基本功能,但它并未提供许多应用程序所需的全部灵活性或功能。 HttpClient
试图通过提供高效,最新且功能丰富的程序包来实现此空白,以实现最新 HTTP 标准和建议的客户端。
HttpClient 是为扩展而设计的,同时提供了对基本 HTTP
协议的强大支持,对于构建 HTTP
感知的客户端应用程序(例如 Web 浏览器,Web 服务客户端或利用或扩展 HTTP
协议进行分布式通信的系统)的任何人来说, HttpClient
都可能会感兴趣。
二、示例前准备
1、接口测试 Server 项目
在介绍如何使用 HttpClient
的示例前,需要提前准备示例项目进行测试的接口,这里本人使用 SpringBoot
提前写了个测试项目,用于后续方便演示,该测试项目 Github 地址为: https://github.com/my-dlq/blog-example/tree/master/java/java-httpclient-example
2、HttpClient 工具使用 Client 项目
(1)、Maven 引入相关依赖
这里是使用 Maven 管理示例项目中的依赖,下面除了需要引入 httpclient
依赖外,还需要引入 logback
、 lombok
与 fastjson
相关依赖来提供使用工具时的便利,其中每个依赖的作用分别为:
- logback:日志框架,方便打印日志。
- fastjson:用于转换 Json 的工具框架。
- lombok:这里是用于方便快速实现实体类的 Get 与 Set 方法,且集成 Slf4j 方法与各个日志框架融合。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>club.mydlq</groupId>
<artifactId>java-httpclient-use-example</artifactId>
<version>1.0.0</version>
<name>java-httpclient-use-example</name>
<description>java httpclient use example</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.0</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<optional>true</optional>
</dependency>
<!--logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</project>
(2)、用于测试的实体类 UserInfo
为了方便后续测试 Json 序列化传递消息的示例,这里提前封装一个用于测试的实体类。
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class UserInfo {
private String name;
private String sex;
}
二、HttpClient 使用示例(同步)
1、HttpClient 工具类封装
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.cookie.BasicCookieStore;
import org.apache.hc.client5.http.cookie.CookieStore;
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.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.http.config.Registry;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.util.TimeValue;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@Slf4j
public class HttpClientUtil {
private HttpClientUtil(){}
/** HttpClient 对象 */
private static CloseableHttpClient httpClient = null;
/** CookieStore 对象 */
private static CookieStore cookieStore = null;
/** Basic Auth 管理对象 **/
private static BasicCredentialsProvider basicCredentialsProvider = null;
// Httpclient 初始化
static {
// 注册访问协议相关的 Socket 工厂
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
// Http 连接池
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(registry);
poolingHttpClientConnectionManager.setDefaultSocketConfig(SocketConfig.custom().setTcpNoDelay(true).build());
poolingHttpClientConnectionManager.setMaxTotal(200);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(200);
poolingHttpClientConnectionManager.setValidateAfterInactivity(TimeValue.ofMinutes(5));
// Http 请求配置
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(5000, TimeUnit.MILLISECONDS)
.setResponseTimeout(5000, TimeUnit.MILLISECONDS)
.setConnectionRequestTimeout(5000, TimeUnit.MILLISECONDS)
.build();
// 设置 Cookie
cookieStore = new BasicCookieStore();
// 设置 Basic Auth 对象
basicCredentialsProvider = new BasicCredentialsProvider();
// 创建监听器,在 JVM 停止或重启时,关闭连接池释放掉连接
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
log.info("执行关闭 HttpClient");
httpClient.close();
log.info("已经关闭 HttpClient");
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}));
// 创建 HttpClient 对象
httpClient = HttpClients.custom()
// 设置 Cookie
.setDefaultCookieStore(cookieStore)
// 设置 Basic Auth
.setDefaultCredentialsProvider(basicCredentialsProvider)
// 设置 HttpClient 请求参数
.setDefaultRequestConfig(requestConfig)
// 设置连接池
.setConnectionManager(poolingHttpClientConnectionManager)
// 设置定时清理连接池中过期的连接
.evictExpiredConnections()
.evictIdleConnections(TimeValue.ofMinutes(3))
.build();
}
/**
* 获取 Httpclient 对象
*
* @return CloseableHttpClient
*/
public static CloseableHttpClient getHttpclient() {
return httpClient;
}
/**
* 获取 CookieStore 对象
*
* @return CookieStore
*/
public static CookieStore getCookieStore() {
return cookieStore;
}
/**
* 获取 BasicCredentialsProvider 对象
*
* @return BasicCredentialsProvider
*/
public static BasicCredentialsProvider getBasicCredentialsProvider(){
return basicCredentialsProvider;
}
}
2、基本操作示例
HttpClient 基本 get、post、form 表单等操作示例:
import club.mydlq.entity.UserInfo;
import club.mydlq.utils.HttpClientUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpGet;
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.CloseableHttpResponse;
import org.apache.hc.core5.http.*;
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.BasicNameValuePair;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/**
* HttpClient 基本 get、post、form 表单等操作示例
*/
@Slf4j
public class BaseExample {
/**
* Http Get 请求示例
*/
public static void httpGet() {
CloseableHttpResponse response = null;
try {
// 创建 HttpGet 对象
HttpGet httpGet = new HttpGet("http://localhost:8080/base/get?name=mydlq&sex=man");
// 执行 Http Get 请求
response = HttpClientUtil.getHttpclient().execute(httpGet);
// 输出响应内容
if (response.getEntity() != null) {
log.info(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
}
// 销毁流
EntityUtils.consume(response.getEntity());
} catch (IOException | ParseException e) {
log.error("", e);
} finally {
// 释放资源
if (response != null) {
try {
response.close();
} catch (IOException e) {
log.error("", e);
}
}
}
}
/**
* Http Post Form 表单请求示例
*/
public static void httpPostForm(){
CloseableHttpResponse response = null;
try {
// 创建 HttpPost 对象
HttpPost httpPost = new HttpPost("http://localhost:8080/base/form");
// 设置 HttpPost 请求参数
List<NameValuePair> params = new ArrayList<>(2);
params.add(new BasicNameValuePair("name", "mydlq"));
params.add(new BasicNameValuePair("sex", "man"));
UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(params, StandardCharsets.UTF_8);
httpPost.setEntity(urlEncodedFormEntity);
// 设置 Content-Type
httpPost.addHeader("Content-Type", ContentType.APPLICATION_FORM_URLENCODED.getMimeType());
// 执行 Http Post 请求
response = HttpClientUtil.getHttpclient().execute(httpPost);
// 输出响应内容
if (response.getEntity() != null) {
log.info(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
}
// 销毁流
EntityUtils.consume(urlEncodedFormEntity);
EntityUtils.consume(response.getEntity());
} catch (IOException | ParseException e) {
log.error("",e);
} finally {
// 释放资源
if (response != null) {
try {
response.close();
} catch (IOException e) {
log.error("", e);
}
}
}
}
/**
* Http Post Json 表单请求示例
*/
public static void httpPostJson(){
CloseableHttpResponse response = null;
InputStream inputStream = null;
try {
// 创建 HttpPost 对象
HttpPost httpPost = new HttpPost("http://localhost:8080/base/json");
// 设置请求对象
UserInfo requestUserInfo = new UserInfo();
requestUserInfo.setName("mydlq");
requestUserInfo.setSex("man");
// 将请求对象通过 fastjson 中方法转换为 Json 字符串,并创建字符串实体对象
StringEntity stringEntity = new StringEntity(JSON.toJSONString(requestUserInfo), StandardCharsets.UTF_8);
// 设置 HttpPost 请求参数
httpPost.setEntity(stringEntity);
// 设置 Content-Type
httpPost.addHeader("Content-Type",ContentType.APPLICATION_JSON);
// 执行 Http Post 请求
response = HttpClientUtil.getHttpclient().execute(httpPost);
// 输出响应内容
if (response.getEntity() != null) {
inputStream = response.getEntity().getContent();
UserInfo userInfo = JSON.parseObject(inputStream, UserInfo.class);
log.info(userInfo.toString());
}
// 销毁流
EntityUtils.consume(stringEntity);
EntityUtils.consume(response.getEntity());
} catch (IOException e) {
log.error("",e);
} finally {
// 释放资源
if (inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
log.error("", e);
}
}
if (response != null) {
try {
response.close();
} catch (IOException e) {
log.error("", e);
}
}
}
}
/** 测试 Main 方法 */
public static void main(String[] args) {
// 执行 Http Get 请求
httpGet();
// 执行 Http Post Form 表单请求
httpPostForm();
// 执行 Http Post Json 请求
httpPostJson();
}
}
3、Form 表单登录示例
import club.mydlq.utils.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@Slf4j
public class LoginExample {
/**
* Form 表单登录,登录成功后会自动存 Session,然后再访问一个需要鉴权的页面
*/
public static void httpFormLogin() {
CloseableHttpResponse response = null;
try {
// 创建 Http 请求
ClassicHttpRequest request = ClassicRequestBuilder.post("http://localhost:8080/login/from")
.addParameter("t_username", "admin")
.addParameter("t_password", "123456")
.build();
// 执行 Http 请求,进行登录
HttpClientUtil.getHttpclient().execute(request);
// 如果登录成功,则能获取 sessionid
String jsessionid = HttpClientUtil.getCookieStore().getCookies().get(0).getValue();
log.info("获取的 sessionid 为:{}", jsessionid);
// 创建 HttpGet 对象
HttpGet httpGet = new HttpGet("http://localhost:8080/login/test");
// 执行 Http Get 请求进行测试
response = HttpClientUtil.getHttpclient().execute(httpGet);
// 输出响应内容
if (response.getEntity() != null) {
log.info(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
}
// 销毁流
EntityUtils.consume(response.getEntity());
} catch (IOException | ParseException e) {
log.error("", e);
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
log.error("", e);
}
}
}
}
/** 测试 Main 方法 */
public static void main(String[] args) {
// 执行 Form 表单登录
httpFormLogin();
}
}
4、Basic Auth 验证示例
import club.mydlq.utils.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
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.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@Slf4j
public class BasicAuthExample {
/**
* Basic Auth 访问验证测试
*/
public static void httpBasicAuth() {
CloseableHttpResponse response = null;
// 配置 Credentials
HttpClientUtil.getBasicCredentialsProvider().setCredentials(
new AuthScope("localhost", 8080),
new UsernamePasswordCredentials("admin", "123456".toCharArray()));
try {
// 创建 Http 请求
HttpGet httpget = new HttpGet("http://localhost:8080/basic/test");
// 执行 Http 请求,进行登录
response = HttpClientUtil.getHttpclient().execute(httpget);
// 输出响应内容
if (response.getEntity() != null) {
log.info(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
}
// 销毁流
EntityUtils.consume(response.getEntity());
} catch (IOException | ParseException e) {
log.error("", e);
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
log.error("", e);
}
}
}
}
/** 测试 Main 方法 */
public static void main(String[] args) {
// 访问需要 BasicAuth 验证的地址,进行测试
httpBasicAuth();
}
}
5、文件上传、下载示例
import club.mydlq.utils.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.mime.*;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.*;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
@Slf4j
public class FileExample {
/**
* Http Post 上传文件示例
*/
public static void httpPostUpload() {
File file = new File("D:" +File.separator + "测试.xlsx");
CloseableHttpResponse response = null;
try {
HttpPost httpPost = new HttpPost("http://localhost:8080/file/upload");
HttpEntity entity = MultipartEntityBuilder.create()
// 设置编码方式
.setCharset(StandardCharsets.UTF_8)
// 设置为兼容模式,解决返回中文乱码问题
.setMode(HttpMultipartMode.LEGACY)
.addPart("file", new FileBody(file))
.build();
httpPost.setEntity(entity);
// 执行提交
response = HttpClientUtil.getHttpclient().execute(httpPost);
// 输出响应内容
if (response.getEntity() != null) {
log.info(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
}
// 销毁流
EntityUtils.consume(response.getEntity());
} catch (IOException | ParseException e) {
log.error("", e);
} finally {
// 释放资源
if (response != null) {
try {
response.close();
} catch (IOException e) {
log.error("", e);
}
}
}
}
/**
* Http Get 下载文件示例
*/
public static void httpGetDownload() {
CloseableHttpResponse response = null;
try {
// 创建 HttpGet 对象
HttpGet httpGet = new HttpGet("http://localhost:8080/file/download");
// 执行 Http Get 请求
response = HttpClientUtil.getHttpclient().execute(httpGet);
// 从 headers 中获取文件名
String content = response.getHeader("Content-Disposition").getValue();
String filename = content.substring(content.lastIndexOf('=') + 1, content.lastIndexOf('.'));
String suffix = content.substring(content.lastIndexOf('.'));
// 将文件名转码
filename = URLDecoder.decode(filename, "UTF-8");
// 获取响应实体对象
HttpEntity entity = response.getEntity();
if (entity != null){
// 获取输入流并且将保存下载的文件
try (InputStream inputStream = entity.getContent();
FileOutputStream fileOutputStream = new FileOutputStream("d://" + filename + suffix)
) {
byte[] tmp = new byte[1024];
int l;
while ((l = inputStream.read(tmp)) != -1) {
fileOutputStream.write(tmp, 0, l);
}
fileOutputStream.flush();
}
}
// 销毁流
EntityUtils.consume(response.getEntity());
} catch (IOException | ProtocolException e) {
log.error("", e);
} finally {
// 释放资源
if (response != null) {
try {
response.close();
} catch (IOException e) {
log.error("", e);
}
}
}
}
/** 测试 Main 方法 */
public static void main(String[] args) {
// 执行下载文件
httpGetDownload();
// 执行上传文件
httpPostUpload();
}
}
三、HttpClient 使用示例(异步)
1、HttpAsyncClient 工具类封装
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.util.TimeValue;
@Slf4j
public class HttpClientAsycnUtil {
/**
* HttpAsyncClient 对象
*/
private static CloseableHttpAsyncClient closeableHttpAsyncClient = null;
// HttpAsyncclient 初始化
static {
//配置io线程
IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setIoThreadCount(Runtime.getRuntime().availableProcessors())
.build();
// Http 异步连接池
PoolingAsyncClientConnectionManager poolingAsyncClientConnectionManager = new PoolingAsyncClientConnectionManager();
poolingAsyncClientConnectionManager.setMaxTotal(200);
poolingAsyncClientConnectionManager.setDefaultMaxPerRoute(200);
poolingAsyncClientConnectionManager.setValidateAfterInactivity(TimeValue.ofMinutes(5));
// HttpAsyncClient
closeableHttpAsyncClient = HttpAsyncClients.custom()
.setIOReactorConfig(ioReactorConfig)
.setConnectionManager(poolingAsyncClientConnectionManager)
.build();
// 运行
closeableHttpAsyncClient.start();
}
/**
* 获取 HttpAsyncclient 对象
*
* @return CloseableHttpAsyncClient
*/
public static CloseableHttpAsyncClient getHttpclient() {
return closeableHttpAsyncClient;
}
}
2、基本操作示例
import club.mydlq.utils.HttpClientAsycnUtil;
import lombok.extern.slf4j.Slf4j;
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.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.Method;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@Slf4j
public class BaseExample {
/**
* Http 异步请求
*/
public static void asyncRequest() {
try {
HttpHost httpHost = new HttpHost("http","localhost",8080);
SimpleHttpRequest simpleHttpRequest = SimpleHttpRequests.create(Method.GET,httpHost,"/base/get?name=test&sex=man");
final Future<SimpleHttpResponse> future = HttpClientAsycnUtil.getHttpclient()
.execute(simpleHttpRequest, new FutureCallback<SimpleHttpResponse>() {
@Override
public void completed(final SimpleHttpResponse response) {
log.info(response.toString());
}
@Override
public void failed(final Exception ex) {
log.error("执行请求失败:", ex);
}
@Override
public void cancelled() {
log.info("取消请求");
}
});
String responseBody = future.get().getBody().getBodyText();
log.info(responseBody);
} catch (ExecutionException | InterruptedException e) {
log.error("", e);
Thread.currentThread().interrupt();
}
}
/** 测试 Main 方法 */
public static void main(final String[] args) {
asyncRequest();
}
}
—END—