輕鬆把玩HttpClient之封裝HttpClient工具類(一)(現有網上分享中的最強大的工具類)
搜了一下網路上別人封裝的HttpClient,大部分特別簡單,有一些看起來比較高階,但是用起來都不怎麼好用。呼叫關係不清楚,結構有點混亂。所以也就萌生了自己封裝HttpClient工具類的想法。要做就做最好的,本工具類支援外掛式配置Header、外掛式配置httpclient物件,這樣就可以方便地自定義header資訊、配置ssl、配置proxy等。
是不是覺得說的有點懸乎了,那就先看看呼叫吧:
public static void testSimple() throws HttpProcessException{
String url = "http://www.oschina.net";
//簡單呼叫
String resp = HttpClientUtil.send(url);
System.out.println("請求結果內容長度:"+ resp.length());
}
public static void testOne() throws HttpProcessException{
String url = "https://sso.tgb.com:8443/cas/login";
//自定義HttpClient,設定超時、代理、ssl
//HttpClient client= HCB.custom().timeout(10000).proxy("127.0.0.1", 8087).ssl().build();//採用預設方式(繞過證書驗證)
HttpClient client= HCB.custom().timeout(10000).ssl("D:\\keys\\wsriakey","tomcat").build();
//設定header資訊
Header[] headers=HttpHeader.custom().keepAlive("false").connection("close").contentType(Headers.APP_FORM_URLENCODED).build();
//執行請求
String resp=HttpClientUtil.send(client, url, headers);
System.out.println("請求結果如下:");
System.out.println(resp);
}
輕鬆配置了代理、自定義證書的ssl、以及各種header頭資訊,是不是覺得還湊合呢,那就繼續看吧。
寫這個工具類時,抽象了一下所有的demo,最後封裝了一個最基本的方法(拆分成了2個方法了),其所有引數列表有:HttpClient物件、url(必須有)、請求方式、請求引數parasMap、header陣列、編碼格式encoding。
由於封裝的是工具類,所以最好是無狀態的,可以支援多執行緒的方式呼叫的,所以方法都是static型別的。這也是為什麼要把HttpClient物件也是作為了一個引數傳入而非成員變量了,而且這樣也為擴充套件HttpClient的配置提供了便利。
因為HTTP1.1規範中定義了6種HTTP方法:GET, HEAD, POST, PUT, DELETE, TRACE 和 OPTIONS,其實還有一個PATCH,這幾個方法在HttpClient中都有一個對應的類:HttpGet,HttpHead,HttpPost,HttpPut,HttpDelete,HttpTrace、HttpOptions以及HttpPatch。所有的這些類均繼承了HttpRequestBase超類,故可以作為引數使用(用列舉類作為引數,用另一個方法來建立具體的請求方法物件)。
Header頭資訊也是作為一個重要的引數,在請求特定網站的時候需要設定不同的Header,而header又是比較繁雜的,所以這裡也是作為了一個引數傳入的,也是方便擴充套件。
使用map來作為post方式傳入引數是習慣使然,不做過多的解釋。
編碼這個引數主要是為了為待提交的資料和反饋結果進行轉碼處理。
簡單說一下流程:
- 建立請求物件request;
- 為request設定header資訊;
- 判斷當前請求物件是否是HttpEntityEnclosingRequestBase的子類,如果是,則支援setEntity方法,來設定引數。
- 執行請求,並拿到結果(同步阻塞);
- 獲取並解碼請求結果實體;
- 關閉連結
就是這麼簡單,具體來看看程式碼吧:
/**
* 請求資源或服務,自定義client物件,傳入請求引數,設定內容型別,並指定引數和返回資料的編碼
*
* @param client client物件
* @param url 資源地址
* @param httpMethod 請求方法
* @param parasMap 請求引數
* @param headers 請求頭資訊
* @param encoding 編碼
* @return 返回處理結果
* @throws HttpProcessException
*/
public static String send(HttpClient client, String url, HttpMethods httpMethod, Map<String,String>parasMap,
Header[] headers, String encoding) throws HttpProcessException {
String body = "";
try {
//建立請求物件
HttpRequestBase request = getRequest(url, httpMethod);
//設定header資訊
request.setHeaders(headers);
//判斷是否支援設定entity(僅HttpPost、HttpPut、HttpPatch支援)
if(HttpEntityEnclosingRequestBase.class.isAssignableFrom(request.getClass())){
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
//檢測url中是否存在引數
url = Utils.checkHasParas(url, nvps);
//裝填引數
Utils.map2List(nvps, parasMap);
//設定引數到請求物件中
((HttpEntityEnclosingRequestBase)request).setEntity(new UrlEncodedFormEntity(nvps, encoding));
logger.debug("請求地址:"+url);
if(nvps.size()>0){
logger.debug("請求引數:"+nvps.toString());
}
}else{
int idx = url.indexOf("?");
logger.debug("請求地址:"+url.substring(0, (idx>0 ? idx-1:url.length()-1)));
if(idx>0){
logger.debug("請求引數:"+url.substring(idx+1));
}
}
//呼叫傳送請求
body = execute(client, request, url, encoding);
} catch (UnsupportedEncodingException e) {
throw new HttpProcessException(e);
}
return body;
}
/**
* 請求資源或服務
*
* @param client client物件
* @param request 請求物件
* @param url 資源地址
* @param parasMap 請求引數
* @param encoding 編碼
* @return 返回處理結果
* @throws HttpProcessException
*/
private static String execute(HttpClient client, HttpRequestBase request,String url, String encoding) throws HttpProcessException {
String body = "";
HttpResponse response =null;
try {
//執行請求操作,並拿到結果(同步阻塞)
response = client.execute(request);
//獲取結果實體
HttpEntity entity = response.getEntity();
if (entity != null) {
//按指定編碼轉換結果實體為String型別
body = EntityUtils.toString(entity, encoding);
logger.debug(body);
}
EntityUtils.consume(entity);
} catch (ParseException | IOException e) {
throw new HttpProcessException(e);
} finally {
close(response);
}
return body;
}
第一個方法中,我們看到有HttpMethods型別的引數,在建立request物件時,用到了它。它是什麼呢?其實只是一個列舉類:
/**
* 列舉HttpMethods方法
*
* @author arron
* @date 2015年11月17日 下午4:45:59
* @version 1.0
*/
public enum HttpMethods{
/**
* 求獲取Request-URI所標識的資源
*/
GET(0, "GET"),
/**
* 向指定資源提交資料進行處理請求(例如提交表單或者上傳檔案)。資料被包含在請求體中。
* POST請求可能會導致新的資源的建立和/或已有資源的修改
*/
POST(1, "POST"),
/**
* 向伺服器索要與GET請求相一致的響應,只不過響應體將不會被返回。
* 這一方法可以在不必傳輸整個響應內容的情況下,就可以獲取包含在響應訊息頭中的元資訊
* 只獲取響應資訊報頭
*/
HEAD(2, "HEAD"),
/**
* 向指定資源位置上傳其最新內容(全部更新,操作冪等)
*/
PUT (3, "PUT"),
/**
* 請求伺服器刪除Request-URI所標識的資源
*/
DELETE (4, "DELETE"),
/**
* 請求伺服器回送收到的請求資訊,主要用於測試或診斷
*/
TRACE(5, "TRACE"),
/**
* 向指定資源位置上傳其最新內容(部分更新,非冪等)
*/
PATCH (6, "PATCH"),
/**
* 返回伺服器針對特定資源所支援的HTTP請求方法。
* 也可以利用向Web伺服器傳送'*'的請求來測試伺服器的功能性
*/
OPTIONS (7, "OPTIONS"),
// /**
// * HTTP/1.1協議中預留給能夠將連線改為管道方式的代理伺服器
// */
// CONNECT(99, "CONNECT"),
;
private int code;
private String name;
private HttpMethods(int code, String name){
this.code = code;
this.name = name;
}
public String getName() {
return name;
}
public int getCode() {
return code;
}
}
通過getRequest方法,來例項化對應方法的請求物件。
/**
* 根據請求方法名,獲取request物件
*
* @param url 資源地址
* @param method 請求方式
* @return
*/
private static HttpRequestBase getRequest(String url, HttpMethods method) {
HttpRequestBase request = null;
switch (method.getCode()) {
case 0:// HttpGet
request = new HttpGet(url);
break;
case 1:// HttpPost
request = new HttpPost(url);
break;
case 2:// HttpHead
request = new HttpHead(url);
break;
case 3:// HttpPut
request = new HttpPut(url);
break;
case 4:// HttpDelete
request = new HttpDelete(url);
break;
case 5:// HttpTrace
request = new HttpTrace(url);
break;
case 6:// HttpPatch
request = new HttpPatch(url);
break;
case 7:// HttpOptions
request = new HttpOptions(url);
break;
default:
request = new HttpPost(url);
break;
}
return request;
}
當然最後的關閉連結也是一個小方法:
/**
* 嘗試關閉response
*
* @param resp HttpResponse物件
*/
private static void close(HttpResponse resp) {
try {
if(resp == null) return;
//如果CloseableHttpResponse 是resp的父類,則支援關閉
if(CloseableHttpResponse.class.isAssignableFrom(resp.getClass())){
((CloseableHttpResponse)resp).close();
}
} catch (IOException e) {
logger.error(e);
}
}
當然各種引數的組合方法也簡單提供一下(為了節約空間,已去掉註釋):
public static String send(String url) throws HttpProcessException {
return send(url, Charset.defaultCharset().name());
}
public static String send(String url, String encoding) throws HttpProcessException {
return send(url, new Header[]{},encoding);
}
public static String send(String url, Header[] headers) throws HttpProcessException {
return send(url, headers, Charset.defaultCharset().name());
}
public static String send(String url, Header[] headers, String encoding) throws HttpProcessException {
return send(url, new HashMap<String,String>(), headers, encoding);
}
public static String send(String url, Map<String,String>parasMap) throws HttpProcessException {
return send(url, parasMap, Charset.defaultCharset().name());
}
public static String send(String url, Map<String,String>parasMap, String encoding) throws HttpProcessException {
return send(url, parasMap, new Header[]{}, encoding);
}
public static String send(String url, Map<String,String>parasMap, Header[] headers) throws HttpProcessException {
return send(url, parasMap, headers, Charset.defaultCharset().name());
}
public static String send(String url, Map<String,String>parasMap, Header[] headers, String encoding) throws HttpProcessException {
return send(url, HttpMethods.POST, parasMap, headers, encoding);
}
public static String send(String url, HttpMethods httpMethod) throws HttpProcessException {
return send(url, httpMethod, Charset.defaultCharset().name());
}
public static String send(String url, HttpMethods httpMethod, String encoding) throws HttpProcessException {
return send(url, httpMethod, new Header[]{},encoding);
}
public static String send(String url, HttpMethods httpMethod, Header[] headers) throws HttpProcessException {
return send(url, httpMethod, headers, Charset.defaultCharset().name());
}
public static String send(String url, HttpMethods httpMethod, Header[] headers, String encoding) throws HttpProcessException {
return send(url, httpMethod, new HashMap<String, String>(), headers, encoding);
}
public static String send(String url, HttpMethods httpMethod, Map<String,String>parasMap) throws HttpProcessException {
return send(url, httpMethod, parasMap, Charset.defaultCharset().name());
}
public static String send(String url, HttpMethods httpMethod, Map<String,String>parasMap, String encoding) throws HttpProcessException {
return send(url, httpMethod, parasMap, new Header[]{}, encoding);
}
public static String send(String url, HttpMethods httpMethod, Map<String,String>parasMap, Header[] headers) throws HttpProcessException {
return send(url, httpMethod, parasMap, headers, Charset.defaultCharset().name());
}
public static String send(String url, HttpMethods httpMethod, Map<String,String>parasMap, Header[] headers, String encoding) throws HttpProcessException {
return send(create(url), url, httpMethod, parasMap, headers, encoding);
}
public static String send(HttpClient client, String url) throws HttpProcessException {
return send(client, url, Charset.defaultCharset().name());
}
public static String send(HttpClient client, String url, String encoding) throws HttpProcessException {
return send(client, url, new Header[]{}, encoding);
}
public static String send(HttpClient client, String url, Header[] headers) throws HttpProcessException {
return send(client, url, headers, Charset.defaultCharset().name());
}
public static String send(HttpClient client, String url, Header[] headers, String encoding) throws HttpProcessException {
return send(client, url, new HashMap<String, String>(), headers, encoding);
}
public static String send(HttpClient client, String url, Map<String,String>parasMap) throws HttpProcessException {
return send(client, url, parasMap, Charset.defaultCharset().name());
}
public static String send(HttpClient client, String url, Map<String,String>parasMap, String encoding) throws HttpProcessException {
return send(client, url, parasMap, new Header[]{}, encoding);
}
public static String send(HttpClient client, String url, Map<String,String>parasMap, Header[] headers) throws HttpProcessException {
return send(client, url, parasMap, headers, Charset.defaultCharset().name());
}
public static String send(HttpClient client, String url, Map<String,String>parasMap,Header[] headers,String encoding) throws HttpProcessException {
return send(client, url, HttpMethods.POST, parasMap, headers, encoding);
}
public static String send(HttpClient client, String url, HttpMethods httpMethod) throws HttpProcessException {
return send(client, url, httpMethod, Charset.defaultCharset().name());
}
public static String send(HttpClient client, String url, HttpMethods httpMethod, String encoding) throws HttpProcessException {
return send(client, url, httpMethod, new Header[]{}, encoding);
}
public static String send(HttpClient client, String url, HttpMethods httpMethod, Header[] headers) throws HttpProcessException {
return send(client, url, httpMethod, headers, Charset.defaultCharset().name());
}
public static String send(HttpClient client, String url, HttpMethods httpMethod, Header[] headers, String encoding) throws HttpProcessException {
return send(client, url, httpMethod, new HashMap<String, String>(), headers, encoding);
}
public static String send(HttpClient client, String url, HttpMethods httpMethod, Map<String,String>parasMap) throws HttpProcessException {
return send(client, url, httpMethod, parasMap, Charset.defaultCharset().name());
}
public static String send(HttpClient client, String url, HttpMethods httpMethod, Map<String,String>parasMap, String encoding) throws HttpProcessException {
return send(client, url, httpMethod, parasMap, new Header[]{}, encoding);
}
public static String send(HttpClient client, String url, HttpMethods httpMethod, Map<String,String>parasMap, Header[] headers) throws HttpProcessException {
return send(client, url, httpMethod, parasMap, headers, Charset.defaultCharset().name());
}
可以看到上面這一堆方法,其實主要分成2類,一類是傳入client物件的,一組是沒有傳入的。也就是說該工具類提供了一種預設的client物件。這個將會在下一篇文章會有補充。
當然,為了方便操作,還是提供了get、post、put、patch、delete、head、options、trace等方法,由於推薦使用send方法,所以這幾個方法只是做了一個簡單的呼叫:
public static String get(String url, Header[] headers,String encoding) throws HttpProcessException {
return get(create(url), url, headers, encoding);
}
public static String get(HttpClient client, String url, Header[] headers,String encoding) throws HttpProcessException {
return send(client, url, HttpMethods.GET, headers, encoding);
}
public static String post(String url, Map<String,String>parasMap,Header[] headers,String encoding) throws HttpProcessException {
return post(create(url), url, parasMap, headers, encoding);
}
public static String post(HttpClient client, String url, Map<String,String>parasMap,Header[] headers,String encoding) throws HttpProcessException {
return send(client, url, HttpMethods.POST, parasMap, headers, encoding);
}
public static String put(String url, Map<String,String>parasMap,Header[] headers,String encoding) throws HttpProcessException {
return put(create(url), url, parasMap, headers, encoding);
}
public static String put(HttpClient client, String url, Map<String,String>parasMap,Header[] headers,String encoding) throws HttpProcessException {
return send(client, url, HttpMethods.PUT, parasMap, headers, encoding);
}
public static String delete(String url, Header[] headers,String encoding) throws HttpProcessException {
return delete(create(url), url, headers, encoding);
}
public static String delete(HttpClient client, String url, Header[] headers,String encoding) throws HttpProcessException {
return send(client, url, HttpMethods.DELETE, headers, encoding);
}
public static String patch(String url, Map<String,String>parasMap,Header[] headers,String encoding) throws HttpProcessException {
return patch(create(url), url, parasMap, headers, encoding);
}
public static String patch(HttpClient client, String url, Map<String,String>parasMap, Header[] headers,String encoding) throws HttpProcessException {
return send(client, url, HttpMethods.PATCH, parasMap, headers, encoding);
}
public static String head(String url, Header[] headers,String encoding) throws HttpProcessException {
return head(create(url), url, headers, encoding);
}
public static String head(HttpClient client, String url, Header[] headers,String encoding) throws HttpProcessException {
return send(client, url, HttpMethods.HEAD, headers, encoding);
}
public static String options(String url, Header[] headers,String encoding) throws HttpProcessException {
return options(create(url), url, headers, encoding);
}
public static String options(HttpClient client, String url, Header[] headers,String encoding) throws HttpProcessException {
return send(client, url, HttpMethods.OPTIONS, headers, encoding);
}
public static String trace(String url, Header[] headers,String encoding) throws HttpProcessException {
return trace(create(url), url, headers, encoding);
}
public static String trace(HttpClient client, String url, Header[] headers,String encoding) throws HttpProcessException {
return send(client, url, HttpMethods.TRACE, headers, encoding);
}
差點忘記了,最後還有一個簡單的通用工具類
/**
*
* @author arron
* @date 2015年11月10日 下午12:49:26
* @version 1.0
*/
public class Utils {
/**
* 檢測url是否含有引數,如果有,則把引數加到引數列表中
*
* @param url 資源地址
* @param nvps 引數列表
* @return 返回去掉引數的url
*/
public static String checkHasParas(String url, List<NameValuePair> nvps) {
// 檢測url中是否存在引數
if (url.contains("?") && url.indexOf("?") < url.indexOf("=")) {
Map<String, String> map = buildParas(url.substring(url
.indexOf("?") + 1));
map2List(nvps, map);
url = url.substring(0, url.indexOf("?"));
}
return url;
}
/**
* 引數轉換,將map中的引數,轉到引數列表中
*
* @param nvps 引數列表
* @param map 引數列表(map)
*/
public static void map2List(List<NameValuePair> nvps, Map<String, String> map) {
if(map==null) return;
// 拼接引數
for (Entry<String, String> entry : map.entrySet()) {
nvps.add(new BasicNameValuePair(entry.getKey(), entry
.getValue()));
}
}
/**
* 生成引數
* 引數格式“k1=v1&k2=v2”
*
* @param paras 引數列表
* @return 返回引數列表(map)
*/
public static Map<String,String> buildParas(String paras){
String[] p = paras.split("&");
String[][] ps = new String[p.length][2];
int pos = 0;
for (int i = 0; i < p.length; i++) {
pos = p[i].indexOf("=");
ps[i][0]=p[i].substring(0,pos);
ps[i][1]=p[i].substring(pos+1);
pos = 0;
}
return buildParas(ps);
}
/**
* 生成引數
* 引數型別:{{"k1","v1"},{"k2","v2"}}
*
* @param paras 引數列表
* @return 返回引數列表(map)
*/
public static Map<String,String> buildParas(String[][] paras){
// 建立引數佇列
Map<String,String> map = new HashMap<String, String>();
for (String[] para: paras) {
map.put(para[0], para[1]);
}
return map;
}
}
簡單的封裝就是這樣了。
由於HttpClient和Header都作為引數傳入,所以也可以進行擴充套件,比如代理、ssl等都是對HttpClient進行配置的,下面的文章就分別分享一下如何外掛式配置HttpClient以及Header。敬請期待。