HttpClient是Apache旗下的專案,是一個負責建立和維護HTTP和相關協議的工具集。
以下分析使用版本為: httpclient-4.5.3.jar, httpcore-4.4.6.jar, jdk1.8.0_131 所有示例程式碼均經過執行測試
傳送請求
httpclient最重要的功能就是傳送http請求,下面介紹如何執行一個get請求:
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet get = new HttpGet("http://www.jianshu.com/u/8a3115bb299c");
try(CloseableHttpResponse response = httpclient.execute(get)) {
HttpEntity entity = response.getEntity();
System.out.println(EntityUtils.toString(entity, "UTF-8"));
} catch (Exception e) {
e.printStackTrace();
}
複製程式碼
httpclient支援Http/1.1規範中定義的所有方法:GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,對應的類是:HttpGet,HttpHead,HttpPost,HttpPut,HttpDelete,HttpTrace,HttpOptions。
請求URI
URI(統一資源識別符號),用於標識應用請求的資源,通常由協議版本,主機名,埠(可選),資源路徑,引數名(可選),引數值(可選)組成。 請求時,可以通過拼接字串的形式訪問:
HttpGet get = new HttpGet("http://www.jianshu.com/p/7021031d6e49?utm_medium=index-banner&utm_source=desktop");
複製程式碼
httpclient也提供了URIBuilder這個類簡化請求URI的建立和修改。
URI uri = new URIBuilder()
.setScheme("http")
.setHost("www.jianshu.com")
.setPath("/p/7021031d6e49")
.setParameter("utm_medium", "index-banner")
.setParameter("utm_source", "desktop")
.build();
HttpGet httpget = new HttpGet(uri);
System.out.println(httpget.getURI());
複製程式碼
輸出
http://www.jianshu.com/p/7021031d6e49?utm_medium=index-banner&utm_source=desktop
複製程式碼
http請求是客戶端發給服務端的一個訊息,訊息的第一行包括了請求方法,URI以及使用的協議版本。
HttpGet get = new HttpGet("http://www.jianshu.com/u/8a3115bb299c");
RequestLine requestLine = get.getRequestLine();
System.out.println(requestLine.getMethod());
System.out.println(requestLine.getUri());
System.out.println(requestLine.getProtocolVersion());
System.out.println(requestLine);
複製程式碼
輸出
GET
http://www.jianshu.com/u/8a3115bb299c
HTTP/1.1
GET http://www.jianshu.com/u/8a3115bb299c HTTP/1.1
複製程式碼
處理響應
服務端接收並處理請求後,會返回訊息給到客戶端,該訊息的第一行包括協議版本,狀態碼以及描述文字。
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet get = new HttpGet("http://www.jianshu.com/u/8a3115bb299c");
try(CloseableHttpResponse response = httpclient.execute(get)) {
System.out.println(response.getStatusLine());
System.out.println(response.getProtocolVersion());
System.out.println(response.getStatusLine().getStatusCode());
System.out.println(response.getStatusLine().getReasonPhrase());
System.out.println(response.getStatusLine().toString());
} catch (Exception e) {
e.printStackTrace();
}
複製程式碼
輸出
HTTP/1.1 200 OK
HTTP/1.1
200
OK
HTTP/1.1 200 OK
複製程式碼
服務端返回的響應被封裝在HttpEntity,通過HttpEntity可以獲取請求響應的一些元資訊,比如Content-Type,Content-Length以及Content-Encoding,元資訊需由服務端提供,否則將是空值。
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet get = new HttpGet("http://www.jianshu.com/u/8a3115bb299c");
try(CloseableHttpResponse response = httpclient.execute(get)) {
HttpEntity entity = response.getEntity();
System.out.println(entity.getContentType());
System.out.println(entity.getContentLength());
System.out.println(entity.getContentEncoding());
} catch (Exception e) {
e.printStackTrace();
}
複製程式碼
輸出
Content-Type: text/html; charset=utf-8
-1
null
複製程式碼
HttpEntity提供了多種方法來讀取正文,官方文件推薦使用以下方法進行讀取:HttpEntity.getContent()
與 HttpEntity.writeTo(outputStream)
HttpEntity.getContent()方式
CloseableHttpClient httpclient = null;
CloseableHttpResponse response = null;
BufferedReader reader = null;
try {
httpclient = HttpClients.createDefault();
HttpGet get = new HttpGet("http://www.jianshu.com/u/8a3115bb299c");
response = httpclient.execute(get);
HttpEntity entity = response.getEntity();
reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
String str = "";
StringBuilder sb = new StringBuilder();
while ((str = reader.readLine()) != null) {
sb.append(str).append("\n");
}
System.out.println(sb);
} catch (Exception e) {
e.printStackTrace();
} finally {
CommonUtils.closeReader(reader);
CommonUtils.closeResponse(response);
CommonUtils.closeHttpClient(httpclient);
}
複製程式碼
CommonUtils
public void closeReader(BufferedReader reader) {
try {
if (reader != null) {
reader.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void closeResponse(CloseableHttpResponse response) {
try {
if (response != null) {
response.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void closeHttpClient(CloseableHttpClient httpclient) {
try {
if (httpclient != null) {
httpclient.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
複製程式碼
HttpEntity.writeTo方式
CloseableHttpClient httpclient = null;
CloseableHttpResponse response = null;
ByteArrayOutputStream outstream = new ByteArrayOutputStream();
try {
httpclient = HttpClients.createDefault();
HttpGet get = new HttpGet("http://www.jianshu.com/u/8a3115bb299c");
response = httpclient.execute(get);
HttpEntity entity = response.getEntity();
entity.writeTo(outstream);
System.out.println(outstream.toString("UTF-8"));
} catch (Exception e) {
e.printStackTrace();
} finally {
CommonUtils.closeOutputStream(outstream);
CommonUtils.closeResponse(response);
CommonUtils.closeHttpClient(httpclient);
}
複製程式碼
CommonUtils
public void closeOutputStream(ByteArrayOutputStream outstream) {
try {
if (outstream != null) {
outstream.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
複製程式碼
為了確保資源的正確釋放,採用上述兩種方式讀取正文時,都需對流進行關閉。除此之外,HttpClient還提供了EntityUtils工具類,方便對正文的讀取操作,不過官方文件中建議,只有當響應實體來自受信任的HTTP伺服器,並且已知其長度有限,才可以採用這種方法。
EntityUtils.toString方式
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet get = new HttpGet("http://www.jianshu.com/u/8a3115bb299c");
try(CloseableHttpResponse response = httpclient.execute(get)) {
HttpEntity entity = response.getEntity();
System.out.println(EntityUtils.toString(entity, "UTF-8"));
} catch (Exception e) {
e.printStackTrace();
} finally {
CommonUtils.closeHttpClient(httpclient);
}
複製程式碼
EntityUtils.toString內部會對HttpEntity中的輸入流進行關閉。 以上三種讀取方式,當CloseableHttpClient的例項不再使用時,都需呼叫close方法進行關閉。
通過BufferedHttpEntity實現響應多次讀取
對於流型別的HttpEntity而言,是不可重複讀取的,若想多次讀取,則需要在某個地方將HttpEntity快取起來,最簡單的方式,可以使用HttpClient提供的BufferedHttpEntity類來實現。
HttpEntity.getContent()+BufferedHttpEntity實現多次讀取
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet get = new HttpGet("http://www.jianshu.com/u/8a3115bb299c");
try(CloseableHttpResponse response = httpclient.execute(get)) {
HttpEntity entity = response.getEntity();
BufferedHttpEntity bufferedEntity = new BufferedHttpEntity(entity);
System.out.println(getEntityContent(bufferedEntity));
System.out.println(getEntityContent(bufferedEntity));
} catch (Exception e) {
e.printStackTrace();
} finally {
CommonUtils.closeHttpClient(httpclient);
}
複製程式碼
public String getEntityContent(HttpEntity entity) {
try(BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"))) {
String str = "";
StringBuilder sb = new StringBuilder();
while ((str = reader.readLine()) != null) {
sb.append(str).append("\n");
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
複製程式碼
HttpEntity.writeTo+BufferedHttpEntity實現多次讀取
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet get = new HttpGet("http://www.jianshu.com/u/8a3115bb299c");
try(CloseableHttpResponse response = httpclient.execute(get)) {
HttpEntity entity = response.getEntity();
BufferedHttpEntity bufferedEntity = new BufferedHttpEntity(entity);
System.out.println(getContentByWriteTo(bufferedEntity));
System.out.println(getContentByWriteTo(bufferedEntity));
} catch (Exception e) {
e.printStackTrace();
} finally {
CommonUtils.closeHttpClient(httpclient);
}
複製程式碼
public String getContentByWriteTo(HttpEntity entity) {
try(ByteArrayOutputStream outstream = new ByteArrayOutputStream()) {
entity.writeTo(outstream);
return outstream.toString("UTF-8");
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
複製程式碼
EntityUtils.toString+BufferedHttpEntity實現多次讀取
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet get = new HttpGet("http://www.jianshu.com/u/8a3115bb299c");
try(CloseableHttpResponse response = httpclient.execute(get)) {
HttpEntity entity = response.getEntity();
BufferedHttpEntity bufferedEntity = new BufferedHttpEntity(entity);
System.out.println(EntityUtils.toString(bufferedEntity, "UTF-8"));
System.out.println(EntityUtils.toString(bufferedEntity, "UTF-8"));
} catch (Exception e) {
e.printStackTrace();
} finally {
CommonUtils.closeHttpClient(httpclient);
}
複製程式碼
至此,通過HttpClient,簡單實現了http get請求的傳送和響應處理。