Apache HttpClient 詳解

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 對象,有如下幾種獲取方式

  1. 默認創(chuàng)建方式

    // 獲取默認配置的 HttpClient 
    CloseableHttpClient httpClient = HttpClients.createDefault();
    
  2. 根據(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ù)
  3. 自定義創(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è)置請求配置。

  1. 創(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();
    
    
    
  2. 設(shè)置全局配置

    // 此種方式可以在創(chuàng)建時 設(shè)置一些默認值
    CloseableHttpClient  httpClient = HttpClients.custom()
                         .setDefaultHeaders(Collections.emptyList())   // 設(shè)置默認請求頭
                         .setDefaultRequestConfig(requestConfig)  // 設(shè)置默認配置
                         .build();
    
  3. 單個請求設(shè)置配置

    // 創(chuàng)建 GET 請求對象
    HttpGet httpGet = new HttpGet(uri);
    
    // 設(shè)置請求參數(shù)
    httpGet.setConfig(requestConfig);
    

5、設(shè)置請求頭信息

在請求時,經(jīng)常會遇到設(shè)置自定義請求頭,或者更改 Conent-Type 的值,可以通過如下兩種方式設(shè)置:

  1. 設(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()
    
  2. 單個請求設(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),如下所示:

  1. 請求參數(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()));
    }
    
  2. 通過 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)容的。

  1. 發(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);
    
  2. 模擬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ù):

  1. getCode() 獲取響應(yīng)狀態(tài)
  2. 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 層層剖析,慢慢揭開 其神秘面紗。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容