(轉(zhuǎn))HttpClient基礎(chǔ)
1.1 執(zhí)行請(qǐng)求
HttpClient的最重要的功能是執(zhí)行HTTP方法。一個(gè)HTTP方法的執(zhí)行涉及到一個(gè)或多個(gè)HTTP請(qǐng)求或HTTP響應(yīng)的交流,HttpClient通常是在內(nèi)部處理的。用戶將提供一個(gè)執(zhí)行請(qǐng)求對(duì)象,HttpClient發(fā)送請(qǐng)求到目標(biāo)服務(wù)器返回一個(gè)相應(yīng)的響應(yīng)對(duì)象,如果執(zhí)行失敗則拋出一個(gè)異常。所以,HttpClient API的主要切入點(diǎn)是HttpClient的接口,它定義了上述約定。
下面是一個(gè)請(qǐng)求執(zhí)行過程中的最簡(jiǎn)單形式的例子:
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
int l;
byte[] tmp = new byte[2048];
while ((l = instream.read(tmp)) != -1) {
}
}
1.1.1 HTTP請(qǐng)求
所有的HTTP請(qǐng)求包含一個(gè)由請(qǐng)求行組成的一個(gè)方法名,一個(gè)請(qǐng)求的URI和一個(gè)HTTP協(xié)議的版本。
HttpClient的支持在HTTP/1.1規(guī)范中定義的所有的HTTP方法:GET, HEAD, POST, PUT, DELETE, TRACE 和 OPTIONS。每有一個(gè)方法都有一個(gè)對(duì)應(yīng)的類:HttpGet,HttpHead,HttpPost,HttpPut,HttpDelete,HttpTrace和HttpOptions。所有的這些類均實(shí)現(xiàn)了HttpUriRequest接口,故可以作為execute的執(zhí)行參數(shù)使用。請(qǐng)求URI是能夠應(yīng)用請(qǐng)求的統(tǒng)一資源標(biāo)識(shí)符。 HTTP請(qǐng)求的URI包含一個(gè)協(xié)議計(jì)劃protocol scheme,主機(jī)名host name,,可選的端口optional port,資源的路徑resource path,可選的查詢optional query和可選的片段optional fragment。
HttpGet httpget = new HttpGet(
"http://www.google.com/search?hl=en&q=httpclient&btnG=Google+Search&aq=f&oq=");
HttpClient提供了一系列實(shí)用的方法來簡(jiǎn)化創(chuàng)建和修改請(qǐng)求URI。
URI可以組裝編程:
URI uri = URIUtils.createURI("http", "www.google.com", -1, "/search",
"q=httpclient&btnG=Google+Search&aq=f&oq=", null);
HttpGet httpget = new HttpGet(uri);
System.out.println(httpget.getURI());
輸出>
http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq=
查詢字符串也可以通過添加參數(shù)列表來生成:
List<NameValuePair> qparams = new ArrayList<NameValuePair>();
qparams.add(new BasicNameValuePair("q", "httpclient"));
qparams.add(new BasicNameValuePair("btnG", "Google Search"));
qparams.add(new BasicNameValuePair("aq", "f"));
qparams.add(new BasicNameValuePair("oq", null));
URI uri = URIUtils.createURI("http", "www.google.com", -1, "/search",
URLEncodedUtils.format(qparams, "UTF-8"), null);
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響應(yīng)
HTTP響應(yīng)是由服務(wù)器收到和解釋一個(gè)請(qǐng)求消息后返回給客戶端的消息。該消息的第一行包含遵循的協(xié)議版本,他由一個(gè)數(shù)字狀態(tài)代碼及其相關(guān)的文本來表示。
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 Headers處理
一個(gè)HTTP消息可以包含一系列headers參數(shù)描述信息,例如內(nèi)容長(zhǎng)度,內(nèi)容類型等。 HttpClient的提供方法來檢索,添加,刪除和枚舉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);
輸出>
Set-Cookie: c1=a; path=/; domain=localhost
Set-Cookie: c2=b; path="/", c3=c; domain="localhost"
2
最有效獲取所有的給定類型的headers方式是使用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"
它還提供了方便的方法來解析HTTP消息的單個(gè)header元素
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 實(shí)體
HTTP消息的可以進(jìn)行內(nèi)容實(shí)體和請(qǐng)求或響應(yīng)關(guān)聯(lián)??梢栽谝恍┮蠛鸵恍┗貞?yīng)中找到實(shí)體,因?yàn)樗鼈兪强蛇x的。實(shí)體內(nèi)附與請(qǐng)求之中。 HTTP規(guī)范定義了兩個(gè)實(shí)體內(nèi)附方法:POST和PUT。響應(yīng)通常將會(huì)附上一個(gè)內(nèi)容實(shí)體。但是響應(yīng)HEAD方法和 204 No Content, 304 Not Modified, 205 Reset Content responses 除外。
HttpClient的區(qū)分三種不同實(shí)體的地方在于內(nèi)容來源于:
streamed流媒體:內(nèi)容是從收到的流,或在運(yùn)行中產(chǎn)生的。特別是包括被從HTTP響應(yīng)收到的實(shí)體。流媒體的實(shí)體一般不可重復(fù)。
self-contained獨(dú)立的:內(nèi)容是在內(nèi)存或以從一個(gè)連接或其他實(shí)體的獨(dú)立獲得的。獨(dú)立的的實(shí)體,一般可重復(fù)的。這種類型的實(shí)體將主要用于內(nèi)附在HTTP請(qǐng)求中。
wrapping包裝:實(shí)體內(nèi)容是從另一個(gè)實(shí)體獲得。
當(dāng)?shù)玫揭粋€(gè)HTTP響應(yīng)流的內(nèi)容的時(shí)候,這種區(qū)分對(duì)于連接管理是很重要的。對(duì)于請(qǐng)求實(shí)體,通過應(yīng)用程序來創(chuàng)建和只通過使用的HttpClient發(fā)送,流和自載之間差別很小。在這種情況下,建議考慮非重復(fù)的流實(shí)體,以及那些重復(fù)的自載實(shí)體。
1.1.4.1 重復(fù)實(shí)體
一個(gè)實(shí)體可以是可重復(fù)的,這意味著它的內(nèi)容可以被讀取一次以上。唯一有可能是的獨(dú)立的實(shí)體(如ByteArrayEntity或StringEntity())
1.1.4.2 使用HTTP實(shí)體
由于一個(gè)實(shí)體能夠表示二進(jìn)制和字符的內(nèi)容,它可以提供編碼的支持(支持文字、IE和字符內(nèi)容)。
這個(gè)實(shí)體在執(zhí)行封閉內(nèi)容的請(qǐng)求的時(shí)候或者在請(qǐng)求成功和響應(yīng)返回成功的時(shí)候被創(chuàng)建。
若要讀取從實(shí)體內(nèi)容,一可以通過檢索HttpEntity#getContent()方法,它返回一個(gè)java.io.InputStream,或一個(gè)可以提供一個(gè)輸出流的HttpEntity#writeTo(OutputStream中)方法的輸入流,這將返回已被寫入給定的流的所有內(nèi)容。
當(dāng)通過傳入的消息收到實(shí)體,方法HttpEntity#getContentType()和HttpEntity#getContentLength()方法可用于閱讀通用元數(shù)據(jù)metadata,如Content-Type,Content-Length headers(如果可用)。由于Content-Type header可以包含一個(gè)像text/plain或者text/html的文本mime-types的character encoding,HttpEntity#getContentEncoding()方法用來讀取此信息。如果headers是不可用,返回的長(zhǎng)度是-1,content type并為NUL。如果Content – Type header可用,將返回一個(gè)header對(duì)象。
當(dāng)創(chuàng)建了一個(gè)即將卸任的消息實(shí)體,該meta data必須提供由該實(shí)體的創(chuàng)造者。
StringEntity myEntity = new StringEntity("important message",
"UTF-8");
System.out.println(myEntity.getContentType());
System.out.println(myEntity.getContentLength());
System.out.println(EntityUtils.getContentCharSet(myEntity));
System.out.println(EntityUtils.toString(myEntity));
System.out.println(EntityUtils.toByteArray(myEntity).length);
stdout >
Content-Type: text/plain; charset=UTF-8
17
UTF-8
important message
17
1.1.5 確保資源釋放
當(dāng)響應(yīng)實(shí)體完成之后,重要的是要確保所有的實(shí)體內(nèi)容已被完全消耗,使該連接可以安全地返回到連接池,重新由連接管理器提供給后續(xù)請(qǐng)求使用。最簡(jiǎn)單的方法是調(diào)用HttpEntity#consumeContent()方法來消耗流上的所有可用的內(nèi)容。當(dāng)檢測(cè)到內(nèi)容已經(jīng)達(dá)到流末尾的時(shí)候,HttpClient會(huì)自動(dòng)釋放底層連接返回到連接管理器。HttpEntity#consumeContent()方法多次調(diào)用也是安全的。
當(dāng)只有小部分實(shí)體響應(yīng)內(nèi)容需要被檢索和消費(fèi)。其余內(nèi)容,使用可重復(fù)的連接性能損失太大,可以簡(jiǎn)單地調(diào)用HttpUriRequest#abort()方法來終止請(qǐng)求。
HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
int byteOne = instream.read();
int byteTwo = instream.read();
// Do not need the rest
httpget.abort();
}
該連接將不可重用,但是所有資源會(huì)被釋放。
1.1.6 獲取實(shí)體內(nèi)容
獲取實(shí)體內(nèi)容推薦的方法是通過使用HttpEntity#getContent()或HttpEntity#writeTo(OutputStream中)方法。 HttpClient的還配備了EntityUtils類,它暴露了一些靜態(tài)方法,以更輕松地閱讀一個(gè)實(shí)體的內(nèi)容或資料。使用這個(gè)類的方法和直接使用java.io.InputStream方法不同的是,他可以檢索字符串中的全部?jī)?nèi)容機(jī)構(gòu)/字節(jié)數(shù)組。強(qiáng)烈建議不要使用EntityUtils,除非響應(yīng)實(shí)體來自一個(gè)可信賴的HTTP服務(wù)器和已知的有限長(zhǎng)度。
HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
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
}
}
在某些情況下,實(shí)體內(nèi)容可能需要能夠讀被多次讀取。在這種情況下實(shí)體的內(nèi)容必須以某種方式被緩沖在內(nèi)存或磁盤上。最簡(jiǎn)單的方法是通過BufferedHttpEntity類來封裝原始實(shí)體。原始實(shí)體內(nèi)容可以從內(nèi)存中的緩沖區(qū)來讀取。其他方式封裝實(shí)體都包含原始實(shí)體。
HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
entity = new BufferedHttpEntity(entity);
}
1.1.7 生產(chǎn)實(shí)體內(nèi)容
HttpClient提供了一些類可以從HTTP連接內(nèi)容中獲得效地流。這些類的實(shí)例可以與實(shí)體內(nèi)附如POST和PUT請(qǐng)求,以便為即將離任的HTTP請(qǐng)求附上實(shí)體內(nèi)容。 HttpClient的為最常見的數(shù)據(jù)容器幾類,如串,字節(jié)數(shù)組輸入流和文件:StringEntity,ByteArrayEntity,InputStreamEntity和FileEntity。
File file = new File("somefile.txt");
FileEntity entity = new FileEntity(file, "text/plain; charset=""UTF-8""");
HttpPost httppost = new HttpPost("http://localhost/action.do");
httppost.setEntity(entity);
請(qǐng)注意InputStreamEntity是不可重復(fù)的,因?yàn)樗荒軓牡讓訑?shù)據(jù)流中讀取一次。一般來說,建議實(shí)現(xiàn)自定義HttpEntity類是自載的,而不是使用通用InputStreamEntity。 FileEntity可以是一個(gè)很好的起點(diǎn)。
1.1.7.1 動(dòng)態(tài)內(nèi)容實(shí)體
通常的HTTP實(shí)體需要在執(zhí)行上下文的時(shí)候動(dòng)態(tài)生成的。 HttpClient的提供使用EntityTemplate實(shí)體類和ContentProducer接口支持動(dòng)態(tài)實(shí)體。內(nèi)容制作是通過寫需求的內(nèi)容到一個(gè)輸出流,每次請(qǐng)求的時(shí)候都會(huì)產(chǎn)生。因此,通過EntityTemplate創(chuàng)建實(shí)體通常是獨(dú)立的,重復(fù)性好。
ContentProducer cp = new ContentProducer() {
public void writeTo(OutputStream outstream) throws IOException {
Writer writer = new OutputStreamWriter(outstream, "UTF-8");
writer.write("<response>");
writer.write(" <content>");
writer.write(" important stuff");
writer.write(" </content>");
writer.write("</response>");
writer.flush();
}
};
HttpEntity entity = new EntityTemplate(cp);
HttpPost httppost = new HttpPost("http://localhost/handler.do");
httppost.setEntity(entity);
1.1.7.2 HTML forms
許多應(yīng)用程序經(jīng)常需要模擬一個(gè)HTML表單提交的過程,例如,以登錄到Web應(yīng)用程序或提交的輸入數(shù)據(jù)。 HttpClient的實(shí)體類UrlEncodedFormEntity可以幫助實(shí)現(xiàn)這一步。
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實(shí)例將使用 URL編碼的編碼參數(shù),并出示下列內(nèi)容:
param1=value1¶m2=value2
1.1.7.3 內(nèi)容組塊
一般來說,建議當(dāng)HTTP消息的發(fā)送的時(shí)候讓HttpClient的選擇最合適的傳輸編碼。這是可能的,但是,HttpClient首選通過設(shè)置HttpEntity#setChunked()為true設(shè)置編碼。請(qǐng)注意的HttpClient將使用這個(gè)標(biāo)志作為提示。當(dāng)使用HTTP協(xié)議的版本不支持時(shí)候該值將被忽略時(shí),如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 響應(yīng)處理程序
最簡(jiǎn)單和最方便的處理響應(yīng)方式是通過使用ResponseHandler接口。這種方法完全免除了用戶去擔(dān)心連接管理。無論執(zhí)行請(qǐng)求是否成功或?qū)е庐惓#?/span>HttpClient的ResponseHandler會(huì)確保自動(dòng)釋放該連接回連接管理器。
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的執(zhí)行上下文
最初的HTTP被設(shè)計(jì)成一個(gè)無狀態(tài),響應(yīng),要求面向協(xié)議。然而,現(xiàn)實(shí)世界應(yīng)用程序通常需要能夠堅(jiān)持通過幾個(gè)邏輯上相關(guān)的請(qǐng)求響應(yīng)交換狀態(tài)信息。為了使應(yīng)用程序能夠保持狀態(tài),HttpClient允許 HTTP請(qǐng)求在一個(gè)特定的上下文中執(zhí)行,被稱為HTTP的上下文。如果一個(gè)邏輯同樣的情況下連續(xù)請(qǐng)求之間重用,多個(gè)邏輯相關(guān)的要求可以參加到會(huì)話中,。 HTTP上下文功能類似于java.util.Map的<String, Object>。它只是一個(gè)任意命名的值的集合。可以在請(qǐng)求執(zhí)行或者執(zhí)行完畢之后校驗(yàn)下文的時(shí)候在添加屬性參數(shù)到應(yīng)用程序。
在HTTP請(qǐng)求執(zhí)行的過程中HttpClient添加屬性到執(zhí)行上下文:
'http.connection':HttpConnection實(shí)例代表實(shí)際連接到目標(biāo)服務(wù)器。
'http.target_host':HttpHost實(shí)例代表連接的目標(biāo)。
'http.proxy_host':HttpHost實(shí)例代表連接代理,如果使用
'http.request':HttpRequest實(shí)例代表實(shí)際的HTTP請(qǐng)求。
'http.response':HttpResponse實(shí)例代表了實(shí)際的HTTP響應(yīng)。
'http.request_sent':java.lang.Boolean的對(duì)象,表示該標(biāo)志指示是否實(shí)際的要求已完全傳輸?shù)竭B接的目標(biāo)。
例如,想確定最后的重定向目標(biāo),一種方法可以在要求執(zhí)行之后交驗(yàn)該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錯(cuò)誤的時(shí)候java.io.IOException如socket timeout或者socket reset。HttpException 如違反了HTTP協(xié)議的HTTP錯(cuò)誤故障。通常的I/O錯(cuò)誤被視為非致命性和可恢復(fù)的,而HTTP協(xié)議錯(cuò)誤被認(rèn)為是致命的錯(cuò)誤,不能自動(dòng)收回。
1.3.1 HTTP傳輸安全
HTTP協(xié)議并沒有適用所有應(yīng)用。 HTTP是一個(gè)簡(jiǎn)單的請(qǐng)求/響應(yīng)協(xié)議,最初旨在支持靜態(tài)或動(dòng)態(tài)生成的內(nèi)容檢索。它從未打算支持事務(wù)操作。例如,HTTP服 務(wù)器將考慮其對(duì)履行合同的一部分,如果它在接收和處理請(qǐng)求成功,產(chǎn)生了反應(yīng),發(fā)出了一個(gè)狀態(tài)代碼返回給客戶端。該服務(wù)器將不作任何嘗試回滾事務(wù),如果客戶 端無法接收的全部原因是讀超時(shí),要求取消或系統(tǒng)崩潰的反應(yīng)。如果客戶決定重試相同的請(qǐng)求,服務(wù)器將不可避免地最終執(zhí)行相同的交易超過一次。在某些情況下, 這可能導(dǎo)致應(yīng)用數(shù)據(jù)損壞或不一致的應(yīng)用現(xiàn)狀。
雖然HTTP從來沒有被設(shè)計(jì)成支持事務(wù)處理,它仍然可以作為傳輸協(xié)議為應(yīng)用程序用執(zhí)行關(guān)鍵任務(wù)。為了確保HTTP傳輸層安全,系統(tǒng)必須確保冪等的HTTP方法在應(yīng)用層。
1.3.2 冪等方法
HTTP/1.1規(guī)范定義冪等方法,
方法含有冪等屬性 指的是(除了錯(cuò)誤或過期問題),N>0的相同請(qǐng)求和一個(gè)單獨(dú)的請(qǐng)求是一樣的。
換句話說,應(yīng)用程序應(yīng)該確保它準(zhǔn)備處理的多個(gè)執(zhí)行相同的方法的影響。這可以實(shí)現(xiàn)的,例如,通過提供一個(gè)獨(dú)特的交易ID和避免同一邏輯操作執(zhí)行的其他方式。
請(qǐng)注意,此問題不是特定的HttpClient?;跒g覽器應(yīng)用程序是完全符合相關(guān)的HTTP方法非冪等同樣的問題。
HttpClient的假設(shè)非實(shí)體內(nèi)附如GET和HEAD是冪等與實(shí)體內(nèi)附 如POST和PUT方法不是。
1.3.3 異常自動(dòng)恢復(fù)
默認(rèn)情況下的HttpClient嘗試自動(dòng)恢復(fù)從I / O異常。默認(rèn)的自動(dòng)恢復(fù)機(jī)制是限于一個(gè)已知是安全的少數(shù)例外。
HttpClient的將不作任何嘗試從任何邏輯或HTTP協(xié)議錯(cuò)誤(從HttpException類派生的)的恢復(fù)。
HttpClient會(huì)自動(dòng)重試那些被假定為冪等的方法。
HttpClient會(huì)自動(dòng)重試的方法,傳輸失敗異常的HTTP請(qǐng)求仍然被傳輸?shù)侥繕?biāo)服務(wù)器
HttpClient會(huì)自動(dòng)重試那些已經(jīng)完全傳輸?shù)椒?wù)器的方法,但服務(wù)器沒有響應(yīng)的HTTP狀態(tài)代碼(服務(wù)器只是丟棄不發(fā)送任何東西連接)。在這種情況下,假設(shè)該請(qǐng)求沒有被服務(wù)器和應(yīng)用程序狀態(tài)的處理并沒有改變。如果這種假設(shè)可能不適用于您的應(yīng)用程序的Web服務(wù)器是真正的目標(biāo),強(qiáng)烈建議提供一個(gè)自定義的異常處理程序。
1.3.4 請(qǐng)求重試處理程序
為了使自定義的異常恢復(fù)機(jī)制應(yīng)該提供一個(gè)HttpRequestRetryHandler接口的實(shí)現(xiàn)。
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
public boolean retryRequest(
IOException exception,
int executionCount,
HttpContext context) {
if (executionCount >= 5) {
// Do not retry if over max retry count
return false;
}
if (exception instanceof NoHttpResponseException) {
// Retry if the server dropped connection on us
return true;
}
if (exception instanceof SSLHandshakeException) {
// Do not retry on SSL handshake exception
return false;
}
HttpRequest request = (HttpRequest) context.getAttribute(
ExecutionContext.HTTP_REQUEST);
boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
if (idempotent) {
// Retry if the request is considered idempotent
return true;
}
return false;
}
};
httpclient.setHttpRequestRetryHandler(myRetryHandler);
1.4 中止請(qǐng)求
在某些情況下執(zhí)行HTTP請(qǐng)求未能完成預(yù)期的時(shí)間范圍內(nèi)由于對(duì)目標(biāo)服務(wù)器或客戶端發(fā)出太多的并發(fā)請(qǐng)求的高負(fù)荷。在這種情況下,可能有必要提前終止和解除封鎖的要求執(zhí)行的線程在I / O操作阻塞。 HTTP請(qǐng)求被處決的HttpClient可以在任何援引HttpUriRequest#abort()方法執(zhí)行階段中止。此方法是線程安全的,可以從任何線程調(diào)用。當(dāng)一個(gè)HTTP請(qǐng)求被中止它的執(zhí)行線程阻塞在一個(gè)I / O操作是保證解鎖通過拋出一個(gè)InterruptedIOException
1.5 攔截HTTP協(xié)議
HTTP協(xié)議的攔截器是一個(gè)例程,實(shí)現(xiàn)了HTTP協(xié)議的具體內(nèi)容。通常協(xié)議攔截行動(dòng)后,預(yù)計(jì)一個(gè)特定標(biāo)題或傳入的消息頭相關(guān)團(tuán)體或填充一個(gè)特定的頭或一組相關(guān)的頭傳出的消息。議定書攔截器還可以操縱的消息封閉的實(shí)體內(nèi)容,透明的內(nèi)容壓縮/解壓是一個(gè)很好的例子。通常這是通過使用'裝飾'的模式,其中一個(gè)包裝實(shí)體類是用來裝飾的原始實(shí)體。幾個(gè)協(xié)議攔截器可以組合成一個(gè)邏輯單元。
攔截器可以通過合作協(xié)議共享信息 - 如處理狀態(tài) - 通過HTTP的執(zhí)行上下文。攔截器可以使用HTTP協(xié)議中的要求來存儲(chǔ)一個(gè)或多個(gè)連續(xù)的請(qǐng)求處理狀態(tài)。
通常在這種攔截器都執(zhí)行不應(yīng)該的問題,只要他們不依賴于特定國(guó)家的執(zhí)行上下文秩序。如果協(xié)議攔截器有相互依存,因此必須在一個(gè)特定的順序執(zhí)行的,他們應(yīng)該被添加到了他們的預(yù)期的執(zhí)行順序相同的順序協(xié)議處理器。
議定書攔截器必須實(shí)現(xiàn)為線程安全的。同樣到Servlet,協(xié)議攔截器不應(yīng)使用實(shí)例變量,除非獲得這些變量的同步。
這個(gè)例子說明本地上下文可用于保存請(qǐng)求的處理之間的連續(xù)狀態(tài):
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 parameters
HttpParams接口代表一個(gè)不可改變的值的集合,定義一個(gè)組件在運(yùn)行時(shí)行為。在許多方面是相似的HttpContext HttpParams。兩者的主要區(qū)別是他們?cè)谶\(yùn)行時(shí)使用。代表了兩個(gè)接口是作為一個(gè)組織的鍵映射對(duì)象值對(duì)象的集合,而是為不同的目的:
HttpParams是為了包含簡(jiǎn)單的對(duì)象:整數(shù),雙打,字符串,集合和對(duì)象在運(yùn)行時(shí)保持不變。
HttpParams預(yù)計(jì)將用在'寫一次 - 準(zhǔn)備許多'模式。 HttpContext的目的是包含很可能發(fā)生變異的HTTP消息處理過程中的復(fù)雜對(duì)象。
該HttpParams目的是確定了其他組件的行為。一般每個(gè)復(fù)雜的組件都有它自己的HttpParams對(duì)象。對(duì)HttpContext的目的,是代表了一個(gè)HTTP進(jìn)程的執(zhí)行狀態(tài)。通常是相同的執(zhí)行上下文之間共享許多合作對(duì)象。
1.6.1 參數(shù)層次
在HTTP請(qǐng)求的HttpRequest對(duì)象執(zhí)行HttpParams當(dāng)然是聯(lián)系在一起的用于執(zhí)行請(qǐng)求的HttpClient的實(shí)例HttpParams。這使得在HTTP請(qǐng)求參數(shù)水平接管HttpParams優(yōu)先在HTTP客戶端的水平。建議的做法是通過設(shè)置在HTTP客戶端級(jí)別的共同所有的HTTP請(qǐng)求參數(shù)和選擇性覆蓋在HTTP請(qǐng)求級(jí)別的具體參數(shù)。
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 parameters beans
HttpParams接口為靈活性的實(shí)現(xiàn)在處理組件的配置。最重要的是,新的參數(shù)可以引進(jìn),而不會(huì)影響與舊版本的二進(jìn)制兼容性。然而,HttpParams也具有一定的缺點(diǎn)相比,常規(guī)的Java bean:HttpParams不能直接投資框架組裝使用。為了緩解這種限制,HttpClient的包括bean class,可以使用,以初始化HttpParams對(duì)象使用標(biāo)準(zhǔn)的Java bean約定的class數(shù)目。
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 執(zhí)行HTTP請(qǐng)求參數(shù)
這些參數(shù)可以影響到執(zhí)行請(qǐng)求的過程:
'http.protocol.version':定義使用HTTP協(xié)議的版本,如果沒有就請(qǐng)求對(duì)象明確。預(yù)計(jì)此參數(shù)類型ProtocolVersion價(jià)值。如果該參數(shù)沒有設(shè)置HTTP/1.1中會(huì)被使用。
'http.protocol.element-charset':定義用于編碼字符集是HTTP協(xié)議分子利用。此參數(shù)預(yù)計(jì)java.lang.String類型的值。如果此參數(shù)設(shè)置不美的ASCII將被使用。
'http.protocol.content-charset':定義為每默認(rèn)用于內(nèi)容主體編碼字符集。此參數(shù)預(yù)計(jì)java.lang.String類型的值。如果該參數(shù)沒有設(shè)置的ISO - 8859 - 1將被使用。
'http.useragent':定義了用戶代理頭的內(nèi)容。此參數(shù)預(yù)計(jì)java.lang.String類型的值。如果此參數(shù)未設(shè)置,HttpClient的將自動(dòng)生成一個(gè)值。
'http.protocol.strict-transfer-encoding':定義是否以無效傳輸編碼頭的反應(yīng),應(yīng)予以拒絕。預(yù)計(jì)此參數(shù)類型java.lang.Boolean的價(jià)值。如果該參數(shù)沒有設(shè)置無效的傳輸編碼值將被忽略。
'http.protocol.expect-continue':激活期望:為封閉的方法,實(shí)體100繼續(xù)握手。作者期望的目的:100繼續(xù)握手,是讓客戶端發(fā)送一個(gè)請(qǐng)求主體請(qǐng)求消息,以確定是否愿意到原始服務(wù)器之前接受客戶端發(fā)送請(qǐng)求的請(qǐng)求(根據(jù)請(qǐng)求頭)機(jī)構(gòu)。作者期望使用:100繼續(xù)握手會(huì)導(dǎo)致一個(gè)附有如POST和PUT請(qǐng)求(實(shí)體顯著的性能改進(jìn)),要求在目標(biāo)服務(wù)器的身份驗(yàn)證。預(yù)計(jì):100繼續(xù)握手應(yīng)謹(jǐn)慎使用,因?yàn)樗赡軙?huì)導(dǎo)致與HTTP代理服務(wù)器的問題和不支持HTTP/1.1協(xié)議。預(yù)計(jì)此參數(shù)類型java.lang.Boolean的價(jià)值。如果該參數(shù)沒有設(shè)置的HttpClient將嘗試使用握手。
'http.protocol.wait-for-continue':定義了以毫秒為單位的時(shí)間最長(zhǎng)的客戶應(yīng)該花費(fèi)100繼續(xù)等待響應(yīng)。預(yù)計(jì)此參數(shù)類型java.lang.Integer價(jià)值。如果該參數(shù)沒有設(shè)置的HttpClient將等待一段時(shí)間才能恢復(fù)請(qǐng)求的身體傳送確認(rèn)3秒。
posted on 2010-09-06 23:57 此號(hào)已被刪 閱讀(1076) 評(píng)論(0) 編輯 收藏 所屬分類: JAVA