【JDK11】 JEP321: HTTP Client (Standard) 详解 + demo

一·背景描述

从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 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 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 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 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 urls = List.of("http://www.baidu.com","http://www.alibaba.com/","http://www.tencent.com");
        List requests = urls.stream()
                .map(url -> HttpRequest.newBuilder(URI.create(url)))
                .map(reqBuilder -> reqBuilder.build())
                .collect(Collectors.toList());

        List>> 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> 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 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 postJSON(URI uri, Map 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 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();

你可能感兴趣的:(Java后端)