【傳輸協議】HttpClient基本使用

Love Lenka發表於2018-04-18

最近工作中是做了一個handoop的hdfs系統的檔案瀏覽器的功能,是利用webhdfs提供的rest api來訪問hdfs來與hdfs進行互動的,其中大量使用HttpClient,之前一直很忙,沒什麼時間來總結,今天閒下來了,可以來好好總結一下這個東西了。

1.HttpClient簡介

       http協議可以說是現在Internet上面最重要,使用最多的協議之一了,越來越多的java應用需要使用http協議來訪問網路資源,特別是現在rest api的流行,HttpClient 是 Apache Jakarta Common 下的子專案,用來提供高效的、最新的、功能豐富的支援 HTTP 協議的客戶端程式設計工具包,並且它支援 HTTP 協議最新的版本和建議。HttpClient 已經應用在很多的專案中,比如 Apache Jakarta 上很著名的另外兩個開源專案 Cactus 和 HTMLUnit 都使用了 HttpClient,很多的java的爬蟲也是透過HttpClient實現的,研究HttpClient對我們來說是非常重要的。

2.HttpClient不是瀏覽器     

        很多人覺得既然HttpClient是一個HTTP客戶端程式設計工具,很多人把他當做瀏覽器來理解,但是其實HttpClient不是瀏覽器,它是一個HTTP通訊庫,因此它只提供一個通用瀏覽器應用程式所期望的功能子集,最根本的區別是HttpClient中沒有使用者介面,瀏覽器需要一個渲染引擎來顯示頁面,並解釋使用者輸入,例如滑鼠點選顯示頁面上的某處,有一個佈局引擎,計算如何顯示HTML頁面,包括級聯樣式表和影像。javascript直譯器執行嵌入HTML頁面或從HTML頁面引用的javascript程式碼。來自使用者介面的事件被傳遞到javascript直譯器進行處理。除此之外,還有用於外掛的介面,可以處理Applet,嵌入式媒體物件(如pdf檔案,Quicktime電影和Flash動畫)或ActiveX控制元件(可以執行任何操作)。HttpClient只能以程式設計的方式透過其API用於傳輸和接受HTTP訊息。HttpClient也是完全內容不可知的。

       另一個主要區別是對錯誤輸入或HTTP標準違規的容忍。 需要允許無效的使用者輸入,以使瀏覽器使用者友好。 還需要對從伺服器檢索的畸形文件的容忍度,以及在執行協議時伺服器行為的缺陷,使盡可能多的使用者可訪問的網站。 然而,HttpClient努力在預設情況下儘可能接近並遵守HTTP標準規範和相關標準。 它還提供了一些手段來放鬆規範所施加的一些限制,這些限制允許或要求與不相容的HTTP源或代理伺服器相容。

3.HttpClient入門使用

        注意這個版本主要是基於HttpClient4.5.2版本的來講解的,也是現在最新的版本,之所以要提供版本說明的是因為HttpClient 3版本和HttpClient 4版本差別還是很多大的,基本HttpClient裡面的介面都變了,你把HttpClient 3版本的程式碼拿到HttpClient 4上面都執行不起來,會報錯的。所以這兒一定要注意,好了廢話不多說了,開始。

3.1.在pom.xml加入對httpclient的必需的jar包的依賴

   //httpclient的介面基本都在這兒 
   <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5.2</version>
    </dependency>
    //httpclient快取
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient-cache</artifactId>
      <version>4.5</version>
    </dependency>
    //http的mime型別都在這裡面
    <dependency>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpmime</artifactId>
     <version>4.3.2</version>
    </dependency>

注意:常見的MIME型別(通用型):

    超文字標記語言文字 .html text/html

    xml文件 .xml text/xml

    XHTML文件 .xhtml application/xhtml+xml

    普通文字 .txt text/plain

    RTF文字 .rtf application/rtf

    PDF文件 .pdf application/pdf

    Microsoft Word檔案 .word application/msword

    PNG影像 .png image/png

    GIF圖形 .gif image/gif

    JPEG圖形 .jpeg,.jpg image/jpeg

    au聲音檔案 .au audio/basic

    MIDI音樂檔案 mid,.midi audio/midi,audio/x-midi

    RealAudio音樂檔案 .ra, .ram audio/x-pn-realaudio

    MPEG檔案 .mpg,.mpeg video/mpeg

    AVI檔案 .avi video/x-msvideo

    GZIP檔案 .gz application/x-gzip

    TAR檔案 .tar application/x-tar

    任意的二進位制資料 application/octet-stream

3.2.抓取網頁的內容並列印到控制檯的demo

先直接貼程式碼:

package fangdd.HttpClientDemo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Locale;

import org.apache.http.HttpEntity;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class HttpGetNewSample {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String url="http://www.baidu.com";

      
        //1.使用預設的配置的httpclient
        CloseableHttpClient client = HttpClients.createDefault();
        //2.使用get方法
        HttpGet httpGet = new HttpGet(url);
        InputStream inputStream = null;
        CloseableHttpResponse response = null;

        try {
            //3.執行請求,獲取響應
            response = client.execute(httpGet);
               

            //看請求是否成功,這兒列印的是http狀態碼
            System.out.println(response.getStatusLine().getStatusCode());
            //4.獲取響應的實體內容,就是我們所要抓取得網頁內容
            HttpEntity entity = response.getEntity();

            //5.將其列印到控制檯上面
            //方法一:使用EntityUtils
            if (entity != null) {
                System.out.println(EntityUtils.toString(entity, "utf-8"));
            }
            EntityUtils.consume(entity);
            
            //方法二  :使用inputStream
           /* if (entity != null) {
                inputStream = entity.getContent();

                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                String line = "";
                while ((line = bufferedReader.readLine()) != null) {
                    System.out.println(line);

                }
            }*/

        } catch (UnsupportedOperationException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }

    }

}

3.3HttpClient編寫程式流程總結

其實從上面我們就可以總結出使用HttpClient其實分為6個步驟

1.建立HttpClient物件

       這兒使用的是org.apache.http.impl.client.CloseableHttpClient,他是HttpClient介面的一個例項,建立該物件的最簡單方法是CloseableHttpClient client = HttpClients.createDefault();

HttpClients是建立CloseableHttpClient的工廠,採用預設的配置來建立例項,一般情況下我們就用這個預設的例項就足夠,後面我們可以去看下怎麼定製自己需求配置的來建立HttpClient介面的例項。如果你去看這個函式的原始碼,你可以看到org.apache.http.client.CookieStore,org.apache.http.client.config.RequestConfig等等都是採用預設的。後面我們會專門有篇部落格探討怎麼根據自己的需求定製httpclient。

2.建立某種請求方法的例項

      建立某種請求的例項,並指定請求的url,如果是get請求,建立物件HttpGet,如果是post 請求,建立物件HttpPost。型別的還有 HttpHead, HttpPost, HttpPut, HttpDelete, HttpTrace, 還有 HttpOptions。分別對應HEAD、POST PUT、DELETE、TRACE、OPTIONS方法,每個方法是做什麼的如下表:

方法 描述 是否包含主體
GET 從伺服器獲取一份文件
HEAD 只從伺服器獲取文件的首部
POST 向伺服器傳送需要處理的資料
PUT 將請求的主體部分儲存在伺服器上
TRACE 對可能經過代理伺服器傳送到伺服器上去的報文進行追蹤
OPTIONS  決定可以在伺服器上執行哪些方法
DELETE 從伺服器上刪除一份文件

可以看得到在Http協議中,只有post方法和put方法的請求裡面有實體

3.如果有請求引數的話,Get方法直接寫在url後面,例如

HttpGet httpget = new HttpGet(
      “http://www.google.com/search?hl=zh-CN&q=httpclient&btnG=Google+Search&aq=f&oq=”);

 或者使用setParameter來設定引數

URI uri = new URIBuilder()
         .setScheme(“http”)
         .setHost(“www.google.com”)
         .setPath(“/ search”)
         .setParameter(“q”,“httpclient”)
         .setParameter(“btnG”,“Google搜尋”)
         .setParameter(“aq”,“f”)
         .setParameter(“oq”,“”)
         。建立();
 HttpGet httpget = new HttpGet(uri);
 System.out.println(httpget.getURI());

stdout>

http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq=

post方法用setEntity(HttpEntity entity)方法來設定請求引數。

後面會詳細的探討Entity這個東西,專門會有一篇部落格的,這兒就不在贅敘。

4.傳送請求。

        呼叫CloseableHttpClient物件的execute(HttpUriRequest request)傳送請求,該方法返回一個CloseableHttpResponse物件。

        CloseableHttpResponse response = client.execute(post);,很明顯CloseableHttpResponse就是用了處理返回資料的實體,透過它我們可以拿到返回的狀態碼、首部、實體等等我們需要的東西。

5.獲取請求結果。

        呼叫CloseableHttpResponse的getAllHeaders()、getHeaders(String name)等方法可獲取伺服器的響應頭;呼叫CloseableHttpResponse的getEntity()方法可獲取HttpEntity物件,該物件包裝了伺服器的響應內容。程式可透過該物件獲取伺服器的響應內容。

 HttpEntity entity = response.getEntity();
            //5.將其列印到顯示器上面
             //方法一:使用EntityUtils
              /*
            if(entity!=null)
            {
                System.out.println(EntityUtils.toString(entity,"utf-8"));
            }
            
            EntityUtils.consume(entity)
            */
            //方法二  
            InputStream inputStream = entity.getContent();

            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String line = "";
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);

            }

透過CloseableHttpEntity的getEntity取得實體之後,有兩種處理結果的方法,

方法一:使用EntityUtils來處理。

       該類是官方提供的一個處理實體的工具類,toSting方法將返回的實體轉換為字串,但是官網不建議使用這個,除非響應實體從一個可信HTTP伺服器發起和已知是有限長度的。

方法二:使用InputStream來讀取

       因為httpEntity.getContent方法返回的就是InputStream型別。這種方法是官網推薦的方式,需要記得的是要自己釋放底層資源。


6.關閉連線,釋放資源。

如果是使用EntityUtils來處理實體的使用    EntityUtils.consume(entity)來釋放資源,可以看得到該函式原始碼為:

 public static void consume(final HttpEntity entity) throws IOException {
        if (entity == null) {
            return;
        }
        if (entity.isStreaming()) {
            final InputStream instream = entity.getContent();
            if (instream != null) {
                instream.close();
            }
        }
}

其實還是透過關閉inputStream,然後最後我們再關閉CloseableHttpResponse就可以了

 

如果是使用InputStream來處理實體的,釋放程式碼如下

CloseableHttpClient httpclient = HttpClients.createDefault();
 HttpGet httpget = new HttpGet(“http:// localhost /”);
 CloseableHttpResponse response = httpclient.execute(httpget);
try{
     HttpEntity entity = response.getEntity();
     if(entity!= null){
         InputStream instream = entity.getContent();
        try{
             //做一些有用的事情
         } finally {
             intream.close();
         }}
     }}
 } finally {
     response.close();
 }}

關閉內容流和關閉響應之間的區別是:前者將嘗試透過消耗實體內容來保持底層連線活動,而後者立即關閉並丟棄連線

至此,一個基本的HttpClient的使用的例子已經成功完成,文章還有很多不足,希望得到大家的批評和指正。

相關文章