Natural

           

          (轉(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)的類:HttpGetHttpHead,HttpPost,HttpPutHttpDelete,HttpTraceHttpOptions。所有的這些類均實(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)附方法:POSTPUT。響應(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í)體(如ByteArrayEntityStringEntity())

          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)容,一可以通過檢索HttpEntitygetContent()方法,它返回一個(gè)java.io.InputStream,或一個(gè)可以提供一個(gè)輸出流的HttpEntitywriteToOutputStream中)方法的輸入流,這將返回已被寫入給定的流的所有內(nèi)容。

          當(dāng)通過傳入的消息收到實(shí)體,方法HttpEntitygetContentType()和HttpEntitygetContentLength()方法可用于閱讀通用元數(shù)據(jù)metadata,如Content-Type,Content-Length headers(如果可用)。由于Content-Type header可以包含一個(gè)像text/plain或者text/html的文本mime-typescharacter encoding,HttpEntitygetContentEncoding()方法用來讀取此信息。如果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)用HttpEntityconsumeContent()方法來消耗流上的所有可用的內(nèi)容。當(dāng)檢測(cè)到內(nèi)容已經(jīng)達(dá)到流末尾的時(shí)候,HttpClient會(huì)自動(dòng)釋放底層連接返回到連接管理器。HttpEntityconsumeContent()方法多次調(diào)用也是安全的。

          當(dāng)只有小部分實(shí)體響應(yīng)內(nèi)容需要被檢索和消費(fèi)。其余內(nèi)容,使用可重復(fù)的連接性能損失太大,可以簡(jiǎn)單地調(diào)用HttpUriRequestabort()方法來終止請(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)容推薦的方法是通過使用HttpEntitygetContent()或HttpEntitywriteToOutputStream中)方法。 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)附如POSTPUT請(qǐng)求,以便為即將離任的HTTP請(qǐng)求附上實(shí)體內(nèi)容。 HttpClient的為最常見的數(shù)據(jù)容器幾類,如串,字節(jié)數(shù)組輸入流和文件:StringEntityByteArrayEntity,InputStreamEntityFileEntity。

          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&param2=value2

          1.1.7.3             內(nèi)容組塊

          一般來說,建議當(dāng)HTTP消息的發(fā)送的時(shí)候讓HttpClient的選擇最合適的傳輸編碼。這是可能的,但是,HttpClient首選通過設(shè)置HttpEntitysetChunked()為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>HttpClientResponseHandler會(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.IOExceptionsocket 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)附如GETHEAD是冪等與實(shí)體內(nèi)附 POSTPUT方法不是。

          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可以在任何援引HttpUriRequestabort()方法執(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 beanHttpParams不能直接投資框架組裝使用。為了緩解這種限制,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è)附有如POSTPUT請(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

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(8)

          隨筆分類(83)

          隨筆檔案(78)

          文章檔案(2)

          相冊(cè)

          收藏夾(7)

          最新隨筆

          搜索

          積分與排名

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 江城| 余姚市| 铜梁县| 自治县| 桑日县| 喀喇沁旗| 六枝特区| 德清县| 宜黄县| 三穗县| 克拉玛依市| 温泉县| 柯坪县| 久治县| 赫章县| 梧州市| 宁强县| 云南省| 松滋市| 崇仁县| 石林| 焦作市| 临清市| 巴林左旗| 通海县| 桐城市| 凤翔县| 扶余县| 涟水县| 汝城县| 滦南县| 德化县| 巴塘县| 盱眙县| 内黄县| 大悟县| 嘉祥县| 舞钢市| 永康市| 逊克县| 清徐县|