Java9之HttpClientAPI实战详解

Wesley13
• 阅读 614

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
  •  Java9之HttpClientAPI实战详解

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

Java9之HttpClientAPI实战详解

  • 使用示例

    • 示例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实例 。 构建器也可以被多次复制和修改,以构建参数不同的多个相关请求。

Java9之HttpClientAPI实战详解

Java9之HttpClientAPI实战详解

Java9之HttpClientAPI实战详解

  • 使用示例

    • 示例1,GET请求

    HttpResponse 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"))); }

Java9之HttpClientAPI实战详解  Java9之HttpClientAPI实战详解 Java9之HttpClientAPI实战详解

实战应用

这里是一个完整的示例应用,涵盖了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示例 *

* 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提供了丰富的功能,这里只是做了个简单的入门与分享,不对之处还望指正。

相关文章

Java9之Shell入门

https://my.oschina.net/u/3048852/blog/1543044

Java9 Module解惑

https://my.oschina.net/u/3048852/blog/1544711

结语

作者: ricky

交流群:244930845

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
AndroidStudio封装SDK的那些事
<divclass"markdown\_views"<!flowchart箭头图标勿删<svgxmlns"http://www.w3.org/2000/svg"style"display:none;"<pathstrokelinecap"round"d"M5,00,2.55,5z"id"raphael
Wesley13 Wesley13
2年前
Java爬虫之JSoup使用教程
title:Java爬虫之JSoup使用教程date:201812248:00:000800update:201812248:00:000800author:mecover:https://imgblog.csdnimg.cn/20181224144920712(https://www.oschin
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这