gembin

          OSGi, Eclipse Equinox, ECF, Virgo, Gemini, Apache Felix, Karaf, Aires, Camel, Eclipse RCP

          HBase, Hadoop, ZooKeeper, Cassandra

          Flex4, AS3, Swiz framework, GraniteDS, BlazeDS etc.

          There is nothing that software can't fix. Unfortunately, there is also nothing that software can't completely fuck up. That gap is called talent.

          About Me

           

          HttpClient 學(xué)習(xí)整理[zhuan]

          轉(zhuǎn)自:http://www.aygfsteel.com/Alpha/archive/2007/01/22/95216.html
          Good  Article
          HttpClient 是我最近想研究的東西,以前想過的一些應(yīng)用沒能有很好的實現(xiàn),發(fā)現(xiàn)這個開源項目之后就有點眉目了,令人頭痛的cookie問題還是有辦法解決滴。在網(wǎng)上整理了一些東西,寫得很好,寄放在這里。


          HTTP 協(xié)議可能是現(xiàn)在 Internet 上使用得最多、最重要的協(xié)議了,越來越多的 Java 應(yīng)用程序需要直接通過 HTTP 協(xié)議來訪問網(wǎng)絡(luò)資源。雖然在 JDK 的 java.net 包中已經(jīng)提供了訪問 HTTP 協(xié)議的基本功能,但是對于大部分應(yīng)用程序來說,JDK 庫本身提供的功能還不夠豐富和靈活。HttpClient 是 Apache Jakarta Common 下的子項目,用來提供高效的、最新的、功能豐富的支持 HTTP 協(xié)議的客戶端編程工具包,并且它支持 HTTP 協(xié)議最新的版本和建議。HttpClient 已經(jīng)應(yīng)用在很多的項目中,比如 Apache Jakarta 上很著名的另外兩個開源項目 Cactus 和 HTMLUnit 都使用了 HttpClient,更多使用 HttpClient 的應(yīng)用可以參見http://wiki.apache.org/jakarta-httpclient/HttpClientPowered。HttpClient 項目非常活躍,使用的人還是非常多的。目前 HttpClient 版本是在 2005.10.11 發(fā)布的 3.0 RC4 。

          ------------------------------------

          應(yīng)用HttpClient來對付各種頑固的WEB服務(wù)器
          轉(zhuǎn)自:http://blog.csdn.net/ambitiontan/archive/2006/01/06/572171.aspx

          一 般的情況下我們都是使用IE或者Navigator瀏覽器來訪問一個WEB服務(wù)器,用來瀏覽頁面查看信息或者提交一些數(shù)據(jù)等等。所訪問的這些頁面有的僅僅 是一些普通的頁面,有的需要用戶登錄后方可使用,或者需要認(rèn)證以及是一些通過加密方式傳輸,例如HTTPS。目前我們使用的瀏覽器處理這些情況都不會構(gòu)成 問題。不過你可能在某些時候需要通過程序來訪問這樣的一些頁面,比如從別人的網(wǎng)頁中“偷”一些數(shù)據(jù);利用某些站點提供的頁面來完成某種功能,例如說我們想 知道某個手機號碼的歸屬地而我們自己又沒有這樣的數(shù)據(jù),因此只好借助其他公司已有的網(wǎng)站來完成這個功能,這個時候我們需要向網(wǎng)頁提交手機號碼并從返回的頁 面中解析出我們想要的數(shù)據(jù)來。如果對方僅僅是一個很簡單的頁面,那我們的程序會很簡單,本文也就沒有必要大張旗鼓的在這里浪費口舌。但是考慮到一些服務(wù)授 權(quán)的問題,很多公司提供的頁面往往并不是可以通過一個簡單的URL就可以訪問的,而必須經(jīng)過注冊然后登錄后方可使用提供服務(wù)的頁面,這個時候就涉及到 COOKIE問題的處理。我們知道目前流行的動態(tài)網(wǎng)頁技術(shù)例如ASP、JSP無不是通過COOKIE來處理會話信息的。為了使我們的程序能使用別人所提供 的服務(wù)頁面,就要求程序首先登錄后再訪問服務(wù)頁面,這過程就需要自行處理cookie,想想當(dāng)你用java.net.HttpURLConnection 來完成這些功能時是多么恐怖的事情啊!況且這僅僅是我們所說的頑固的WEB服務(wù)器中的一個很常見的“頑固”!再有如通過HTTP來上傳文件呢?不需要頭 疼,這些問題有了“它”就很容易解決了!

          我們不可能列舉所有可能的頑固,我們會針對幾種最常見 的問題進行處理。當(dāng)然了,正如前面說到的,如果我們自己使用java.net.HttpURLConnection來搞定這些問題是很恐怖的事情,因此在 開始之前我們先要介紹一下一個開放源碼的項目,這個項目就是Apache開源組織中的httpclient,它隸屬于Jakarta的commons項 目,目前的版本是2.0RC2。commons下本來已經(jīng)有一個net的子項目,但是又把httpclient單獨提出來,可見http服務(wù)器的訪問絕非 易事。

          Commons-httpclient項目就是專門設(shè)計來簡化HTTP客戶端與服務(wù)器進行各種通訊編程。通過它可以讓原來很 頭疼的事情現(xiàn)在輕松的解決,例如你不再管是HTTP或者HTTPS的通訊方式,告訴它你想使用HTTPS方式,剩下的事情交給httpclient替你完 成。本文會針對我們在編寫HTTP客戶端程序時經(jīng)常碰到的幾個問題進行分別介紹如何使用httpclient來解決它們,為了讓讀者更快的熟悉這個項目我 們最開始先給出一個簡單的例子來讀取一個網(wǎng)頁的內(nèi)容,然后循序漸進解決掉前進中的所有問題。

          1. 讀取網(wǎng)頁(HTTP/HTTPS)內(nèi)容

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

          /*
          * 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方式訪問某個頁面
            *@authorLiudong
          */

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

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

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

           

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


          2. 以GET或者POST方式向網(wǎng)頁提交參數(shù)

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

           

          /*
          * 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.*;

          /** 
           *提交參數(shù)演示
           *該程序連接到一個用于查詢手機號碼所屬地的頁面
           *以便查詢號碼段1330227所在的省份以及城市
           *@authorLiudong
           */

          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 方式提交數(shù)據(jù) 
                client.executeMethod(method);   //打印服務(wù)器返回的狀態(tài) 
                System.out.println(method.getStatusLine());   //打印結(jié)果頁面
                Stringresponse=newString(method.getResponseBodyAsString().getBytes("8859_1"));

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

             /** 
              * 使用 GET 方式提交數(shù)據(jù) 
              *@return 
              */

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

              /** 
               * 使用 POST 方式提交數(shù)據(jù) 
               *@return 
               */

              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需 要一個參數(shù)是simcard,這個參數(shù)值為手機號碼段,即手機號碼的前七位,服務(wù)器會返回提交的手機號碼對應(yīng)的省份、城市以及其他詳細(xì)信息。GET的提交 方法只需要在URL后加入?yún)?shù)信息,而POST則需要通過NameValuePair類來設(shè)置參數(shù)名稱和它所對應(yīng)的值。

          3. 處理頁面重定向

          在JSP/Servlet 編程中response.sendRedirect方法就是使用HTTP協(xié)議中的重定向機制。它與JSP中的<jsp:forward …>的區(qū)別在于后者是在服務(wù)器中實現(xiàn)頁面的跳轉(zhuǎn),也就是說應(yīng)用容器加載了所要跳轉(zhuǎn)的頁面的內(nèi)容并返回給客戶端;而前者是返回一個狀態(tài)碼,這些狀態(tài)碼 的可能值見下表,然后客戶端讀取需要跳轉(zhuǎn)到的頁面的URL并重新加載新的頁面。就是這樣一個過程,所以我們編程的時候就要通過 HttpMethod.getStatusCode()方法判斷返回值是否為下表中的某個值來判斷是否需要跳轉(zhuǎn)。如果已經(jīng)確認(rèn)需要進行頁面跳轉(zhuǎn)了,那么可 以通過讀取HTTP頭中的location屬性來獲取新的地址。

          狀態(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 地址 
             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. 模擬輸入用戶名和口令進行登錄

          本 小節(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)到登錄頁面中要求用戶 輸入帳號和口令進行登錄。這就是一般使用JSP開發(fā)網(wǎng)站在處理用戶登錄的比較通用的方法。

          這樣一來,對于HTTP的客戶端 來講,如果要訪問一個受保護的頁面時就必須模擬瀏覽器所做的工作,首先就是請求登錄頁面,然后讀取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 
                GetMethodget=newGetMethod("/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)證保護的頁面:

          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

          posted on 2008-03-26 18:38 gembin 閱讀(2259) 評論(0)  編輯  收藏 所屬分類: JavaEE

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿(6)

          隨筆分類(440)

          隨筆檔案(378)

          文章檔案(6)

          新聞檔案(1)

          相冊

          收藏夾(9)

          Adobe

          Android

          AS3

          Blog-Links

          Build

          Design Pattern

          Eclipse

          Favorite Links

          Flickr

          Game Dev

          HBase

          Identity Management

          IT resources

          JEE

          Language

          OpenID

          OSGi

          SOA

          Version Control

          最新隨筆

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          free counters
          主站蜘蛛池模板: 衡水市| 综艺| 谷城县| 云南省| 宿州市| 江油市| 二连浩特市| 酉阳| 含山县| 会泽县| 文成县| 辉南县| 高平市| 利川市| 汤原县| 保德县| 永年县| 甘孜| 新兴县| 连平县| 安乡县| 赣榆县| 通渭县| 永州市| 邯郸市| 武冈市| 三河市| 和平县| 容城县| 辉县市| 锦屏县| 涿鹿县| 克山县| 龙川县| 子长县| 昌江| 大同县| 娄烦县| 阜新市| 农安县| 五河县|