• Java开发笔记(一百一十二)Java11新增的HttpClient


    前面介绍了基于HttpURLConnection的网络访问请求,包括GET方式调用接口、POST方式调用接口、下载网络文件、上传本地文件这四种HTTP操作。虽然通过HttpURLConnection能够实现相应的业务功能,但是它的编码过程却有些繁琐,需要时时刻刻注意有关细节,一不留神便会掉到坑里。比如下列编码细节就经常令初学者头痛不已:
    1、HttpURLConnection工具独自一人承担了所有的方法实现,分不清哪些方法与请求有关,哪些方法与应答有关;
    2、HTTP调用的步骤太多,诸如参数设置、开启连接、写入请求报文、读取应答报文、断开连接这些操作的次序得牢牢记住,一旦弄错顺序就无法正常调用;
    3、对于请求报文与应答报文,HttpURLConnection只笼统提供了输出流和输入流,剩下的事全凭开发者自由发挥,害得开发者忙于I/O流与字符串/文件之间的转换工作;
    4、服务器返回的应答报文,其数据有可能采用gzip压缩,还可能采取GBK字符编码,然而HttpURLConnection默认情况下却袖手旁观,必须由开发者对数据手工解压和重新编码;
    总而言之,HttpURLConnection要求开发者掌握太多的技术细节,容易造成初学者对其望而却步。为此第三方的HTTP框架层出不穷,意图通过简单明了的方法调用来简化HTTP通信编程。Apache旗下的HttpClient便是其中一个佼佼者,它封装了大部分的编码细节,开发者只需书写寥寥数行代码,即可完成常见的HTTP访问操作。当然,Apache的HttpClient毕竟是个外来者,它运用得越广泛,Java的老板Oracle越是觉得不爽,老财主Oracle心想:咱卧榻之侧,岂容他人鼾睡?与其依赖Apache,不如自己动手丰衣足食,于是从Java11开始,JDK新增了自己的HttpClient框架,总算在自力更生的道路上迈开了小小的一步。
    Java11的HttpClient体系由三部分组成,分别是表示HTTP客户端的HttpClient、表示HTTP请求过程的HttpRequest、表示HTTP应答过程的HttpResponse。其中HttpClient用于描述通用的客户端连接信息,包括HTTP协议的版本号、HTTP代理、重定向方式、连接超时时间、身份认证、SSL证书等等。下面是创建HTTP客户端对象的代码例子:

    		// 创建一个自定义的HTTP客户端对象
    		HttpClient client = HttpClient.newBuilder()
    				.version(Version.HTTP_1_1) // 遵循HTTP协议的1.1版本
    				.followRedirects(Redirect.NORMAL) // 正常的重定向
    				.connectTimeout(Duration.ofMillis(5000)) // 连接的超时时间为5秒
    				.authenticator(Authenticator.getDefault()) // 默认的身份认证
    				.build();
    

    显然以上的代码例子很啰嗦,对于普通的HTTP连接,一律按照默认的参数就行。于是HTTP客户端对象的创建代码可缩短到如下一行:

    		// 创建默认的HTTP客户端对象
    		HttpClient client = HttpClient.newHttpClient();
    

    至于HttpRequest,则用于描述本次网络访问的请求信息,包括对方地址、接口的调用方式(GET还是POST)、请求的超时时间、请求的头部属性等等。下面是创建HTTP请求对象的代码例子:

    		// 创建一个自定义的HTTP请求对象
    		HttpRequest request = HttpRequest.newBuilder()
    				.GET() // 调用方式为GET
    				.uri(URI.create(url)) // 待调用的url地址
    				.header("Accept-Language", "zh-CN") // 设置头部参数,中文文本
    				.timeout(Duration.ofMillis(5000)) // 请求的超时时间为5秒
    				.build();
    

    对于一般的GET调用而言,HTTP请求可以使用默认的参数,再把对方地址作为newBuilder方法的输入参数,如此一来HTTP请求对象的创建代码也可缩短到如下一行:

    		// 创建默认的HTTP请求对象(默认GET调用)
    		HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build();
    

    接着调用HTTP客户端对象的send方法,第一个参数填HTTP请求对象,第二个参数填BodyHandlers.ofString()表示要求返回字符串形式的应答报文,而send方法的返回值便是HttpResponse对象。HttpResponse主要提供了下列三个方法,以便开发者处理应答数据:

    statusCode:获取应答的状态码。
    body:获取应答报文的内容。
    headers:获取应答的所有头部属性。
    接下来结合HttpClient、HttpRequest、HttpResponse,很容易写出GET方式的HTTP调用代码,具体代码如下所示:

    	// 对指定url发起GET调用
    	private static void testCallGet(String url) {
    		// 创建默认的HTTP客户端对象
    		HttpClient client = HttpClient.newHttpClient();
    		// 创建默认的HTTP请求对象(默认GET调用)
    		HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build();
    		try {
    			// 客户端传递请求信息,且返回字符串形式的应答报文
    			HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
    			// 获取应答的所有头部属性
    			HttpHeaders headers = response.headers();
    			// 打印HTTP调用的应答内容长度、内容类型、压缩方式
    			System.out.println( String.format("应答内容长度=%s, 内容类型=%s, 压缩方式=%s", 
    					headers.firstValue("Content-Length").orElse(null),
    					headers.firstValue("Content-Type").orElse(null),
    					headers.firstValue("Content-Encoding").orElse(null)) );
    			// 打印HTTP调用的应答状态码和应答报文
    			System.out.println( String.format("应答状态码=%d, 应答报文=%s", 
    					response.statusCode(), response.body()) );
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    

    然后在外部调用上面的testCallGet方法,以股指查询的接口地址为例,查询上证指数的调用代码如下:

    		testCallGet("https://hq.sinajs.cn/list=s_sh000001");
    

    运行以上的股指查询代码,观察到以下的查询日志,可见HttpClient已经自动完成了中文字符的GBK编码。

    应答内容长度=75, 内容类型=application/javascript; charset=GBK, 压缩方式=null
    应答状态码=200, 应答报文=var hq_str_s_sh000001="上证指数,3244.8103,-1.7611,-0.05,5045184,50643124";
    

    利用HttpClient发起POST方式的调用过程类似GET方式,唯一的区别在于:创建HTTP请求对象之时要调用POST方法并传入请求报文。下面是采取POST方式访问服务地址的HttpClient代码例子:

    	// 对指定url发起POST调用
    	private static void testCallPost(String url, String body) {
    			System.out.println("请求报文="+body);
    		// 创建默认的HTTP客户端对象
    		HttpClient client = HttpClient.newHttpClient();
    		// 创建一个自定义的HTTP请求对象
    		HttpRequest request = HttpRequest.newBuilder(URI.create(url)) // 待调用的url地址
    				.POST(BodyPublishers.ofString(body)) // 调用方式为POST,且请求报文为字符串
    				.header("Content-Type", "application/json") // 设置头部参数,内容类型为json
    				.build();
    		try {
    			// 客户端传递请求信息,且返回字符串形式的应答报文
    			HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
    			// 打印HTTP调用的应答状态码和应答报文
    			System.out.println( String.format("应答状态码=%d, 应答报文=%s", 
    					response.statusCode(), response.body()) );
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    

    接着由外部调用上面的testCallPost方法,这里访问的是本机的HTTP服务,交互报文为json格式,具体代码如下所示:

    		testCallPost("http://localhost:8080/NetServer/checkUpdate", "{"package_list":[{"package_name":"com.qiyi.video"}]}");
    

    运行以上的服务访问代码,观察到以下的接口日志,可见HttpClient正确完成了POST方式的接口调用。

    请求报文={"package_list":[{"package_name":"com.qiyi.video"}]}
    应答状态码=200, 应答报文={"package_list":[{"package_name":"com.qiyi.video","download_url":"https://3g.lenovomm.com/w3g/yydownload/com.qiyi.video/60020","new_version":"10.2.0"}]}
    

      

    更多Java技术文章参见《Java开发笔记(序)章节目录

  • 相关阅读:
    每天进步一点点------Allegro 布线完成后如何修改线宽
    每天进步一点点------Allegro 布线时显示延迟以及相对延迟信息
    每天进步一点点------Allegro 修线
    每天进步一点点------Allegro 蛇形走线
    每天进步一点点------Allegro 动态显示走线长度
    每天进步一点点------Allegro 群组布线
    每天进步一点点------Allegro 手工布线时控制面板各选项说明
    每天进步一点点------Allegro 铺铜、内电层分割
    每天进步一点点------Allegro 铺铜详解
    每天进步一点点------Allegro使用脚本记录文件设置工作环境的颜色
  • 原文地址:https://www.cnblogs.com/pinlantu/p/11042476.html
Copyright © 2020-2023  润新知