1. 程式人生 > 其它 >通用HttpClient請求工具類

通用HttpClient請求工具類

前言

現在大多數Java專案開發中,經常都用通過HTTP協議來呼叫網路資源資料(1、爬蟲爬取網頁資料;2、請求第三方系統進行資料互動等),雖然JDK8及以前的版本,也提供了響應的請求工具包,但是使用起來很不靈活,所以大多數都是採用Apache的HttpClient包來封裝自己的請求工具類,方便整個專案開發使用。

使用流程

1、引入maven依賴

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId
> <version>4.5.13</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.75</version> </dependency>

2、書寫請求工具類

/**
 * http請求工具類
 */
@Slf4j
public class HttpUtil {

    
/** * 請求連線構造物件 */ private static final HttpClientBuilder httpClientBuilder = HttpClients.custom(); /** * 連線池最大連線數 */ private static final int MAX_TOTAL = 8; /** * 每個路由最大預設連線數 */ private static final int DEFAULT_MAX_RER_ROUTE = 8; /** * 獲取連接獲取超時時間
*/ private static final int CONNECTION_REQUEST_TIMEOUT = 2000; /** * 連線超時時間 */ private static final int CONNECTION_TIMEOUT = 2000; /** * 資料響應超時時間 */ private static final int SOCKET_TIMEOUT = 10000; static { /* 1、繞開不安全的https請求的證書驗證(不需要可以註釋,然後使用空引數的PoolingHttpClientConnectionManager構造連線池管理物件) */ Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", trustHttpsCertificates()) .build(); /* 2、建立請求連線池管理 */ PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry); // 設定連線池最大連線數 cm.setMaxTotal(MAX_TOTAL); // 設定每個路由最大預設連線數 cm.setDefaultMaxPerRoute(DEFAULT_MAX_RER_ROUTE); httpClientBuilder.setConnectionManager(cm); /* 3、設定預設請求配置 */ RequestConfig requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT) // 設定獲取連接獲取超時時間 .setConnectTimeout(CONNECTION_TIMEOUT) // 設定連線超時時間 .setSocketTimeout(SOCKET_TIMEOUT) // 設定資料響應超時時間 .build(); httpClientBuilder.setDefaultRequestConfig(requestConfig); } /** * 執行get請求(網頁) * @param url 請求地址(含有特殊符號需要URLEncoder編碼) * @param headers 請求頭引數 * @return 響應資料 */ public static String getPage(String url, Map<String, String> headers) { CloseableHttpClient closeableHttpClient = httpClientBuilder.build(); HttpGet httpGet = new HttpGet(url); // 請求頭設定,如果常用的請求頭設定,也可以寫死,特殊的請求才傳入 httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38"); if (headers != null) { for (String headerKey : headers.keySet()) { httpGet.setHeader(headerKey, headers.get(headerKey)); } } CloseableHttpResponse response = null; try { response = closeableHttpClient.execute(httpGet); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode== HttpStatus.SC_OK) { // 請求響應成功 HttpEntity entity = response.getEntity(); return EntityUtils.toString(entity, StandardCharsets.UTF_8); } else { log.error("請求地址({})失敗:{}", url, statusCode); } } catch (Exception e) { log.error("請求地址({})失敗", url, e); throw new RuntimeException("請求地址("+url+")失敗"); } finally { HttpClientUtils.closeQuietly(response); } return null; } /** * 執行post請求(form表單) * @param url 請求地址 * @param headers 請求頭引數 * @return 響應資料 */ public static String postForm(String url, Map<String, String> headers, Map<String, String> params) { CloseableHttpClient closeableHttpClient = httpClientBuilder.build(); HttpPost httpPost = new HttpPost(url); // 請求頭設定,如果常用的請求頭設定,也可以寫死,特殊的請求才傳入 httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38"); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); if (headers != null) { for (String headerKey : headers.keySet()) { httpPost.setHeader(headerKey, headers.get(headerKey)); } } // 設定請求引數 if (params!=null) { List<NameValuePair> nvList = new ArrayList<>(params.size()); for (String paramKey : params.keySet()) { NameValuePair nv = new BasicNameValuePair(paramKey, params.get(paramKey)); nvList.add(nv); } HttpEntity paramsEntity = new UrlEncodedFormEntity(nvList, StandardCharsets.UTF_8); httpPost.setEntity(paramsEntity); } CloseableHttpResponse response = null; try { response = closeableHttpClient.execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode== HttpStatus.SC_OK) { // 請求響應成功 HttpEntity entity = response.getEntity(); return EntityUtils.toString(entity, StandardCharsets.UTF_8); } else { log.error("請求地址({})失敗:{}", url, statusCode); } } catch (IOException e) { log.error("請求地址({})失敗", url, e); throw new RuntimeException("請求地址("+url+")失敗"); } finally { HttpClientUtils.closeQuietly(response); } return null; } /** * 執行post請求(介面) * @param url 請求地址 * @param headers 請求頭引數 * @return 響應資料 */ public static String getJson(String url, Map<String, String> headers) { CloseableHttpClient closeableHttpClient = httpClientBuilder.build(); HttpGet httpGet = new HttpGet(url); // 請求頭設定,如果常用的請求頭設定,也可以寫死,特殊的請求才傳入 if (headers != null) { for (String headerKey : headers.keySet()) { httpGet.setHeader(headerKey, headers.get(headerKey)); } } CloseableHttpResponse response = null; try { response = closeableHttpClient.execute(httpGet); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode== HttpStatus.SC_OK) { // 請求響應成功 HttpEntity entity = response.getEntity(); return EntityUtils.toString(entity, StandardCharsets.UTF_8); } else { log.error("請求地址({})失敗:{}", url, statusCode); } } catch (IOException e) { log.error("請求地址({})失敗", url, e); throw new RuntimeException("請求地址("+url+")失敗"); } finally { HttpClientUtils.closeQuietly(response); } return null; } /** * 執行post請求(介面) * @param url 請求地址 * @param headers 請求頭引數 * @return 響應資料 */ public static String postJson(String url, Map<String, String> headers, Map<String, String> params) { CloseableHttpClient closeableHttpClient = httpClientBuilder.build(); HttpPost httpPost = new HttpPost(url); // 請求頭設定,如果常用的請求頭設定,也可以寫死,特殊的請求才傳入 httpPost.setHeader("Content-Type", "application/json;charset=UTF-8"); if (headers != null) { for (String headerKey : headers.keySet()) { httpPost.setHeader(headerKey, headers.get(headerKey)); } } if (params!=null) { HttpEntity paramEntity = new StringEntity(JSON.toJSONString(params), StandardCharsets.UTF_8); httpPost.setEntity(paramEntity); } CloseableHttpResponse response = null; try { response = closeableHttpClient.execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode== HttpStatus.SC_OK) { // 請求響應成功 HttpEntity entity = response.getEntity(); return EntityUtils.toString(entity, StandardCharsets.UTF_8); } else { log.error("請求地址({})失敗:{}", url, statusCode); } } catch (IOException e) { log.error("請求地址({})失敗", url, e); throw new RuntimeException("請求地址("+url+")失敗"); } finally { HttpClientUtils.closeQuietly(response); } return null; } /** * 構建https安全連線工廠 * @return 安全連線工廠 */ private static ConnectionSocketFactory trustHttpsCertificates() { SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); try { sslContextBuilder.loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { return true; } }); SSLContext sslContext = sslContextBuilder.build(); SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}, // 支援的https安全認證協議 null, NoopHostnameVerifier.INSTANCE); return sslConnectionSocketFactory; } catch (Exception e) { log.error("構建安全連線工廠失敗", e); throw new RuntimeException("構建安全連線工廠失敗"); } } }

3、額外說明

  • 專案中HttpClient基本最好構建請求連線池進行請求,提升效能,加快請求速度;
  • 如果你的應用不請求不信任的https連線,則不需要繞過https安全認證(不需要使用到trustHttpsCertificates方法);
  • 專案請求在工具類一般可以確定請求頭,可以寫死,從而少傳入一個引數;(特殊要求傳入的請求才保留該引數設定)
  • 每次請求後一定要確認消費和關閉響應;(HttpClientUtils.closeQuietly(response))
  • 部分爬蟲需要設定代理,使用示例如下:
HttpGet httpGet = new HttpGet(url);
HttpHost proxy = new HttpHost("49.70.17.48", 8888);
RequestConfig config = RequestConfig.custom()
        .setProxy(proxy) //設定代理
        .setConnectTimeout(2000) // 設定HTTP連線超時時間
        .setSocketTimeout(3000)  // 設定資料響應超時時間
        .setConnectionRequestTimeout(2000) // 設定從連線池獲取連線的超時時間
        .build();
httpGet.setConfig(config);