本文档基于 JDK 自带模块 java.net.http(Java 11+,本项目使用 Java 17)编写,
围绕本机运行的 User Management API(http://localhost:8080)进行实战演示。
每个知识点均包含:示例代码、JUnit 测试、文字说明。
运行示例/测试前,请确保 API 服务已在本机 8080 端口启动。
HTTP Client 的基本使用流程只有一步:构造请求并发送。核心 API 有三个:
| 类 | 职责 |
|---|---|
java.net.http.HttpClient |
HTTP 客户端,负责发送请求、管理连接 |
java.net.http.HttpRequest |
请求对象,描述 URI、方法、请求头、请求体 |
java.net.http.HttpResponse<T> |
响应对象,携带状态码、响应头和响应体 |
最小化流程分四步:
HttpClient.newHttpClient() 用 JDK 默认配置创建实例;HttpRequest.newBuilder().uri(...).GET().build() 链式构建;client.send(request, BodyHandlers.ofString()) 同步阻塞发送;HttpResponse 获取 statusCode()、body() 等。注意:
send()会抛出IOException(IO 失败)和InterruptedException(线程被中断), 需要显式处理或向上抛出。
见 QuickStart.java,核心代码如下:
// 1. 创建 HttpClient:newHttpClient() 使用 JDK 默认的配置创建一个客户端
HttpClient httpClient = HttpClient.newHttpClient();
// 2. 构造请求:HttpRequest.newBuilder() 返回一个 Builder,链式配置请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/api/users")) // 设置请求的目标地址
.GET() // 指定请求方法为 GET
.build(); // 结束构建,返回不可变对象
// 3. 发送请求:send() 同步阻塞,BodyHandlers.ofString() 将响应体转为字符串
HttpResponse<String> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// 4. 处理响应
System.out.println("HTTP 状态码: " + response.statusCode());
System.out.println("响应体: " + response.body());
见 QuickStartTest.java。测试会真实调用本地 API,运行后控制台打印响应结果。
(测试目标:GET http://localhost:8080/api/users,正常情况下返回
{"code":200,"message":"OK","data":[...]})
GET 是最常用的 HTTP 方法,用于从服务器获取数据,且不携带请求体。 用 JDK HTTP Client 发送 GET 请求时,有三种常见写法:
.uri(url) + .GET(),例如查询用户列表;/api/users/1;?key=value 跟在 URI 后面,服务端按查询条件过滤。要点总结:
| 要点 | 说明 |
|---|---|
.GET() |
显式声明请求方法;省略时默认也是 GET,但显式写出更清晰 |
| 路径参数 | 直接拼在 URL 中,如 /api/users/{id} |
| 查询参数 | 拼在 ? 之后,多个用 & 连接 |
| 无请求体 | GET 请求使用 BodyPublishers.noBody()(默认),无需设置请求体 |
| 响应码 | 200 找到资源;404 资源不存在 |
见 GetExample.java。核心代码如下:
// 不带参数的 GET:查询所有用户
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/api/users"))
.GET()
.build();
HttpResponse<String> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// 带路径参数的 GET:根据 id 查询单个用户
HttpRequest request2 = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/api/users/" + id))
.GET()
.build();
HttpResponse<String> response2 =
httpClient.send(request2, HttpResponse.BodyHandlers.ofString());
// 带查询字符串的 GET(标准写法演示)
URI uri = URI.create(BASE_URL + "/api/users?account=alice01");
见 GetExampleTest.java,测试点包括:
getUsers:GET /api/users 返回 200,且响应体含 code 包装字段;getUserById_notExists:GET /api/users/999999999 返回 404;buildRequestWithQuery:验证带 ?account=alice01 查询参数的请求
URI 拼接正确,且 GET 请求体为空。POST 用于向服务器提交数据(创建资源),与 GET 最大的区别是携带请求体。 提交 JSON 的标准流程:
ObjectMapper);Content-Type: application/json,告诉服务器请求体格式;BodyPublishers.ofString(json) 把字符串包装为请求体;.POST(publisher) 指定请求方法及请求体;Result<UserVO>,用
TypeReference 反序列化拿到具体数据对象。| 要点 | 说明 |
|---|---|
.POST(BodyPublishers.ofString(json)) |
POST 方法必须携带请求体发布器 |
BodyPublishers.ofString |
将字符串作为请求体;另有 ofInputStream/ofByteArray 等 |
| 状态码 200 | 创建成功(该服务返回 200) |
| 状态码 400 | 请求体格式不正确 / 缺少必填字段 |
| 状态码 409 | 账号已存在,创建冲突 |
注意:为便于演示,本项目引入了 Jackson(
jackson-databind)。 JDK HttpClient 本身不关心请求体是 JSON 还是其他格式,序列化的职责由调用方承担。
见 PostExample.java,核心代码如下:
// 1. 使用 Jackson 把对象序列化为 JSON 字符串
String json = objectMapper.writeValueAsString(user);
// 2. 构造 POST 请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/api/users"))
.header("Content-Type", "application/json") // 声明请求体是 JSON
.POST(BodyPublishers.ofString(json)) // 指定请求体
.build();
// 3. 发送请求
HttpResponse<String> response =
HttpClient.newHttpClient().send(request, BodyHandlers.ofString());
// 4. 反序列化统一包装结构 Result<UserVO>
Result<UserVO> result = objectMapper.readValue(response.body(),
new TypeReference<Result<UserVO>>() { });
配套模型类位于 model/ 包下:UserRequest(请求体)、UserVO(用户响应)、
Result<T>(统一包装)。
见 PostExampleTest.java,测试点包括:
createUser_success:POST 唯一账号返回 200,响应体含 code 与账号;createUserAndGetUser_success:解析出创建后的用户对象,账号与请求一致;createUser_duplicateAccount:相同账号重复创建返回 409。测试使用
System.nanoTime()生成带时间戳的唯一账号,避免与历史数据冲突。