HttpClient 簡介
HttpClient 簡介HttpClient 是 Apache Jakarta Common 下的子項目,可以用來提供高效的、最新的、功能豐富的支持 HTTP 協(xié)議的客戶端編程工具包,并且它支持 H
HttpClient 簡介
HttpClient 是 Apache Jakarta Common 下的子項目,可以用來提供高效的、最新的、功能豐富的支持 HTTP 協(xié)議的客戶端編程工具包,并且它支持 HTTP 協(xié)議最新的版本和建議。本文首先介紹
HTTPClient ,然后根據(jù)作者實際工作經(jīng)驗給出了一些常見問題的解決方法。HTTP 協(xié)議可能是現(xiàn)在 Internet 上使用得最多、最重要的協(xié)議了,越來越多的 Java 應用程序需要直接通過 HTTP 協(xié)議來訪問網(wǎng)絡資源。雖然在 JDK 的 java.net 包中已經(jīng)提供了訪問 HTTP 協(xié)議的基本功能,但是對于大部分應用程序來說,JDK 庫本身提供的功能還不夠豐富和靈活。HttpClient 是 Apache Jakarta Common 下的子項目,用來提供高效的、最新的、功能豐富的支持 HTTP 協(xié)議的客戶端編程工具包,并且它支持 HTTP 協(xié)議最新的版本和建議。HttpClient 已經(jīng)應用在很多的項目中,比如 Apache Jakarta 上很著名的另外兩個開源項目 Cactus 和 HTMLUnit 都使用了 HttpClient 。現(xiàn)在HttpClient 最新版本為 HttpClient 4.0-beta2
2.HttpClient 功能介紹
以下列出的是 HttpClient 提供的主要的功能,要知道更多詳細的功能可以參見 HttpClient 的主頁。
(1)實現(xiàn)了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
(2)支持自動轉向
(3)支持 HTTPS 協(xié)議
(4)支持代理服務器等
3.HttpClient 基本功能的使用
(1) GET方法
使用 HttpClient 需要以下 6 個步驟:
,1. 創(chuàng)建 HttpClient 的實例
2. 創(chuàng)建某種連接方法的實例,在這里是 GetMethod 。在 GetMethod 的構造函數(shù)中傳入待連接的地址
3. 調(diào)用第一步中創(chuàng)建好的實例的 execute 方法來執(zhí)行第二步中創(chuàng)建好的 method 實例
4. 讀 response
5. 釋放連接。無論執(zhí)行方法是否成功,都必須釋放連接
6. 對得到后的內(nèi)容進行處理
根據(jù)以上步驟,我們來編寫用GET 方法來取得某網(wǎng)頁內(nèi)容的代碼。 大部分情況下 HttpClient 默認的構造函數(shù)已經(jīng)足夠使用。 HttpClient httpClient = new HttpClient();
創(chuàng)建GET 方法的實例。在GET 方法的構造函數(shù)中傳入待連接的地址即可。用GetMethod 將會自動處理轉發(fā)過程,如果想要把自動處理轉發(fā)過程去掉 的話,可以調(diào)用方法setFollowRedirects(false)。
GetMethod getMethod = new GetMethod(".....");
調(diào)用實例httpClient 的executeMethod 方法來執(zhí)行getMethod 。由于是執(zhí)行在網(wǎng)絡上的程序,在運行 executeMethod 方法的時候,需要處理兩個異常,分別是HttpException 和IOException 。引起第一種異常的原因主要可能是 在構造getMethod 的時候傳入的協(xié)議不對,比如不小心將"http" 寫成"htp" ,或者服務器端返回的內(nèi)容不正常等,并且該異常發(fā)生是不可恢復 的;第二種異常一般是由于網(wǎng)絡原因引起的異常,對于這種異常 (IOException ),HttpClient 會根據(jù)你指定的恢復策略自動試著重新執(zhí)行executeMethod 方法。HttpClient 的恢復 策略可以自定義(通過實現(xiàn)接口HttpMethodRetryHandler 來實現(xiàn))。通過httpClient 的方法setParameter 設置你實 現(xiàn)的恢復策略,本文中使用的是系統(tǒng)提供的默認恢復策略,該策略在碰到第二類異常的時候將自動重試3次。executeMethod 返回值是一個整數(shù),表示 了執(zhí)行該方法后服務
,器返回的狀態(tài)碼,該狀態(tài)碼能表示出該方法執(zhí)行是否成功、需要認證或者頁面發(fā)生了跳轉(默認狀態(tài)下GetMethod 的實例是自動處理跳 轉的)等。 //設置成了默認的恢復策略,在發(fā)生異常時候將自動重試3次,在這里你也可以設置成自定義的恢復策略
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler());
//執(zhí)行getMethod
int statusCode = client.executeMethod(getMethod); if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: "
getMethod.getStatusLine());
}
在返回的狀態(tài)碼正確后,即可取得內(nèi)容。取得目標地址的內(nèi)容有三種方法:第一種,getResponseBody ,該方法返回的是目標的二進制的 byte 流;第二種,getResponseBodyAsString ,這個方法返回的是String 類型,值得注意的是該方法返回的String 的編碼 是根據(jù)系統(tǒng)默認的編碼方式,所以返回的String 值可能編碼類型有誤,在本文的" 字符編碼" 部分中將對此做詳細介紹;第三 種,
getResponseBodyAsStream ,這個方法對于目標地址中有大量數(shù)據(jù)需要傳輸是最佳的。在這里我們使用了最簡單的 getResponseBody 方法。 byte[] responseBody = method.getResponseBody(); 釋放連接。無論執(zhí)行方法是否成功,都必須釋放連接。
method.releaseConnection();
處理內(nèi)容。在這一步中根據(jù)你的需要處理內(nèi)容,在例子中只是簡單的將內(nèi)容打印到控制臺。 System.out.println(new
,String(responseBody));
下面是程序的完整代碼,這些代碼也可在附件中的test.GetSample 中找到。
package test;
import java.io.IOException;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.GetMethod; import
org.apache.commons.httpclient.params.HttpMethodParams; public class GetSample{
public static void main(String[] args) {
//構造HttpClient 的實例
HttpClient httpClient = new HttpClient();
//創(chuàng)建GET 方法的實例
GetMethod getMethod = new GetMethod("...");
//使用系統(tǒng)提供的默認的恢復策略
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler());
try {
//執(zhí)行getMethod
int statusCode = httpClient.executeMethod(getMethod); if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: "
getMethod.getStatusLine());
}
,//讀取內(nèi)容
byte[] responseBody = getMethod.getResponseBody(); //處理內(nèi)容
System.out.println(new String(responseBody));
} catch (HttpException e) {
//發(fā)生致命的異常,可能是協(xié)議不對或者返回的內(nèi)容有問題
System.out.println("Please check your provided http address!"); e.printStackTrace();
} catch (IOException e) {
//發(fā)生網(wǎng)絡異常
e.printStackTrace();
} finally {
//釋放連接
getMethod.releaseConnection();
}
}
}
(2)POST 方法
根據(jù)RFC2616,對POST 的解釋如下:POST 方法用來向目的服務器發(fā)出請求,要求它接受被附在請求后的實體,并把它當作請求隊列(Request-Line )中請求URI 所指定資源的附加新子項。POST 被設計成用統(tǒng)一的方法實現(xiàn)下列功能:
對現(xiàn)有資源的注釋(Annotation of existing resources) 向電子公告欄、新聞組,郵件列表或類似討論組發(fā)送消息 提交數(shù)據(jù)塊,如將表單的結果提交給數(shù)據(jù)處理過程
通過附加操作來擴展數(shù)據(jù)庫
調(diào)用HttpClient 中的PostMethod 與GetMethod 類似,除了設置
,PostMethod 的實例與GetMethod 有些不同之 外,剩下的步驟都差不多。在下面的例子中,省去了與GetMethod 相同的步驟,只說明與上面不同的地方,并以登錄清華大學BBS 為例子進行說明。
構造PostMethod 之前的步驟都相同,與GetMethod 一樣,構造PostMethod 也需要一個URI 參數(shù)。在創(chuàng)建了 PostMethod 的實例之后,需要給method 實例填充表單的值,在BBS 的登錄表單中需要有兩個域,第一個是用戶名(域名叫id ),第二個是密碼 (域名叫
passwd )。表單中的域用類NameValuePair 來表示,該類的構造函數(shù)第一個參數(shù)是域名,第二參數(shù)是該域的值;將表單所有的值設置到
PostMethod 中用方法setRequestBody 。另外由于BBS 登錄成功后會轉向另外一個頁面,但是HttpClient 對于要求接受后繼服 務的請求,比如POST 和PUT ,不支持自動轉發(fā),因此需要自己對頁面轉向做處理。具體的頁面轉向處理請參見下面的" 自動轉向" 部分。代碼如下: String url = "....";
PostMethod postMethod = new PostMethod(url);
// 填入各個表單域的值
NameValuePair[] data = { new NameValuePair("id",
"youUserName"),
new NameValuePair("passwd", "yourPwd") };
// 將表單的值放入postMethod 中
postMethod.setRequestBody(data);
// 執(zhí)行postMethod
int statusCode = httpClient.executeMethod(postMethod); // HttpClient對于要求接受后繼服務的請求,象POST 和PUT 等不能自動處理轉發(fā)
// 301或者302
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
,statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { // 從頭中取出轉向的地址
Header locationHeader =
postMethod.getResponseHeader("location");
String location = null;
if (locationHeader != null) {
location = locationHeader.getValue();
System.out.println("The page was redirected to:" location); } else {
System.err.println("Location field value is null.");
}
return;
}
[編輯本段]4 使用HttpClient 過程中常見的一些問題 下面介紹在使用HttpClient 過程中常見的一些問題。
字符編碼
某目標頁的編碼可能出現(xiàn)在兩個地方,第一個地方是服務器返回的http 頭中,另外一個地方是得到的html/xml頁面中。
在http 頭的Content-Type 字段可能會包含字符編碼信息。例如可能返回的頭會包含這樣子的信息:Content-Type: text/html;
charset=UTF-8。這個頭信息表明該頁的編碼是UTF-8,但是服務器返回的頭信息未必與內(nèi)容能匹配上。比如對于一些雙字節(jié)語言國家,可能服務 器返回的編碼類型是UTF-8,但真正的內(nèi)容卻不是UTF-8編碼的,因此需要在另外的地方去得到頁面的編碼信息;但是如果服務器返回的編碼不是UTF- 8,而是具體的一些編碼,比如gb2312等,那服務器返回的可能是正確的編碼信息。通過method 對象的getResponseCharSet()方 法就可以得到http 頭中的編碼信息。
,對于象xml 或者html 這樣的文件,允許作者在頁面中直接指定編碼類型。比如在html 中會有
content="text/html; charset=gb2312"/>這樣的標簽;或者在xml 中會有這樣的標簽,在這些情況下,可能與http 頭中返回的編碼信息沖突,需要用戶自己判斷到底那種編碼類型應該 是真正的編碼。
自動轉向
根據(jù)RFC2616中對自動轉向的定義,主要有兩種:301和302。301表示永久的移走(Moved Permanently),當返回的是301,則表示請求的資源已經(jīng)被移到一個固定的新地方,任何向該地址發(fā)起請求都會被轉到新的地址上。302表示暫時 的轉向,比如在服務器端的servlet 程序調(diào)用了sendRedirect 方法,則在客戶端就會得到一個302的代碼,這時服務器返回的頭信息中 location 的值就是sendRedirect 轉向的目標地址。
HttpClient 支持自動轉向處理,但是象POST 和PUT 方式這種要求接受后繼服務的請求方式,暫時不支持自動轉向,因此如果碰到POST 方式 提交后返回的是301或者302的話需要自己處理。就像剛才在
POSTMethod 中舉的例子:如果想進入登錄BBS 后的頁面,必須重新發(fā)起登錄的請求, 請求的地址可以在頭字段location 中得到。不過需要注意的是,有時候location 返回的可能是相對路徑,因此需要對location 返回的值做 一些處理才可以發(fā)起向新地址的請求。
另外除了在頭中包含的信息可能使頁面發(fā)生重定向外,在頁面中也有可能會發(fā)生頁面的重定向。引起頁面自動轉發(fā)的標簽是:。如果你想在程序中也處理這種情況的話得自己分析頁面來實現(xiàn)轉向。需要注意的是,在上面那個標簽中url 的值也可以是一個相對 地址,如果是這樣的話,需要對它做一些處理后才可以轉發(fā)。
,處理HTTPS 協(xié)議
HttpClient 提供了對SSL 的支持,在使用SSL 之前必須安裝JSSE 。在Sun 提供的1.4以后的版本中,JSSE 已經(jīng)集成到JDK 中,如 果你使用的是JDK1.4以前的版本則必須安裝JSSE 。JSSE 不同的廠家有不同的實現(xiàn)。下面介紹怎么使用HttpClient 來打開Https 連接。 這里有兩種方法可以打開https 連接,第一種就是得到服務器頒發(fā)的證書,然后導入到本地的keystore 中;另外一種辦法就是通過擴展 HttpClient 的類來實現(xiàn)自動接受證書。
方法1,取得證書,并導入本地的keystore :
安裝JSSE (如果你使用的JDK 版本是1.4或者1.4以上就可以跳過這一步)。本文以IBM 的JSSE 為例子說明。先到IBM 網(wǎng)站上下載JSSE 的安裝包。然后解 壓開之后將ibmjsse.jar 包拷貝到 home>libext目錄下。 取得并且導入證書。證書可以通過IE 來獲得: 1. 用IE 打開需要連接的https 網(wǎng)址,會彈出如下對話框: 2. 單擊"View Certificate",在彈出的對話框中選擇"Details" ,然后再單擊"Copy to File",根據(jù)提供的向導生成待訪問網(wǎng)頁的證書文件 3. 向導第一步,歡迎界面,直接單擊"Next" , 4. 向導第二步,選擇導出的文件格式,默認,單擊"Next" , 5. 向導第三步,輸入導出的文件名,輸入后,單擊"Next" , 6. 向導第四步,單擊"Finish" ,完成向導 7. 最后彈出一個對話框,顯示導出成功 用keytool 工具把剛才導出的證書倒入本地keystore 。Keytool 命令在 home>libsecurity目錄下,運行下面的命令: keytool -import -noprompt -keystore cacerts -storepass changeit -alias yourEntry1 -file your.cer 其中參數(shù)alias 后跟的值是當前證書在keystore 中的唯一標識符,但是大小寫不區(qū)分;參數(shù)file 后跟的是剛才通過IE 導出的證書所在的路徑和文件名;如果你想刪除剛才導入到keystore 的證書,可以用命令:   keytool -delete -keystore cacerts -storepass changeit -alias yourEntry1 寫程序訪問https 地址。如果想測試是否能連上https ,只需要稍改一下GetSample 例子,把請求的目標變成一個https 地址。    GetMethod getMethod = new GetMethod("your url");   運行該程序可能出現(xiàn)的問題: 1. 拋出異常java.net.SocketException: Algorithm SSL not available 。出現(xiàn)這個異??赡苁且驗闆]有加JSSEProvider ,如果用的是IBM 的JSSE Provider,在程序中加入這樣的一行: if(Security.getProvider("com.ibm.jsse.IBMJSSEProvider") == null) Security.addProvider(new IBMJSSEProvider()); 或者也可以打開 security.provider.2=com.ibm.crypto.provider.IBMJCE 后面加入security.provider.3=com.ibm.jsse.IBMJSSEProvider 2. 拋出異常java.net.SocketException: SSL implementation not available 。出現(xiàn)這個異??赡苁悄銢]有把ibmjsse.jar 拷貝到 3. 拋出異常javax.net.ssl.SSLHandshakeException: unknown certificate 。出現(xiàn)這個異常表明你的JSSE 應該已經(jīng)安裝正確,但是可能因為你沒有把證書導入到當前運行JRE 的keystore 中,請按照前 面介紹的步驟來導入你的證書。 方法2,擴展HttpClient 類實現(xiàn)自動接受證書