1、簡介
HttpClient 是 Apache Jakarta Common 下的子項目,用來提供高效的、最新的、功能豐富的支持 HTTP 協(xié)議的客戶端編程工具包,并且它支持 HTTP 協(xié)議最新的版本和建議。HttpClient 已經(jīng)應(yīng)用在很多的項目中,比如 Apache Jakarta 上很著名的另外兩個開源項目 Cactus 和 HTMLUnit 都使用了 HttpClient。
HttpClient 相比傳統(tǒng) JDK 自帶的 URLConnection,增加了易用性和靈活性,它不僅是客戶端發(fā)送 HTTP 請求變得容易,而且也方便了開發(fā)人員測試接口(基于 HTTP 協(xié)議的),即提高了開發(fā)的效率,也方便提高代碼的健壯性。因此熟練掌握 HttpClient 是很重要的必修內(nèi)容,掌握 HttpClient 后,相信對于 HTTP 協(xié)議的了解會更加深入。
2、引入依賴
HttpClient 是三方工具,首先需要引入依賴。如下:
<!-- 此處使用的是 5.x 版本,可以根據(jù)自身情況引入版本 -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.1.1</version>
</dependency>
3、獲取客戶端
在使用時,首先需要 獲取 HttpClient 對象,有如下幾種獲取方式
-
默認創(chuàng)建方式
// 獲取默認配置的 HttpClient CloseableHttpClient httpClient = HttpClients.createDefault(); -
根據(jù)系統(tǒng)配置創(chuàng)建
// 此種方式是通過 根據(jù) 系統(tǒng)配置創(chuàng)建 HttpClient CloseableHttpClient httpClient = HttpClients.createSystem();在項目啟動時可以通過設(shè)置如下JVM啟動參數(shù):
- http.agent 配置 userAgent
- http.keepAlive 配置 keepAlive 數(shù)據(jù)
-
自定義創(chuàng)建
// 此種方式可以在創(chuàng)建時 設(shè)置一些默認值 CloseableHttpClient httpClient = HttpClients.custom() .setDefaultHeaders(Collections.emptyList()) // 設(shè)置默認請求頭 .setDefaultRequestConfig(RequestConfig.DEFAULT) // 設(shè)置默認配置 .build();
4、配置參數(shù)
HttpClient 可以通過在創(chuàng)建 HttpClient 對象時就設(shè)置全局配置,也可以為單個請求設(shè)置請求配置。
-
創(chuàng)建配置對象
// 創(chuàng)建請求配置信息 RequestConfig requestConfig = RequestConfig.custom() // 設(shè)置連接超時時間 .setConnectTimeout(Timeout.of(3000, TimeUnit.MILLISECONDS)) // 設(shè)置響應(yīng)超時時間 .setResponseTimeout(3000, TimeUnit.MILLISECONDS) // 設(shè)置從連接池獲取鏈接的超時時間 .setConnectionRequestTimeout(3000, TimeUnit.MILLISECONDS) .build(); -
設(shè)置全局配置
// 此種方式可以在創(chuàng)建時 設(shè)置一些默認值 CloseableHttpClient httpClient = HttpClients.custom() .setDefaultHeaders(Collections.emptyList()) // 設(shè)置默認請求頭 .setDefaultRequestConfig(requestConfig) // 設(shè)置默認配置 .build(); -
單個請求設(shè)置配置
// 創(chuàng)建 GET 請求對象 HttpGet httpGet = new HttpGet(uri); // 設(shè)置請求參數(shù) httpGet.setConfig(requestConfig);
5、設(shè)置請求頭信息
在請求時,經(jīng)常會遇到設(shè)置自定義請求頭,或者更改 Conent-Type 的值,可以通過如下兩種方式設(shè)置:
-
設(shè)置公共請求頭
List<Header> headers = new ArrayList<>(); headers.add(new BasicHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)); headers.add(new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, x-gzip, deflate")); headers.add(new BasicHeader(HttpHeaders.CONNECTION, "keep-alive")); // 創(chuàng)建 一個默認的 httpClient CloseableHttpClient httpClient = HttpClients.custom() .setDefaultHeaders(headers) // 設(shè)置默認請求頭 .build() -
單個請求設(shè)置請求頭
// 創(chuàng)建 POST 請求 HttpPost httpPost = new HttpPost(uri); // 添加 Content-Type 請求頭 httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED); // 添加 accept 請求頭 httpPost.addHeader(new BasicHeader(HttpHeaders.ACCEPT, "*/*"));
6、發(fā)送請求
GET 請求
GET請求的所有參數(shù)是直接拼接在 URL 后面的,在 HttpClient 中 有兩種方式可以實現(xiàn),如下所示:
-
請求參數(shù)直接拼接在請求路徑后面
String name = URLEncoder.encode("張三", "utf-8"); // 請求路徑及參數(shù) String url = "http://localhost:10010/user/params?age=20&name=" + name; // 創(chuàng)建 GET 請求對象 HttpGet httpGet = new HttpGet(url); // 調(diào)用 HttpClient 的 execute 方法執(zhí)行請求 CloseableHttpResponse response = httpClient.execute(httpGet); // 獲取請求狀態(tài) int code = response.getCode(); // 如果請求成功 if(code == HttpStatus.SC_OK){ LOGGER.info("響應(yīng)結(jié)果為:{}", EntityUtils.toString(response.getEntity())); } -
通過 URIBuilder 構(gòu)建請求路徑
// 構(gòu)建請求路徑,及參數(shù) URL url = new URL("http://localhost:10010/user/params"); URI uri = new URIBuilder() .setScheme(url.getProtocol()) .setHost(url.getHost()) .setPort(url.getPort()) .setPath(url.getPath()) // 構(gòu)建參數(shù) .setParameters( new BasicNameValuePair("name", "張三"), new BasicNameValuePair("age", "20") ).build(); // 創(chuàng)建 GET 請求對象 HttpGet httpGet = new HttpGet(uri); // 調(diào)用 HttpClient 的 execute 方法執(zhí)行請求 CloseableHttpResponse response = httpClient.execute(httpGet); // 獲取請求狀態(tài) int code = response.getCode(); // 如果請求成功 if(code == HttpStatus.SC_OK){ LOGGER.info("響應(yīng)結(jié)果為:{}", EntityUtils.toString(response.getEntity())); }
POST 請求
眾所周知,HTTP 中的 POST 請求的數(shù)據(jù)是包含在請求體中的。在 HttpClient 中 POST 請求發(fā)送數(shù)據(jù)是通過,調(diào)用 HttpPost 類的 setEntity(HttpEntity entity) 方法設(shè)置消息內(nèi)容的。
-
發(fā)送 JSON 數(shù)據(jù)
HttpClient 中發(fā)送 JSON 數(shù)據(jù)可以使用 StringHttpEntity 類實現(xiàn),如下所示:
// 請求參數(shù) String url = "http://localhost:10010/user/body"; // 創(chuàng)建 GET 請求對象 HttpPost httpPost = new HttpPost(url); // 構(gòu)建對象 User user = new User(); user.setName("張三") .setAge(20) .setAddress(new Address() .setCounty("中國") .setCity("北京")) .setAihao(Arrays.asList("跑步", "爬山", "看書")); // 創(chuàng)建 字符串實體對象 HttpEntity httpEntity = new StringEntity(JSON.toJSONString(user)); httpPost.setEntity(httpEntity); // 發(fā)送 POST 請求 httpClient.execute(httpPost); -
模擬form表單數(shù)據(jù)
在實際使用時,可以存在 需要模擬
form表單的情況進行請求數(shù)據(jù),在 HttpClent 中也可以很方便的實現(xiàn) form 的提交功能。操作步驟如下:-
修改 contentType
// 創(chuàng)建 ContentType 對象為 form 表單模式 ContentType contentType = ContentType.create("application/x-www-form-urlencoded", StandardCharsets.UTF_8); // 添加到 HttpPost 頭中 httpPost.setHeader(HttpHeaders.CONTENT_TYPE, contentType); -
創(chuàng)建請求數(shù)據(jù) HttpEntity
// 方式一、自己拼接請求數(shù)據(jù),并且創(chuàng)建 StringEntity 對象 String query = "name="+ URLEncoder.encode("張三", "utf-8") +"&age=20"; HttpEntity httpEntity = new StringEntity(query); // 方式二、通過UrlEncodedFormEntity 創(chuàng)建 HttpEntity HttpEntity httpEntity = new UrlEncodedFormEntity( Arrays.asList(new BasicNameValuePair("name", "張三"), new BasicNameValuePair("age", "20")), StandardCharsets.UTF_8 ); // 把 HttpEntity 設(shè)置到 HttpPost 中 httpPost.setEntity(httpEntity);
完整代碼如下所示:
// 創(chuàng)建 POST 請求對象 HttpPost httpPost = new HttpPost("http://localhost:10010/user/map"); /*String query = "name="+ URLEncoder.encode("張三", "utf-8") +"&age=20"; HttpEntity httpEntity = new StringEntity(query);*/ HttpEntity httpEntity = new UrlEncodedFormEntity( Arrays.asList(new BasicNameValuePair("name", "張三"), new BasicNameValuePair("age", "20")), StandardCharsets.UTF_8 ); // 設(shè)置請求數(shù)據(jù) httpPost.setEntity(httpEntity); // 設(shè)置請求頭 ContentType contentType = ContentType.APPLICATION_FORM_URLENCODED.withCharset(StandardCharsets.UTF_8); httpPost.setHeader(HttpHeaders.CONTENT_TYPE, contentType); // 調(diào)用 HttpClient 的 execute 方法執(zhí)行請求 CloseableHttpResponse response = httpClient.execute(httpPost); // 獲取請求狀態(tài) int code = response.getCode(); // 如果請求成功 if(code == HttpStatus.SC_OK){ LOGGER.info("響應(yīng)結(jié)果為:{}", EntityUtils.toString(response.getEntity())); } -
7、上傳下載
上傳
在 HttpClient 中實現(xiàn)上傳功能是非常方便的,可以通過 MultipartEntityBuilder直接構(gòu)建 HttpEntity 即可。
//要上傳的文件
File file = new File("F:/20150703212056_Yxi4L.jpeg");
// 創(chuàng)建對象
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
// 添加二進制消息體
builder.addBinaryBody("file", file);
// 也可以添加文本消息
ContentType contentType = ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8);
builder.addTextBody("name", "張三", contentType);
// 通過 MultipartEntityBuilder 構(gòu)建消息體
HttpEntity httpEntity = builder.build();
HttpPost httpPost = new HttpPost("http://localhost:10010/user/upload");
httpPost.setEntity(httpEntity);
CloseableHttpResponse response = httpClient.execute(httpPost);
// 獲取請求狀態(tài)
int code = response.getCode();
// 如果請求成功
if(code == HttpStatus.SC_OK){
LOGGER.info("響應(yīng)結(jié)果為:{}", EntityUtils.toString(response.getEntity()));
}
下載
HttpClient 中的下載也相當(dāng)簡單,只需要發(fā)送請求,判斷請求成功后,讀取輸入流,然后保存到響應(yīng)的路徑下即可,如下:
// 請求下載路徑
HttpGet httpGet = new HttpGet("http://localhost:10010/user/downLoad");
CloseableHttpResponse response = httpClient.execute(httpGet);
// 如果請求成功
if (response.getCode() == HttpStatus.SC_OK){
// 獲取下載文件的文件名,此處的 File-Name 頭信息,需要在服務(wù)端進行自定義
Header header = response.getFirstHeader("File-Name");
String value = header.getValue();
// 讀取數(shù)據(jù)
byte[] bytes = EntityUtils.toByteArray(response.getEntity());
try (OutputStream outputStream = new FileOutputStream("F:/" + value);){
outputStream.write(bytes);
outputStream.flush();
}
}
8、響應(yīng)處理
在 HttpClient 中把響應(yīng)封裝成了 CloseableHttpResponse 對象,在此對象中可以獲取如下數(shù)據(jù):
- getCode() 獲取響應(yīng)狀態(tài)
- getEntity() 獲取響應(yīng)數(shù)據(jù)
// 如果請求成功
if(code == HttpStatus.SC_OK){
LOGGER.info("響應(yīng)結(jié)果為:{}", EntityUtils.toString(response.getEntity()));
}
HttpClient 提供了 EntityUtils工具類,可以很好的把 響應(yīng)的 HttpEntity 轉(zhuǎn)換為 字節(jié)數(shù)組或者字符串
// 轉(zhuǎn)換為字符串
EntityUtils.toString(response.getEntity());
// 轉(zhuǎn)換為字節(jié)數(shù)組
EntityUtils.toByteArray(response.getEntity());
除了上述外,還可以在 調(diào)用 HttpClient 的 execute()方法時 傳入,響應(yīng)處理器,返回自定義的數(shù)據(jù)類型。如下所示,是返回一個 自定義的 Response 對象
// 自定義響應(yīng)對象
@Data
@Accessors(chain = true)
class Response {
// 響應(yīng)狀態(tài)
private int code;
// 響應(yīng)描述
private String msg;
// 響應(yīng)體
private String body;
}
// 調(diào)用 execute 時自定義 響應(yīng)處理類
Response execute = httpClient.execute(httpGet, response -> {
return new Response().setCode(response.getCode())
.setMsg(response.getReasonPhrase())
.setBody(EntityUtils.toString(response.getEntity(),
StandardCharsets.UTF_8));
});
9、會話保持
在實際項目中,經(jīng)常會遇到需要先登錄然后才能進行訪問其他接口,那么, 在 HttpClient 中提供了 HttpClientContext 類,可以很好的實現(xiàn),會話保持功能。
- 創(chuàng)建
HttpClientContext - 在 execute() 方法中傳入 第一步創(chuàng)建的對象
如下所示:
// 創(chuàng)建 HttpClientContext對象
HttpContext httpContext = new BasicHttpContext();
httpContext.setAttribute("name", "zhangsan");
HttpClientContext httpClientContext = HttpClientContext.adapt(httpContext);
// 登錄
httpClient.execute(new HttpPost(""), httpClientContext);
// 獲取數(shù)據(jù)
httpClient.execute(new HttpGet(""), httpClientContext);
10、總結(jié)
這里只是 HttpClient 的使用做了簡單的介紹,起到拋磚引玉的作用,其實,HttpClient 提供的功能非常強大的,在接下來會對 HttpClient 層層剖析,慢慢揭開 其神秘面紗。