网络编程Java 11标准化的HTTP Client
一·背景描述
从jdk9开始引入HTTP Client 标准化,根据用户的反馈在jdk10开始更新,有了显著的改进,使用方式基本保持不变。通过CompletableFutures提供了非阻塞请求和响应式。流量控制可以在java.util.concurrent.Flow API 提供支持。
在jdk9和jdk10时进行时几乎完全重写了实现,实现了完全异步,以前的http1.1实现是阻塞的,RX Flow概念的使用已经实现,现在可以更容易的跟踪数据流:从用户请求发布者和响应订阅者,一直到底层套接字。显著的减少了代码的复杂性,并最大化了HTTP/1.1和HTTP/2之间重用的可能性。
二·demo
1·Synchronous Get :
(1)Response body as a String
public void get(String uri) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
上面的实例使用HttpResponse.BodyHandlers.ofString() 将响应字节转换为字符串,我们必须为每一个HttpRequest提供一个HttpResponse.BodyHandler,一旦response的的头和状态码可用就会在收到response字节之前调用BodyHandlers,BodyHandler负责创建BodySubscriber,它是一个响应流的订阅者,BodySubscriber负责将接受到的字节转换为更高级的java类型。
(2)Response body as a File
public void getToFile(String uri) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.build();
HttpResponse<Path> response =
client.send(request, HttpResponse.BodyHandlers.ofFile(Paths.get("body.txt")));
System.out.println("Response in file:" + response.body());
}
HttpResponse为创建BodyHandler提供了很多方便的静态方法,有一些响应式的字节会在内存中一直累计,直到完全接收,然后将其转换为更高级的java类型,例如HttpResponse.BodyHandlers.ofByteArray()和HttpResponse.BodyHandlers.ofString(),另一些则在完全接收数据后HttpResponse.BodyHandlers.ofFile(),HttpResponse.BodyHandlers.ofByteArrayConsumer(),HttpResponse.BodyHandlers.ofInputStream(),当然你也可以自定义处理方式,如 转换为JSON对象。
2·Asynchronous Get
异步的api理解返回一个CompletableFuture,当HttpResponse接收完时可以使用它,在java8时开始支持,并支持异步编程。
(1)Response body as a String
public CompletableFuture<String> getCompletableFuture(String uri) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.build();
return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body);
}
CompletableFuture.thenApply(Function)方法可以将HttpResponse映射到它的实体类型,状态码等。
(2)Response body as a File
public CompletableFuture<Path> getCompletableFuturePath(String uri) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.build();
return client.sendAsync(request, HttpResponse.BodyHandlers.ofFile(Paths.get("body.txt")))
.thenApply(HttpResponse::body);
}
3·Post
请求的数据由HttpRequest.BodyPublisher提供。
public void post(String uri, String data) throws Exception {
HttpClient client = HttpClient.newBuilder().build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.POST(HttpRequest.BodyPublishers.ofString(data))
.build();
HttpResponse<?> response = client.send(request, HttpResponse.BodyHandlers.discarding());
System.out.println(response.statusCode());
}
上面的示例,通过使用BodyPublisher.fromString将字符串转为请求需要的字节。BodyPublisher是一个响应流的发布者,HttpRequest.Builder 支持 POST PUT 等方法。
4·Concurrent Requests
我们很容易结合 Java Streams 和CompletableFuture 来并发请求,并等待他们的响应结果,下面的示例为list中每个uri发送请求,并将请求转换为字符串。
public void testConcurrentRequests(){
HttpClient client = HttpClient.newHttpClient();
List<String> urls = List.of("http://www.baidu.com","http://www.alibaba.com/","http://www.tencent.com");
List<HttpRequest> requests = urls.stream()
.map(url -> HttpRequest.newBuilder(URI.create(url)))
.map(reqBuilder -> reqBuilder.build())
.collect(Collectors.toList());
List<CompletableFuture<HttpResponse<String>>> futures = requests.stream()
.map(request -> client.sendAsync(request, HttpResponse.BodyHandlers.ofString()))
.collect(Collectors.toList());
futures.stream()
.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();
}
5·Get JSON
很多情况下,响应结果 是更高级的格式(json),可以使用一些第三方JSON工具类把响应结果做转换。
public CompletableFuture<Map<String,String>> JSONBodyAsMap(URI uri) {
UncheckedObjectMapper objectMapper = new UncheckedObjectMapper();
HttpRequest request = HttpRequest.newBuilder(uri)
.header("Accept", "application/json")
.build();
return HttpClient.newHttpClient()
.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenApply(objectMapper::readValue);
}
class UncheckedObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper {
/** Parses the given JSON string into a Map. */
Map<String,String> readValue(String content) {
try {
return this.readValue(content, new TypeReference<>(){});
} catch (IOException ioe) {
throw new CompletionException(ioe);
}
}
6·Post JSON
很多情况下我们提交的请求提是JSON格式,我们通过使用BodyPublisher::fromString + 第三方JSON工具 将请求数据key/value 映射为JSON.
public CompletableFuture<Void> postJSON(URI uri, Map<String, String> map)
throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
String requestBody = objectMapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(map);
HttpRequest request = HttpRequest.newBuilder(uri)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
return HttpClient.newHttpClient()
.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::statusCode)
.thenAccept(System.out::println);
}
7·Setting a Proxy
我们可以通过ProxySelector在HttpClient配置一个proxy。
public CompletableFuture<String> get(String uri) {
HttpClient client = HttpClient.newBuilder()
.proxy(ProxySelector.of(new InetSocketAddress("www-proxy.com", 8080)))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.build();
return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body);
}
也可以使用系统默认的ip代理
HttpClient.newBuilder()
.proxy(ProxySelector.getDefault())
.build();
- 点赞
- 收藏
- 关注作者
评论(0)