前面介绍了基于HttpURLConnection的网络访问请求,包括GET方式调用接口、POST方式调用接口、下载网络文件、上传本地文件这四种HTTP操做。虽然经过HttpURLConnection可以实现相应的业务功能,可是它的编码过程却有些繁琐,须要时时刻刻注意有关细节,一不留神便会掉到坑里。好比下列编码细节就常常令初学者头痛不已:
一、HttpURLConnection工具独自一人承担了全部的方法实现,分不清哪些方法与请求有关,哪些方法与应答有关;
二、HTTP调用的步骤太多,诸如参数设置、开启链接、写入请求报文、读取应答报文、断开链接这些操做的次序得紧紧记住,一旦弄错顺序就没法正常调用;
三、对于请求报文与应答报文,HttpURLConnection只笼统提供了输出流和输入流,剩下的事全凭开发者自由发挥,害得开发者忙于I/O流与字符串/文件之间的转换工做;
四、服务器返回的应答报文,其数据有可能采用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客户端对象的代码例子:javascript
// 建立一个自定义的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客户端对象的建立代码可缩短到以下一行:html
// 建立默认的HTTP客户端对象 HttpClient client = HttpClient.newHttpClient();
至于HttpRequest,则用于描述本次网络访问的请求信息,包括对方地址、接口的调用方式(GET仍是POST)、请求的超时时间、请求的头部属性等等。下面是建立HTTP请求对象的代码例子:java
// 建立一个自定义的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主要提供了下列三个方法,以便开发者处理应答数据:json
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编码。app
应答内容长度=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格式,具体代码以下所示:ide
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开发笔记(序)章节目录》