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