1. 概述
在本文中,我们将通过 HttpClient 5 演示如何发起 POST 请求,包括带认证的请求、JSON 数据提交、使用 Fluent API、上传文件以及监控上传进度等内容。
HttpClient 是 Java 中非常流行的 HTTP 客户端库,广泛用于构建 REST 客户端、调用远程服务等场景。我们将从基础用法讲起,逐步深入到文件上传及进度监控等高级用法。
2. 基础 POST 请求
我们先来看一个最简单的 POST 请求示例。使用 NameValuePair
来构造表单参数,然后通过 UrlEncodedFormEntity
设置请求体。
@Test
void whenSendPostRequestUsingHttpClient_thenCorrect() throws IOException {
final HttpPost httpPost = new HttpPost(SAMPLE_URL);
final List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("username", DEFAULT_USER));
params.add(new BasicNameValuePair("password", DEFAULT_PASS));
httpPost.setEntity(new UrlEncodedFormEntity(params));
try (CloseableHttpClient client = HttpClients.createDefault();
CloseableHttpResponse response = (CloseableHttpResponse) client
.execute(httpPost, new CustomHttpClientResponseHandler())) {
final int statusCode = response.getCode();
assertThat(statusCode, equalTo(HttpStatus.SC_OK));
}
}
✅ 关键点:
- 使用
NameValuePair
构建参数列表 - 使用
UrlEncodedFormEntity
设置请求体 - 推荐使用 try-with-resources 管理资源
3. 带认证的 POST 请求
接下来演示如何向需要 Basic 认证的接口发起 POST 请求。
我们通过 BasicCredentialsProvider
设置用户名和密码,然后将其绑定到 HttpClient 实例上:
@Test
void whenSendPostRequestWithAuthorizationUsingHttpClient_thenCorrect() throws IOException {
final HttpPost httpPost = new HttpPost(URL_SECURED_BY_BASIC_AUTHENTICATION);
httpPost.setEntity(new StringEntity("test post"));
final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
final UsernamePasswordCredentials credentials =
new UsernamePasswordCredentials(DEFAULT_USER, DEFAULT_PASS.toCharArray());
credsProvider.setCredentials(new AuthScope(URL_SECURED_BY_BASIC_AUTHENTICATION, 80), credentials);
try (CloseableHttpClient client = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.build();
CloseableHttpResponse response = (CloseableHttpResponse) client
.execute(httpPost, new CustomHttpClientResponseHandler())) {
final int statusCode = response.getCode();
assertThat(statusCode, equalTo(HttpStatus.SC_OK));
}
}
✅ 关键点:
- 使用
UsernamePasswordCredentials
设置账号密码 - 使用
AuthScope
指定认证作用域 - 使用
BasicCredentialsProvider
注入认证信息
4. 发送 JSON 数据的 POST 请求
在 RESTful 接口中,我们通常需要发送 JSON 格式的请求体。HttpClient 也支持直接发送 JSON。
我们使用 StringEntity
构造 JSON 字符串,并设置 Content-Type
为 application/json
:
@Test
void whenPostJsonUsingHttpClient_thenCorrect() throws IOException {
final HttpPost httpPost = new HttpPost(SAMPLE_URL);
final String json = "{\"id\":1,\"name\":\"John\"}";
final StringEntity entity = new StringEntity(json);
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Content-type", "application/json");
try (CloseableHttpClient client = HttpClients.createDefault();
CloseableHttpResponse response = (CloseableHttpResponse) client
.execute(httpPost, new CustomHttpClientResponseHandler())) {
final int statusCode = response.getCode();
assertThat(statusCode, equalTo(HttpStatus.SC_OK));
}
}
✅ 关键点:
- 使用
StringEntity
构建 JSON 请求体 - 显式设置
Content-Type
和Accept
请求头 - 注意 JSON 字符串格式正确性,避免语法错误
5. 使用 HttpClient Fluent API 发起 POST 请求
HttpClient 提供了一个 Fluent API,语法更简洁,适合快速构建请求。
以下是一个使用 Fluent API 提交表单的示例:
@Test
void whenPostFormUsingHttpClientFluentAPI_thenCorrect() throws IOException {
Request request = Request.post(SAMPLE_URL)
.bodyForm(Form.form()
.add("username", DEFAULT_USER)
.add("password", DEFAULT_PASS)
.build());
HttpResponse response = request.execute()
.returnResponse();
assertThat(response.getCode(), equalTo(HttpStatus.SC_OK));
}
✅ 关键点:
- 使用
Request.post()
快速构建 POST 请求 - 使用
Form.form()
构建表单数据 - 链式调用更直观,适合简单场景
6. 发起 Multipart POST 请求
上传文件通常使用 multipart/form-data
格式。HttpClient 提供了 MultipartEntityBuilder
来构造这类请求。
下面演示如何上传一个文件,并携带用户名和密码字段:
@Test
void whenSendMultipartRequestUsingHttpClient_thenCorrect() throws IOException {
final HttpPost httpPost = new HttpPost(SAMPLE_URL);
final MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("username", DEFAULT_USER);
builder.addTextBody("password", DEFAULT_PASS);
builder.addBinaryBody(
"file", new File("src/test/resources/test.in"), ContentType.APPLICATION_OCTET_STREAM, "file.ext");
final HttpEntity multipart = builder.build();
httpPost.setEntity(multipart);
try (CloseableHttpClient client = HttpClients.createDefault();
CloseableHttpResponse response = (CloseableHttpResponse) client
.execute(httpPost, new CustomHttpClientResponseHandler())) {
final int statusCode = response.getCode();
assertThat(statusCode, equalTo(HttpStatus.SC_OK));
}
}
✅ 关键点:
- 使用
MultipartEntityBuilder
构建 multipart 请求体 addTextBody()
添加文本字段addBinaryBody()
添加二进制文件
7. 使用 HttpClient 上传文件
如果我们只想上传一个文件,可以省略其他字段,仅添加文件部分:
@Test
void whenUploadFileUsingHttpClient_thenCorrect() throws IOException {
final HttpPost httpPost = new HttpPost(SAMPLE_URL);
final MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addBinaryBody(
"file", new File("src/test/resources/test.in"), ContentType.APPLICATION_OCTET_STREAM, "file.ext");
final HttpEntity multipart = builder.build();
httpPost.setEntity(multipart);
try (CloseableHttpClient client = HttpClients.createDefault();
CloseableHttpResponse response = (CloseableHttpResponse) client
.execute(httpPost, new CustomHttpClientResponseHandler())) {
final int statusCode = response.getCode();
assertThat(statusCode, equalTo(HttpStatus.SC_OK));
}
}
✅ 关键点:
- 只上传文件时,可以不添加其他文本字段
- 文件名和内容类型需明确指定
8. 监控文件上传进度
有时候我们需要知道上传的进度,例如在上传大文件时显示进度条。HttpClient 本身不直接支持进度监控,但可以通过扩展 HttpEntityWrapper
实现。
以下是完整的进度监控实现:
8.1 定义监听器接口
public static interface ProgressListener {
void progress(float percentage);
}
8.2 扩展 HttpEntityWrapper
public class ProgressEntityWrapper extends HttpEntityWrapper {
private ProgressListener listener;
public ProgressEntityWrapper(HttpEntity entity, ProgressListener listener) {
super(entity);
this.listener = listener;
}
@Override
public void writeTo(OutputStream outstream) throws IOException {
super.writeTo(new CountingOutputStream(outstream, listener, getContentLength()));
}
}
8.3 扩展 FilterOutputStream
实现计数
public static class CountingOutputStream extends FilterOutputStream {
private ProgressListener listener;
private long transferred;
private long totalBytes;
public CountingOutputStream(OutputStream out, ProgressListener listener, long totalBytes) {
super(out);
this.listener = listener;
transferred = 0;
this.totalBytes = totalBytes;
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
transferred += len;
listener.progress(getCurrentProgress());
}
@Override
public void write(int b) throws IOException {
out.write(b);
transferred++;
listener.progress(getCurrentProgress());
}
private float getCurrentProgress() {
return ((float) transferred / totalBytes) * 100;
}
}
8.4 使用示例
@Test
void whenGetUploadFileProgressUsingHttpClient_thenCorrect() throws IOException {
final HttpPost httpPost = new HttpPost(SAMPLE_URL);
final MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addBinaryBody(
"file", new File("src/test/resources/test.in"), ContentType.APPLICATION_OCTET_STREAM, "file.ext");
final HttpEntity multipart = builder.build();
final ProgressEntityWrapper.ProgressListener pListener =
percentage -> assertFalse(Float.compare(percentage, 100) > 0);
httpPost.setEntity(new ProgressEntityWrapper(multipart, pListener));
try (CloseableHttpClient client = HttpClients.createDefault();
CloseableHttpResponse response = (CloseableHttpResponse) client
.execute(httpPost, new CustomHttpClientResponseHandler())) {
final int statusCode = response.getCode();
assertThat(statusCode, equalTo(HttpStatus.SC_OK));
}
}
✅ 关键点:
- 通过包装
HttpEntity
实现上传进度监听 - 继承
FilterOutputStream
跟踪已写入字节数 - 使用回调接口
ProgressListener
返回上传进度
9. 小结
本文通过多个示例详细讲解了如何使用 Apache HttpClient 5 发起 POST 请求,包括:
功能 | 示例说明 |
---|---|
基础 POST | 使用 NameValuePair 提交表单 |
带认证的 POST | 使用 BasicCredentialsProvider 设置认证信息 |
JSON 请求 | 使用 StringEntity 构造 JSON 请求体 |
Fluent API | 使用链式调用简化请求构建 |
Multipart 请求 | 使用 MultipartEntityBuilder 提交文件和文本字段 |
文件上传 | 仅上传文件的简化方式 |
上传进度监控 | 扩展 HttpEntityWrapper 和 FilterOutputStream 实现实时监控 |
所有示例代码均可在 GitHub 项目 中找到。
✅ 踩坑提醒:
- JSON 请求中注意
Content-Type
设置 - 文件上传时要指定文件名和 MIME 类型
- 使用 try-with-resources 管理资源,避免内存泄漏
- 上传大文件时建议使用进度监控,提升用户体验
如需进一步封装,可结合 Spring 或其他框架简化调用逻辑。