HttpClient(學習筆記)

HttpClient(學習筆記)

HttpClient是Apache Jakarta Common下的子項目,用來提供高效的、最新的、功能豐富的支持HTTP協議的客戶端編程工具包,並且它支持HTTP協議最新的版本和建議。HttpClient已經應用在很多的項目中,比如Apache Jakarta上很著名的另外兩個開源項目Cactus和HTMLUnit都使用了HttpClient

一: 依賴

		<dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.11</version>
        </dependency>

二: 簡單發送無參數GET請求

	@Test
    public void test1() throws Exception {
        //1.創建HTTP請求客戶端
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //2.創建發送Get請求對象
        HttpGet httpGet = new HttpGet("http://localhost:8080/nolanj");
        //請求配置使用默認
        httpGet.setConfig(RequestConfig.DEFAULT);
        //3.執行請求, 獲取響應對象response
        CloseableHttpResponse response = httpClient.execute(httpGet);
        //4.獲取響應內容實體
        HttpEntity responseEntity = response.getEntity();
        //5.解析內容爲字符串  讀取完畢後會將流關閉
        String string = EntityUtils.toString(responseEntity,"utf-8");
        //6.釋放資源
        httpClient.close();
    }

三: 簡單發送有參數GET請求

 	@Test
    public void test1() throws Exception {
        //構造請求參數
        StringBuffer buffer = new StringBuffer();
        Map<String,String> paramsMap = new HashMap<>();
        paramsMap.put("name","張先生");
        paramsMap.put("age","25");
        paramsMap.put("sex","男");
        //進行參數拼接 name=xxx&age=xxx
        buffer.append("?");
        for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
            buffer.append(entry.getKey()).append("=")
                    .append(entry.getValue()).append("&");
        }
        //刪除拼接完成後的最後一個 '&'
        buffer.deleteCharAt(buffer.length()-1);
        //1.創建HTTP請求客戶端
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //2.創建發送Get請求對象											//拼接參數
        HttpGet httpGet = new HttpGet("http://localhost:8080/nolanj"+buffer.toString());
        //請求配置使用默認
        httpGet.setConfig(RequestConfig.DEFAULT);
        //5.執行請求, 獲取響應對象response
        CloseableHttpResponse response = httpClient.execute(httpGet);
        //6.獲取響應內容實體
        HttpEntity responseEntity = response.getEntity();
        //7.解析內容爲字符串  讀取完畢後會將流關閉
        String string = EntityUtils.toString(responseEntity,"utf-8");
        //8.釋放資源
        httpClient.close();
    }

四: 簡單發送參數爲表單格式POST請求

	@Test
    public void test1() throws Exception {
        //1.創建HTTP請求客戶端
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //2.創建發送post請求對象
        HttpPost httpPost = new HttpPost("http://localhost:8080/byte");
        //請求配置使用默認
        httpPost.setConfig(RequestConfig.DEFAULT);
        //構造請求參數
        LinkedList<NameValuePair> parameters = new LinkedList<>();
        parameters.add(new BasicNameValuePair("name","張韶涵"));
        //3.創建表單實體請求參數對象
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters,"utf-8");
        //4.添加請求參數實體
        httpPost.setEntity(entity);
        //5.執行請求, 獲取響應對象response
        CloseableHttpResponse response = httpClient.execute(httpPost);
        //6.獲取響應內容實體
        HttpEntity responseEntity = response.getEntity();
        //7.解析內容爲字符串  讀取完畢後會將流關閉
        String string = EntityUtils.toString(responseEntity,"utf-8");
        //8.釋放資源
        httpClient.close();
    }

五: 簡單發送無參數POST`請求

	@Test
    public void test1() throws Exception {
        //1.創建HTTP請求客戶端
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //2.創建發送post請求對象
        HttpPost httpPost = new HttpPost("http://localhost:8080/byte");
        //請求配置使用默認
        httpPost.setConfig(RequestConfig.DEFAULT);
        //3.執行請求, 獲取響應對象response
        CloseableHttpResponse response = httpClient.execute(httpPost);
        //4.獲取響應內容實體
        HttpEntity responseEntity = response.getEntity();
        //5.解析內容爲字符串  讀取完畢後會將流關閉
        String string = EntityUtils.toString(responseEntity,"utf-8");
        //6.釋放資源
        httpClient.close();
    }

六: 轉碼爲String類型發送POST請求來傳輸文件

	@Test
    public void test1() throws Exception {
        //1.創建HTTP請求客戶端
        HttpClient httpClient = HttpsUtils.getSSLHttpClient();
        //2.創建發送post請求對象
        HttpPost HttpPost = new HttpPost("http://localhost:8080/byte");
        //3.設置請求配置
        HttpPost.setConfig(HttpsUtils.getRequestConfig());
        //將讀取文件方法返回的字節數組編碼爲字符串
        String encode = Base64.getEncoder().encodeToString(this.test02());
        //4.添加請求參數實體
        LinkedList<NameValuePair> parameters = new LinkedList<>();
        parameters.add(new BasicNameValuePair("file",encode));
        parameters.add(new BasicNameValuePair("fileName","張韶涵.jpg"));
        HttpPost.setEntity(new UrlEncodedFormEntity(parameters,"utf-8"));
        //5.執行請求, 獲取響應對象response
        HttpResponse response = httpClient.execute(HttpPost);
        //6.獲取響應內容實體
        HttpEntity responseEntity = response.getEntity();
        //7.解析內容爲字符串  讀取完畢後會將流關閉
        String string = EntityUtils.toString(responseEntity,"utf-8");
    }

    /**
     *  讀取本地文件通過字節輸出流轉換成字節數組
     *
     * @return byte[]
     * @author: zhihao
     * @date: 27/2/2020
     */
    public byte[] test02() throws Exception {
        BufferedInputStream is = new BufferedInputStream(new FileInputStream("D:\\admin\\4.jpg"));
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] bytes = new byte[1024*2];
        int len = 0;
        while ((len =  is.read(bytes) ) != -1) {
            outputStream.write(bytes,0,len);
        }
        outputStream.close();
        byte[] resultByte = outputStream.toByteArray();
        return resultByte;
    }



//----------------------------------其他後端控制層------------------------------------------
	@PostMapping("/byte")
    public void test03(HttpServletRequest request) {
        BufferedOutputStream os = null;
        try {
            //從參數獲取文件名與文件字符串
            String fileName = request.getParameter("fileName");
            String file = request.getParameter("file");
            os = new BufferedOutputStream(new FileOutputStream(new File("D:admin\\123\\" + fileName)));
            //將文件字符串解碼爲字節數組
            byte[] decode = Base64.getDecoder().decode(file);
            os.write(decode);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IoUtil.close(os);
        }
    }

七: 發送字節數組請求來進行傳輸文件

		//1.創建HTTP請求客戶端
        //2.創建發送post請求對象
        //3.設置請求配置
        //4.添加請求參數實體                    //獲取讀取文件後的字節輸出流的數組
        HttpPost.setEntity(new ByteArrayEntity(this.test02()));
        //文件名通過請求頭髮送                解決亂碼問題
        HttpPost.setHeader("fielName",URLEncoder.encode("66.jpg","utf-8"));
        //5.執行請求, 獲取響應對象response
        //6.獲取響應內容實體
        //7.解析內容爲字符串  讀取完畢後會將流關閉

//--------------------------------其他後端控制層--------------------------------------------
	@PostMapping("/byte")
    public void test03(HttpServletRequest request) {
        BufferedOutputStream os = null;
        ServletInputStream is = null;
        try {
            is = request.getInputStream();
            //從請求頭獲取文件名
            String fileName = URLDecoder.decode(request.getHeader("fielName"),"utf-8");
            os = new BufferedOutputStream(new FileOutputStream(new File("D:\\123\\" + fileName)));
            byte[] bytes = new byte[1024*2];
            int len = 0;
            while ((len = is.read(bytes)) != -1) {
                os.write(bytes,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IoUtil.close(os);
            IoUtil.close(is);
        }
    }

八:其他請求方式省略中…

進行抽取get與post請求的工具類:

import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * @Author: zhihao
 * @Date: 29/2/2020 下午 3:58
 * @Description: HTTP請求工具類 (http走的是默認Http客戶端,https走的是SSL協議)
 * @Versions 1.0
 **/
public class HttpsUtils {

    //連接池管理器
    private static PoolingHttpClientConnectionManager connMgr;
    //請求配置
    private static RequestConfig requestConfig;
    //http請求重試處理程序
    private static HttpRequestRetryHandler httpRequestRetryHandler;
    //http客戶端
    private static HttpClient httpClient;
    //最大超時時間
    private static final int MAX_TIMEOUT = 10000;
    //cookie存儲,可選
    private static BasicCookieStore basicCookieStore = new BasicCookieStore();

    private static final Logger log = LoggerFactory.getLogger(HttpsUtils.class);

    private HttpsUtils() {}

    static {
        //初始化
        init();
    }

    /**
     * 發送 GET請求,不帶參數
     *
     * @param url 地址
     * @return java.lang.String 結果字符串,自行json解析
     */
    public static String sendGetRequest(String url) {
        return sendGetRequest(url, new HashMap<String, Object>());
    }

    /**
     * 發送GET帶參數請求 ,K-V形式
     *
     * @param url
     * @param params
     * @return java.lang.String 結果字符串,自行json解析
     */
    public static String sendGetRequest(String url, Map<String, Object> params) {
        StringBuffer param = new StringBuffer();
        //先添加請求鏈接
        param.append(url);
        int i = 0;
        for (String key : params.keySet()) {
            if (i == 0) {
                param.append("?");
            } else {
                param.append("&");
            }
            param.append(key).append("=").append(params.get(key));
            i++;
        }
        String resultString = null;
        //如果是發送https請求使用安全ssl構建
        if (param.toString().startsWith("https")) {
            httpClient = getSSLHttpClient();
        } else {
            httpClient = HttpClients.createDefault();
        }
        try {
            HttpGet httpGet = new HttpGet(param.toString());
            httpGet.setConfig(requestConfig);
            HttpResponse response = httpClient.execute(httpGet);
            HttpEntity entity = response.getEntity();
            resultString = EntityUtils.toString(entity);
        } catch (IOException e) {
            log.error("請求異常: "+getErrorString(e));
        }
        return resultString;
    }




    /**
     * 發送 POST 請求(HTTP),不帶輸入數據
     *
     * @param apiUrl 地址
     * @return java.lang.String 結果字符串,自行json解析
     * @date: 27/2/2020
     */
    public static String sendPostRequest(String apiUrl) {
        return sendPostRequest(apiUrl, new HashMap<String, Object>());
    }

    /**
     * 發送POST請求,K-V形式 表單形式
     *
     * @param apiUrl API接口URL
     * @param params 參數map
     * @return java.lang.String 結果字符串,自行json解析
     */
    public static String sendPostRequest(String apiUrl, Map<String, Object> params) {
        if (apiUrl.startsWith("https")) {
            httpClient = getSSLHttpClient();
        } else {
            httpClient = HttpClients.createDefault();
        }
        String resultString = null;
        HttpPost httpPost = new HttpPost(apiUrl);
        try {
            httpPost.setConfig(requestConfig);
            //構造請求參數
            List<NameValuePair> pairList = new ArrayList<>(params.size());
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue().toString());
                pairList.add(pair);
            }
            httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("UTF-8")));
            HttpResponse response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            resultString = EntityUtils.toString(entity, "UTF-8");
        } catch (IOException e) {
            log.error("請求異常: "+getErrorString(e));
        }
        return resultString;
    }

    /**
     * 發送json方式POST請求
     *
     * @param json   json對象
     * @param apiUrl 地址
     * @return java.lang.String 結果字符串,自行json解析
     */
    public static String sendJsonPostRequest(String apiUrl, Object json) {
        if (apiUrl.startsWith("https")) {
            httpClient = getSSLHttpClient();
        } else {
            httpClient = HttpClients.createDefault();
        }
        String resultString = null;
        HttpPost httpPost = new HttpPost(apiUrl);
        try {
            httpPost.setConfig(requestConfig);
            StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");// 解決中文亂碼問題
            //json請求
            httpPost.addHeader("content-type", "application/json;charset=UTF-8");
            httpPost.setEntity(stringEntity);
            HttpResponse response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            resultString = EntityUtils.toString(entity, "UTF-8");
        } catch (IOException e) {
            log.error("請求異常: "+getErrorString(e));
        }
        return resultString;
    }

    /**
     * <p>
     *      獲取HttpClient客戶端進行自定義發送請求(例如發送帶cookie請求),
     *      注意: 由於使用的是共享的cookie集合, 如果請求過多的不同網站,cookie集合可能會非常大,
     *      並且如果使用過添加cookie方法,那麼此工具類獲取的getSSLHttpClient請求都會帶上所有cookie發送,
     *      並由於配置了DefaultCookieStore(默認cookie存儲),
     *      所有getSSLHttpClient請求都會帶上響應的cookie進行下一次請求!
     * </p>
     *
     * @param
     * @return org.apache.http.client.HttpClient
     * @author: zhihao
     * @date: 29/2/2020
     * {@link #}
     */
    public static HttpClient getSSLHttpClient() {
//        basicCookieStore = new BasicCookieStore(); //每次請求都是新的cookie
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(createSSLConnSocketFactory())
                .setConnectionManager(connMgr)
                .setDefaultRequestConfig(requestConfig)
                .setRetryHandler(httpRequestRetryHandler)
                //可選配置,如不設置下面的添加,獲取,清空cookie將無效
                .setDefaultCookieStore(basicCookieStore)
                .build();
        //清理過期的cookie節省資源
        basicCookieStore.clearExpired(new Date());
        return httpClient;
    }

    /**
     * 添加cookie
     *
     * @param domain 域名 例如:發送的請求是 www.baidu.com
     * @param minute 過期時間
     * @param path   cookie範圍
     * @param key
     * @param value
     * @return boolean
     * @author: zhihao
     * @date: 29/2/2020
     */
    public static boolean addCookie(String domain, Integer minute,
                                    String path, String key, String value) throws UnsupportedEncodingException {
        //對value進行編碼解決中文亂碼
        value = URLEncoder.encode(value, "utf-8");
        BasicClientCookie cookie = new BasicClientCookie(key, value);
        cookie.setDomain(domain);
        cookie.setExpiryDate(new Date(System.currentTimeMillis() + 60 * minute * 1000));
        cookie.setPath(path);
        //添加進cookieStore靜態變量中 httpClient使用的是靜態變量中的cookieStore,修改對象裏面的值相當修改httpClient裏面的cookieStore
        basicCookieStore.addCookie(cookie);
        return true;
    }

    /**
     * <p>
     *     進行清空cookie
     * </p>
     *
     * @author: zhihao
     * @date: 29/2/2020 DoNotShare
     */
    public static void ClearBasicCookieStore() {
         basicCookieStore.clear();
    }

    /**
     * <p>
     * 獲取響應後的cookie
     * 使用在執行發送請求execute()後才能獲取響應的cookie值
     * 使用在之前是獲取發送cookie的數據
     * </p>
     *
     * @param
     * @return java.util.List<org.apache.http.cookie.Cookie>
     * @author: zhihao
     * @date: 29/2/2020
     */
    public static List<Cookie> getResponseCookie() {
        return basicCookieStore.getCookies();
    }

    /**
     * 獲取請求配置
     *
     * @return org.apache.http.client.config.RequestConfig
     * @author: zhihao
     * @date: 27/2/2020
     */
    public static RequestConfig getRequestConfig() {
        return requestConfig;
    }

    /**
     * 創建SSL安全連接
     *
     * @return
     */
    private static SSLConnectionSocketFactory createSSLConnSocketFactory() {
        SSLConnectionSocketFactory sslsf = null;
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                // 信任所有
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();

            sslsf = new SSLConnectionSocketFactory(sslContext, new HostnameVerifier() {
                @Override
                public boolean verify(String arg0, SSLSession arg1) {
                    return true;
                }
            });
        } catch (GeneralSecurityException e) {
            log.error(e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
        return sslsf;
    }

    /**
     * 初始化連接參數
     *
     * @return void
     * @date: 27/2/2020
     */
    private static void init() {
        // 設置連接池
        connMgr = new PoolingHttpClientConnectionManager();
        // 設置連接池大小
        connMgr.setMaxTotal(100);
        connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal());
        // Validate connections after 1 sec of inactivity
        connMgr.setValidateAfterInactivity(1000);
        RequestConfig.Builder configBuilder = RequestConfig.custom();
        // 設置連接超時
        configBuilder.setConnectTimeout(MAX_TIMEOUT);
        // 設置讀取超時
        configBuilder.setSocketTimeout(MAX_TIMEOUT);
        // 設置從連接池獲取連接實例的超時
        configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT);
        //設置Cookie規範
        configBuilder.setCookieSpec(CookieSpecs.STANDARD_STRICT);
        requestConfig = configBuilder.build();
        //http請求重試處理程序
        httpRequestRetryHandler = new HttpRequestRetryHandler() {
            @Override
            public boolean retryRequest(IOException exception,
                                        int executionCount, HttpContext context) {
                // 如果已經重試了3次,就放棄
                if (executionCount >= 3) {
                    log.error("----鏈接已經重連了3次未能成功,將放棄->>");
                    return false;
                }
                // 如果服務器丟掉了連接,那麼就重試
                if (exception instanceof NoHttpResponseException) {
                    return true;
                }
                if (exception instanceof SSLHandshakeException) {// 不要重試SSL握手異常
                    log.error("----不要重試SSL握手異常->>");
                    return false;
                }
                if (exception instanceof InterruptedIOException) {// 超時
                    log.error("----超時異常->>");
                    return false;
                }
                if (exception instanceof UnknownHostException) {// 目標服務器不可達
                    log.error("----目標服務器不可達異常->>");
                    return false;
                }
                if (exception instanceof ConnectTimeoutException) {// 連接被拒絕
                    log.error("----連接被拒絕異常->>");
                    return false;
                }
                if (exception instanceof SSLException) {// SSL握手異常
                    log.error("----SSL握手異常->>");
                    return false;
                }

                HttpClientContext clientContext = HttpClientContext.adapt(context);
                HttpRequest request = clientContext.getRequest();

                if (!(request instanceof HttpEntityEnclosingRequest)) {
                    return true;
                }
                return false;
            }
        };
    }

    /**
     *  將異常轉換爲字符串
     *
     * @param e 異常
     * @return java.lang.String
     * @author: zhihao
     * @date: 1/3/2020
     */
    private static String getErrorString(Throwable e) {
        if (null == e){
            return null;
        }
        StringBuilder builder = new StringBuilder();
        builder.append(e.getMessage());
        StackTraceElement[] stackTrace = e.getStackTrace();
        for (StackTraceElement traceElement : stackTrace) {
            builder.append("\n");
            builder.append(traceElement.toString());
        }
        return builder.toString();
    }
}

擴展資料:

Request請求對象不會繼承客戶端級別的請求配置,所以在自定義Request的時候,需要將客戶端的默認配置設置過去:HttpPost.setConfig(HttpsUtils.getRequestConfig());

-----------------------------------------------------------------------------

備忘錄:

獲取響應狀態碼: response.getStatusLine().getStatusCode()

獲取響應流: InputStream inputStream = response.getEntity().getContent();

禁止過多無用的日記信息:

如果未安裝Log4j庫,則HttpClient(以及JWebUnit)將使用logback。 在這種情況下,創建或編輯logback.xml以包括:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <!-- 定義有顏色的日記輸出格式   -->
    <property name="CONSOLE_LOG_PATTERN"
              value="%red(%d{yyyy-MM-dd HH:mm:ss.SSS}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger) --- %cyan(%msg%n)"/>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
        </filter>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!--    包括下面3個-->
    <logger name="org.apache" level="ERROR"/>
    <logger name="org.apache.http.wire" level="ERROR"/>
    <logger name="org.apache.http.headers" level="ERROR"/>
    <root level="info">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章