JDK 11 之JEP 321 HTTP Client(Standard)

1. 摘要

JEP 321 标准化了JDK 9引入孵化的HTTP Client(JEP 110)

2. 动机

JDK8中的HttpURLConnectionAPI及其实现存在许多问题:

  • URLConnection API是设计时考虑了多种协议,几乎所有这些都是现在已经不存在(ftp,gopher,等)。
  • API早于HTTP / 1.1并且过于抽象。
  • 难于使用,很多没有文档化的行为
  • 仅能在阻塞模式下工作
  • 难于维护

3. 目标

HTTP Client的目标

  • API必须是易于使用的,包括简单的阻塞模式
  • 必须支持通知机制如HTTP消息头收到、错误码、HTTP消息体收到
  • 简洁的API能够支持80-90%的需求
  • 必须支持标准和通用身份验证机制
  • 必须能够轻松使用WebSocket
  • 必须支持HTTP 2
  • 必须执行与现有网络API一致的安全检查
  • 必须对lambda表达式等新语言功能很友好
  • 应该对嵌入式系统友好,避免永久运行的后台线程
  • 必须支持HTTPS / TLS
  • 满足HTTP 1.1和HTTP 2的性能要求

4. 使用

4.1 Get

	private static void testGet() {
		var httpClient = HttpClient.newBuilder().build();
		var request = HttpRequest.newBuilder().uri(URI.create("http://www.taobao.com")).build();
		try {
			var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
	        System.out.println(response.body());
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

4.2 连接超时和socke read超时

		var httpClient = HttpClient.newBuilder()
				.connectTimeout(Duration.ofMillis(3000))
				.build();
		var request = HttpRequest.newBuilder()
				.timeout(Duration.ofMillis(3000))
				.uri(URI.create("http://www.taobao.com"))
				.build();
		try {
			var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
	        System.out.println(response.body());
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

连接超时后,抛出java.net.http.HttpConnectTimeoutException: HTTP connect timed out
Socket读取超时后,抛出java.net.http.HttpTimeoutException: request timed out

4.3 设置HTTP header

		var httpClient = HttpClient.newBuilder()
				.connectTimeout(Duration.ofMillis(3000))
				.build();
		var request = HttpRequest.newBuilder()
				.timeout(Duration.ofMillis(3000))
				.header("key1", "v1")
				.header("key2", "v2")
				.uri(URI.create("http://www.taobao.com"))
				.build();
		try {
			var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
	        System.out.println(response.body());
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

4.4 异步Get

        var client = HttpClient.newHttpClient();
        var request = HttpRequest.newBuilder()
                .uri(URI.create("http://www.taobao.com"))
                .build();

        var result = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
        		.thenApply(HttpResponse::body);
        try {
			System.out.println(result.get());
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}

异步请求通过CompletableFuture实现。

4.5 POST

		String json = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
        var httpClient = HttpClient.newHttpClient();
        var request = HttpRequest.newBuilder()
                .uri(URI.create("http://www.tabao.com"))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(json))
                .build();

        try {
			var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
	        System.out.println(response.body());
		} catch (IOException | InterruptedException e) {
			e.printStackTrace();
		}

4.6 HTTP2

		var httpClient = HttpClient.newBuilder()
				.connectTimeout(Duration.ofMillis(3000))
				.version(HttpClient.Version.HTTP_2)
				.build();
		var request = HttpRequest.newBuilder()
				.timeout(Duration.ofMillis(3000))
				.header("key1", "v1")
				.header("key2", "v2")
				.uri(URI.create("https://www.google.com"))
				.build();
		try {
			var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
	        System.out.println(response.body());
	        System.out.println(response.version());
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

注意,由于HTTP 2协议的强制要求,如果目标URI是HTTP的,则无法使用HTTP 2协议。

4.7 异常处理

        var client = HttpClient.newHttpClient();
        var request = HttpRequest.newBuilder()
                .uri(URI.create("http://127.0.0.1"))
                .build();

        var result = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
        		.thenApply(HttpResponse::body)
        		.exceptionally(e -> {
                    e.printStackTrace();
                    return "err: " + e.getMessage();
                });;
        try {
			System.out.println(result.get());
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}

exceptionally方法的入参是Function<T,R>

5. 结论

JDK 11中将HTTP Client从孵化版本升级为正式版本,正常情况下不再需要引入三方http client包。但是仍然没有对文件上传的原生支持,上传文件时仍然需要apache http client等三方包协助。

6. 引用

http://openjdk.java.net/jeps/321

猜你喜欢

转载自blog.csdn.net/a860MHz/article/details/88398927