httpclient4 中文版幫助文檔,最新官方版翻譯版(第一章 下)

(第一章 下)

1.1.7.2 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, "UTF-8");

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

httppost.setEntity(entity);

UrlEncodedFormEntity 實例將會使用URL編碼來編碼參數,生成如下的內容:

param1=value1?m2=value2

1.1.7.3 內容分塊

通常,我們推薦讓HttpClient選擇基於被傳遞的HTTP報文屬性的最適合的編碼轉換。這是可能的,但是,設置 HttpEntity#setChunked()方法爲true是通知HttpClient分塊編碼的首選。請注意HttpClient將會使用標識作爲提示。當使用的HTTP協議版本,如HTTP/1.0版本,不支持分塊編碼時,這個值會被忽略。

StringEntity entity = new StringEntity("important message",

"text/plain; charset=\"UTF-8\"");

entity.setChunked(true);

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

httppost.setEntity(entity);

1.1.8 響應控制器

控制響應的最簡便和最方便的方式是使用ResponseHandler接口。這個放完完全減輕了用戶關於連接管理的擔心。當使用ResponseHandler時,HttpClient將會自動關注並保證釋放連接到連接管理器中去,而不管請求執行是否成功或引發了異常。

HttpClient httpclient = new DefaultHttpClient();

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

ResponseHandler<byte[]> handler = new ResponseHandler<byte[]>() {

public byte[] handleResponse(HttpResponse response) throws ClientProtocolException, IOException {

HttpEntity entity = response.getEntity();

if (entity != null) {

return EntityUtils.toByteArray(entity);

} else {

return null;

}

}

};

byte[] response = httpclient.execute(httpget, handler);

1.2 HTTP執行的環境

最初,HTTP是被設計成無狀態的,面向請求-響應的協議。然而,真實的應用程序經常需要通過一些邏輯相關的請求-響應交換來持久狀態信息。爲了開啓應用程序來維持一個過程狀態,HttpClient允許HTTP請求在一個特定的執行環境中來執行,簡稱爲HTTP上下文。如果相同的環境在連續請求之間重用,那麼多種邏輯相關的請求可以參與到一個邏輯會話中。HTTP上下文功能和 java.util.Map<String,Object>很相似。它僅僅是任意命名參數值的集合。應用程序可以在請求之前或在檢查上下文執行完成之後來填充上下文屬性。

在HTTP請求執行的這一過程中,HttpClient添加了下列屬性到執行上下文中:

'http.connection' :HttpConnection實例代表了連接到目標服務器的真實連接。

'http.target_host' :HttpHost實例代表了連接目標。

'http.proxy_host' :如果使用了,HttpHost實例代表了代理連接。

'http.request' :HttpRequest實例代表了真實的HTTP請求。

'http.response' :HttpResponse實例代表了真實的HTTP響應。

'http.request_sent' :java.lang.Boolean對象代表了暗示真實請求是否被完全傳送到目標連接的標識。

比如,爲了決定最終的重定向目標,在請求執行之後,可以檢查http.target_host屬性的值:

DefaultHttpClient httpclient = new DefaultHttpClient();

HttpContext localContext = new BasicHttpContext();

HttpGet httpget = new HttpGet("http://www.google.com/");

HttpResponse response = httpclient.execute(httpget, localContext);

HttpHost target = (HttpHost) localContext.getAttribute(

ExecutionContext.HTTP_TARGET_HOST);

System.out.println("Final target: " + target);

HttpEntity entity = response.getEntity();

if (entity != null) {

entity.consumeContent();

}

輸出內容爲:

Final target: http://www.google.ch

1.3 異常處理

HttpClient能夠拋出兩種類型的異常:在I/O失敗時,如套接字連接超時或被重置的java.io.IOException異常,還有標誌HTTP請求失敗的信號,如違反HTTP協議的HttpException異常。通常I/O錯誤被認爲是非致命的和可以恢復的,而HTTP協議錯誤則被認爲是致命的而且是不能自動恢復的。

1.3.1 HTTP運輸安全

要理解HTTP協議並不是對所有類型的應用程序都適合的,這一點很重要。HTTP是一個簡單的面向請求/響應的協議,最初被設計用來支持取回靜態或動態生成的內容。它從未向支持事務性操作方向發展。比如,如果成功收到和處理請求,HTTP服務器將會考慮它的其中一部分是否完成,生成一個響應併發送一個狀態碼到客戶端。如果客戶端因爲讀取超時,請求取消或系統崩潰導致接收響應實體失敗時,服務器不會試圖回滾事務。如果客戶端決定重新這個請求,那麼服務器將不可避免地不止一次執行這個相同的事務。在一些情況下,這會導致應用數據損壞或者不一致的應用程序狀態。

儘管HTTP從來都沒有被設計來支持事務性處理,但它也能被用作於一個傳輸協議對關鍵的任務應用提供被滿足的確定狀態。要保證HTTP傳輸層的安全,系統必須保證HTTP方法在應用層的冪等性。

1.3.2 冪等的方法

HTTP/1.1 明確地定義了冪等的方法,描述如下

[方法也可以有“冪等”屬性在那些(除了錯誤或過期問題)N的副作用>0的相同請求和獨立的請求是相同的]

換句話說,應用程序應該保證準備着來處理多個相同方法執行的實現。這是可以達到的,比如,通過提供一個獨立的事務ID和其它避免執行相同邏輯操作的方法。

請注意這個問題對於HttpClient是不具體的。基於應用的瀏覽器特別受和非冪等的HTTP方法相關的相同問題的限制。

HttpClient假設沒有實體包含方法,比如GET和HEAD是冪等的,而實體包含方法,比如POST和PUT則不是。

1.3.3 異常自動恢復

默認情況下,HttpClient會試圖自動從I/O異常中恢復。默認的自動恢復機制是受很少一部分已知的異常是安全的這個限制。

HttpClient不會從任意邏輯或HTTP協議錯誤(那些是從HttpException類中派生出的)中恢復的。

HttpClient將會自動重新執行那麼假設是冪等的方法。

HttpClient將會自動重新執行那些由於運輸異常失敗,而HTTP請求仍然被傳送到目標服務器(也就是請求沒有完全被送到服務器)失敗的方法。

HttpClient將會自動重新執行那些已經完全被送到服務器,但是服務器使用HTTP狀態碼(服務器僅僅丟掉連接而不會發回任何東西)響應時失敗的方法。在這種情況下,假設請求沒有被服務器處理,而應用程序的狀態也沒有改變。如果這個假設可能對於你應用程序的目標Web服務器來說不正確,那麼就強烈建議提供一個自定義的異常處理器。

1.3.4 請求重試處理

爲了開啓自定義異常恢復機制,應該提供一個HttpRequestRetryHandler接口的實現。

DefaultHttpClient httpclient = new DefaultHttpClient();

HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {

public boolean retryRequest(IOException exception,

int executionCount,HttpContext context) {

if (executionCount >= 5) {

// 如果超過最大重試次數,那麼就不要繼續了

return false;

}

if (exception instanceof NoHttpResponseException) {

// 如果服務器丟掉了連接,那麼就重試

return true;

}

if (exception instanceof SSLHandshakeException) {

// 不要重試SSL握手異常

return false;

}

HttpRequest request = (HttpRequest) context.getAttribute(

ExecutionContext.HTTP_REQUEST);

boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);

if (idempotent) {

// 如果請求被認爲是冪等的,那麼就重試

return true;

}

return false;

}

};

httpclient.setHttpRequestRetryHandler(myRetryHandler);

1.4 中止請求

在一些情況下,由於目標服務器的高負載或客戶端有很多活動的請求,那麼HTTP請求執行會在預期的時間框內而失敗。這時,就可能不得不過早地中止請求,解除封鎖在I/O執行中的線程封鎖。被HttpClient執行的HTTP請求可以在執行的任意階段通過調用 HttpUriRequest#abort()方法而中止。這個方法是線程安全的,而且可以從任意線程中調用。當一個HTTP請求被中止時,它的執行線程就封鎖在I/O操作中了,而且保證通過拋出InterruptedIOException異常來解鎖。

1.5 HTTP協議攔截器

HTTP協議攔截器是一個實現了特定HTPP協議方面的慣例。通常協議攔截器希望作用於一個特定頭部信息上,或者一族收到報文的相關頭部信息,或使用一個特定的頭部或一族相關的頭部信息填充發出的報文。協議攔截器也可以操縱包含在報文中的內容實體,透明的內容壓縮/解壓就是一個很好的示例。通常情況下這是由包裝器實體類使用了“裝飾者”模式來裝飾原始的實體完成的。一些協議攔截器可以從一個邏輯單元中來結合。

協議攔截器也可以通過共享信息來共同合作-比如處理狀態-通過HTTP執行上下文。協議攔截器可以使用HTTP內容來爲一個或多個連續的請求存儲一個處理狀態。

通常攔截器執行的順序不應該和它們基於的特定執行上下文狀態有關。如果協議攔截器有相互依存關係,那麼它們必須按特定順序來執行,正如它們希望執行的順序一樣,它們應該在相同的序列中被加到協議處理器。

協議攔截器必須實現爲線程安全的。和Servlet相似,協議攔截器不應該使用實例變量,除非訪問的那些變量是同步的。

這個示例給出了本地內容在連續的請求中怎麼被用於持久一個處理狀態的:

DefaultHttpClient httpclient = new DefaultHttpClient();

HttpContext localContext = new BasicHttpContext();

AtomicInteger count = new AtomicInteger(1);

localContext.setAttribute("count", count);

httpclient.addRequestInterceptor(new HttpRequestInterceptor() {

public void process(final HttpRequest request,

final HttpContext context) throws HttpException, IOException {

AtomicInteger count = (AtomicInteger) context.getAttribute("count");

request.addHeader("Count", Integer.toString(count.getAndIncrement()));

}

});

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

for (int i = 0; i < 10; i++) {

HttpResponse response = httpclient.execute(httpget, localContext);

HttpEntity entity = response.getEntity();

if (entity != null) {

entity.consumeContent();

}

}

1.6 HTTP參數

HttpParams接口代表了定義組件運行時行爲的一個不變的值的集合。很多情況下,HttpParams和HttpContext相似。二者之間的主要區別是它們在運行時使用的不同。這兩個接口表示了對象的集合,它們被視作爲訪問對象值的鍵的Map,但是服務於不同的目的:

HttpParams旨在包含簡單對象:整型,浮點型,字符串,集合,還有運行時不變的對象。

HttpParams希望被用在“一次寫入-多處準備”模式下。HttpContext旨在包含很可能在HTTP報文處理這一過程中發生改變的複雜對象

HttpParams的目標是定義其它組件的行爲。通常每一個複雜的組件都有它自己的HttpParams對象。HttpContext的目標是來表示一個HTTP處理的執行狀態。通常相同的執行上下文在很多合作的對象中共享。

1.6.1 參數層次

在HTTP請求執行過程中,HttpRequest對象的HttpParams是和用於執行請求的HttpClient實例的 HttpParams聯繫在一起的。這使得設置在HTTP請求級別的參數優先於設置在HTTP客戶端級別的HttpParams。推薦的做法是設置普通參數對所有的在HTTP客戶端級別的HTTP請求共享,而且可以選擇性重寫具體在HTTP請求級別的參數。

DefaultHttpClient httpclient = new DefaultHttpClient();

httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,HttpVersion.HTTP_1_0);

httpclient.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET,"UTF-8");

HttpGet httpget = new HttpGet("http://www.google.com/");

httpget.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,HttpVersion.HTTP_1_1);

httpget.getParams().setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE,Boolean.FALSE);

httpclient.addRequestInterceptor(new HttpRequestInterceptor() {

public void process(final HttpRequest request,

final HttpContext context) throws HttpException, IOException {

System.out.println(request.getParams().getParameter(

CoreProtocolPNames.PROTOCOL_VERSION));

System.out.println(request.getParams().getParameter(

CoreProtocolPNames.HTTP_CONTENT_CHARSET));

System.out.println(request.getParams().getParameter(

CoreProtocolPNames.USE_EXPECT_CONTINUE));

System.out.println(request.getParams().getParameter(

CoreProtocolPNames.STRICT_TRANSFER_ENCODING));

}

});

輸出內容爲:

HTTP/1.1

UTF-8

false

Null

1.6.2 HTTP參數bean

HttpParams接口允許在處理組件的配置上很大的靈活性。很重要的是,新的參數可以被引入而不會影響老版本的二進制兼容性。然而,和常規的 Java bean相比,HttpParams也有一個缺點:HttpParams不能使用DI框架來組合。爲了緩解這個限制,HttpClient包含了一些 bean類,它們可以用來按順序使用標準的Java eban慣例初始化HttpParams對象。

HttpParams params = new BasicHttpParams();

HttpProtocolParamBean paramsBean = new HttpProtocolParamBean(params);

paramsBean.setVersion(HttpVersion.HTTP_1_1);

paramsBean.setContentCharset("UTF-8");

paramsBean.setUseExpectContinue(true);

System.out.println(params.getParameter(

CoreProtocolPNames.PROTOCOL_VERSION));

System.out.println(params.getParameter(

CoreProtocolPNames.HTTP_CONTENT_CHARSET));

System.out.println(params.getParameter(

CoreProtocolPNames.USE_EXPECT_CONTINUE));

System.out.println(params.getParameter(

CoreProtocolPNames.USER_AGENT));

輸出內容爲:

HTTP/1.1

UTF-8

false

Null

1.7 HTTP請求執行參數

這些參數會影響到請求執行的過程:

'http.protocol.version':如果沒有在請求對象中設置明確的版本信息,它就定義了使用的HTTP協議版本。這個參數期望得到一個ProtocolVersion類型的值。如果這個參數沒有被設置,那麼就使用HTTP/1.1。

'http.protocol.element-charset':定義了編碼HTTP協議元素的字符集。這個參數期望得到一個java.lang.String類型的值。如果這個參數沒有被設置,那麼就使用US-ASCII。

'http.protocol.eontent-charset':定義了爲每個內容主體編碼的默認字符集。這個參數期望得到一個java.lang.String類型的值。如果這個參數沒有被設置,那麼就使用ISO-8859-1。

'http.useragent':定義了頭部信息User-Agent的內容。這個參數期望得到一個java.lang.String類型的值。如果這個參數沒有被設置,那麼HttpClient將會爲它自動生成一個值。

'http.protocol.strict-transfer-encoding':定義了響應頭部信息中是否含有一個非法的Transfer-Encoding,都要拒絕掉。

'http.protocol.expect-continue':爲包含方法的實體激活Expect: 100-Continue握手。Expect: 100-Continue握手的目的是允許客戶端使用請求體發送一個請求信息來決定源服務器是否希望在客戶端發送請求體之前得到這個請求(基於請求頭部信息)。Expect: 100-Continue握手的使用可以對需要目標服務器認證的包含請求的實體(比如POST和PUT)導致明顯的性能改善。Expect: 100-Continue握手應該謹慎使用,因爲它和HTTP服務器,不支持HTTP/1.1協議的代理使用會引起問題。這個參數期望得到一個 java.lang.Boolean類型的值。如果這個參數沒有被設置,那麼HttpClient將會試圖使用握手。

'http.protocol.wait-for-continue':定義了客戶端應該等待100-Continue響應最大的毫秒級時間間隔。這個參數期望得到一個java.lang.Integer類型的值。如果這個參數沒有被設置,那麼HttpClient將會在恢復請求體傳輸之前爲確認等待3秒。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章