一,使用HttpClient發送請求、接收響應在項目中是經常使用的,使用也很簡單,常見的有如下幾步:
1. 創建對象:HttpClient對象。
2. 創建請求方法的實例,並指定請求URL。如果需要發送GET請求,創建HttpGet對象;如果需要發送POST請求,創建HttpPost對象。
3. 如果需要發送請求參數,可調用HttpGet、HttpPost共同的setParams(HetpParams params)方法來添加請求參數;對於HttpPost對象而言,也可調用setEntity(HttpEntity entity)方法來設置請求參數。
4. 調用HttpClient對象的execute(HttpUriRequest request)發送請求,該方法返回一個HttpResponse。
5. 調用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可獲取服務器的響應頭;調用HttpResponse的getEntity()方法可獲取HttpEntity對象,該對象包裝了服務器的響應內容。程序可通過該對象獲取服務器的響應內容。
6. 釋放連接。無論執行方法是否成功,都必須釋放連接。
二, 使用HttpClient遇到的問題。
1)字符編碼
某目標頁的編碼可能出現在兩個地方,第一個地方是服務器返回的http頭中,另外一個地方是得到的html/xml頁面中。
在http頭的Content-Type字段可能會包含字符編碼信息。例如可能返回的頭會包含這樣子的信息:Content-Type: text/html; charset=UTF-8。這個頭信息表明該頁的編碼是UTF-8,但是服務器返回的頭信息未必與內容能匹配上。比如對於一些雙字節語言國家,可能服務器返回的編碼類型是UTF-8,但真正的內容卻不是UTF-8編碼的,因此需要在另外的地方去得到頁面的編碼信息;但是如果服務器返回的編碼不是UTF-8,而是具體的一些編碼,比如gb2312等,那服務器返回的可能是正確的編碼信息。通過method對象的getResponseCharSet()方法就可以得到http頭中的編碼信息。
對於象xml或者html這樣的文件,允許作者在頁面中直接指定編碼類型。比如在html中會有`<meta http-equiv="Content-Type" content="text/html; charset=gb2312"/>`這樣的標籤;或者在xml中會有`<?xml version="1.0" encoding="gb2312"?>`這樣的標籤,在這些情況下,可能與http頭中返回的編碼信息衝突,需要用戶自己判斷到底那種編碼類型應該是真正的編碼。
2)自動轉向
根據RFC2616中對自動轉向的定義,主要有兩種:301和302。301表示永久的移走(Moved
Permanently),當返回的是301,則表示請求的資源已經被移到一個固定的新地方,任何向該地址發起請求都會被轉到新的地址上。302表示暫時的轉向,比如在服務器端的servlet程序調用了sendRedirect方法,則在客戶端就會得到一個302的代碼,這時服務器返回的頭信息中location的值就是sendRedirect轉向的目標地址。
HttpClient支持自動轉向處理,但是像 POST 和 PUT 方式這種要求接受後繼服務的請求方式,暫時不支持自動轉向,因此如果遇到 POST 方式提交後返回的是 301或者302 的話需要自己處理。
另外除了在頭中包含的信息可能使頁面發生重定向外,在頁面中也有可能會發生頁面的重定向。引起頁面自動轉發的標籤是:`<meta http-equiv="refresh" content="5; url=....">`。如果你想在程序中也處理這種情況的話得自己分析頁面來實現轉向。需要注意的是,在上面那個標籤中 url 的值也可以是一個相對地址,如果是這樣的話,需要對它做一些處理後纔可以轉發。
3)Demo 如下:
Demo1- 無參數的 get請求發送。
/** * 處理get請求. * @param url 請求路徑 * @return json */ public String getTest(String url){ //實例化httpclient CloseableHttpClient httpclient = HttpClients.createDefault(); //實例化get方法 HttpGet httpget = new HttpGet(url); //請求結果 CloseableHttpResponse response = null; String content =""; try { //執行get方法 response = httpclient.execute(httpget); if(response.getStatusLine().getStatusCode() == 200){ content = EntityUtils.toString(response.getEntity(),"utf-8"); log.debug("content: {}", content); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return content; }
Demo2- 無參數的 get 請求發送。
/** * 發送Get 請求 * * @param url 請求URL * @return json */ public static String sendGet(String url) { //1.獲得一個httpclient對象 CloseableHttpClient httpclient = HttpClients.createDefault(); //2.生成一個get請求 HttpGet httpget = new HttpGet(url); CloseableHttpResponse response = null; try { //3.執行get請求並返回結果 response = httpclient.execute(httpget); } catch (IOException e1) { e1.printStackTrace(); } String result = null; try { //4.處理結果,這裏將結果返回爲字符串 HttpEntity entity = response.getEntity(); if (entity != null) { result = EntityUtils.toString(entity); log.debug("result ---------- {}", result); } } catch (ParseException | IOException e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { e.printStackTrace(); } } return result; }
Demo3- 有參數的 post 請求發送。
/** * 處理有參數的 post請求.測試1 * @param url 請求路徑 * @param params 參數 * @return json */ public String postTest(String url, Map<String, String> params){ //實例化httpClient CloseableHttpClient httpclient = HttpClients.createDefault(); //實例化post方法 HttpPost httpPost = new HttpPost(url); //處理參數 List<NameValuePair> nvps = new ArrayList<NameValuePair>(); Set<String> keySet = params.keySet(); for(String key : keySet) { nvps.add(new BasicNameValuePair(key, params.get(key))); } //結果 CloseableHttpResponse response = null; String content=""; try { //提交的參數 UrlEncodedFormEntity uefEntity = new UrlEncodedFormEntity(nvps, "UTF-8"); //將參數給post方法 httpPost.setEntity(uefEntity); //執行post方法 response = httpclient.execute(httpPost); if(response.getStatusLine().getStatusCode() == 200){ content = EntityUtils.toString(response.getEntity(),"utf-8"); log.debug("content ---------- {}", content); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return content; }
/** * 有參數的 post 測試2 * * @param url * @param map * @return */ public static String sendPost(String url, Map<String, String> map) { CloseableHttpClient httpclient = HttpClients.createDefault(); List<NameValuePair> formparams = new ArrayList<NameValuePair>(); for (Map.Entry<String, String> entry : map.entrySet()) { //給參數賦值 formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8); HttpPost httppost = new HttpPost(url); httppost.setEntity(entity); CloseableHttpResponse response = null; try { response = httpclient.execute(httppost); } catch (IOException e) { e.printStackTrace(); } HttpEntity entity1 = response.getEntity(); String result = null; try { result = EntityUtils.toString(entity1); } catch (ParseException | IOException e) { e.printStackTrace(); } return result; }
Demo4- 無 參數的 post 請求發送。
/** * 發送HttpPost的方法和發送HttpGet很類似,只是將請求類型給位HttpPost即可。 * 發送 不帶參數的 HttpPost請求 * @param url * @return */ public static String sendPostNoParams(String url) { //1.獲得一個httpclient對象 CloseableHttpClient httpclient = HttpClients.createDefault(); //2.生成一個post請求 HttpPost httppost = new HttpPost(url); CloseableHttpResponse response = null; try { //3.執行get請求並返回結果 response = httpclient.execute(httppost); } catch (IOException e) { e.printStackTrace(); } //4.處理結果,這裏將結果返回爲字符串 HttpEntity entity = response.getEntity(); String result = null; try { result = EntityUtils.toString(entity); log.debug("result: {}", result); } catch (ParseException | IOException e) { e.printStackTrace(); } return result; }