1. 讀取網頁(HTTP/HTTPS)內容

下面是我們给出的一個簡單的例子用來訪問某個頁面

/*
* Created on 2003-12-14 by Liudong
*/

package http.demo;
import java.io.IOException;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;

/**
*最簡單的HTTP客戶端,用來演示通過GET或者POST方式訪問某個頁面

*/
public class SimpleClient {
public static void main(String[] args) throws IOException
{
HttpClient client = new HttpClient();
      // 設置代理服務器地址和端口      

      //client.getHostConfiguration().setProxy("proxy_host_addr",proxy_port);
      // 使用 GET 方法 ,如果服務器需要通過 HTTPS 連接,那只需要將下面 URL 中的 http 換成 https
         HttpMethodmethod=newGetMethod("http://java.sun.com");
      //使用POST方法
      //HttpMethod method = new PostMethod("http://java.sun.com");
      client.executeMethod(method);

      //打印服務器返回的狀態
       System.out.println(method.getStatusLine());
      //打印返回的信息
      System.out.println(method.getResponseBodyAsString());
      //釋放連接
      method.releaseConnection();
   }
}

在這個例子中首先創建一個HTTP客戶端(HttpClient)的實例,然後選擇提交的方法是GET或者POST,最後在HttpClient實例上執行提交的方法,最後從所選擇的提交方法中讀取服務器反饋回來的結果。這就是使用HttpClient的基本流程。其實用一行代碼也就可以搞定整個請求的過程,非常的簡單!

2. 以GET或者POST方式向網頁提交参數

其實前面一個最簡單的示例中我們已經介紹了如何使用GET或者POST方式來請求一個頁面,本小節與之不同的是多了提交時設定頁面所需的参數,我們知道如果是GET的請求方式,那麼所有参數都直接放到頁面的URL後面用問號與頁面地址隔開,每個参數用&隔開,例如:http://java.sun.com/?name=liudong&mobile=123456,但是當使用POST方法時就會稍微有一點點麻煩。本小節的例子演示向如何查詢手機號碼所在的城市,代碼如下:


/*
* Created on 2003-12-7 by Liudong
*/
package http.demo;
import java.io.IOException;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;

/**
*提交参數演示
*該程序連接到一個用於查詢手機號碼所屬地的頁面
*以便查詢號碼段1330227所在的省份以及城市

*/
public class SimpleHttpClient {
   public static void main(String[] args) throws IOException {
      HttpClient client = new HttpClient();
      client.getHostConfiguration().setHost( "www.imobile.com.cn" , 80, "http" );
      method = getPostMethod();    // 使用 POST 方式提交數據
      client.executeMethod(method);   //打印服務器返回的狀態
      System.out.println(method.getStatusLine());   //打印結果頁面
      Stringresponse=newString(method.getResponseBodyAsString().getBytes("8859_1"));

      //打印返回的信息
      System.out.println(response);
      method.releaseConnection();
   }

   /**
    * 使用 GET 方式提交數據
   
    */

   privatestaticHttpMethodgetGetMethod(){
      returnnewGetMethod("/simcard.php?simcard=1330227");
   }

    /**
     * 使用 POST 方式提交數據
    
     */
    private static HttpMethod getPostMethod(){
      PostMethod post = new PostMethod( "/simcard.php" );
      NameValuePair simcard = new NameValuePair( "simcard" , "1330227" );
      post.setRequestBody( new NameValuePair[] { simcard});
      return post;
   }
}

在上面的例子中頁面http://www.imobile.com.cn/simcard.php需要一個参數是simcard,這個参數值为手機號碼段,即手機號碼的前七位,服務器會返回提交的手機號碼對應的省份、城市以及其他詳細信息。GET的提交方法只需要在URL後加入参數信息,而POST則需要通過NameValuePair類來設置参數名稱和它所對應的值。

3. 處理頁面重定向

在JSP/Servlet編程中response.sendRedirect方法就是使用HTTP協議中的重定向機制。它與JSP中的<jsp:forward …>的區別在於後者是在服務器中實現頁面的跳轉,也就是說應用容器加載了所要跳轉的頁面的內容並返回给客戶端;而前者是返回一個狀態碼,這些狀態碼的可能值見下表,然後客戶端讀取需要跳轉到的頁面的URL並重新加載新的頁面。就是這样一個過程,所以我們編程的時候就要通過HttpMethod.getStatusCode()方法判斷返回值是否为下表中的某個值來判斷是否需要跳轉。如果已經確認需要進行頁面跳轉了,那麼可以通過讀取HTTP頭中的location屬性來獲取新的地址。

狀態碼

對應 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 地址
   Headerheader=post.getResponseHeader("location");
   if (header!=null){
      Stringnewuri=header.getValue();
      if((newuri==null)||(newuri.equals("")))
         newuri="/";
         GetMethodredirect=newGetMethod(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
      GetMethodget=newGetMethod("/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);

創作者介紹
創作者 shadow 的頭像
shadow

資訊園

shadow 發表在 痞客邦 留言(0) 人氣()