1. 程式人生 > >輕鬆把玩HttpClient之封裝HttpClient工具類(一)(現有網上分享中的最強大的工具類)

輕鬆把玩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方式傳入引數是習慣使然,不做過多的解釋。

       編碼這個引數主要是為了為待提交的資料和反饋結果進行轉碼處理。

       簡單說一下流程:

    1. 建立請求物件request;
    2. 為request設定header資訊;
    3. 判斷當前請求物件是否是HttpEntityEnclosingRequestBase的子類,如果是,則支援setEntity方法,來設定引數。
    4. 執行請求,並拿到結果(同步阻塞);
    5. 獲取並解碼請求結果實體;
    6. 關閉連結

       就是這麼簡單,具體來看看程式碼吧:

	/**
	 * 請求資源或服務,自定義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。敬請期待。

       httpclientUtil (QQ交流群:548452686 httpclientUtil交流