最近,業務需要在java服務端發起http請求,需要實現"GET","POST","PUT"等基本方法。於是想以 "HttpClient" 為基礎,封裝基本實現方法。在github上面看到一個項目cn-umbrella/httpclientDemo,里面實現簡單的 "GET","POST"。一方面,demo中實現的邏輯不能滿足業務需要,同時實現過程中,存在使用過期的類,代碼封裝不足等問題。也是我就重新封裝了httpclient-util,抱着大家相互學習的心態,將代碼上傳到github上面,相關鏈接,錯誤的地方歡迎大家指正。
回歸正題,下面講講工具類實現的基本思路。
1.主要依賴
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
<!--httpmime主要用於構建multi-part/form-data 請求-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.2</version>
</dependency>
2.HttpClientUtil 主要方法
/**
* Do http request
* @param request request object
* @return response object
*/
public static Response doRequest(Request request)
其中,Request 封裝了請求參數,Response 封裝了返回內容。
3.Request 說明
支持方法:"GET","OPTIONS","HEAD","POST","PUT","PATCH"。支持請求體: "application/x-www-form-urlencoded","json字符串","multipart/form-data"。
Request的繼承關系
Request (沒有請求body的請求,支持方法:"GET", "OPTIONS", "HEAD")
|---BaseEntityRequest (抽象類,定義有請求body的請求, 支持方法:"POST", "PUT", "PATCH")
|--- UrlEncodedFormRequest (application/x-www-form-urlencoded)
|--- JsonRequest (請求body為json字符串)
|--- MultiPartFormRequest (multipart/form-data)
同時,Request 支持使用 ssl/tls,使用request.setUseSSL(true);
設置即可。
HttpEntity就是對應請求body相關實體對象。
3.BaseEntityRequest 介紹
任何實現具有請求body的請求,需要實現 BaseEntityRequest 以下方法
/**
* Get HttpEntity about request body
* @return HttpEntity
*/
public abstract HttpEntity getEntity();
3.1 UrlEncodedFormRequest 核心代碼
這個實現比較簡單,"K-V" 的形式,返回 UrlEncodedFormEntity,注意參數編碼。
@Override
public HttpEntity getEntity() {
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);
}
return new UrlEncodedFormEntity(pairList, Charset.forName(getRequestCharset())); //參數編碼
}
3.2 JsonRequest 核心代碼
請求body就是一個 "json字符串",返回一個StringEntity 實體即可。
@Override
public HttpEntity getEntity() {
StringEntity stringEntity = new StringEntity(jsonObject.toString(), getRequestCharset()); //解決中文亂碼問題,需要注意編碼,如使用"utf-8"
stringEntity.setContentEncoding(getResponseDefaultCharset());
stringEntity.setContentType(JSON_CONTENT_TYPE);
return stringEntity;
}
3.3 MultiPartFormRequest 核心代碼
httpclient 類庫中不直接支持構造 "multi-part/form-data" ,需要引入 httpmime,並使用 MultipartEntityBuilder 構造請求body。如果是文件類型的參數對象,使用 FileBody 構建 part,否則,使用 StringBody,注意編碼。
@Override
public HttpEntity getEntity() {
try {
Charset charset = CharsetUtils.get(getRequestCharset());
//使用瀏覽器兼容模式,並且設置編碼,防止文件名亂碼問題
MultipartEntityBuilder builder = MultipartEntityBuilder.create()
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
.setCharset(charset); //編碼
parts.forEach((key, value) -> {
if (null != value && value instanceof File) { //for file
FileBody fileBody = new FileBody((File) value);
builder.addPart(key, fileBody);
} else {
StringBody stringBody = new StringBody(null == value ? "" : value.toString()
, ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), charset)); //編碼
builder.addPart(key, stringBody);
}
});
return builder.build();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Charset '" + getRequestCharset() + "' is unsupported!");
}
}
4.注意地方
使用 https 過程中,如果出現下述異常:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
...
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
可以參考我的上篇博文解決 java 使用ssl過程中出現"PKIX path building failed:...,當然在包 com.xiaobenma020.http.cert 下面可以找到類 InstallCert
關鍵步驟
% java InstallCert _web_site_hostname_
顯示相關的證書信息
此時輸入'q' 則為'退出', '1' 則將添加CA證書。
將新生成的 "jssecacerts" 移到"$JAVA_HOME/jre/lib/security"
5.使用demo
@Test
public void doResponse() throws Exception {
UrlEncodedFormRequest request = new UrlEncodedFormRequest("https://xxx.com/login", RequestMethod.POST);
//url form param
request.addParam("loginId", "loginId");
request.addParam("password", "password");
//query string param
request.addUrlParam("version", "v1");
//ssl
request.setUseSSL(true);
Response response = HttpClientUtil.doRequest(request);
System.out.println(response.getResponseText()); //response text
System.out.println(response.getCode()); //response code
System.out.println(response.getHeader("Set-Cookie"));
}