JDK17新特性之--JDK新的HttpClient
前言
JDK版本都升级到20了,我们还在使用JDK8,最近我们准备新项目直接升级到JDK17了,JDK9-JDK17还是有很多新功能的, 最近也在学习相关的新功能,准备写一个系列文章,主要学习JDK9-JDK17升级的新功能,本篇先学习JDK自带的HTTPClient。 新的HTTPClient是在JDK9就有了1,JDK9到JDK17使用方式略有同,我们就按最新的JDK17使用方法来学习好了。
HttpClient初始化
HttpClient的初始化有点像OKHTTP,可以通过version指定HTTP协议版本,通过connectTimeout设置超时时间,通过authenticator设置鉴权,通过proxy设置代理
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(20))
.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("admin", "password".toCharArray());
}
})
.proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80)))
.build();
GET请求
为了方便测试,创建一个简单的GET请求方法,接收@PathVariable、@RequestParam、@RequestHeader、@CookieValue类型参数:
@GetMapping(value = "/user/{id}")
public Map<String, Object> user(@PathVariable Long id,
@RequestParam String userName,
@RequestHeader String userHeader,
HttpServletResponse response,
@CookieValue("JSESSIONID") String cookie) {
response.addHeader("X-USER-ID", id.toString());
response.addHeader("cookie", cookie);
return Map.of("userName", userName, "id", id, "userHeader", userHeader);
}
请求前我们需要构建出一个HttpRequest,设置对应的URL,Header
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://127.0.0.1/user/1?userName=admin"))
.header("userHeader", "myHeader")
.header("cookie", "JSESSIONID=111")
.timeout(Duration.ofSeconds(10))
.GET()
.build();
使用同步方法发送GET请求
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
response.headers().map().forEach((k, v) -> {
System.out.println(k + "-->" + v);
});
System.out.println("同步请求:" + response.body());
使用异步方法发送GET,可以通过exceptionally处理异常
CompletableFuture<Void> voidCompletableFuture = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.exceptionally(err -> {
err.printStackTrace();
return "error" + err.getMessage();
})
.thenAccept(x -> System.out.println("异步结果:" + x));
voidCompletableFuture.join();
POST表单数据
创建一个POST接口,接收表单POST数据
@PostMapping(value = "/user")
public User addUser(User user) {
System.out.println(user.getId() + ":" + user.getUserName());
return user;
}
通过POST提交Key-Value数据
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://127.0.0.1/user"))
.timeout(Duration.ofMinutes(2))
.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
.POST(HttpRequest.BodyPublishers.ofString("id=1&userName=赵云"))
.build();
String body = client.send(request, HttpResponse.BodyHandlers.ofString()).body();
System.out.println("阻塞请求结果:" + body);
POST JONS数据
写一个PostMapping 使用@RequestBody来接收JSON数据
@PostMapping(value = "/user/update")
public User updateUser(@RequestBody User user) {
System.out.println(user.getId() + ":" + user.getUserName());
return user;
}
通过header设置Content-type为"application/json;charset=utf-8",HttpRequest.BodyPublishers.ofString()传JSON数据
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://127.0.0.1/user/update"))
.timeout(Duration.ofMinutes(2))
.header("Content-Type", "application/json;charset=utf-8")
.POST(HttpRequest.BodyPublishers.ofString(
"""
{"id":1,"userName":"赵云"}
"""
))
.build();
String body = client.send(request, HttpResponse.BodyHandlers.ofString()).body();
System.out.println("阻塞请求结果:" + body);
上传文件
创建一个接口,模拟一个用户设置头像,接口接收用户ID和头像文件:
@PostMapping(value = "/upload")
public Long updateUser(@RequestParam Long userId, @RequestParam("img") MultipartFile img) throws IOException {
System.out.println("userId:"+userId+" upload img :"+img.getName());
img.transferTo(new File("D://1.jpg"));
return img.getSize();
}
HTTPClient上传文件不是很方便,需要引用httpmime 构建一个MultipartEntity,大图片会报ContentTooLongException: Content length is too long:2,目前没找到比较好的方法来实现文件上传
//引入httpmime
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version>
<scope>test</scope>
</dependency>
//要上传的图片
Path sourcePath = Path.of("D:\\timg.jpg");
String multipartFormDataBoundary = "Java11HttpClientFormBoundary";
//构建MultipartEntity
HttpEntity httpEntity = MultipartEntityBuilder
.create()
.addBinaryBody("img", sourcePath.toFile(), ContentType.IMAGE_JPEG, "1.png")
.setBoundary(multipartFormDataBoundary)
.build();
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://127.0.0.1/upload?userId=1"))
.timeout(Duration.ofMinutes(2))
.header("Content-Type", "multipart/form-data; boundary=" + multipartFormDataBoundary)
.POST(HttpRequest.BodyPublishers.ofInputStream(() -> {
try {
return httpEntity.getContent();
} catch (IOException e) {
throw new RuntimeException(e);
}
}))
.build();
String body = client.send(request, HttpResponse.BodyHandlers.ofString()).body();
System.out.println("阻塞请求结果:" + body);
下载文件
通过CompletableFuture异步下载图片:
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://mc-public.zjol.com.cn/10529082382412_hd.mp4"))
.timeout(Duration.ofMinutes(2))
.build();
CompletableFuture<Path> result = client.sendAsync(request, HttpResponse.BodyHandlers.ofFile(Paths.get("D://1.mp4"))).thenApply(HttpResponse::body);
System.out.println(result.get());
并发请求
通过CompletableFuture合并请求
HttpClient client = HttpClient.newHttpClient();
List<HttpRequest> requests = IntStream.range(1, 10)
.mapToObj(x -> String.format("http://127.0.0.1/user/%s", x))
.map(url -> HttpRequest.newBuilder(URI.create(url)))
.map(HttpRequest.Builder::build)
.collect(Collectors.toList());
List<CompletableFuture<HttpResponse<String>>> futures = requests.stream()
.map(request -> client.sendAsync(request, HttpResponse.BodyHandlers.ofString()))
.collect(Collectors.toList());
futures.forEach(e -> e.whenComplete((resp, err) -> {
if (err != null) {
err.printStackTrace();
} else {
System.out.println(resp.body());
System.out.println(resp.statusCode());
}
}));
CompletableFuture.allOf(futures.toArray(CompletableFuture<?>[]::new)).join();
websocket长连接
使用HttpClient创建WebSocket实时通信
HttpClient client = HttpClient.newHttpClient();
WebSocket webSocket = client.newWebSocketBuilder()
.buildAsync(URI.create("ws://localhost:8080/hello"), new WebSocket.Listener() {
@Override
public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
webSocket.request(1);
return CompletableFuture.completedFuture(data).thenAccept(System.out::println);
}
}).join();
webSocket.sendText("hello ", false);
webSocket.sendText("world ", true);
TimeUnit.SECONDS.sleep(10);
webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "ok").join();
参考文章
Footnotes
转载自:https://juejin.cn/post/7229625586150735928