Java HttpClient-Restful工具各種請求高度封裝提煉及總結(jié)
總思路
總的工具要求底層完全可復(fù)用的代碼全部提煉,也就是不通類型(GET, POST, DELETE, PUT 等等)請求的決定性公共步驟其實(shí)是可以提煉出來的。
即 一個請求,請求頭一定會有,請求路徑一定會有,發(fā)起請求一定會有,返回處理一定會有。
但同時由于請求頭內(nèi)容可能會有不同的要求或者加密方式,所以需要將相關(guān)加工過程放到基礎(chǔ)工具類之外,保證調(diào)用基礎(chǔ)工具類時只執(zhí)行所有請求都需要的的步驟,不帶有特殊處理。
這里主要使用的都是 org.apache.http 已包裝的 httpClient ,項(xiàng)目中進(jìn)一步將各種類型的請求做進(jìn)一步提煉和封裝。
從最底層開始說明
RestfulService
基礎(chǔ) RestfulService 工具代碼可以參考如下:
個別說明加入到注釋中或示例代碼結(jié)尾
......
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
public class MyRestfulService {
private static xxxLogger = new xxxLog(MyRestfulService .class);
// 所有請求都由 httpClient.execute() 方式發(fā)出
private CloseableHttpClient httpClient;
// 由于 httpClient 也存在自定義各種屬性,所以這里也作為一個定義的參數(shù)
// 通過構(gòu)造方法傳入外側(cè)定義的 CloseableHttpClient
public MyRestfulService(CloseableHttpClient client) {
this.httpClient = client;
}
// 一般的GET 請求
public String jsonGet(String url, final NameValuePair[] headerParams) throws IOException, XxxxException {
URI uri = URI.create(url);
HttpGet get = new HttpGet(uri);
this.addHeaders(headerParams, get);
CloseableHttpResponse response = httpClient.execute(get);
return this.parseResponseData(response, url);
}
// Get請求 獲取文件某字符串開頭的
public String fileGetLine(String url, final NameValuePair[] headerParams, String head) throws IOException,
XxxxException {
URI uri = URI.create(url);
HttpGet get = new HttpGet(uri);
this.addHeaders(headerParams, get);
CloseableHttpResponse response = httpClient.execute(get);
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
String output;
while ((output = br.readLine()) != null) {
if (output.contains(head) && Objects.equals(output.split("=")[0], head)) {
return output;
}
}
return null;
} catch (Exception e) {
logger.error("Failed to get rest response", e);
// 為自定義異常類型
throw new XxxxException(ExceptionType.XXXXXX);
} finally {
if (br != null) {
br.close();
}
response.close();
}
}
// 攜帶請求體即 Body 的GET 請求 其中 HttpGetWithEntity 需要自定義到文件中 稍后給出示例
public String jsonGetWithBody(String url, final NameValuePair[] headerParams, String requestBody)
throws IOException, XxxxException {
URI uri = URI.create(url);
HttpGetWithEntity get = new HttpGetWithEntity(uri);
this.addHeaders(headerParams, get);
StringEntity input = new StringEntity(requestBody, ContentType.APPLICATION_JSON);
get.setEntity(input);
CloseableHttpResponse response = httpClient.execute(get);
return this.parseResponseData(response, url);
}
// 普通的POST 請求
public String jsonPost(String url, final NameValuePair[] headerParams, String requestBody)
throws IOException, XxxxException {
HttpPost post = new HttpPost(url);
this.addHeaders(headerParams, post);
if (requestBody != null) {
StringEntity input = new StringEntity(requestBody, ContentType.APPLICATION_JSON);
post.setEntity(input);
}
CloseableHttpResponse response = httpClient.execute(post);
return this.parseResponseData(response, url);
}
// 普通 put 請求
public String jsonPut(String url, final NameValuePair[] headerParams, String requestBody)
throws IOException, XxxxException {
HttpPut put = new HttpPut(url);
this.addHeaders(headerParams, put);
StringEntity input = new StringEntity(requestBody, ContentType.APPLICATION_JSON);
put.setEntity(input);
CloseableHttpResponse response = httpClient.execute(put);
return this.parseResponseData(response, url);
}
// 一般的DELETE 請求
public String jsonDelete(String url, final NameValuePair[] headerParams) throws IOException, XxxxException {
HttpDelete delete = new HttpDelete(url);
this.addHeaders(headerParams, delete);
CloseableHttpResponse response = null;
response = httpClient.execute(delete);
return this.parseResponseData(response, url);
}
// 攜帶請求體的DELETE 請求 HttpDeleteWithBody
public String jsonDeleteWithBody(String url, final NameValuePair[] headerParams, String requestBody)
throws IOException, XxxxException {
HttpDeleteWithBody delete = new HttpDeleteWithBody(url);
this.addHeaders(headerParams, delete);
StringEntity input = new StringEntity(requestBody, ContentType.APPLICATION_JSON);
delete.setEntity(input);
CloseableHttpResponse response = null;
response = httpClient.execute(delete);
return this.parseResponseData(response, url);
}
// 文件類傳入 上傳
public String uploadFile(String url, final NameValuePair[] headerParams, HttpEntity multipartEntity)
throws IOException, XxxxException {
HttpPost post = new HttpPost(url);
post.setEntity(multipartEntity);
post.addHeader(HttpHeaders.CONTENT_TYPE, post.getEntity().getContentType().getValue());
if (headerParams != null) {
for (NameValuePair nameValuePair : headerParams) {
post.addHeader(nameValuePair.getName(), nameValuePair.getValue());
}
}
return this.parseResponseData(httpClient.execute(post), url);
}
// 數(shù)據(jù)結(jié)果轉(zhuǎn)換
private String parseResponseData(CloseableHttpResponse response, String url) throws IOException,
XxxxException {
BufferedReader br = null;
try {
// 編碼轉(zhuǎn)義結(jié)果
br = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8));
StringBuilder sbuilder = new StringBuilder();
String output;
while ((output = br.readLine()) != null) {
sbuilder.append(output);
}
logger.debug("MyRestfulService Request-URL: " + url + "; response: " + response.toString() + "; data:" + sbuilder + "}");
int statusCode = response.getStatusLine().getStatusCode();
// 200 可用已有常量替代 HTTP 本身有提供
if (statusCode != 200) {
logger.info("Failed to get restful response, http error code = " + statusCode);
}
return sbuilder.toString();
} catch (Exception e) {
logger.error("Failed to get rest response", e);
// 自定義異常
throw new XxxxException(ExceptionType.XXXXXXX);
} finally {
if (br != null) {
br.close();
}
response.close();
}
}
// 公用 添加自定義請求頭信息
private void addHeaders(final NameValuePair[] headerParams, HttpRequestBase requestBase) {
if (headerParams != null) {
for (int i = 0; i < headerParams.length; i++) {
NameValuePair nameValuePair = headerParams[i];
requestBase.addHeader(nameValuePair.getName(), nameValuePair.getValue());
}
}
this.addCommonHeader(requestBase);
}
// 公用 添加公共請求頭或公共操作
private void addCommonHeader(HttpRequestBase method) {
// 去除個別屬性
if (method.containsHeader("Content-Length")) {
method.removeHeaders("Content-Length");
}
// 沒有則添加
if (!method.containsHeader("Content-Type")) {
method.addHeader("Content-Type", "application/json;charset=utf-8");
}
// 強(qiáng)制更新或添加
method.setHeader("accept", "application/json,text/plain,text/html");
}
// 文件下載
public String downloadFile(String url, OutputStream outputStream,NameValuePair[] headerParams)
throws IOException, DXPException {
HttpGet httpget = new HttpGet(url);
this.addHeaders(headerParams, httpget);
CloseableHttpResponse execute = httpClient.execute(httpget);
if(null != execute && execute.containsHeader("Content-Disposition")){
HttpEntity entity = execute.getEntity();
if (entity != null ) {
entity.writeTo(outputStream);
}
}else {
return this.parseResponseData(execute, url);
}
return null;
}
// httpClient 對應(yīng)的get set 方法
public CloseableHttpClient getHttpClient() {
return this.httpClient;
}
public void setHttpClient(CloseableHttpClient httpClient) {
this.httpClient = httpClient;
}
}上面
HttpDeleteWithBody 定義:
import java.net.URI;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
public class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase {
public static final String METHOD_NAME = "DELETE";
public HttpDeleteWithBody(final String uri) {
super();
setURI(URI.create(uri));
}
public HttpDeleteWithBody(final URI uri) {
super();
setURI(uri);
}
public HttpDeleteWithBody() {
super();
}
public String getMethod() {
return METHOD_NAME;
}
}上面 HttpGetWithBody:
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
public class HttpGetWithEntity extends HttpEntityEnclosingRequestBase {
private static final String METHOD_NAME = "GET";
public HttpGetWithEntity() {
super();
}
public HttpGetWithEntity(final URI uri) {
super();
setURI(uri);
}
HttpGetWithEntity(final String uri) {
super();
setURI(URI.create(uri));
}
@Override
public String getMethod() {
return METHOD_NAME;
}
}具體來源其實(shí)就是照抄 源碼 httppost POST 的結(jié)構(gòu)

然后換個名字以及屬性名即可完成請求體的攜帶
NameValuePair[]
示例中用到了大量的 NameValuePair[] 其,內(nèi)部結(jié)構(gòu)類似于 Map 但內(nèi)部屬性為name, value。
實(shí)際也可以使用 Map 來替代這種存儲結(jié)構(gòu), NameValuePair 也是 org.apache.http 內(nèi)部提供的類。
工具類上一層
其他調(diào)用者。用于處理特殊的請求頭信息,拼接請求參數(shù)以及請求路徑等內(nèi)容
并構(gòu)建使用的 CloseableHttpClient 傳入工具類。
示例:
某一 上層 Service 的內(nèi)容示例:
......
......
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpHeaders;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@Service("XXXRestfulService")
public class XXXRestfulService implements IXxxRestfulService {
private static final XXXLog xxxLog = new XXXLog(XXXRestfulService .class);
private static int maxConnPerHost = 20;
private static int maxTotalConn = 20;
/**
* 數(shù)據(jù)讀取超時時間
*/
private static int soTimeout = 30000;
/**
* http連接超時時間
*/
private static int connectionTimeout = 10000;
/**
* 連接管理器超時時間
*/
private static int connectionManagerTimeout = 10000;
// 基礎(chǔ)工具類對象聲明
private MyRestfulService restService;
private CloseableHttpClient createHttpClient() {
CloseableHttpClient httpClient = null;
try {
@SuppressWarnings("deprecation") ConnectionConfig cConfig =
ConnectionConfig.custom().setCharset(StandardCharsets.UTF_8).build();
SocketConfig config = SocketConfig.custom().setSoTimeout(soTimeout).build();
RequestConfig defaultRequestConfig =
RequestConfig.custom().setExpectContinueEnabled(true).setCookieSpec(CookieSpecs.DEFAULT)
.setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
.setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
.setConnectionRequestTimeout(connectionManagerTimeout).setConnectTimeout(connectionTimeout)
.setSocketTimeout(soTimeout).build();
httpClient = HttpClientBuilder.create().setMaxConnPerRoute(maxConnPerHost).setMaxConnTotal(maxTotalConn)
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.setDefaultRequestConfig(RequestConfig.copy(defaultRequestConfig).build())
.setDefaultConnectionConfig(cConfig).setDefaultSocketConfig(config).build();
} catch (Exception e) {
xxxLog.error("Create Http Client Failed", e);
}
return httpClient;
}
// 類初始化時執(zhí)行的方法
@PostConstruct
public void initProperties() {
try {
CloseableHttpClient client = this.createHttpClient();
this.restService = new MyRestfulService(client);
} catch (Exception e) {
xxxLog.error("Failed To Init DataFillRestfulService", e);
}
}
// 關(guān)閉資源,如果每次都重新請求則也可以放到工具類內(nèi)每次請求完成都關(guān)閉
@PreDestroy // @PreDestroy 實(shí)際 Servlet 被銷毀前調(diào)用的方法
public void destroy() {
try {
CloseableHttpClient httpclient = restService.getHttpClient();
httpclient.close();
} catch (IOException e) {
xxxLog.error("Failed To Destroy HttpClient", e);
}
}
// 對請求頭內(nèi)容的特殊處理
private NameValuePair[] getBaseHeaders(String methodName, String urlStr) {
// 對請求頭內(nèi)容的特殊處理 若沒有則直接添加到 返回值 NameValuePair[] 數(shù)組即可
............
}
// 如果需要URL編碼則可以使用該方法
private String encodeHeader(String value) throws UnsupportedEncodingException {
if (StringUtils.isEmpty(value)) {
return value;
}
return URLEncoder.encode(value, "UTF-8");
}
// 拼接實(shí)際請求路徑
// XXXXConfig 可以作為一個類專門加載配置文件中的一些有關(guān)的地址信息等
public String getRequestUrl(String actionUrl) {
return XXXXConfig.getXxxxServer() + actionUrl;
}
@Override
public String get(final String actionUrl, Map<String, String> map) {
String requestUrl = getRequestUrl(actionUrl);
// 傳入實(shí)際地址等 調(diào)用基礎(chǔ)工具類請求
.....
}
@Override
public String post(final String actionUrl, final String jsonBody) {
String requestUrl = getRequestUrl(actionUrl);
// 傳入實(shí)際地址等 調(diào)用基礎(chǔ)工具類請求
.....
}
@Override
public String delete(final String actionUrl) {
String requestUrl = getRequestUrl(actionUrl);
// 傳入實(shí)際地址等 調(diào)用基礎(chǔ)工具類請求
.....
}
}
可以看的出,上層工具類先聲明了 工具類對象 然后在當(dāng)前類初始化時完成了對當(dāng)前Service請求自己 httpClient 的相關(guān)創(chuàng)建以及配置的賦值,并將該對象 傳遞給工具類,使得調(diào)用工具類時依舊使用的是自己創(chuàng)建的那一個對象。
并通過一般的工具類將需要的配置文件的信息加載后,直接在該類中引用。完成請求信息的拼接。
由于不同的產(chǎn)品間的請求頭要求信息可能不同,所以需要自己額外處理請求頭相關(guān)信息。
這里其實(shí)還是在封裝部分內(nèi)容,使得業(yè)務(wù)請求調(diào)用時進(jìn)一步簡化。
一些工具類特殊的傳入如下示例:
文件上傳方式
public String uploadFile(String url, final NameValuePair[] headerParams, HttpEntity multipartEntity)
中 HttpEntity 中加入文件的方式
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.setCharset(StandardCharsets.UTF_8);
builder.addBinaryBody("file", inputStream, ContentType.DEFAULT_BINARY, fileName);
builder.build(); // 最終可以到的 想要的 HttpEntity 攜帶文件內(nèi)容其中 inputStream 為 InputStream 類型的文件輸入流, fileName 為 String 文件名;
獲取文件流
public String downloadFile(String url, OutputStream outputStream,NameValuePair[] headerParams)
獲得請求中的文件流,則需要傳入 OutputStream 類型的 輸出流對象 outputStream。
再上一層的業(yè)務(wù)調(diào)用
業(yè)務(wù)層處則通過直接定義指定的方法和入?yún)?,在?nèi)部直接寫入請求映射路徑,并要求傳入指定參數(shù)完成業(yè)務(wù)封裝,類似于:
@Override
public XxxxxxxDTO<XXX> getXxxxList(XxxxxListRequest request,
String xxx, String xxxx) throws XXXException {
String url = "/xxxx/api/xxxx/xxxx/list";
String result = XxxxxRestfulService.post(url, JsonUtil.getJsonStr(request), xxx, xxxx);
// 對 result 進(jìn)行轉(zhuǎn)化 轉(zhuǎn)為自定義類或者M(jìn)ap 等返回
return ......;
}這樣在外部調(diào)用該業(yè)務(wù)方法時需要感知的只有入?yún)ⅲ渌麕缀醺兄坏?,與一般的方法差別幾乎沒有
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
- java?11新特性HttpClient主要組件及發(fā)送請求示例詳解
- Java通過httpclient比較重定向和請求轉(zhuǎn)發(fā)
- Java HttpClient執(zhí)行請求時配置cookie流程詳細(xì)講解
- java中httpclient封裝post請求和get的請求實(shí)例
- java爬蟲之使用HttpClient模擬瀏覽器發(fā)送請求方法詳解
- java發(fā)送form-data請求實(shí)現(xiàn)文件上傳的示例代碼
- Java請求調(diào)用參數(shù)格式為form-data類型的接口代碼示例
- Java后臺接收數(shù)據(jù)的三種方式(url、form-data與application/json)
- Java httpclient請求form-data格式并設(shè)置boundary代碼實(shí)現(xiàn)方法
相關(guān)文章
Spring導(dǎo)入properties配置文件代碼示例
這篇文章主要介紹了Spring導(dǎo)入properties配置文件代碼示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-10-10
舉例講解Java編程中this關(guān)鍵字與super關(guān)鍵字的用法
這篇文章主要介紹了Java編程中this關(guān)鍵字與super關(guān)鍵字的用法示例,super是this的父輩,在繼承過程中兩個關(guān)鍵字經(jīng)常被用到,需要的朋友可以參考下2016-03-03
Java使用正則表達(dá)式進(jìn)行匹配且對匹配結(jié)果逐個替換
這篇文章主要介紹了Java使用正則表達(dá)式進(jìn)行匹配且對匹配結(jié)果逐個替換,文章圍繞主題展開詳細(xì)的內(nèi)容戒殺,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09
SpringBoot整合RabbitMQ實(shí)現(xiàn)延遲隊(duì)列的示例詳解
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何整合RabbitMQ實(shí)現(xiàn)延遲隊(duì)列,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價值,感興趣的可以了解一下2023-04-04
淺談圖片上傳利用request.getInputStream()獲取文件流時遇到的問題
下面小編就為大家?guī)硪黄獪\談圖片上傳利用request.getInputStream()獲取文件流時遇到的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11
Java Druid連接池與Apache的DBUtils使用教程
這篇文章主要介紹了Java Druid連接池與Apache的DBUtils使用方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-12-12
如何使用Spring Boot ApplicationRunner解析命令行中的參數(shù)
這篇文章主要介紹了使用Spring Boot ApplicationRunner解析命令行中的參數(shù),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2018-12-12

