HttpClient4.5學習梳理以及驗證

致敬大神:http://blog.csdn.net/column/details/httpclient.htmljavascript

如下只是對大神做品學習以及驗證:html

《一》明白HttpClient可以作什麼,這個很重要java

        超文本傳輸協議(HTTP)也許是當今互聯網上所使用的最重要的協議了。Web services,聯網設備和網絡計算的發展,都持續擴展了HTTP協議的角色,超越了用戶使用的Web瀏覽器範疇,同時,也增長了須要HTTP協議支持的應用程序的數量。
      儘管java.net包提供了經過HTTP訪問資源的基本功能,但它缺乏足夠的靈活性和其它不少應用程序須要的功能。HttpClient經過提供一個有效的,保持更新的,功能豐富的軟件包來實現客戶端最新的HTTP標準和建議,來彌補java.net包的在某些技術上的空白。 
         HttpClient爲擴展而設計,同時爲基本的HTTP協議提供強大的支持。有一些人會對HttpClient感興趣,這些人一般是構建 HTTP 客戶端應用程序(好比web瀏覽器,web服務客戶端,利用或擴展 HTTP 協議進行來實現的分佈式通訊系統)的開發人員。
1.HttpClient 的範圍
●基於HttpCore[http://hc.apache.org/httpcomponents-core/index.html]的客戶端HTTP通訊庫
●基於經典(阻塞) I/O
●內容無關   --我理解爲不關注內容
2.HttpClient 所不能作的
●HttpClient 不是一個瀏覽器。它是一個客戶端的 HTTP 通訊實現庫。HttpClient 的目標是發送和接收HTTP 報文。HttpClient 不會去處理內容,執行嵌入在 HTML頁面中的javascript 代碼,猜想內容類型,若是沒有明確設置,不然不會從新格式化請求/重定向URI,或其它和HTTP通訊無關的功能。web

------------------------------------------------------------------------------------------------------apache

(1)HttpClient最基本的功能是執行HTTP方法;json

(2)一個 HTTP 方法的執行包含一個或多個 HTTP 請求/HTTP 相應的交換,一般由 HttpClient的內部來處理。數組

(3)使用者被要求提供一個Request對象來執行,HttpClient就會把請求傳送給目標服務器並返回一個相對應的response對象,若是執行不成功,將會拋出一個異常。瀏覽器

--顯然,HttpClient API 的主要切入點就是定義描述上述契約的HttpClient接口。緩存

下邊就說服務器

1>什麼是HTTP請求(Request對象)?

2>什麼是HTTP相應的交換

3>HTTP報文

4>Request對象處理

5>Response對象處理

 

 

《1》什麼是HTTP請求(Request對象)?

全部 HTTP 請求都有一個請求起始行,這個起始行由方法名,請求 URI 和 HTTP 協議版本組成。HttpClient很好地支持了HTTP/1.1規範中全部的HTTP方法:GET,HEAD, POST,PUT, DELETE, TRACE 和 OPTIONS。每一個方法都有一個特別的類:HttpGet,HttpHead, HttpPost,HttpPut,HttpDelete,HttpTrace和HttpOptions。URI是統一資源標識符的縮寫,用來標識與請求相符合的資源。HTTP 請求URI包含了一個協議名稱,主機名,可選端口,資源路徑,可選的參數,可選的片斷。

寫法以下:

HttpGet httpget = new HttpGet(  

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

HttpClient提供了URIBuilder工具類來簡化建立、修改請求 URI。

因此上邊寫法還能夠改爲

URI uri = new URIBuilder()  

          .setScheme("http")  

          .setHost("www.google.com";)  

          .setPath("/search")  

          .setParameter("q", "httpclient")  

          .setParameter("btnG", "Google Search")  

          .setParameter("aq", "f")  

          .setParameter("oq", "")  

          .build();  

HttpGet httpget = new HttpGet(uri);

 

2>什麼是HTTP相應的交換

HttpClient表明HTTP請求執行的最基本約定。它沒有強加限制或具體細節給請求執行過程,它保留了鏈接管理,狀態管理,認證,重定向等處理細節的我的實現。使用額外的功能來裝飾這個接口是很是容易的,好比設置響應體緩存。

HttpClient實現做爲特殊目的的處理器或策略接口的門面,負責處理HTTP協議特定的方面,好比重定向處理,認證處理,爲鏈接持久化作決定或者保持持續鏈接。這使得用戶使用自定義的、應用程序特定方面的實現來取代那些默認的實現。

這一塊涉及不少細節,後邊再單獨一一列舉講解

 

3>HTTP報文

http報文專門單列出來說。。

 

 

4>Request對象處理(HTTP 響應(Response))

HTTP 響應是服務器接收並解析請求信息後返回給客戶端的信息,它的起始行包含了一個協議版本,一個狀態碼和描述狀態的短語。

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1  

                             ,HttpStatus.SC_OK, "OK");  

System.out.println(response.getProtocolVersion());  

System.out.println(response.getStatusLine().getStatusCode());  

System.out.println(response.getStatusLine().getReasonPhrase());  

System.out.println(response.getStatusLine().toString());  

 

處理報文首部(Headers)

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC_OK, "OK");  

response.addHeader("Set-Cookie","c1=a; path=/; domain=localhost");  

response.addHeader("Set-Cookie","c2=b; path=\"/\", c3=c; domain=\"localhost\"");  

Header h1 = response.getFirstHeader("Set-Cookie");  

System.out.println(h1);  

Header h2 = response.getLastHeader("Set-Cookie");  

System.out.println(h2);  

Header[] hs = response.getHeaders("Set-Cookie");  

System.out.println(hs.length);

 

得到全部指定類型首部最有效的方式是使用HeaderIterator接口

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC_OK, "OK");  

response.addHeader("Set-Cookie","c1=a; path=/; domain=localhost");  

response.addHeader("Set-Cookie","c2=b; path=\"/\", c3=c; domain=\"localhost\"");  

HeaderIterator it = response.headerIterator("Set-Cookie");  

while (it.hasNext()) {  

     System.out.println(it.next());  

}

HttpClient也提供了其餘便利的方法吧HTTP報文轉化爲單個的HTTP元素。

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC_OK, "OK");  

response.addHeader("Set-Cookie","c1=a; path=/; domain=localhost");  

response.addHeader("Set-Cookie","c2=b; path=\"/\", c3=c; domain=\"localhost\"");  

HeaderElementIterator it = new BasicHeaderElementIterator(  

response.headerIterator("Set-Cookie"));  

while (it.hasNext()) {  

     HeaderElement elem = it.nextElement();  

     System.out.println(elem.getName() + " = " + elem.getValue());  

     NameValuePair[] params = elem.getParameters();  

     for (int i = 0; i < params.length; i++) {  

          System.out.println(" " + params[i]);  

     }  

}

 

HTTP實體(HTTP Entity)

HTTP報文可以攜帶與請求或響應相關聯的內容實體。

實體存在於某些請求、響應中,他們是可選的。

 

使用實體的請求被稱爲內含實體請求;HTTP規範定義了兩種內含實體請求,POST和PUT。

而響應老是內含實體。但有些響應不符合這一規則,好比,對HEAD方法的響應和狀態爲204 No Content, 304 Not Modified, 205 Reset Content的響應。

 

依據實體的內容來源,HttpClient區分出三種實體:

流式實體(streamed):內容來源於一個流,或者在運行中產生。特別的,這個類別包括從響應中接收到的實體。流式實體不可重複。

自包含實體(self-contained):在內存中的內容或者經過獨立的鏈接/其餘實體得到的內容。自包含實體是可重複的。這類實體大部分是HTTP內含實體請求。

包裝實體(wrapping):從另一個實體中獲取內容

 

 

使用HTTP實體

因爲一個實體可以表現爲二進制和字符內容,它支持二進制編碼(爲了支持後者,即字符內容)。

實體將會在一些狀況下被建立:當執行一個含有內容的請求時或者當請求成功,響應體做爲結果返回給客戶端時。爲了讀取實體的內容,能夠經過HttpEntity#getContent() 方法取出輸入流,返回一個java.io.InputStream,或者提供一個輸出流給HttpEntity#writeTo(OutputStream) 方法,它將會返回寫入給定流的全部內容。

當實體內部含有信息時,使用HttpEntity#getContentType()和HttpEntity#getContentLength()方法將會讀取一些基本的元數據,好比Content-Type和Content-Length這樣的首部(若是他們可用的話),因爲Content-Type首部可以包含文本MIME類型(像 text/plain 或text/html),它也包含了與MIME類型相對應的字符編碼,HttpEntity#getContentEncoding()方法被用來讀取這些字符編碼。若是對應的首部不存在,則Content-Length的返回值爲-1,Content-Type返回值爲NULL。若是Content-Type是可用的,一個Header類的對象將會返回。

 

當咱們構建一個具備可用信息的實體時,元數據將會被實體構建器提供。

StringEntity myEntity = new StringEntity("important message",  

                          ContentType.create("text/plain", "UTF-8"));  

System.out.println(myEntity.getContentType());  

System.out.println(myEntity.getContentLength());  

System.out.println(EntityUtils.toString(myEntity));  

System.out.println(EntityUtils.toByteArray(myEntity).length);

 

確保釋放低級別的資源

爲了確保正確的釋放系統資源,你必須關掉與實體與實體相關的的內容流,還必須關掉響應自己。

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 {  

            // do something useful  

        } finally {  

            instream.close();  

        }  

   }  

} finally {  

     response.close();  

}

 

關閉內容流和關閉響應的不一樣點是:前者將會經過消費實體內容保持潛在的鏈接,然後者迅速的關閉並丟棄鏈接。

請注意,一旦實體被HttpEntity#writeTo(OutputStream)方法成功地寫入時,也須要確保正確地釋放系統資源。若是方法得到經過HttpEntity#getContent(),它也須要在一個finally子句中關閉流。

當使用實體時,你可使用EntityUtils#consume(HttpEntity)方法來確保實體內容徹底被消費而且使潛在的流關閉。

某些狀況,整個響應內容的僅僅一小部分須要被取出,會使消費其餘剩餘內容的性能代價和鏈接可重用性代價過高,這時能夠經過關閉響應來終止內容流。

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();  

    int byteOne = instream.read();  

        int byteTwo = instream.read();  

    // Do not need the rest  

}  

} finally {  

    response.close();  

}

這樣,鏈接將不會被重用,它分配的全部級別的資源將會被解除。

 

消費實體內容

爲了消費實體內容,推薦的方式是使用HttpEntity#getContent()或者 HttpEntity#writeTo(OutputStream)方法。HttpClient也提供了一個EntityUtils類,它有幾個靜態方法更容易的從實體中讀取內容或信息。取代了直接讀取java.io.InputStream,你能夠經過這個類的方法取出所有內容體並放入一個String 中或者byte數組中。但是,強烈不建議使用EntityUtils,除非響應實體來自於信任的HTTP服務器而且知道它的長度。

CloseableHttpClient httpclient = HttpClients.createDefault();  

HttpGet httpget = new HttpGet("http://localhost/";);  

CloseableHttpResponse response = httpclient.execute(httpget);  

try {  

HttpEntity entity = response.getEntity();  

if (entity != null) {  

        long len = entity.getContentLength();  

        if (len != -1 && len < 2048) {  

             System.out.println(EntityUtils.toString(entity));  

    } else {  

        // Stream content out  

    }  

}  

} finally {  

    response.close();  

}

在某些狀況下,屢次讀取實體內容是必要的。在這種狀況下,實體內容必須以一些方式緩衝,內存或者硬盤中。爲了達到這個目的,最簡單的方法是把原始的實體用BufferedHttpEntity類包裝起來。這將會使原始實體的內容讀入一個in-memory緩衝區。全部方式的實體包裝都是表明最原始的那個實體。

CloseableHttpResponse response = <...>  

HttpEntity entity = response.getEntity();  

if (entity != null) {  

    entity = new BufferedHttpEntity(entity);  

}

 

生產實體內容

HttpClient提供了幾個類,用來經過HTTP鏈接高效地傳輸內容。

這些類的實例均與內含實體請求有關,好比POST和PUT,它們可以把實體內容封裝進友好的HTTP請求中。

對於基本的數據容器String, byte array, input stream, and file,

HttpClient爲它們提供了幾個對應的類:StringEntity, ByteArrayEntity, InputStreamEntity, and FileEntity。

File file = new File("somefile.txt");

FileEntity entity = new FileEntity(file,ContentType.create("text/plain", "UTF-8"));

HttpPost httppost = new HttpPost("http://localhost/action.do";);

httppost.setEntity(entity);

請注意InputStreamEntity是不可重複的,由於它僅僅可以從數據流中讀取一次。

通常建議實現一個定製的HttpEntity類來代替使用通常的InputStreamEntity。

FileEntity將會是一個好的出發點。

 

HTML表單

許多應用須要模仿一個登錄HTML表單的過程,

好比:爲了登錄一個web應用或者提交輸入的數據。HttpClient提供了UrlEncodedFormEntity類來簡化這個過程。

List<NameValuePair> formparams = new ArrayList<NameValuePair>();

formparams.add(new BasicNameValuePair("param1", "value1"));

formparams.add(new BasicNameValuePair("param2", "value2"));

UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);

HttpPost httppost = new HttpPost("http://localhost/handler.do";);

httppost.setEntity(entity);

UrlEncodedFormEntity實例像上面同樣使用URL編碼方式來編碼參數並生成下面的內容:

param1=value1&param2=value2

 

內容分塊

一般,咱們推薦讓HttpClient選擇基於被傳遞的HTTP報文屬相最合適的傳輸編碼方式。可能地,能夠經過設置HttpEntity#setChunked()爲true來通知HttpClient你要進行分塊編碼。注意HttpClient將會使用這個標誌做爲提示。當使用一些不支持分塊編碼的HTTP版本(好比HTTP/1.0.)時,這個值將會忽略。

【譯者:分塊編碼是是HTTP1.1協議中定義的Web用戶向服務器提交數據的一種方法】

StringEntity entity = new StringEntity("important message", ContentType.create("plain/text", Consts.UTF_8)); entity.setChunked(true); 

HttpPost httppost = new HttpPost("http://localhost/acrtion.do";); 

httppost.setEntity(entity);

 

響應處理器

最簡單、最方便的方式來處理響應是使用ResponseHandler接口,它包含了一個handleResponse(HttpResponse response)方法。這個方法減輕使用者對於鏈接管理的擔憂。

當你使用ResponseHandler時,不管是請求執行成功亦或出現異常,HttpClient將會自動地確保鏈接釋放回鏈接管理器中。

CloseableHttpClient httpclient = HttpClients.createDefault(); 

HttpGet httpget = new HttpGet("http://localhost/json";); 

ResponseHandler<MyJsonObject> rh = new ResponseHandler<MyJsonObject>() 

@Override 

public JsonObject handleResponse(final HttpResponse response) throws IOException

 { 

StatusLine statusLine = response.getStatusLine(); 

HttpEntity entity = response.getEntity();

 if (statusLine.getStatusCode() >= 300) 

throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); 

}

 if (entity == null) 

{

 throw new ClientProtocolException("Response contains no content"); 

Gson gson = new GsonBuilder().create(); 

ContentType contentType = ContentType.getOrDefault(entity); 

Charset charset = contentType.getCharset(); 

Reader reader = new InputStreamReader(entity.getContent(), charset); 

return gson.fromJson(reader, MyJsonObject.class); 

}

 }; 

MyJsonObject myjson = client.execute(httpget, rh);

相關文章
相關標籤/搜索