IMLC.ME

Java JDK11 Http Client

JEP 321: HTTP Client 在 JDK 11 正式交付。 新的 HTTP Client 代替了传统的贼难用的 HttpUrlConnection 类成为 Java 内建的 HTTP 客户端。

HttpUrlConnection 已经服役多年,略显老态。

  • HttpUrlConnection 支持 FTP, gopher 等比较有年代感的协议,目前业界已经很少使用(以 HttpUrlConnection 的方式)。
  • 实现该类的时候,HTTP/1.1 还没定型。API 接口设计过于抽象,对开发者并不友好。
  • 不适合现在万物皆异步的时代趋势
  • 不支持新出现的协议,例如 HTTP/2、WebSocket、HTTP/2、QUIC、Server-Sent Event 等。

新的 HTTP Client 提供了新的特性,例如:

  • 同时支持同步&异步编程
  • 支持 HTTP/2 协议、WebSocket 等新的互联网协议

同步请求

HttpClient httpClient = HttpClient.newBuilder().build();

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://nghttp2.org/httpbin/get"))
    .version(Version.HTTP_2)
    .GET()
    .build();

HttpResponse<String> resp = httpClient.send(req, BodyHandlers.ofString());

System.out.printf("Status: %s\nBody: %s%n", resp.statusCode(), resp.body());

异步请求

HttpRequest req = HttpRequest.newBuilder()
        .uri(URI.create("https://nghttp2.org/httpbin/get"))
        .version(Version.HTTP_2)
        .GET()
        .build();

CompletableFuture<HttpResponse<String>> response = httpClient.sendAsync(
    req, BodyHandlers.ofString());

response.thenAcceptAsync((resp) -> {
      System.out.printf("Status: %s\nBody: %s%n", resp.statusCode(), resp.body());
    })
    .get();

HTTP GET/POST/DELETE/PUT/PATCH

HTTP GET
HttpRequest.newBuilder()
        .uri(URI.create("https://nghttp2.org/httpbin/get"))
        .GET()
        .build();
HTTP POST
HttpRequest req = HttpRequest.newBuilder()
        .uri(URI.create("https://httpbin.org/anything"))
        .POST(HttpRequest.BodyPublishers.ofString("""
        {
          "message": "Hello, world!"
        }
        """
        ))
        .build();

总结

新的 API 确实好用了很多。但是又没有真的很符合直观。
指定 url 的时候只有 HttpRequest.newBuilder().uri() 可用,开发者必须自己 URI.create("https://nghttp2.org/httpbin/get")。 就用户体验来说,最常用的传入字符串的操作反而不支持,令人迷惑。

另外,不管同步还是异步,httpclient 都要求传入 BodyHandler 作为第二入参。

跟 Stream API 的 collect() 一个风格,开发者此处可以通过 BodyHandlers.ofXXX() 方法获取 Java 自带的 BodyHandlers。

BodyHandler 负责将 HTTP 响应转换成想要的形式。 例如 BodyHandlers.ofString()、BodyHandlers.ofFile()、BodyHandlers.ofLines() 和 BodyHandlers.ofByteArray() 等等。

这确实极大地方便开发者。但是把这个功能抽象成 send() 方法的第二参数,真是一股 Java 的老派味道😂。

另外,现代的 http client 提供的特性在 jdk 自带的 http client 上也看不到。
例如 --http2-prior-knowledge 节省协议协商的过程、HTTP/2 Cleartext 似乎也不支持。