欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-28 16:52:33
前面介绍了基于HttpURLConnection的网络访问请求,包括GET方式调用接口、POST方式调用接口、下载网络文件、上传本地文件这四种HTTP操作。虽然通过HttpURLConnection能够实现相应的业务功能,但是它的编码过程却有些繁琐,需要时时刻刻注意有关细节,一不留神便会掉到坑里。比 ......

前面介绍了基于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开发笔记(序)章节目录