HttpClient 完整教程

來源:互聯網
上載者:User
前言

Http協議應該是互連網中最重要的協議。持續增長的web服務、可連網的家用電器等都在繼承並拓展著Http協議,向著瀏覽器之外的方向發展。

雖然jdk中的java.net包中提供了一些基本的方法,通過http協議來訪問網路資源,但是大多數情境下,它都不夠靈活和強大。HttpClient致力於填補這個空白,它可以提供有效、最新的、功能豐富的包來實現http用戶端。

為了拓展,HttpClient即支援基本的http協議,還支援http-aware用戶端程式,如web瀏覽器,Webservice用戶端,以及利用or拓展http協議的分布式系統。

1、HttpClient的範圍/特性 是一個基於HttpCore的用戶端Http傳輸類庫 基於傳統的(阻塞)IO 內容無關

2、HttpClient不能做的事情 HttpClient不是瀏覽器,它是一個用戶端http協議傳輸類庫。HttpClient被用來發送和接受Http訊息。HttpClient不會處理http訊息的內容,不會進行javascript解析,不會關心content type,如果沒有明確設定,httpclient也不會對請求進行格式化、重新導向url,或者其他任何和http訊息傳輸相關的功能。

  第一章 基本概念 1.1. 請求執行

HttpClient最基本的功能就是執行Http方法。一個Http方法的執行涉及到一個或者多個Http請求/Http響應的互動,通常這個過程都會自動被HttpClient處理,對使用者透明。使用者只需要提供Http請求對象,HttpClient就會將http請求發送給目標伺服器,並且接收伺服器的響應,如果http請求執行不成功,httpclient就會拋出異樣。

下面是個很簡單的http請求執行的例子:

  CloseableHttpClient httpclient = HttpClients.createDefault();   HttpGet httpget = new HttpGet("http://localhost/");   CloseableHttpResponse response = httpclient.execute(httpget);   try {       <...>   } finally {       response.close();   }  

  1.1.1. HTTP請求

所有的Http請求都有一個請求行(request line),包括方法名、請求的URI和Http版本號碼。

HttpClient支援HTTP/1.1這個版本定義的所有Http方法:GET,HEAD,POST,PUT,DELETE,TRACE和OPTIONS。對於每一種http方法,HttpClient都定義了一個相應的類:HttpGet, HttpHead, HttpPost, HttpPut, HttpDelete, HttpTrace和HttpOpquertions。

Request-URI即統一資源定位器,用來標明Http請求中的資源。Http request URIs包含協議名、主機名稱、主機連接埠(可選)、資源路徑、query(可選)和片段資訊(可選)。

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

 

HttpClient提供URIBuilder工具類來簡化URIs的建立和修改過程。

  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);   System.out.println(httpget.getURI());  

 

上述代碼會在控制台輸出:

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

  1.1.2. HTTP響應

 

伺服器收到用戶端的http請求後,就會對其進行解析,然後把響應發給用戶端,這個響應就是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());  
上述代碼會在控制台輸出:

 

  HTTP/1.1   200   OK   HTTP/1.1 200 OK  

  1.1.3. 訊息頭

一個Http訊息可以包含一系列的訊息頭,用來對http訊息進行描述,比如訊息長度,訊息類型等等。HttpClient提供了方法來擷取、添加、移除、枚舉訊息頭。

  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);  
上述代碼會在控制台輸出:

 

  Set-Cookie: c1=a; path=/; domain=localhost   Set-Cookie: c2=b; path="/", c3=c; domain="localhost"   2  

 

最有效擷取指定類型的訊息頭的方法還是使用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());   }  

 

上述代碼會在控制台輸出:

  Set-Cookie: c1=a; path=/; domain=localhost   Set-Cookie: c2=b; path="/", c3=c; domain="localhost"  

 

HeaderIterator也提供非常便捷的方式,將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]);       }   }  

 

上述代碼會在控制台輸出:

  c1 = a   path=/   domain=localhost   c2 = b   path=/   c3 = c   domain=localhost  

  1.1.4. HTTP實體

Http訊息可以攜帶http實體,這個http實體既可以是http請求,也可以是http響應的。Http實體,可以在某些http請求或者響應中發現,但不是必須的。Http規範中定義了兩種包含請求的方法:POST和PUT。HTTP響應一般會包含一個內容實體。當然這條規則也有異常情況,如Head方法的響應,204沒有內容,304沒有修改或者205內容資源重設。

HttpClient根據來源的不同,劃分了三種不同的Http實體內容。 streamed流式: 內容是通過流來接受或者在運行中產生。特別是,streamed這一類包含從http響應中擷取的實體內容。一般說來,streamed實體是不可重複的。 self-contained自我包含式:內容在記憶體中或通過獨立的串連或其它實體中獲得。self-contained類型的實體內容通常是可重複的。這種類型的實體通常用於關閉http請求。 wrapping封裝式: 這種類型的內容是從另外的http實體中擷取的。

當從Http響應中讀取內容時,上面的三種區分對於連線管理員來說是非常重要的。對於由應用程式建立而且只使用HttpClient發送的請求實體,streamed和self-contained兩種類型的不同就不那麼重要了。這種情況下,建議考慮如streamed流式這種不能重複的實體,和可以重複的self-contained自我包含式實體。 1.1.4.1. 可重複的實體

一個實體是可重複的,也就是說它的包含的內容可以被多次讀取。這種多次讀取只有self contained(自包含)的實體能做到(比如ByteArrayEntity或者StringEntity)。 1.1.4.2. 使用Http實體

由於一個Http實體既可以表示二進位內容,又可以表示常值內容,所以Http實體要支援字元編碼(為了支援後者,即常值內容)。

當需要執行一個完整內容的Http請求或者Http請求已經成功,伺服器要發送響應到用戶端時,Http實體就會被建立。

如果要從Http實體中讀取內容,我們可以利用HttpEntity類的getContent方法來擷取實體的輸入資料流(java.io.InputStream),或者利用HttpEntity類的writeTo(OutputStream)方法來擷取輸出資料流,這個方法會把所有的內容寫入到給定的流中。
當實體類已經被接受後,我們可以利用HttpEntity類的getContentType()和getContentLength()方法來讀取Content-Type和Content-Length兩個頭訊息(如果有的話)。由於Content-Type包含mime-types的字元編碼,比如text/plain或者text/html,HttpEntity類的getContentEncoding()方法就是讀取這個編碼的。如果頭資訊不存在,getContentLength()會返回-1,getContentType()會返回NULL。如果Content-Type資訊存在,就會返回一個Header類。

當為發送訊息建立Http實體時,需要同時附加meta資訊。

  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);  
上述代碼會在控制台輸出:

 

  Content-Type: text/plain; charset=utf-8   17   important message   17  

  1.1.5. 確保底層的資源串連被釋放

 

為了確保系統資源被正確地釋放,我們要麼管理Http實體的內容流、要麼關閉Http響應。

  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();   }  
關閉Http實體內容流和關閉Http響應的區別在於,前者通過消耗掉Http實體內容來保持相關的http串連,然後後者會立即關閉、丟棄http串連。

 

請注意HttpEntity的writeTo(OutputStream)方法,當Http實體被寫入到OutputStream後,也要確保釋放系統資源。如果這個方法內調用了HttpEntity的getContent()方法,那麼它會有一個java.io.InpputStream的執行個體,我們需要在finally中關閉這個流。

但是也有這樣的情況,我們只需要擷取Http響應內容的一小部分,而擷取整個內容並、實現串連的可重複性代價太大,這時我們可以通過關閉響應的方式來關閉內容輸入、輸出資料流。

  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();   }  
上面的代碼執行後,串連變得不可用,所有的資源都將被釋放。

  1.1.6. 消耗HTTP實體內容

HttpClient推薦使用HttpEntity的getConent()方法或者HttpEntity的writeTo(OutputStream)方法來消耗掉Http實體內容。HttpClient也提供了EntityUtils這個類,這個類提供一些靜態方法可以更容易地讀取Http實體的內容和資訊。和以java.io.InputStream流讀取內容的方式相比,EntityUtils提供的方法可以以字串或者位元組數組的形式讀取Http實體。但是,強烈不推薦使用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();   }  

 

有些情況下,我們希望可以重複讀取Http實體的內容。這就需要把Http實體內容緩衝在記憶體或者磁碟上。最簡單的方法就是把Http Entity轉化成BufferedHttpEntity,這樣就把原Http實體的內容緩衝到了記憶體中。後面我們就可以重複讀取BufferedHttpEntity中的內容。

  CloseableHttpResponse response = <...>   HttpEntity entity = response.getEntity();   if (entity != null) {       entity = new BufferedHttpEntity(entity);   }  

  1.1.7. 建立HTTP實體內容

 

HttpClient提供了一些類,這些類可以通過http串連高效地輸出Http實體內容。HttpClient提供的這幾個類涵蓋的常見的資料類型,如String,byte數組,輸入資料流,和檔案類型:StringEntity,ByteArrayEntity,InputStreamEntity,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 這個自包含的類來自訂HttpEntity類,而不是直接使用 InputStreamEntity 這個類。 FileEntity 就是一個很好的起點(FileEntity就是繼承的HttpEntity)。

  1.7.1.1. HTML表單

很多應用程式需要類比提交Html表單的過程,舉個例子,登陸一個網站或者將輸入內容提交給伺服器。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&m2=value2  

  1.1.7.2. 內容分塊

一般來說,推薦讓HttpClient自己根據Http訊息傳遞的特徵來選擇最合適的傳輸編碼。當然,如果非要手動控制也是可以的,可以通過設定HttpEntity

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.