HttpURLConnection與HttpClient比較和使用示例
1. GET請求與POST請求
HTTP協議是現在Internet上使用得最多、最重要的協議了,越來越多的Java應用程式需要直接通過HTTP協議來訪問網路資源。
在介紹HttpURLConnection前,我們還是再來說一下URL請求最常用的兩種方式:GET請求與POST請求。
GET請求的資料會附在URL之後(就是把資料放置在HTTP協議頭中),以?分割URL和傳輸資料,引數之間以&相連,如:http://localhost:8080/test.do?name=test&password=123456。
GET請求傳送的引數如果資料是英文字母或數字,則按原樣傳送,如果是空格,則轉換為+,如果是中文或其他字元,則直接把字串用BASE64加密,得出如 %E4%BD%A0%E5%A5%BD
POST請求的引數不是放在URL字串裡面,而是放在HTTP請求的正文內,請求的引數被封裝起來以流的形式傳送給服務端。
對於GET方式提交資料的大小,HTTP協議並沒有硬性限制,但某些瀏覽器及伺服器會對它進行限制,如IE對URL長度的限制是2083位元組(2K+35)。理論上POST也沒有限制,可傳較大量的資料。
POST的安全性要比GET的安全性高。比如:通過GET提交資料,使用者名稱和密碼將明文出現在URL上,因為登入頁面有可能被瀏覽器快取,如果其他人檢視瀏覽器的歷史紀錄,那麼別人就可以拿到你的賬號和密碼了,除此之外,使用GET提交資料還可能會造成Cross-site request forgery(CSRF,跨站請求偽造)攻擊。
一般來說,Get是向伺服器索取資料的一種請求,而Post是向伺服器提交資料的一種請求。
2. HttpURLConnection簡介
在JDK的java.net包中已經提供了訪問HTTP協議的基本功能的類:HttpURLConnection。
HttpURLConnection是Java的標準類,它繼承自URLConnection,可用於向指定網站傳送GET請求、POST請求。它在URLConnection的基礎上提供瞭如下便捷的方法:
int getResponseCode(); // 獲取伺服器的響應程式碼。 String getResponseMessage(); // 獲取伺服器的響應訊息。String getResponseMethod(); // 獲取傳送請求的方法。 void setRequestMethod(String method); // 設定傳送請求的方法。
3. HttpURLConnection的使用
3.1 使用GET方式訪問HTTP
package com.qf.demo; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; /** * GET請求示例 * * @author 小明 * */ public class GetDemo { public static void main(String[] args) { try { // 1. 得到訪問地址的URL URL url = new URL( "http://localhost:8080/Servlet/do_login.do?username=test&password=123456"); // 2. 得到網路訪問物件java.net.HttpURLConnection HttpURLConnection connection = (HttpURLConnection) url .openConnection(); /* 3. 設定請求引數(過期時間,輸入、輸出流、訪問方式),以流的形式進行連線 */ // 設定是否向HttpURLConnection輸出 connection.setDoOutput(false); // 設定是否從httpUrlConnection讀入 connection.setDoInput(true); // 設定請求方式 connection.setRequestMethod("GET"); // 設定是否使用快取 connection.setUseCaches(true); // 設定此 HttpURLConnection 例項是否應該自動執行 HTTP 重定向 connection.setInstanceFollowRedirects(true); // 設定超時時間 connection.setConnectTimeout(3000); // 連線 connection.connect(); // 4. 得到響應狀態碼的返回值 responseCode int code = connection.getResponseCode(); // 5. 如果返回值正常,資料在網路中是以流的形式得到服務端返回的資料 String msg = ""; if (code == 200) { // 正常響應 // 從流中讀取響應資訊 BufferedReader reader = new BufferedReader( new InputStreamReader(connection.getInputStream())); String line = null; while ((line = reader.readLine()) != null) { // 迴圈從流中讀取 msg += line + "\n"; } reader.close(); // 關閉流 } // 6. 斷開連線,釋放資源 connection.disconnect(); // 顯示響應結果 System.out.println(msg); } catch (IOException e) { e.printStackTrace(); } } }
3.2 使用POST方式訪問HTTP
package com.qf.demo; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; /** * POST請求示例 * * @author 小明 * */ public class PostDemo { public static void main(String[] args) { try { // 1. 獲取訪問地址URL URL url = new URL("http://localhost:8080/Servlet/do_login.do"); // 2. 建立HttpURLConnection物件 HttpURLConnection connection = (HttpURLConnection) url .openConnection(); /* 3. 設定請求引數等 */ // 請求方式 connection.setRequestMethod("POST"); // 超時時間 connection.setConnectTimeout(3000); // 設定是否輸出 connection.setDoOutput(true); // 設定是否讀入 connection.setDoInput(true); // 設定是否使用快取 connection.setUseCaches(false); // 設定此 HttpURLConnection 例項是否應該自動執行 HTTP 重定向 connection.setInstanceFollowRedirects(true); // 設定使用標準編碼格式編碼引數的名-值對 connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); // 連線 connection.connect(); /* 4. 處理輸入輸出 */ // 寫入引數到請求中 String params = "username=test&password=123456"; OutputStream out = connection.getOutputStream(); out.write(params.getBytes()); out.flush(); out.close(); // 從連線中讀取響應資訊 String msg = ""; int code = connection.getResponseCode(); if (code == 200) { BufferedReader reader = new BufferedReader( new InputStreamReader(connection.getInputStream())); String line; while ((line = reader.readLine()) != null) { msg += line + "\n"; } reader.close(); } // 5. 斷開連線 connection.disconnect(); // 處理結果 System.out.println(msg); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
3.3 說明
- HttpURLConnection物件不能直接構造,需要通過URL類中的openConnection()方法來獲得。
- HttpURLConnection的connect()函式,實際上只是建立了一個與伺服器的TCP連線,並沒有實際傳送HTTP請求。HTTP請求實際上直到我們獲取伺服器響應資料(如呼叫getInputStream()、getResponseCode()等方法)時才正式傳送出去。
- 對HttpURLConnection物件的配置都需要在connect()方法執行之前完成。
- HttpURLConnection是基於HTTP協議的,其底層通過socket通訊實現。如果不設定超時(timeout),在網路異常的情況下,可能會導致程式僵死而不繼續往下執行。
- HTTP正文的內容是通過OutputStream流寫入的, 向流中寫入的資料不會立即傳送到網路,而是存在於記憶體緩衝區中,待流關閉時,根據寫入的內容生成HTTP正文。
- 呼叫getInputStream()方法時,返回一個輸入流,用於從中讀取伺服器對於HTTP請求的返回資訊。
- 我們可以使用HttpURLConnection.connect()方法手動的傳送一個HTTP請求,但是如果要獲取HTTP響應的時候,請求就會自動的發起,比如我們使用HttpURLConnection.getInputStream()方法的時候,所以完全沒有必要呼叫connect()方法。
4. HttpClient簡介
在一般情況下,如果只是需要向Web站點的某個簡單頁面提交請求並獲取伺服器響應,HttpURLConnection完全可以勝任。但在絕大部分情況下,Web站點的網頁可能沒這麼簡單,這些頁面並不是通過一個簡單的URL就可訪問的,可能需要使用者登入而且具有相應的許可權才可訪問該頁面。在這種情況下,就需要涉及Session、Cookie的處理了,如果打算使用HttpURLConnection來處理這些細節,當然也是可能實現的,只是處理起來難度就大了。
為了更好地處理向Web站點請求,包括處理Session、Cookie等細節問題,Apache開源組織提供了一個HttpClient專案,看它的名稱就知道,它是一個簡單的HTTP客戶端(並不是瀏覽器),可以用於傳送HTTP請求,接收HTTP響應。但不會快取伺服器的響應,不能執行HTML頁面中嵌入的Javascript程式碼;也不會對頁面內容進行任何解析、處理。
簡單來說,HttpClient就是一個增強版的HttpURLConnection,HttpURLConnection可以做的事情HttpClient全部可以做;HttpURLConnection沒有提供的有些功能,HttpClient也提供了,但它只是關注於如何傳送請求、接收響應,以及管理HTTP連線。
5. HttpClient的使用
使用HttpClient傳送請求、接收響應很簡單,只要如下幾步即可。
- 建立HttpClient物件。
- 如果需要傳送GET請求,建立HttpGet物件;如果需要傳送POST請求,建立HttpPost物件。
- 如果需要傳送請求引數,可呼叫HttpGet、HttpPost共同的setParams(HttpParams params)方法來新增請求引數;對於HttpPost物件而言,也可呼叫setEntity(HttpEntity entity)方法來設定請求引數。
- 呼叫HttpClient物件的execute(HttpUriRequest request)傳送請求,執行該方法返回一個HttpResponse。
- 呼叫HttpResponse的getAllHeaders()、getHeaders(String name)等方法可獲取伺服器的響應頭;呼叫HttpResponse的getEntity()方法可獲取HttpEntity物件,該物件包裝了伺服器的響應內容。程式可通過該物件獲取伺服器的響應內容。
5.1 使用GET方式訪問HTTP
package com.qf.client; import java.io.IOException; import org.apache.http.HttpEntity; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; /** * GET請求示例 * * @author 小明 * */ public class GetDemo { public static void main(String[] args) { // 1. 建立HttpClient物件 CloseableHttpClient httpClient = HttpClientBuilder.create().build(); // 2. 建立HttpGet物件 HttpGet httpGet = new HttpGet( "http://localhost:8080/Servlet/do_login.do?username=test&password=123456"); CloseableHttpResponse response = null; try { // 3. 執行GET請求 response = httpClient.execute(httpGet); System.out.println(response.getStatusLine()); // 4. 獲取響應實體 HttpEntity entity = response.getEntity(); // 5. 處理響應實體 if (entity != null) { System.out.println("長度:" + entity.getContentLength()); System.out.println("內容:" + EntityUtils.toString(entity)); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { // 6. 釋放資源 try { response.close(); httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } } }
5.2 使用POST方式訪問HTTP
package com.qf.client; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; /** * POST請求測試 * * @author 小明 * */ public class PostDemo { public static void main(String[] args) { // 1. 建立HttpClient物件 CloseableHttpClient httpClient = HttpClientBuilder.create().build(); // 2. 建立HttpPost物件 HttpPost post = new HttpPost( "http://localhost:8080/Servlet/do_login.do"); // 3. 設定POST請求傳遞引數 List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("username", "test")); params.add(new BasicNameValuePair("password", "12356")); try { UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params); post.setEntity(entity); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } // 4. 執行請求並處理響應 try { CloseableHttpResponse response = httpClient.execute(post); HttpEntity entity = response.getEntity(); if (entity != null){ System.out.println("響應內容:"); System.out.println(EntityUtils.toString(entity)); } response.close(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { // 釋放資源 try { httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } } }
5.3 說明
HttpClient相比傳統JDK自帶的URLConnection,增加了易用性和靈活性,它不僅使客戶端傳送HTTP請求變得容易,而且也方便了開發人員測試介面(基於Http協議的),即提高了開發的效率,也方便提高程式碼的健壯性。