HttpClient學習研究---第一章:基礎知識

Chapter 1. 第1章。Fundamentals基本面

1.1. 1.1。Request execution請求執行

The most essential function of HttpClient is to execute HTTP methods. 最基本的功能是執行HttpClient HTTP方法。Execution of an HTTP method involves one or several HTTP request / HTTP response exchanges, usually handled internally by HttpClient. 執行一個HTTP方法涉及一個或多個HTTP請求/ HTTP響應交流,通常在內部處理由HttpClient。The user is expected to provide a request object to execute and HttpClient is expected to transmit the request to the target server return a corresponding response object, or throw an exception if execution was unsuccessful.用戶預計將提供一個請求對象來執行和HttpClient預計將傳送到目標服務器的請求返回相應的響應對象,或者拋出一個異常如果執行不成功。

Quite naturally, the main entry point of the HttpClient API is the HttpClient interface that defines the contract described above.很自然地,主入口點的HttpClient API是HttpClient接口,定義了上面描述的合同。

Here is an example of request execution process in its simplest form:下面是一��示例請求執行過程在其最簡單的形式:

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

1.1.1. 1.1.1。HTTP requestHTTP請求

All HTTP requests have a request line consisting a method name, a request URI and an HTTP protocol version.所有HTTP請求有一個請求線組成一個方法名,請求URI和HTTP協議版本。

HttpClient supports out of the box all HTTP methods defined in the HTTP/1.1 specification:HttpClient支持開箱即用的所有HTTP方法定義在HTTP / 1.1規範: GET, HEAD, POST, PUT, DELETE, TRACEand OPTIONS. There is a specific class for each method type.:有一個特定的類爲每個方法類型。 HttpGet, HttpHead, HttpPost, HttpPut, HttpDelete, HttpTrace, and,和 HttpOptions.

The Request-URI is a Uniform Resource Identifier that identifies the resource upon which to apply the request. 請求uri所指定資源是一個統一資源標識符標識資源在應用請求。HTTP request URIs consist of a protocol scheme, host name, optional port, resource path, optional query, and optional fragment.HTTP請求uri包含協議方案,主機名稱,可選端口、資源路徑,可選的查詢,和可選的片段。

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

HttpClient providesHttpClient提供 URIBuilderutility class to simplify creation and modification of request URIs.工具類來簡化創建和修改請求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);
System.out.println(httpget.getURI());

stdout >stdout >

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

1.1.2. 1.1.2。HTTP responseHTTP響應

HTTP response is a message sent by the server back to the client after having received and interpreted a request message. HTTP響應消息發送回客戶機的服務器在接受了一個請求消息和解釋。The first line of that message consists of the protocol version followed by a numeric status code and its associated textual phrase.第一行的消息包括協議版本後跟一個數字狀態碼和它相關的文本短語。

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

stdout >stdout >

HTTP/1.1
200
OK
HTTP/1.1 200 OK

1.1.3. 1.1.3。Working with message headers處理消息標頭

An HTTP message can contain a number of headers describing properties of the message such as the content length, content type and so on. 一個HTTP消息可以包含許多標題描述的屬性信息,如內容長度,內容類型等等。HttpClient provides methods to retrieve, add, remove and enumerate headers.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);

stdout >stdout >

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

The most efficient way to obtain all headers of a given type is by using the最有效的方式來獲得給定類型的所有標題是通過使用 HeaderIteratorinterface.接口。

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

stdout >stdout >

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

It also provides convenience methods to parse HTTP messages into individual header elements.它還提供了方便的方法來解析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]);
    }
}

stdout >stdout >

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

1.1.4. 1 1 4。HTTP entityHTTP實體

HTTP messages can carry a content entity associated with the request or response. HTTP消息可以攜帶一個內容實體相關的請求或響應。Entities can be found in some requests and in some responses, as they are optional. 實體可以發現在一些請求和在某些反應,因爲它們是可選的。Requests that use entities are referred to as entity enclosing requests. 要求使用實體被稱爲實體封閉請求。The HTTP specification defines two entity enclosing request methods:HTTP規範定義了兩個實體封閉請求方法: POSTand PUT. Responses are usually expected to enclose a content entity. 反應通常將附上一個內容實體。There are exceptions to this rule such as responses to當然也有例外,如反應 HEADmethod and方法和 204 No Content, 304 Not Modified, 205 Reset Contentresponses.響應。

HttpClient distinguishes three kinds of entities, depending on where their content originates:HttpClient區分三種類型的實體,根據他們的內容來源於:

  • streamed:流:The content is received from a stream, or generated on the fly. 收到的內容從一個流,或動態生成。In particular, this category includes entities being received from HTTP responses. 特別是,這個類別包括實體被收到HTTP響應。Streamed entities are generally not repeatable.流實體通常不是可重複的。

  • self-contained:獨立的:The content is in memory or obtained by means that are independent from a connection or other entity. 內容是在內存或得到意味着獨立於一個連接或其他實體。Self-contained entities are generally repeatable. 獨立的實體通常是可重複的。This type of entities will be mostly used for entity enclosing HTTP requests.這種類型的實體將主要用於實體封閉的HTTP請求。

  • wrapping:包裝:The content is obtained from another entity.內容可由另一個實體。

This distinction is important for connection management when streaming out content from an HTTP response. 這種區別是重要的連接管理當涌出內容從一個HTTP響應。For request entities that are created by an application and only sent using HttpClient, the difference between streamed and self-contained is of little importance. 爲請求的實體,是由一個應用程序,只發送使用HttpClient,區別和獨立的流是不重要的。In that case, it is suggested to consider non-repeatable entities as streamed, and those that are repeatable as self-contained.在這種情況下,建議考慮不可重複實體,那些流可重複的獨立。

1.1.4.1. 1 1 4 1。Repeatable entities可重複的實體

An entity can be repeatable, meaning its content can be read more than once. 一個實體可以是可重複的,這意味着它的內容可以讀不止一次。This is only possible with self contained entities (like這是唯一可能與自包含的實體(如 ByteArrayEntityor StringEntity)

1.1.4.2. 1 1 4 2。Using HTTP entities使用HTTP實體

Since an entity can represent both binary and character content, it has support for character encodings (to support the latter, ie. 因爲一個實體可以表示兩��二進制和字符內容,它支持字符編碼(支持後者,即。character content).字符內容)。

The entity is created when executing a request with enclosed content or when the request was successful and the response body is used to send the result back to the client.實體是執行請求時創建與封閉的內容或當請求成功了,身體的反應是用於將結果發送回客戶端。

To read the content from the entity, one can either retrieve the input stream via the閱讀內容的實體,一個可以獲取輸入流的通過 HttpEntity#getContent()method, which returns an方法,它返回一個 java.io.InputStream, or one can supply an output stream to the,或一個可以提供一個輸出流 HttpEntity#writeTo(OutputStream)method, which will return once all content has been written to the given stream.方法,該方法將返回一旦所有內容已經被寫入給定的流。

When the entity has been received with an incoming message, the methods當實體已收到與傳入消息的方法 HttpEntity#getContentType()and HttpEntity#getContentLength()methods can be used for reading the common metadata such as方法可以用於閱讀常見的元數據,例如 Content-Typeand Content-Lengthheaders (if they are available). 標題(如果可用)。Since the Content-Typeheader can contain a character encoding for text mime-types like text/plain or text/html, the頭可以包含字符編碼文本mime類型text /普通或者像text / html, HttpEntity#getContentEncoding()method is used to read this information. 方法是用來讀取這些信息。If the headers aren't available, a length of -1 will be returned, and NULL for the content type. 如果標題並不可用,一個長度爲1,將返回空的內容類型。If the如果 Content-Typeheader is available, a頭可用,一個 Headerobject will be returned.對象將被返回。

When creating an entity for a outgoing message, this meta data has to be supplied by the creator of the entity.當創建一個實體,一個即將卸任的消息,這個元數據必須由造物主的實體。

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

stdout >stdout >

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

1.1.5. 1 1 5。Ensuring release of low level resources確保發佈的資源水平低

In order to ensure proper release of system resources one must close either the content stream associated with the entity or the response itself爲了確保適當的釋放系統資源必須關閉或內容流相關實體或響應本身

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

The difference between closing the content stream and closing the response is that the former will attempt to keep the underlying connection alive by consuming the entity content while the latter immediately shuts down and discards the connection.關的區別內容流和關閉響應是前者將試圖保持底層連接活着通過消費實體內容而後者立即關閉和丟棄連接。

Please note that the請注意, HttpEntity#writeTo(OutputStream)method is also required to ensure proper release of system resources once the entity has been fully written out. 方法也需要確保適當的釋放系統資源一旦實體已經完全寫出來。If this method obtains an instance of如果這種方法獲得的一個實例 java.io.InputStreamby calling通過調用 HttpEntity#getContent(), it is also expected to close the stream in a finally clause.,它也將關閉該流在最後條款。

When working with streaming entities, one can use the在處理流的實體,一個可以使用 EntityUtils#consume(HttpEntity)method to ensure that the entity content has been fully consumed and the underlying stream has been closed.方法以確保實體內容已經被完全消耗和底層流已經關閉。

There can be situations, however, when only a small portion of the entire response content needs to be retrieved and the performance penalty for consuming the remaining content and making the connection reusable is too high, in which case one can terminate the content stream by closing the response.在某些情況下,然而,當只有一小部分整個響應內容需要被檢索和性能損失消耗剩餘的內容並進行連接可重用的太高了,在這種情況下,一個可以終止內容流關閉響應。

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

The connection will not be reused, but all level resources held by it will be correctly deallocated.該連接將不被重用,但所有水平持有的資源分配將是正確的。

1.1.6. 1 1 6。Consuming entity content消費實體內容

The recommended way to consume the content of an entity is by using its推薦的方式消費內容的一個實體是利用它的 HttpEntity#getContent()or HttpEntity#writeTo(OutputStream)methods. 方法。HttpClient also comes with theHttpClient採用的也是 EntityUtilsclass, which exposes several static methods to more easily read the content or information from an entity. 類,它暴露了一些靜態方法更容易閱讀的內容或信息從一個實體。Instead of reading the而不是閱讀 java.io.InputStreamdirectly, one can retrieve the whole content body in a string / byte array by using the methods from this class. 直接,一個可以檢索整個內容的身體在一個字符串/字節數組的使用方法從這個類。However, the use of然而,使用 EntityUtilsis strongly discouraged unless the response entities originate from a trusted HTTP server and are known to be of limited length.不提倡,除非響應實體源自一個可信的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();
}

In some situations it may be necessary to be able to read entity content more than once. 在某些情況下可能需要能夠閱讀實體內容超過一次。In this case entity content must be buffered in some way, either in memory or on disk. 在這種情況下實體內容必須以某種方式進行緩衝,無論是在內存或磁盤。The simplest way to accomplish that is by wrapping the original entity with the最簡單的實現方式,是通過包裝原始實體的 BufferedHttpEntityclass. 類。This will cause the content of the original entity to be read into a in-memory buffer. 這將導致原實體的內容讀取到內存緩衝區。In all other ways the entity wrapper will be have the original one.在所有其他方面,實體包裝會有原始的一個。

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

1.1.7. 1 1 7。Producing entity content生產實體內容

HttpClient provides several classes that can be used to efficiently stream out content though HTTP connections. HttpClient提供幾個類,可以用來有效地流內容雖然HTTP連接。Instances of those classes can be associated with entity enclosing requests such as這些類的實例可以關聯到實體封閉等請求 POSTand PUTin order to enclose entity content into outgoing HTTP requests. 爲了附上實體內容爲即將離任的HTTP請求。HttpClient provides several classes for most common data containers such as 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);

Please note請注意 InputStreamEntityis not repeatable, because it can only read from the underlying data stream once. 是不可重複的,因爲它只能讀從底層數據流一次。Generally it is recommended to implement a custom一般建議來實現一個自定義的 HttpEntityclass which is self-contained instead of using the generic類,它是獨立的,而不是使用泛型 InputStreamEntity. FileEntitycan be a good starting point.可以是一個很好的起點。

1.1.7.1. 1 1 7 1。HTML formsHTML表單

Many applications need to simulate the process of submitting an HTML form, for instance, in order to log in to a web application or submit input data. 許多應用程序需要模擬一個HTML表單提交的過程,例如,以登錄到web應用程序或提交輸入數據。HttpClient provides the entity classHttpClient提供��體類 UrlEncodedFormEntityto facilitate the process.促進這個過程。

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

The這個 UrlEncodedFormEntityinstance will use the so called URL encoding to encode parameters and produce the following content:實例將使用所謂的URL編碼編碼參數和生成以下內容:

param1=value1&param2=value2

1.1.7.2. 1 1 7 2。Content chunking內容分塊

Generally it is recommended to let HttpClient choose the most appropriate transfer encoding based on the properties of the HTTP message being transferred. 一般建議讓HttpClient選擇最適當的傳輸編碼基於HTTP消息的屬性被轉移。It is possible, however, to inform HttpClient that chunk coding is preferred by setting它是可能的,然而,通知HttpClient,塊編碼優先設置 HttpEntity#setChunked()to true. 爲true。Please note that HttpClient will use this flag as a hint only. 請注意,HttpClient將使用此標誌作爲一個提示只有。This value will be ignored when using HTTP protocol versions that do not support chunk coding, such as HTTP/1.0.這個值將被忽略在使用HTTP協議版本,不支持塊編碼,如HTTP / 1.0。

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

1.1.8. 1.1.8。Response handlers響應處理程序

The simplest and the most convenient way to handle responses is by using the最簡單、最方便的方式來處理響應是通過使用 ResponseHandlerinterface, which includes the接口,包括 handleResponse(HttpResponse response)method. 法。This method completely relieves the user from having to worry about connection management. 這個方法完全緩解用戶不必擔心連接管理。When using a當使用一個 ResponseHandler, HttpClient will automatically take care of ensuring release of the connection back to the connection manager regardless whether the request execution succeeds or causes an exception.,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);

1.2. 1.2。HttpClient interfaceHttpClient接口

HttpClientinterface represents the most essential contract for HTTP request execution. 接口代表了最基本的HTTP請求執行合同。It imposes no restrictions or particular details on the request execution process and leaves the specifics of connection management, state management, authentication and redirect handling up to individual implementations. 它沒有強加任何限制或特定的細節在請求執行過程和樹葉的細節連接管理、狀態管理、身份驗證和重定向處理個人實現。This should make it easier to decorate the interface with additional functionality such as response content caching.這將使它更容易裝飾界面附加功能如響應內容緩存。

Generally一般 HttpClientimplementations act as a facade to a number of special purpose handler or strategy interface implementations responsible for handling of a particular aspect of the HTTP protocol such as redirect or authentication handling or making decision about connection persistence and keep alive duration. 實現作爲一個正面,一個數量的專用處理程序或策略接口實現負責處理的特定方面的HTTP協議如重定向或身份驗證處理、決策有關連接持久性和維持時間。This enables the users to selectively replace default implementation of those aspects with custom, application specific ones.這使得用戶可以選擇性地替換默認實現使用自定義的那些方面,特定於應用程序的。

ConnectionKeepAliveStrategy keepAliveStrat = new DefaultConnectionKeepAliveStrategy() {

    @Override
    public long getKeepAliveDuration(
            HttpResponse response,
            HttpContext context) {
        long keepAlive = super.getKeepAliveDuration(response, context);
        if (keepAlive == -1) {
            // Keep connections alive 5 seconds if a keep-alive value
            // has not be explicitly set by the server
            keepAlive = 5000;
        }
        return keepAlive;
    }

};
CloseableHttpClient httpclient = HttpClients.custom()
        .setKeepAliveStrategy(keepAliveStrat)
        .build();

1.2.1. 1.2.1。HttpClient thread safetyHttpClient線程安全

HttpClientimplementations are expected to be thread safe. 實現預計是線程安全的。It is recommended that the same instance of this class is reused for multiple request executions.建議相同的這個類的實例是在多個請求重用死刑。

1.2.2. 1.2.2。HttpClient resource deallocationHttpClient資源回收

When an instance當一個實例 CloseableHttpClientis no longer needed and is about to go out of scope the connection manager associated with it must be shut down by calling the不再需要和即將走出範圍與之關聯的連接管理器必須關閉通過調用嗎 CloseableHttpClient#close()method.法。

CloseableHttpClient httpclient = HttpClients.createDefault();
try {
    <...>
} finally {
    httpclient.close();
}

1.3. 1.3。HTTP execution contextHTTP執行上下文

Originally HTTP has been designed as a stateless, response-request oriented protocol. 最初的HTTP被設計成爲一個無狀態、響應請求定向協議。However, real world applications often need to be able to persist state information through several logically related request-response exchanges. 然而,真實世界的應用程序通常需要能夠持續狀態信息通過一些邏輯上相關的請求-響應交換。In order to enable applications to maintain a processing state HttpClient allows HTTP requests to be executed within a particular execution context, referred to as HTTP context. 爲了使應用程序能夠保持處理狀態HttpClient允許HTTP請求執行在一個特定的執行上下文,稱爲HTTP上下文。Multiple logically related requests can participate in a logical session if the same context is reused between consecutive requests. 多個邏輯上相關的請求可以參與一個邏輯會話如果相同的上下文是連續請求之間重用。HTTP context functions similarly to aHTTP上下文功能類似 java.util.Map<String, Object>. It is simply a collection of arbitrary named values. 它僅僅是一組任意命名的值。An application can populate context attributes prior to request execution or examine the context after the execution has been completed.一個應用程序可以填充上下文屬性請求執行之前或檢查上下文執行後已經完成。

HttpContextcan contain arbitrary objects and therefore may be unsafe to share between multiple threads. 可以包含任意的對象,因此可能是不安全的,在多個線程間共享。It is recommended that each thread of execution maintains its own context.建議每個線程執行的維護自己的上下文。

In the course of HTTP request execution HttpClient adds the following attributes to the execution context:HTTP請求的過程中添加以下屬性執行HttpClient到執行上下文:

  • HttpConnectioninstance representing the actual connection to the target server.實例代表實際連接到目標服務器。

  • HttpHostinstance representing the connection target.實例代表連接的目標。

  • HttpRouteinstance representing the complete connection route實例代表完整的連接線路

  • HttpRequestinstance representing the actual HTTP request. 實例代表實際的HTTP請求。The final HttpRequest object in the execution context always represents the state of the message最後的HttpRequest對象在執行上下文總是代表着國家的消息 exactly到底as it was sent to the target server. 因爲它是發送到目標服務器。Per default HTTP/1.0 and HTTP/1.1 use relative request URIs. 每違約HTTP / 1.0和HTTP / 1.1使用相對uri的請求。However if the request is sent via a proxy in a non-tunneling mode then the URI will be absolute.然而如果請求被髮送通過代理在一個非隧道模式然後URI將是絕對的。

  • HttpResponseinstance representing the actual HTTP response.實例代表實際的HTTP響應。

  • java.lang.Booleanobject representing the flag indicating whether the actual request has been fully transmitted to the connection target.對象,該對象代表了標誌,指示是否實際的請求已經被完全傳輸到連接的目標。

  • RequestConfigobject representing the actual request configuation.對象代表實際的要求配製。

  • java.util.List<URI>object representing a collection of all redirect locations received in the process of request execution.對象代表一個集合的所有位置重定向的過程中收到的請求執行。

One can use一個可以使用 HttpClientContextadaptor class to simplify interractions with the context state.適配器類來簡化interractions與上下文狀態。

HttpContext context = <...>
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpHost target = clientContext.getTargetHost();
HttpRequest request = clientContext.getRequest();
HttpResponse response = clientContext.getResponse();
RequestConfig config = clientContext.getRequestConfig();

Multiple request sequences that represent a logically related session should be executed with the same多個請求序列代表一個邏輯相關會話應該執行相同的 HttpContextinstance to ensure automatic propagation of conversation context and state information between requests.實例以確保自動傳播的對話上下文和國家之間的信息請求。

In the following example the request configuration set by the initial request will be kept in the execution context and get propagatd to the consecutive requests sharing the same context.在以下示例請求配置設置的初始請求將被保存在執��上下文和得到propagatd連續的請求共享相同的上下文。

CloseableHttpClient httpclient = HttpClients.createDefault();
RequestConfig requestConfig = RequestConfig.custom()
        .setSocketTimeout(1000)
        .setConnectTimeout(1000)
        .build();

HttpGet httpget1 = new HttpGet("http://localhost/1");
httpget1.setConfig(requestConfig);
CloseableHttpResponse response1 = httpclient.execute(httpget1, context);
try {
    HttpEntity entity1 = response1.getEntity();
} finally {
    response1.close();
}
HttpGet httpget2 = new HttpGet("http://localhost/2");
CloseableHttpResponse response2 = httpclient.execute(httpget2, context);
try {
    HttpEntity entity2 = response2.getEntity();
} finally {
    response2.close();
}

1.4. 1.4。Exception handling異常處理

HttpClient can throw two types of exceptions:HttpClient可以拋出兩種類型的異常: java.io.IOExceptionin case of an I/O failure such as socket timeout or an socket reset and在案件的一個I / O失敗如套接字超時或一個插座復位和 HttpExceptionthat signals an HTTP failure such as a violation of the HTTP protocol. ,信號一個HTTP失敗如違反了HTTP協議。Usually I/O errors are considered non-fatal and recoverable, whereas HTTP protocol errors are considered fatal and cannot be automatically recovered from.通常的I / O錯誤被認爲是致命的和可恢復的,而HTTP協議錯誤被認爲是致命的,不能自動恢復。

1.4.1. 1.4.1。HTTP transport safetyHTTP傳輸安全

It is important to understand that the HTTP protocol is not well suited to all types of applications. 重要的是要理解HTTP協議,並不適合所有類型的應用程序。HTTP is a simple request/response oriented protocol which was initially designed to support static or dynamically generated content retrieval. HTTP是一個簡單的請求/響應定向協議最初設計用來支持靜態或動態生成的內容檢索。It has never been intended to support transactional operations. 它從未打算支持事務操作。For instance, the HTTP server will consider its part of the contract fulfilled if it succeeds in receiving and processing the request, generating a response and sending a status code back to the client. 例如,HTTP服務器將考慮它的部分合同完成的如果它成功地接收和處理請求,生成一個響應和發送回客戶機的狀態代碼。The server will make no attempt to roll back the transaction if the client fails to receive the response in its entirety due to a read timeout, a request cancellation or a system crash. 服務器將不打算回滾該事務,如果客戶未能接收響應完整由於讀超時,請求取消或系統崩潰。If the client decides to retry the same request, the server will inevitably end up executing the same transaction more than once. 如果客戶決定重試相同的請求,服務器最終都不可避免地執行相同的事務不止一次。In some cases this may lead to application data corruption or inconsistent application state.在某些情況下,這可能導致應用程序數據腐敗或不一致的應用程序的狀態。

Even though HTTP has never been designed to support transactional processing, it can still be used as a transport protocol for mission critical applications provided certain conditions are met. 儘管HTTP從未被設計用來支持事務處理,它仍然可以被用作一個傳輸協議爲關鍵任務的應用程序提供了符合特定的條件。To ensure HTTP transport layer safety the system must ensure the idempotency of HTTP methods on the application layer.以確保HTTP傳輸層安全系統必須確保冪等性的HTTP方法在應用程序層。

1.4.2. 1.4.2。Idempotent methods冪等方法

HTTP/1.1 specification defines an idempotent method asHTTP / 1.1規範定義了一個冪等方法

[Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request方法也可以有屬性的“冪等性”,(除了錯誤或過期問題)的副作用N > 0完全相同的請求一樣單個請求]

In other words the application ought to ensure that it is prepared to deal with the implications of multiple execution of the same method. 換句話說,應用程序應該確保它是準備好應對多個執行的含義相同的方法。This can be achieved, for instance, by providing a unique transaction id and by other means of avoiding execution of the same logical operation.這可以實現,例如,通過提供一個獨特的事務id和通過其他方式避免執行相同的邏輯操作。

Please note that this problem is not specific to HttpClient. 請注意,這個問題不是特定於HttpClient。Browser based applications are subject to exactly the same issues related to HTTP methods non-idempotency.基於瀏覽器的應用程序都受到相同的HTTP方法相關問題非冪等性。

HttpClient assumes non-entity enclosing methods such asHttpClient假定非封閉等方法 GETand HEADto be idempotent and entity enclosing methods such as是冪等和實體方法如封閉 POSTand PUTto be not.不。

1.4.3. 3。Automatic exception recovery自動異常恢復

By default HttpClient attempts to automatically recover from I/O exceptions. 默認情況下HttpClient嘗試自動恢復I / O例外。The default auto-recovery mechanism is limited to just a few exceptions that are known to be safe.默認自動還原機制是侷限於少數例外,已知是安全的。

  • HttpClient will make no attempt to recover from any logical or HTTP protocol errors (those derived fromHttpClient沒有試圖恢復任何邏輯或HTTP協議錯誤(來自 HttpExceptionclass).類)。

  • HttpClient will automatically retry those methods that are assumed to be idempotent.HttpClient將自動重試這些方法,被認爲是等冪的。

  • HttpClient will automatically retry those methods that fail with a transport exception while the HTTP request is still being transmitted to the target server (i.e. the request has not been fully transmitted to the server).HttpClient將自動重試失敗的那些方法與運輸異常而HTTP請求仍被傳送到目標服務器(即請求尚未完全傳遞到服務器)。

1.4.4. 1 4 4。Request retry handler請求重試處理程序

In order to enable a custom exception recovery mechanism one should provide an implementation of the爲了使一個自定義異常恢復機制應該提供一個實現的 HttpRequestRetryHandlerinterface.接口。

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 InterruptedIOException) {
            // Timeout
            return false;
        }
        if (exception instanceof UnknownHostException) {
            // Unknown host
            return false;
        }
        if (exception instanceof ConnectTimeoutException) {
            // Connection refused
            return false;
        }
        if (exception instanceof SSLException) {
            // SSL handshake exception
            return false;
        }
        HttpClientContext clientContext = HttpClientContext.adapt(context);
        HttpRequest request = clientContext.getRequest();
        boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
        if (idempotent) {
            // Retry if the request is considered idempotent
            return true;
        }
        return false;
    }

};
CloseableHttpClient httpclient = HttpClients.custom()
        .setRetryHandler(myRetryHandler)
        .build();

1.5. 1.5。Aborting requests流產請求

In some situations HTTP request execution fails to complete within the expected time frame due to high load on the target server or too many concurrent requests issued on the client side. 在某些情況下HTTP請求執行未能完全在預期的時間框架由於高負荷在目標服務器上或太多的併發請求發佈了在客戶端。In such cases it may be necessary to terminate the request prematurely and unblock the execution thread blocked in a I/O operation. 在這種情況下可能需要提前終止請求和開啓執行線程阻塞在I / O操作。HTTP requests being executed by HttpClient can be aborted at any stage of execution by invoking正在執行HTTP請求HttpClient可以中止執行的任何階段通過調用 HttpUriRequest#abort()method. 法。This method is thread-safe and can be called from any thread. 這個方法是線程安全的,可以要求任何線程。When an HTTP request is aborted its execution thread - even if currently blocked in an I/O operation - is guaranteed to unblock by throwing a當一個HTTP請求中止執行線程,即使目前的阻塞I / O操作,保證開啓通過拋出 InterruptedIOException

1.6. 1.6。HTTP protocol interceptorsHTTP協議攔截器

Th HTTP protocol interceptor is a routine that implements a specific aspect of the HTTP protocol. 攔截器是一個HTTP協議Th常規,實現一個特定的方面的HTTP協議。Usually protocol interceptors are expected to act upon one specific header or a group of related headers of the incoming message, or populate the outgoing message with one specific header or a group of related headers. 通常協議攔截器將一個特定的行動頭或一組相關的標題傳入消息,或填充輸出消息與一個特定的頭或一組相關的標題。Protocol interceptors can also manipulate content entities enclosed with messages - transparent content compression / decompression being a good example. 協議攔截器也可以操縱內容實體附信息-透明的內容壓縮/解壓是一個很好的例子。Usually this is accomplished by using the 'Decorator' pattern where a wrapper entity class is used to decorate the original entity. 這通常是通過使用“裝飾”模式,其中一個包裝實體類是用於裝飾原來的實體。Several protocol interceptors can be combined to form one logical unit.幾個協議攔截器可以合併,形成一個邏輯單元。

Protocol interceptors can collaborate by sharing information - such as a processing state - through the HTTP execution context. 協議攔截器可以通過共享信息協作——比如處理狀態——通過HTTP執行上下文。Protocol interceptors can use HTTP context to store a processing state for one request or several consecutive requests.協議攔截器可以使用HTTP上下文存儲處理狀態爲一個請求或連續的請求。

Usually the order in which interceptors are executed should not matter as long as they do not depend on a particular state of the execution context. 通常的順序執行攔截並不重要,只要他們不依賴於一個特定狀態的執行上下文。If protocol interceptors have interdependencies and therefore must be executed in a particular order, they should be added to the protocol processor in the same sequence as their expected execution order.如果協議攔截器有相互依賴關係,因此必須以特定的順序,他們應該被添加到協議處理器在同一序列作爲他們的預期的執行順序。

Protocol interceptors must be implemented as thread-safe. 協議攔截器必須實現爲線程安全的。Similarly to servlets, protocol interceptors should not use instance variables unless access to those variables is synchronized.類似於servlet、協議攔截器不應使用實例變量,除非訪問這些變量是同步的。

This is an example of how local context can be used to persist a processing state between consecutive requests:這是一個例子,說明本地上下文可以用來持久化處理狀態連續之間的請求:

CloseableHttpClient httpclient = HttpClients.custom()
        .addInterceptorLast(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()));
            }

        })
        .build();

AtomicInteger count = new AtomicInteger(1);
HttpClientContext localContext = HttpClientContext.create();
localContext.setAttribute("count", count);

HttpGet httpget = new HttpGet("http://localhost/");
for (int i = 0; i < 10; i++) {
    CloseableHttpResponse response = httpclient.execute(httpget, localContext);
    try {
        HttpEntity entity = response.getEntity();
    } finally {
        response.close();
    }
}

1.7. 1.7。Redirect handling重定向處理

HttpClient handles all types of redirects automatically, except those explicitly prohibited by the HTTP specification as requiring user intervention.HttpClient處理所有類型的自動重定向,除明確禁止的HTTP規範需要用戶干預。 See Other(status code 303) redirects on(狀態碼303)重定向在 POSTand PUTrequests are converted to請求轉換爲 GETrequests as required by the HTTP specification. 請求所需的HTTP規範。One can use a custom redirect strategy to relaxe restrictions on automatic redirection of POST methods imposed by the HTTP specification.一個可以使用一個自定義的重定向策略限制自動重定向的放鬆一下POST方法實施的HTTP規範。

LaxRedirectStrategy redirectStrategy = new LaxRedirectStrategy();
CloseableHttpClient httpclient = HttpClients.custom()
        .setRedirectStrategy(redirectStrategy)
        .build();

HttpClient often has to rewrite the request message in the process of its execution. HttpClient常常不得不重寫請求消息的過程中,其執行。Per default HTTP/1.0 and HTTP/1.1 generally use relative request URIs. 每違約HTTP / 1.0和HTTP / 1.1一般使用相對uri的請求。Likewise, original request may get redirected from location to another multiple times. 同樣,原始請求可能會重定向到另一個多次從位置。The final interpreted absolute HTTP location can be built using the original request and the context. 最後的解釋絕對HTTP位置可以使用原始的請求和上下文。The utility method該實用程序方法 URIUtils#resolvecan be used to build the interpreted absolute URI used to generate the final request. 可以用來構建解釋絕對URI用於生成最後的請求。This method includes the last fragment identifier from the redirect requests or the original request.該方法包括過去的片段標識符的請求重定向或原始請求。

CloseableHttpClient httpclient = HttpClients.createDefault();
HttpClientContext context = HttpClientContext.create();
HttpGet httpget = new HttpGet("http://localhost:8080/");
CloseableHttpResponse response = httpclient.execute(httpget, context);
try {
    HttpHost target = context.getTargetHost();
    List<URI> redirectLocations = context.getRedirectLocations();
    URI location = URIUtils.resolve(httpget.getURI(), target, redirectLocations);
    System.out.println("Final HTTP location: " + location.toASCIIString());
    // Expected to be an absolute URI
} finally {
    response.close();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章