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.java
關閉InputStream和關閉response的區別在於:關閉InputStream會經過消費實體內容與底層保持鏈接,而關閉response則會當即中止和丟棄鏈接。web
class HttpResponseProxy implements CloseableHttpResponse { private final HttpResponse original; private final ConnectionHolder connHolder; public HttpResponseProxy(final HttpResponse original, final ConnectionHolder connHolder) { this.original = original; this.connHolder = connHolder; ResponseEntityProxy.enchance(original, connHolder); } public void close() throws IOException { if (this.connHolder != null) { this.connHolder.abortConnection(); } }
丟棄的代碼:json
public void abortConnection() { synchronized (this.managedConn) { if (this.released) { return; } this.released = true; try { this.managedConn.shutdown(); log.debug("Connection discarded"); } catch (final IOException ex) { if (this.log.isDebugEnabled()) { this.log.debug(ex.getMessage(), ex); } } finally { this.manager.releaseConnection( this.managedConn, null, 0, TimeUnit.MILLISECONDS); } } }
若是是使用EntityUtils#consume(HttpEntity)這個方法,它會自動去關閉inputStream緩存
若是不須要讀取所有的實體,則能夠直接關閉response,來終止InputSteam的讀取(關閉response會自動關閉InputStream)。安全
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(); }
不推薦使用EntityUtils的toString方法,若是須要的話,最好本身判斷長度:服務器
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(); }
由於toString方法默認最大的長度是Integer.MAX_VALUE,這個有點危險,會致使緩衝區溢出。app
/** * Get the entity content as a String, using the provided default character set * if none is found in the entity. * If defaultCharset is null, the default "ISO-8859-1" is used. * * @param entity must not be null * @param defaultCharset character set to be applied if none found in the entity * @return the entity content as a String. May be null if * {@link HttpEntity#getContent()} is null. * @throws ParseException if header elements cannot be parsed * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE * @throws IOException if an error occurs reading the input stream * @throws UnsupportedCharsetException Thrown when the named charset is not available in * this instance of the Java virtual machine */ public static String toString( final HttpEntity entity, final Charset defaultCharset) throws IOException, ParseException { Args.notNull(entity, "Entity"); final InputStream instream = entity.getContent(); if (instream == null) { return null; } try { Args.check(entity.getContentLength() <= Integer.MAX_VALUE, "HTTP entity too large to be buffered in memory"); int i = (int)entity.getContentLength(); if (i < 0) { i = 4096; } Charset charset = null; try { final ContentType contentType = ContentType.get(entity); if (contentType != null) { charset = contentType.getCharset(); } } catch (final UnsupportedCharsetException ex) { throw new UnsupportedEncodingException(ex.getMessage()); } if (charset == null) { charset = defaultCharset; } if (charset == null) { charset = HTTP.DEF_CONTENT_CHARSET; } final Reader reader = new InputStreamReader(instream, charset); final CharArrayBuffer buffer = new CharArrayBuffer(i); final char[] tmp = new char[1024]; int l; while((l = reader.read(tmp)) != -1) { buffer.append(tmp, 0, l); } return buffer.toString(); } finally { instream.close(); } }
若是entity須要反覆使用的話,最好使用緩存起來:ide
CloseableHttpResponse response = <...> HttpEntity entity = response.getEntity(); if (entity != null) { entity = new BufferedHttpEntity(entity); }
主要有四種:StringEntity, ByteArrayEntity, InputStreamEntity,和FileEntitypost
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);
注意:InputStreamEnity只能讀取一次。ui
表單:
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);
它會自動對參數進行URL編碼。
param1=value1¶m2=value2
最直接的方式是使用ResponseHandler,它不須要去本身管理鏈接和資源釋放,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);
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();
Http是請求響應式的,本是無狀態的,不過web應用一般須要在幾個連續的請求之間保持聯繫,所以可使用這個來傳遞變量,注意這個不是線程安全的,建議每一個線程使用一個。
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(); }
兩次請求,經過context共享配置。
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();
若是是冪等的操做,則能夠重試。
在一些狀況下,因爲目標服務器的高負載或客戶端有不少活動的請求,那麼 HTTP 請求執行會在預期的時間框內而失敗。這時,就可能不得不過早地停止請求,解除封鎖在 I/O 執行中的線程封鎖。被 HttpClient 執行的 HTTP 請求能夠在執行的任意階段經過調用HttpUriRequest#abort()方法而停止。這個方法是線程安全的,並且能夠從任意線程中調用。當一個 HTTP 請求被停止時,它的執行線程就封鎖在 I/O 操做中了,並且保證經過拋出 InterruptedIOException 異常來解鎖。
協議攔截器可使用 HTTP 內容來爲一個或多個連續的請求存儲一個處理狀態。 協議攔截器必須實現爲線程安全的。和 Servlet 類似,協議攔截器不該該使用實例變量,除非訪問的那些變量是同步的。
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(); } }
HttpClient會自動處理重定向,固然也能夠本身自定義策略。
LaxRedirectStrategy redirectStrategy = new LaxRedirectStrategy(); CloseableHttpClient httpclient = HttpClients.custom() .setRedirectStrategy(redirectStrategy) .build();
resolve能夠用來構建絕對路徑:
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(); }