狀態(tài)碼 | 對(duì)應(yīng)HttpServletResponse的常量 | 詳細(xì)描述 |
301 | SC_MOVED_PERMANENTLY | 頁(yè)面已經(jīng)永久移到另外一個(gè)新地址 |
302 | SC_MOVED_TEMPORARILY | 頁(yè)面暫時(shí)移動(dòng)到另外一個(gè)新的地址 |
303 | SC_SEE_OTHER | 客戶端請(qǐng)求的地址必須通過(guò)另外的URL來(lái)訪問(wèn) |
307 | SC_TEMPORARY_REDIRECT | 同SC_MOVED_TEMPORARILY |
下面的代碼片段演示如何處理頁(yè)面的重定向
client.executeMethod(post);
????????System.out.println(post.getStatusLine().toString());?
????????post.releaseConnection();
????????
????????//檢查是否重定向
????????int?statuscode?=?post.getStatusCode();
????????if?((statuscode?==?HttpStatus.SC_MOVED_TEMPORARILY)?||
????????????(statuscode?==?HttpStatus.SC_MOVED_PERMANENTLY)?||
????????????(statuscode?==?HttpStatus.SC_SEE_OTHER)?||
(statuscode?==?HttpStatus.SC_TEMPORARY_REDIRECT))?{
//讀取新的URL地址
????????????Header?header?=?post.getResponseHeader("location");
????????????if?(header?!=?null)?{
????????????????String?newuri?=?header.getValue();
????????????????if?((newuri?==?null)?||?(newuri.equals("")))
????????????????????newuri?=?"/";?
????????????????GetMethod?redirect?=?new?GetMethod(newuri);
????????????????client.executeMethod(redirect);
????????????????System.out.println("Redirect:"+?redirect.getStatusLine().toString());?
????????????????redirect.releaseConnection();
????????????}?else?
????????????????System.out.println("Invalid?redirect");
????????}
我們可以自行編寫(xiě)兩個(gè)JSP頁(yè)面,其中一個(gè)頁(yè)面用response.sendRedirect方法重定向到另外一個(gè)頁(yè)面用來(lái)測(cè)試上面的例子。
4.?模擬輸入用戶名和口令進(jìn)行登錄
本小節(jié)應(yīng)該說(shuō)是HTTP客戶端編程中最常碰見(jiàn)的問(wèn)題,很多網(wǎng)站的內(nèi)容都只是對(duì)注冊(cè)用戶可見(jiàn)的,這種情況下就必須要求使用正確的用戶名和口令登錄成功后,方可瀏覽到想要的頁(yè)面。因?yàn)镠TTP協(xié)議是無(wú)狀態(tài)的,也就是連接的有效期只限于當(dāng)前請(qǐng)求,請(qǐng)求內(nèi)容結(jié)束后連接就關(guān)閉了。在這種情況下為了保存用戶的登錄信息必須使用到Cookie機(jī)制。以JSP/Servlet為例,當(dāng)瀏覽器請(qǐng)求一個(gè)JSP或者是Servlet的頁(yè)面時(shí),應(yīng)用服務(wù)器會(huì)返回一個(gè)參數(shù),名為jsessionid(因不同應(yīng)用服務(wù)器而異),值是一個(gè)較長(zhǎng)的唯一字符串的Cookie,這個(gè)字符串值也就是當(dāng)前訪問(wèn)該站點(diǎn)的會(huì)話標(biāo)識(shí)。瀏覽器在每訪問(wèn)該站點(diǎn)的其他頁(yè)面時(shí)候都要帶上jsessionid這樣的Cookie信息,應(yīng)用服務(wù)器根據(jù)讀取這個(gè)會(huì)話標(biāo)識(shí)來(lái)獲取對(duì)應(yīng)的會(huì)話信息。
對(duì)于需要用戶登錄的網(wǎng)站,一般在用戶登錄成功后會(huì)將用戶資料保存在服務(wù)器的會(huì)話中,這樣當(dāng)訪問(wèn)到其他的頁(yè)面時(shí)候,應(yīng)用服務(wù)器根據(jù)瀏覽器送上的?Cookie中讀取當(dāng)前請(qǐng)求對(duì)應(yīng)的會(huì)話標(biāo)識(shí)以獲得對(duì)應(yīng)的會(huì)話信息,然后就可以判斷用戶資料是否存在于會(huì)話信息中,如果存在則允許訪問(wèn)頁(yè)面,否則跳轉(zhuǎn)到登錄頁(yè)面中要求用戶輸入帳號(hào)和口令進(jìn)行登錄。這就是一般使用JSP開(kāi)發(fā)網(wǎng)站在處理用戶登錄的比較通用的方法。
這樣一來(lái),對(duì)于HTTP的客戶端來(lái)講,如果要訪問(wèn)一個(gè)受保護(hù)的頁(yè)面時(shí)就必須模擬瀏覽器所做的工作,首先就是請(qǐng)求登錄頁(yè)面,然后讀取Cookie值;再次請(qǐng)求登錄頁(yè)面并加入登錄頁(yè)所需的每個(gè)參數(shù);最后就是請(qǐng)求最終所需的頁(yè)面。當(dāng)然在除第一次請(qǐng)求外其他的請(qǐng)求都需要附帶上Cookie信息以便服務(wù)器能判斷當(dāng)前請(qǐng)求是否已經(jīng)通過(guò)驗(yàn)證。說(shuō)了這么多,可是如果你使用httpclient的話,你甚至連一行代碼都無(wú)需增加,你只需要先傳遞登錄信息執(zhí)行登錄過(guò)程,然后直接訪問(wèn)想要的頁(yè)面,跟訪問(wèn)一個(gè)普通的頁(yè)面沒(méi)有任何區(qū)別,因?yàn)轭怘ttpClient已經(jīng)幫你做了所有該做的事情了,太棒了!下面的例子實(shí)現(xiàn)了這樣一個(gè)訪問(wèn)的過(guò)程。
/*
?*?Created?on?2003-12-7?by?Liudong
?*/
package?http.demo;
?
import?org.apache.commons.httpclient.*;
import?org.apache.commons.httpclient.cookie.*;
import?org.apache.commons.httpclient.methods.*;
?
/**
?*?用來(lái)演示登錄表單的示例
?*?@author?Liudong
?*/
public?class?FormLoginDemo?{
?
????static?final?String?LOGON_SITE?=?"localhost";
????static?final?int????LOGON_PORT?=?8080;
????
????public?static?void?main(String[]?args)?throws?Exception{
????????HttpClient?client?=?new?HttpClient();
????????client.getHostConfiguration().setHost(LOGON_SITE,?LOGON_PORT);
???????
???????//模擬登錄頁(yè)面login.jsp->main.jsp
????????PostMethod?post?=?new?PostMethod("/main.jsp");
????????NameValuePair?name?=?new?NameValuePair("name",?"ld");?????
????????NameValuePair?pass?=?new?NameValuePair("password",?"ld");?????
????????post.setRequestBody(new?NameValuePair[]{name,pass});
???????int?status?=?client.executeMethod(post);
????????System.out.println(post.getResponseBodyAsString());
????????post.releaseConnection();??
???????
???????//查看cookie信息
????????CookieSpec?cookiespec?=?CookiePolicy.getDefaultSpec();
????????Cookie[]?cookies?=?cookiespec.match(LOGON_SITE,?LOGON_PORT,?"/",?false,?client.getState().getCookies());
???????if?(cookies.length?==?0)?{
???????????System.out.println("None");????
???????}?else?{
???????????for?(int?i?=?0;?i?<?cookies.length;?i++)?{
???????????????System.out.println(cookies[i].toString());????
???????????}
???????}
???????//訪問(wèn)所需的頁(yè)面main2.jsp
????????GetMethod?get?=?new?GetMethod("/main2.jsp");
????????client.executeMethod(get);
????????System.out.println(get.getResponseBodyAsString());
????????get.releaseConnection();
????}
}
5.?提交XML格式參數(shù)
提交XML格式的參數(shù)很簡(jiǎn)單,僅僅是一個(gè)提交時(shí)候的ContentType問(wèn)題,下面的例子演示從文件文件中讀取XML信息并提交給服務(wù)器的過(guò)程,該過(guò)程可以用來(lái)測(cè)試Web服務(wù)。
import?java.io.File;
import?java.io.FileInputStream;
?
import?org.apache.commons.httpclient.HttpClient;
import?org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import?org.apache.commons.httpclient.methods.PostMethod;
?
/**
?*?用來(lái)演示提交XML格式數(shù)據(jù)的例子
?*/
public?class?PostXMLClient?{
?
????public?static?void?main(String[]?args)?throws?Exception?{
????????File?input?=?new?File(“test.xml”);
????????PostMethod?post?=?new?PostMethod(“http://localhost:8080/httpclient/xml.jsp”);
????????//?設(shè)置請(qǐng)求的內(nèi)容直接從文件中讀取
????????post.setRequestBody(new?FileInputStream(input));
????????
????????if?(input.length()?<?Integer.MAX_VALUE)?
????????????post.setRequestContentLength(input.length());
????????else????????????post.setRequestContentLength(EntityEnclosingMethod.CONTENT_LENGTH_CHUNKED);
????????
????????//?指定請(qǐng)求內(nèi)容的類型
????????post.setRequestHeader("Content-type",?"text/xml;?charset=GBK");
????????
????????HttpClient?httpclient?=?new?HttpClient();?
????????int?result?=?httpclient.executeMethod(post);?
????????System.out.println("Response?status?code:?"?+?result);
????????System.out.println("Response?body:?");
????????System.out.println(post.getResponseBodyAsString());
????????post.releaseConnection();
????}
}
6.?通過(guò)HTTP上傳文件
httpclient使用了單獨(dú)的一個(gè)HttpMethod子類來(lái)處理文件的上傳,這個(gè)類就是MultipartPostMethod,該類已經(jīng)封裝了文件上傳的細(xì)節(jié),我們要做的僅僅是告訴它我們要上傳文件的全路徑即可,下面的代碼片段演示如何使用這個(gè)類。
MultipartPostMethod?filePost?=?new?MultipartPostMethod(targetURL);
filePost.addParameter("fileName",?targetFilePath);
HttpClient?client?=?new?HttpClient();
//由于要上傳的文件可能比較大,因此在此設(shè)置最大的連接超時(shí)時(shí)間
client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
int?status?=?client.executeMethod(filePost);
?
上面代碼中,targetFilePath即為要上傳的文件所在的路徑。
7.?訪問(wèn)啟用認(rèn)證的頁(yè)面
我們經(jīng)常會(huì)碰到這樣的頁(yè)面,當(dāng)訪問(wèn)它的時(shí)候會(huì)彈出一個(gè)瀏覽器的對(duì)話框要求輸入用戶名和密碼后方可,這種用戶認(rèn)證的方式不同于我們?cè)谇懊娼榻B的基于表單的用戶身份驗(yàn)證。這是HTTP的認(rèn)證策略,httpclient支持三種認(rèn)證方式包括:基本、摘要以及NTLM認(rèn)證。其中基本認(rèn)證最簡(jiǎn)單、通用但也最不安全;摘要認(rèn)證是在HTTP?1.1中加入的認(rèn)證方式,而NTLM則是微軟公司定義的而不是通用的規(guī)范,最新版本的NTLM是比摘要認(rèn)證還要安全的一種方式。
下面例子是從httpclient的CVS服務(wù)器中下載的,它簡(jiǎn)單演示如何訪問(wèn)一個(gè)認(rèn)證保護(hù)的頁(yè)面:
import?org.apache.commons.httpclient.HttpClient;
import?org.apache.commons.httpclient.UsernamePasswordCredentials;
import?org.apache.commons.httpclient.methods.GetMethod;
?
public?class?BasicAuthenticationExample?{
????public?BasicAuthenticationExample()?{
????}
????public?static?void?main(String[]?args)?throws?Exception?{
????????HttpClient?client?=?new?HttpClient();
????????client.getState().setCredentials(
????????????"www.verisign.com",
????????????"realm",
????????????new?UsernamePasswordCredentials("username",?"password")
????????);
????????GetMethod?get?=?new?GetMethod("https://www.verisign.com/products/index.html");
????????get.setDoAuthentication(?true?);
????????int?status?=?client.executeMethod(?get?);
????????System.out.println(status+"\n"+?get.getResponseBodyAsString());
????????get.releaseConnection();
????}
}
8.?多線程模式下使用httpclient
多線程同時(shí)訪問(wèn)httpclient,例如同時(shí)從一個(gè)站點(diǎn)上下載多個(gè)文件。對(duì)于同一個(gè)HttpConnection同一個(gè)時(shí)間只能有一個(gè)線程訪問(wèn),為了保證多線程工作環(huán)境下不產(chǎn)生沖突,httpclient使用了一個(gè)多線程連接管理器的類:?MultiThreadedHttpConnectionManager,要使用這個(gè)類很簡(jiǎn)單,只需要在構(gòu)造HttpClient實(shí)例的時(shí)候傳入即可,代碼如下:
MultiThreadedHttpConnectionManager?connectionManager?=?
???new?MultiThreadedHttpConnectionManager();
HttpClient?client?=?new?HttpClient(connectionManager);
以后盡管訪問(wèn)client實(shí)例即可。
參考資料:
httpclient首頁(yè):????http://jakarta.apache.org/commons/httpclient/
關(guān)于NTLM是如何工作:??http://davenport.sourceforge.net/ntlm.html?