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>