JDK HttpClient - Java 11 可用的 JDK 内置的 HTTP 客户端

在 Java 应用的开发中,发送 HTTP 请求是一个常见的需求。应用在开发时,通常会使用流行的开源第三方库作为 HTTP 客户端,如 Apache HttpClient 或 OkHttp 等。这里介绍的是 JDK 自带的 HttpClient 实现,Java 11 可用。说到这里,还在用 Java 8 的朋友不用着急划走,Java 8 的下一个 LTS 版本就是 Java 11 了,没准什么时候就升级到 Java 11,提前了解一下也没坏处。

JDK 的 HttpClient 在 jdk.net.http 模块中,该模块提供了 HTTP 客户端和 WebSocket 支持。HttpClient 发送 HTTP 请求并对响应进行处理。

HttpClient 的实例通过构建器(HttpClient.Builder)来创建。在创建 HttpClient 时可以进行配置,配置项包括连接超时时间,认证处理器,代理,Cookie处理器,是否跟随跳转,HTTP 协议版本,SSL 配置等。

下图是 HttpClient.Builder 类中包含的方法。

418aca96a83f37133448013f14758e7c.png

从构建器创建出 HttpClient 之后,可以发送 HTTP 请求。HTTP 请求以 HttpRequest 表示。HttpRequest 也通过构建器(HttpRequest.Builder)来创建。在创建时,可以指定 HTTP 请求的URI,方法,HTTP头,超时时间和请求体的内容。

下图是 HttpRequest.Builder 类中包含的方法。

601ad242ec4b5c78f4a1a43e8b58b5f3.png

与 HttpRequest 对应的是表示 HTTP 响应的 HttpResponse。HttpResponse 并不是直接创建的,而是由 BodyHandler 对 HTTP 响应进行处理了之后得到的。在发送请求时,需要提供 BodyHandler 接口的实现来声明对 HTTP 响应的处理方式。在很多时候,并不需要创建自己的 BodyHandler 实现,BodyHandlers 类中提供了很多常用的实现,比如把响应内容转换成 byte[]、String、InputStream,或是把响应内容保存到本地磁盘。

HttpClient 支持同步和异步两种请求发送模式。

  • 同步请求方式使用的是 send 方法,参数是 HttpRequest 和 BodyHandler,返回值是 HttpResponse<T>。

  • 异步请求方式使用的是sendAsync方法,参数同样是 HttpRequest 和 BodyHandler,返回值是 CompletableFuture<HttpResponse<T>>。

下面通过几个具体的例子来说明 JDK HttpClient 的用法。  使用 HttpClient 时,通常按照下面的步骤:

  1. 从 HttpClient 构建器中创建出 HttpClient 对象。

  2. 从 HttpRequest 构建器中创建出 HttpRequest 对象。

  3. 使用 HttpClient 的 send 或 sendAsync 方法发送请求,提供 HttpRequest  和 BodyHandler,并获取表示响应的 HttpResponse。

  4. 从 HttpResponse 中获取 HTTP 响应。

  5. 对响应内容进行处理。

第一个例子是同步方式发送 GET 请求。使用的是 BodyHandlers 中的 ofString 方法,把响应转换成 String。

public class SyncGet {


  void run() throws IOException, InterruptedException {
    HttpClient httpClient = HttpClient.newBuilder()
        .followRedirects(Redirect.NORMAL)
        .connectTimeout(Duration.ofMinutes(1))
        .version(Version.HTTP_1_1)
        .build();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("http://www.baidu.com"))
        .GET()
        .build();
    HttpResponse<String> response = httpClient.send(request,
        BodyHandlers.ofString());
    System.out.println(response.body());
  }


  public static void main(String[] args)
      throws IOException, InterruptedException {
    new SyncGet().run();
  }
}

第二个例子是同步方式发送 POST 请求。请求的内容以 BodyPublisher 表示,BodyPublishers 中包含了常用的 BodyPublisher 实现,ofString 方法从 String 中创建出 BodyPublisher。

public class SyncPost {


  void run() throws IOException, InterruptedException {
    HttpClient httpClient = HttpClient.newBuilder()
        .followRedirects(Redirect.NORMAL)
        .connectTimeout(Duration.ofMinutes(1))
        .version(Version.HTTP_1_1)
        .build();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://httpbin.org/post"))
        .POST(BodyPublishers.ofString("{\"name\": \"Alex\"}"))
        .build();
    HttpResponse<String> response = httpClient.send(request,
        BodyHandlers.ofString());
    System.out.println(response.body());
  }


  public static void main(String[] args)
      throws IOException, InterruptedException {
    new SyncPost().run();
  }
}

第三个例子是异步方式发送 GET 请求,并把响应保存在本地磁盘。使用的是 BodyHandlers 的 ofFile 方法,把响应保存在指定的文件中。因为 sendAsync 方法的返回值是 CompletableFuture,调用 get 方法等待请求完成并获取响应。

public class AsyncSaveToFile {


  void run() throws ExecutionException, InterruptedException, IOException {
    HttpClient httpClient = HttpClient.newBuilder()
        .followRedirects(Redirect.NORMAL)
        .connectTimeout(Duration.ofMinutes(1))
        .version(Version.HTTP_1_1)
        .build();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("http://www.baidu.com"))
        .GET()
        .build();
    Path savedFile = Files.createTempFile("jdk-http-client-", ".html");
    HttpResponse<Path> response = httpClient.sendAsync(request,
            BodyHandlers.ofFile(savedFile))
        .get();
    System.out.println("Response saved to " + response.body());
  }


  public static void main(String[] args)
      throws ExecutionException, InterruptedException, IOException {
    new AsyncSaveToFile().run();
  }
}

介绍了 JDK 的 HttpClient 之后,最后的一个问题是,什么时候应该使用 JDK HttpClient。JDK 的 HttpClient 最大的优势是开箱即用,并不需要外部依赖,JDK 11 支持了直接以 java 命令运行 Java 源文件,可以用 Java 写脚本。如果需要临时发送 HTTP请求并对响应进行处理,可以写一个 Java 源文件,其中直接使用 JDK HttpClient,再用 java 直接运行即可。使用 JDK HttpClient 可以避免引入外部依赖,减少了应用的部署尺寸和运行时的消耗。

最后展示一个使用 JDK HttpClient 作为脚本的例子,这个脚本的作用是获取百度热搜上的新闻标题。使用 HttpClient 获取到网页的内容,再用正则表达式进行解析。该脚本可以用 java 命令直接运行,输出结果。

该脚本的代码如下所示。

public class ExtractBaiduHotSearchWords {


  List<String> extract() throws IOException, InterruptedException {
    HttpClient httpClient = HttpClient.newBuilder()
        .followRedirects(Redirect.NORMAL)
        .connectTimeout(Duration.ofMinutes(1))
        .version(Version.HTTP_1_1)
        .build();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://top.baidu.com/board"))
        .GET()
        .build();
    HttpResponse<String> response = httpClient.send(request,
        BodyHandlers.ofString());
    Pattern pattern = Pattern.compile(
        "\"word\":\"(.*?)\"",
        Pattern.MULTILINE);
    Matcher matcher = pattern.matcher(response.body());
    List<String> result = new ArrayList<>();
    int count = 0;
    while (matcher.find() && count < 10) {
      result.add(matcher.group(1));
      count++;
    }
    return result;
  }


  public static void main(String[] args)
      throws IOException, InterruptedException {
    new ExtractBaiduHotSearchWords().extract().forEach(System.out::println);
  }
}

运行效果如下图所示。这里直接使用 java 命令运行源文件。

28b1c8312879799e578d6083c7b8b4e0.png

完整的示例代码见 GitHub (alexcheng1982/jdk-http-client-example)。

  • 17
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值