狀態(tài)碼 | 對應(yīng)HttpServletResponse的常量 | 詳細(xì)描述 |
301 | SC_MOVED_PERMANENTLY | 頁面已經(jīng)永久移到另外一個新地址 |
302 | SC_MOVED_TEMPORARILY | 頁面暫時移動到另外一個新的地址 |
303 | SC_SEE_OTHER | 客戶端請求的地址必須通過另外的URL來訪問 |
307 | SC_TEMPORARY_REDIRECT | 同SC_MOVED_TEMPORARILY |
下面的代碼片段演示如何處理頁面的重定向
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");
????????}
我們可以自行編寫兩個JSP頁面,其中一個頁面用response.sendRedirect方法重定向到另外一個頁面用來測試上面的例子。
4.?模擬輸入用戶名和口令進(jìn)行登錄
本小節(jié)應(yīng)該說是HTTP客戶端編程中最常碰見的問題,很多網(wǎng)站的內(nèi)容都只是對注冊用戶可見的,這種情況下就必須要求使用正確的用戶名和口令登錄成功后,方可瀏覽到想要的頁面。因為HTTP協(xié)議是無狀態(tài)的,也就是連接的有效期只限于當(dāng)前請求,請求內(nèi)容結(jié)束后連接就關(guān)閉了。在這種情況下為了保存用戶的登錄信息必須使用到Cookie機制。以JSP/Servlet為例,當(dāng)瀏覽器請求一個JSP或者是Servlet的頁面時,應(yīng)用服務(wù)器會返回一個參數(shù),名為jsessionid(因不同應(yīng)用服務(wù)器而異),值是一個較長的唯一字符串的Cookie,這個字符串值也就是當(dāng)前訪問該站點的會話標(biāo)識。瀏覽器在每訪問該站點的其他頁面時候都要帶上jsessionid這樣的Cookie信息,應(yīng)用服務(wù)器根據(jù)讀取這個會話標(biāo)識來獲取對應(yīng)的會話信息。
對于需要用戶登錄的網(wǎng)站,一般在用戶登錄成功后會將用戶資料保存在服務(wù)器的會話中,這樣當(dāng)訪問到其他的頁面時候,應(yīng)用服務(wù)器根據(jù)瀏覽器送上的?Cookie中讀取當(dāng)前請求對應(yīng)的會話標(biāo)識以獲得對應(yīng)的會話信息,然后就可以判斷用戶資料是否存在于會話信息中,如果存在則允許訪問頁面,否則跳轉(zhuǎn)到登錄頁面中要求用戶輸入帳號和口令進(jìn)行登錄。這就是一般使用JSP開發(fā)網(wǎng)站在處理用戶登錄的比較通用的方法。
這樣一來,對于HTTP的客戶端來講,如果要訪問一個受保護(hù)的頁面時就必須模擬瀏覽器所做的工作,首先就是請求登錄頁面,然后讀取Cookie值;再次請求登錄頁面并加入登錄頁所需的每個參數(shù);最后就是請求最終所需的頁面。當(dāng)然在除第一次請求外其他的請求都需要附帶上Cookie信息以便服務(wù)器能判斷當(dāng)前請求是否已經(jīng)通過驗證。說了這么多,可是如果你使用httpclient的話,你甚至連一行代碼都無需增加,你只需要先傳遞登錄信息執(zhí)行登錄過程,然后直接訪問想要的頁面,跟訪問一個普通的頁面沒有任何區(qū)別,因為類HttpClient已經(jīng)幫你做了所有該做的事情了,太棒了!下面的例子實現(xiàn)了這樣一個訪問的過程。
/*
?*?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.*;
?
/**
?*?用來演示登錄表單的示例
?*?@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);
???????
???????//模擬登錄頁面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());????
???????????}
???????}
???????//訪問所需的頁面main2.jsp
????????GetMethod?get?=?new?GetMethod("/main2.jsp");
????????client.executeMethod(get);
????????System.out.println(get.getResponseBodyAsString());
????????get.releaseConnection();
????}
}
5.?提交XML格式參數(shù)
提交XML格式的參數(shù)很簡單,僅僅是一個提交時候的ContentType問題,下面的例子演示從文件文件中讀取XML信息并提交給服務(wù)器的過程,該過程可以用來測試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;
?
/**
?*?用來演示提交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è)置請求的內(nèi)容直接從文件中讀取
????????post.setRequestBody(new?FileInputStream(input));
????????
????????if?(input.length()?<?Integer.MAX_VALUE)?
????????????post.setRequestContentLength(input.length());
????????else????????????post.setRequestContentLength(EntityEnclosingMethod.CONTENT_LENGTH_CHUNKED);
????????
????????//?指定請求內(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.?通過HTTP上傳文件
httpclient使用了單獨的一個HttpMethod子類來處理文件的上傳,這個類就是MultipartPostMethod,該類已經(jīng)封裝了文件上傳的細(xì)節(jié),我們要做的僅僅是告訴它我們要上傳文件的全路徑即可,下面的代碼片段演示如何使用這個類。
MultipartPostMethod?filePost?=?new?MultipartPostMethod(targetURL);
filePost.addParameter("fileName",?targetFilePath);
HttpClient?client?=?new?HttpClient();
//由于要上傳的文件可能比較大,因此在此設(shè)置最大的連接超時時間
client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
int?status?=?client.executeMethod(filePost);
?
上面代碼中,targetFilePath即為要上傳的文件所在的路徑。
7.?訪問啟用認(rèn)證的頁面
我們經(jīng)常會碰到這樣的頁面,當(dāng)訪問它的時候會彈出一個瀏覽器的對話框要求輸入用戶名和密碼后方可,這種用戶認(rèn)證的方式不同于我們在前面介紹的基于表單的用戶身份驗證。這是HTTP的認(rèn)證策略,httpclient支持三種認(rèn)證方式包括:基本、摘要以及NTLM認(rèn)證。其中基本認(rèn)證最簡單、通用但也最不安全;摘要認(rèn)證是在HTTP?1.1中加入的認(rèn)證方式,而NTLM則是微軟公司定義的而不是通用的規(guī)范,最新版本的NTLM是比摘要認(rèn)證還要安全的一種方式。
下面例子是從httpclient的CVS服務(wù)器中下載的,它簡單演示如何訪問一個認(rèn)證保護(hù)的頁面:
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
多線程同時訪問httpclient,例如同時從一個站點上下載多個文件。對于同一個HttpConnection同一個時間只能有一個線程訪問,為了保證多線程工作環(huán)境下不產(chǎn)生沖突,httpclient使用了一個多線程連接管理器的類:?MultiThreadedHttpConnectionManager,要使用這個類很簡單,只需要在構(gòu)造HttpClient實例的時候傳入即可,代碼如下:
MultiThreadedHttpConnectionManager?connectionManager?=?
???new?MultiThreadedHttpConnectionManager();
HttpClient?client?=?new?HttpClient(connectionManager);
以后盡管訪問client實例即可。
參考資料:
httpclient首頁:????http://jakarta.apache.org/commons/httpclient/
關(guān)于NTLM是如何工作:??http://davenport.sourceforge.net/ntlm.html?