前面介紹了基於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開發筆記(序)章節目錄》