Java9之HttpClientAPI实战详解

Java9 之 HttpClientAPI 实战详解

前言

相信关注 java9 的小伙伴们都知道 java9 版本内置模块提供了 Http 功能,当然并不是说之前 jdk 之前并不支持,那么这次更新又多了什么呢?或者是解决了什么问题?

说明

自 JDK 1.0 以来,Java 已经支持 HTTP/1.1。 HTTP API 由 java.net 包中的几种类型组成。 现有的 API 有以下问题:

  • 它被设计为支持多个协议,如 http,ftp,gopher 等,其中许多协议不再被使用。
  • 太抽象了,很难使用。
  • 它包含许多未公开的行为。
  • 它只支持一种模式,阻塞模式,这要求每个请求 / 响应有一个单独的线程。

2015 年 5 月,IETF(Internet Engineering Task Force)发布了 HTTP/2 规范。 有关 HTTP/2 规范的完整文本,请访问 https://tools.ietf.org/html/rfc7540。 HTTP/2 不会修改应用程序级语义。 也就是说,对应用程序中的 HTTP 协议的了解和使用情况并没有改变。 它具有更有效的方式准备数据包,然后发送到客户端和服务器之间的电线。 所有之前知道的 HTTP,如 HTTP 头,方法,状态码,URL 等都保持不变。 HTTP/2 尝试解决与 HTTP/1 连接所面临的许多性能相关的问题:

  • HTTP/2 支持二进制数据交换,来代替 HTTP/1.1 支持的文本数据。
  • HTTP/2 支持多路复用和并发,这意味着多个数据交换可以同时发生在 TCP 连接的两个方向上,而对请求的响应可以按顺序接收。 这消除了在对等体之间具有多个连接的开销,这在使用 HTTP/1.1 时通常是这种情况。 在 HTTP/1.1 中,必须按照发送请求的顺序接收响应,这称为 head-of-line 阻塞。 HTTP/2 通过在同一 TCP 连接上进行复用来解决线路阻塞问题。
  • 客户端可以建议请求的优先级,服务器可以在对响应进行优先级排序时予以遵守。
  • HTTP 首部(header)被压缩,这大大降低了首部大小,从而降低了延迟。
  • 它允许从服务器到客户端的资源推送。

JDK 9 不是更新现有的 HTTP/1.1 API,而是提供了一个支持 HTTP/1.1 和 HTTP/2 的 HTTP/2 Client API。 该 API 旨在最终取代旧的 API。 新 API 还包含使用 WebSocket 协议开发客户端应用程序的类和接口。 有关完整的 WebSocket 协议规范,请访问 https://tools.ietf.org/html/rfc6455。新的 HTTP/2 客户端 API 与现有的 API 相比有以下几个好处:

  • 在大多数常见情况下,学习和使用简单易用。
  • 它提供基于事件的通知。 例如,当收到首部信息,收到正文并发生错误时,会生成通知。
  • 它支持服务器推送,这允许服务器将资源推送到客户端,而客户端不需要明确的请求。 它使得与服务器的 WebSocket 通信设置变得简单。
  • 它支持 HTTP/2 和 HTTPS/TLS 协议。
  • 它同时工作在同步(阻塞模式)和异步(非阻塞模式)模式。

如果想使用 Java9 的 HttpClient 服务,那么你必须熟悉(jdk.incubator.http)包中的以下三个类:

HttpClient http 客户端

该类是 Java9 开始引入的,官方文档的翻译说明是这样的

  • HttpClient 是一个对多个请求配置了公共信息的容器。所有的请求通过一个 HttpClient 进行发送。HttpClients 是不可变的,通过 HttpClient 的 newBuilder () 创建返回。请求 Builders 被 HttpRequest#newBuilder () 来创建。
  • 接口 API

API 中 Builder 部分用来构建客户端的配置,send 相关的几个方法是进行请求发送,不同的是 Async 是异步操作。其他的基本是客户端的参数配置信息 (包括代理,线程,版本,SSL,cookie 等),同时也提供了 socket 支持。

  • 使用示例

    • 示例 1,使用默认配置

    HttpClient client = HttpClient.newHttpClient();

    • 示例 2, 自定义配置。

    try {
    Authenticator authenticator=new Authenticator() {
    };
    client= HttpClient.newBuilder()
    .authenticator(authenticator)//配置authenticator
    .sslContext(SSLContext.getDefault())//配置 sslContext
    .sslParameters(new SSLParameters())//配置 sslParameters
    .proxy(ProxySelector.getDefault())//配置 proxy
    .executor(Executors.newCachedThreadPool())//配置 executor
    .followRedirects(HttpClient.Redirect.ALWAYS)//配置 followRedirects
    .cookieManager(new CookieManager())//配置 cookieManager
    .version(HttpClient.Version.HTTP_2)//配置 version
    .build();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    
  • 说明

    由于 HttpClient 隶属于 jdk.incubator.httpclient,所以使用的时候需要添加模块依赖方可执行。 如果你是单个 class,没有引入模块概念的话需要在 VM 参数中添加模块支持 --add-modules jdk.incubator.httpclient。如果你引入了模块的概念,需要在 你的 module.info 中添加 requires jdk.incubator.httpclient; 依赖。

HttpRequest 请求

  • API 文档说明

表示可以发送到服务器的一个 HTTP 请求。 HttpRequest 由 HttpRequest builders 构建生成。 HttpRequest 通过调用 HttpRequest.newBuilder 获得实例。 一个请求的 URI ,head 和 body 都可以设置。 请求体提供了 HttpRequest.BodyProcessor 对象的 DELETE , POST 或 PUT 方法。 GET 不用设置 body。 一旦所有必需的参数都在构建器设置, HttpRequest.Builder.build () 将返回一个 HttpRequest 实例 。 构建器也可以被多次复制和修改,以构建参数不同的多个相关请求。

  • 使用示例

    • 示例 1,GET 请求

    HttpResponse<String> response = client.send(
    HttpRequest
    .newBuilder(new URI(“http://www.foo.com/”))
    .headers(“Foo”, “foovalue”, “Bar”, “barvalue”)
    .GET()
    .build(),
    BodyHandler.asString()
    );
    int statusCode = response.statusCode();
    String body = response.body();

    • 示例 2,POST 请求。

    HttpResponse response = client.send(
    HttpRequest
    .newBuilder(new URI(“http://www.foo.com/”))
    .headers(“Foo”, “foovalue”, “Bar”, “barvalue”)
    .POST(BodyProcessor.fromString(“Hello world”))
    .build(),
    BodyHandler.asFile(Paths.get(“/path”))
    );
    int statusCode = response.statusCode();
    Path body = response.body(); // should be “/path”
    }

HttpResponse 响应

  • API 文档说明

表示 HttpRequest 的响应。 通常在响应正文,响应状态代码和 headers 被接收之后,HttpResponse 才是可用的。 这取决于发送请求时提供的响应体处理程序。 在所有情况下,在 Body 被读取之前调用 response body handler 程序。 此类中提供了访问响应头和响应主体的方法。

响应处理程序和处理器

Response bodies 有两种处理方式。 应用程序代码提供响应处理程序( HttpResponse.BodyHandler ), 一个是可以检查响应状态代码和头文件, 一个是返回一个 HttpResponse.BodyProcessor 以实际读取(或丢弃)正文并将其转换为一些有用的 Java 对象类型。 处理程序可以返回预定义的处理器类型之一或定制处理器, 如果正文被丢弃,则可以调用 BodyProcessor.discard () 并返回丢弃响应正文的处理器。 处理器和处理器的静态实现分别在 BodyHandler 和 BodyProcessor 中提供。 在所有情况下,提供的处理程序功能都是方便的实现, 它忽略了提供的状态代码和头文件,并返回相关的预定义的 BodyProcessor 。

  • 使用示例

    • 示例 1,BodyHandler

    HttpResponse resp = HttpRequest
    .create(URI.create(“http://www.foo.com”))
    .GET()
    .response(BodyHandler.asFile(Paths.get(“/tmp/f”)));
    }

    • 示例 2,BodyProcessor。

    HttpResponse resp1 = HttpRequest
    .create(URI.create(“http://www.foo.com”))
    .GET()
    .response(
    (status, headers) -> status == 200
    ? BodyProcessor.asFile(Paths.get(“/tmp/f”))
    : BodyProcessor.discard(Paths.get(“/NULL”)));
    }

实战应用

这里是一个完整的示例应用,涵盖了 HttpClient,httpRequest,HttpResponse 等的使用。

package com.javanine.http;

import jdk.incubator.http.HttpClient;
import jdk.incubator.http.HttpRequest;
import jdk.incubator.http.HttpResponse;
import jdk.incubator.http.MultiMapResult;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;

/**
 * Created by bgt on 2017/10/1.
 * Java9http示例
 * <p>
 * VM 参数中添加模块支持
 * --add-modules jdk.incubator.httpclient
 */
public class HttpDemo {
   // private static HttpClient client = HttpClient.newHttpClient();
    private static HttpClient client = null;

    static {
    //这里是httpclient的配置
      init();


    }

    public static void main(String[] args) {
       // HttpDemo.HttpGet();
        HttpDemo.HttpGet2();
    }

    public static void init(){
        try {
            Authenticator authenticator=new Authenticator() {
            };
        client= HttpClient.newBuilder()
                .authenticator(authenticator)//配置authenticator
                .sslContext(SSLContext.getDefault())//配置 sslContext
                .sslParameters(new SSLParameters())//配置 sslParameters
                .proxy(ProxySelector.getDefault())//配置 proxy
                .executor(Executors.newCachedThreadPool())//配置 executor
                .followRedirects(HttpClient.Redirect.ALWAYS)//配置 followRedirects
                .cookieManager(new CookieManager())//配置 cookieManager
                .version(HttpClient.Version.HTTP_2)//配置 version
                .build();

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    }
    /**
     * 普通get方式
     *
     *
     * 结果:{
     "msg" : "未查询到用户,请认真检查账户或者是否登录",
     "success" : false
     }
     响应码:200

     */
    public static void HttpGet() {

        HttpRequest request = HttpRequest
                //.newBuilder(URI.create("http://blog.csdn.net/u014042066"))
                .newBuilder(URI.create("http://www.rqbao.com/lotteryAward/gettenrecordlist"))
                .header("refer", "http://www.oschina.com")//携带的参数
                .header("userId", "d3e750db32004972b0ae58f8129f50fc")
                .timeout(Duration.ofSeconds(2))//2秒过期
                .GET()
                .build();
        getReponse(request);

    }

    public static void HttpGet2() {

        HttpRequest request = HttpRequest
                //.newBuilder(URI.create("http://blog.csdn.net/u014042066"))
                .newBuilder(URI.create("http://www.rqbao.com/lotteryAward/gettenrecordlist"))
                .header("refer", "http://www.oschina.com")//携带的参数
                .header("userId", "d3e750db32004972b0ae58f8129f50fc")
                .timeout(Duration.ofSeconds(2))//2秒过期
                .GET()
                .build();
        getAsyReponse2(request);

    }

    /**
     * 文件上传
     */
    public static void HttpPost() {
        try {
            HttpRequest request = HttpRequest
                    .newBuilder(URI.create("http://blog.csdn.net/u014042066"))
                    .header("refer", "http://www.oschina.com")
                    .POST(HttpRequest.BodyProcessor.fromFile(Paths.get("/url.txt")))
                    .build();
            getAsyReponse(request);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }

    /**
     * 携带参数
     */
    public static void HttpPost2() {
        HttpRequest request = HttpRequest
                .newBuilder(URI.create("http://blog.csdn.net/u014042066"))
                .header("refer", "http://www.oschina.com")
                .POST(HttpRequest.BodyProcessor.fromString("name=ricky,pwd=123456"))
                .build();
        getReponse(request);
    }

    /**
     * 输出响应结果
     * @param request
     * @throws IOException
     * @throws InterruptedException
     */
    public static void getReponse(HttpRequest request) {
        HttpResponse<String> response = null;
        try {
            if (client==null) {
                init();
            }
            response = client.send(request, HttpResponse.BodyHandler.asString());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String result = response.body();
        int code = response.statusCode();
        if (code == 200) {
            System.out.println("结果:" + result);
        }
        System.out.println("响应码:" + code);
    }

    /**
     * 输出响应结果 带path形式
     * @param request
     * @throws IOException
     * @throws InterruptedException
     */
    public static void getReponsePath(HttpRequest request) {
        HttpResponse<Path> response = null;
        try {
            client.send(request, HttpResponse.BodyHandler.asFile(Paths.get("/url.text")));
            Path body = response.body();
            System.out.println(body);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 输出响应结果 带path形式的异步
     * @param request
     * @throws IOException
     * @throws InterruptedException
     */
    public static void getAsyReponsePath(HttpRequest request) {
        CompletableFuture<Path> response = null;
        client
                .sendAsync(request, HttpResponse.BodyHandler.asFile(Paths.get("/url.text")))
                .thenApply((response1) -> response1.body());

        response.join();
    }

    /**
     * 异步的输出响应结果
     * @param request
     * @throws IOException
     * @throws InterruptedException
     */
    public static void getAsyReponse(HttpRequest request) {
        CompletableFuture<HttpResponse<String>> cf;
        cf = client.sendAsync(request, HttpResponse.BodyHandler.asString());
        HttpResponse<String> response = cf.join();
        System.out.println(response.body());
    }
    /**
     * 异步的输出响应结果
     * @param request
     * @throws IOException
     * @throws InterruptedException
     */
    public static void getAsyReponse2(HttpRequest request) {
        MultiMapResult<String> ress;
        ress = client.sendAsync(request, HttpResponse.MultiProcessor.asMap((req)-> Optional.of(
                HttpResponse.BodyHandler.asString()
        ))).join();
      ress.forEach((req,cf)->{
          HttpResponse<String> resp=cf.join();
          System.out.println("uri:"+resp.uri()+"---body:"+resp.body());
      });
    }
}

Java9 的 HttpClient 提供了丰富的功能,这里只是做了个简单的入门与分享,不对之处还望指正。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值