HttpClient工具類

jopen 11年前發布 | 81K 次閱讀 網絡工具包 HttpClient

HttpClient 是 Apache Jakarta Common 下的子項目,用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,并且它支持 HTTP 協議最新的版本和建議。HttpClient 已經應用在很多的項目中,比如 Apache Jakarta 上很著名的另外兩個開源項目 Cactus 和 HTMLUnit 都使用了 HttpClient。

HttpClient 提供的主要的功能,要知道更多詳細的功能可以參見 HttpClient 的主頁。

  • 實現了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
  • 支持自動轉向
  • 支持 HTTPS 協議
  • 支持代理服務器等
  • </ul>

    import java.io.IOException;
    import java.net.SocketTimeoutException;
    import java.nio.charset.Charset;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;

    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLException;
    import javax.net.ssl.SSLSession;
    import javax.net.ssl.SSLSocket;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;

    import org.apache.http.Header;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.NameValuePair;
    import org.apache.http.ParseException;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.HttpClient;
    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.conn.ConnectTimeoutException;
    import org.apache.http.conn.scheme.Scheme;
    import org.apache.http.conn.ssl.SSLSocketFactory;
    import org.apache.http.conn.ssl.X509HostnameVerifier;
    import org.apache.http.entity.ContentType;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.params.CoreConnectionPNames;
    import org.apache.http.protocol.HTTP;
    import org.apache.http.util.EntityUtils;

    /**

    • 封裝了采用HttpClient發送HTTP請求的方法
    • @see 本工具所采用的是HttpComponents-Client-4.2.1
    • @see ===================================================================================================
    • @see 開發HTTPS應用時,時常會遇到兩種情況
    • @see 1、測試服務器沒有有效的SSL證書,客戶端連接時就會拋異常
    • @see javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    • @see 2、測試服務器有SSL證書,但可能由于各種不知名的原因,它還是會拋一堆爛碼七糟的異常,諸如下面這兩種
    • @see javax.net.ssl.SSLException: hostname in certificate didn't match: <123.125.97.66> != <123.125.97.241>
    • @see 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
    • @see ===================================================================================================
    • @see 這里使用的是HttpComponents-Client-4.2.1創建的連接,所以就要告訴它使用一個不同的TrustManager
    • @see 由于SSL使用的模式是X.509,對于該模式,Java有一個特定的TrustManager,稱為X509TrustManager
    • @see TrustManager是一個用于檢查給定的證書是否有效的類,所以我們自己創建一個X509TrustManager實例
    • @see 而在X509TrustManager實例中,若證書無效,那么TrustManager在它的checkXXX()方法中將拋出CertificateException
    • @see 既然我們要接受所有的證書,那么X509TrustManager里面的方法體中不拋出異常就行了
    • @see 然后創建一個SSLContext并使用X509TrustManager實例來初始化之
    • @see 接著通過SSLContext創建SSLSocketFactory,最后將SSLSocketFactory注冊給HttpClient就可以了
    • @see ===================================================================================================
    • @version v1.7
    • @history v1.0-->新建<code>sendGetRequest()</code>和<code>sendPostRequest()</code>方法
    • @history v1.1-->新增<code>sendPostSSLRequest()</code>方法,用于發送HTTPS的POST請求
    • @history v1.2-->新增<code>sendPostRequest()</code>方法,用于發送HTTP協議報文體為任意字符串的POST請求
    • @history v1.3-->新增<code>java.net.HttpURLConnection</code>實現的<code>sendPostRequestByJava()</code>
    • @history v1.4-->所有POST方法中增加連接超時限制和讀取超時限制
    • @history v1.5-->重組各方法,并補充自動獲取HTTP響應文本編碼的方式,移除<code>sendPostRequestByJava()</code>
    • @history v1.6-->整理GET和POST請求方法,使之更為適用
    • @history v1.7-->修正<code>sendPostRequest()</code>請求的CONTENT_TYPE頭信息,并優化各方法參數及內部處理細節
    • @create Feb 1, 2012 3:02:27 PM
    • @update Jul 23, 2013 1:18:35 PM
    • @author 玄玉<http://blog.csdn.net/jadyer&gt; */
      public class HttpClientUtil {
      private HttpClientUtil(){}

      /**

      • 發送HTTP_GET請求
      • @see 1)該方法會自動關閉連接,釋放資源
      • @see 2)方法內設置了連接和讀取超時時間,單位為毫秒,超時或發生其它異常時方法會自動返回"通信失敗"字符串
      • @see 3)請求參數含中文時,經測試可直接傳入中文,HttpClient會自動編碼發給Server,應用時應根據實際效果決定傳入前是否轉碼
      • @see 4)該方法會自動獲取到響應消息頭中[Content-Type:text/html; charset=GBK]的charset值作為響應報文的解碼字符集
      • @see 若響應消息頭中無Content-Type屬性,則會使用HttpClient內部默認的ISO-8859-1作為響應報文的解碼字符集
      • @param requestURL 請求地址(含參數)
      • @return 遠程主機響應正文 */
        public static String sendGetRequest(String reqURL){
        String respContent = "通信失敗"; //響應內容
        HttpClient httpClient = new DefaultHttpClient(); //創建默認的httpClient實例
        //設置代理服務器
        //httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, new HttpHost("10.0.0.4", 8080));
        httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000); //連接超時10s
        httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 20000); //讀取超時20s
        HttpGet httpGet = new HttpGet(reqURL); //創建org.apache.http.client.methods.HttpGet
        try{
         HttpResponse response = httpClient.execute(httpGet); //執行GET請求  
         HttpEntity entity = response.getEntity();            //獲取響應實體  
         if(null != entity){  
             //respCharset=EntityUtils.getContentCharSet(entity)也可以獲取響應編碼,但從4.1.3開始不建議使用這種方式  
             Charset respCharset = ContentType.getOrDefault(entity).getCharset();  
             respContent = EntityUtils.toString(entity, respCharset);  
             //Consume response content  
             EntityUtils.consume(entity);  
         }  
         System.out.println("-------------------------------------------------------------------------------------------");  
         StringBuilder respHeaderDatas = new StringBuilder();  
         for(Header header : response.getAllHeaders()){  
             respHeaderDatas.append(header.toString()).append("\r\n");  
         }  
         String respStatusLine = response.getStatusLine().toString(); //HTTP應答狀態行信息  
         String respHeaderMsg = respHeaderDatas.toString().trim();    //HTTP應答報文頭信息  
         String respBodyMsg = respContent;                            //HTTP應答報文體信息  
         System.out.println("HTTP應答完整報文=[" + respStatusLine + "\r\n" + respHeaderMsg + "\r\n\r\n" + respBodyMsg + "]");  
         System.out.println("-------------------------------------------------------------------------------------------");  
        
        } catch (ConnectTimeoutException cte){
         //Should catch ConnectTimeoutException, and don`t catch org.apache.http.conn.HttpHostConnectException  
         LogUtil.getLogger().error("請求通信[" + reqURL + "]時連接超時,堆棧軌跡如下", cte);  
        
        } catch (SocketTimeoutException ste){
         LogUtil.getLogger().error("請求通信[" + reqURL + "]時讀取超時,堆棧軌跡如下", ste);  
        
        }catch(ClientProtocolException cpe){
         //該異常通常是協議錯誤導致:比如構造HttpGet對象時傳入協議不對(將'http'寫成'htp')or響應內容不符合HTTP協議要求等  
         LogUtil.getLogger().error("請求通信[" + reqURL + "]時協議異常,堆棧軌跡如下", cpe);  
        
        }catch(ParseException pe){
         LogUtil.getLogger().error("請求通信[" + reqURL + "]時解析異常,堆棧軌跡如下", pe);  
        
        }catch(IOException ioe){
         //該異常通常是網絡原因引起的,如HTTP服務器未啟動等  
         LogUtil.getLogger().error("請求通信[" + reqURL + "]時網絡異常,堆棧軌跡如下", ioe);  
        
        }catch (Exception e){
         LogUtil.getLogger().error("請求通信[" + reqURL + "]時偶遇異常,堆棧軌跡如下", e);  
        
        }finally{
         //關閉連接,釋放資源  
         httpClient.getConnectionManager().shutdown();  
        
        }
        return respContent;
        }
    /** 
     * 發送HTTP_POST請求 
     * @see 1)該方法允許自定義任何格式和內容的HTTP請求報文體 
     * @see 2)該方法會自動關閉連接,釋放資源 
     * @see 3)方法內設置了連接和讀取超時時間,單位為毫秒,超時或發生其它異常時方法會自動返回"通信失敗"字符串 
     * @see 4)請求參數含中文等特殊字符時,可直接傳入本方法,并指明其編碼字符集encodeCharset參數,方法內部會自動對其轉碼 
     * @see 5)該方法在解碼響應報文時所采用的編碼,取自響應消息頭中的[Content-Type:text/html; charset=GBK]的charset值 
     * @see   若響應消息頭中未指定Content-Type屬性,則會使用HttpClient內部默認的ISO-8859-1 
     * @param reqURL        請求地址 
     * @param reqData       請求參數,若有多個參數則應拼接為param11=value11&22=value22&33=value33的形式 
     * @param encodeCharset 編碼字符集,編碼請求數據時用之,此參數為必填項(不能為""或null) 
     * @return 遠程主機響應正文 
     */  
    public static String sendPostRequest(String reqURL, String reqData, String encodeCharset){  
        String reseContent = "通信失敗";  
        HttpClient httpClient = new DefaultHttpClient();  
        httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);  
        httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 20000);  
        HttpPost httpPost = new HttpPost(reqURL);  
        //由于下面使用的是new StringEntity(....),所以默認發出去的請求報文頭中CONTENT_TYPE值為text/plain; charset=ISO-8859-1  
        //這就有可能會導致服務端接收不到POST過去的參數,比如運行在Tomcat6.0.36中的Servlet,所以我們手工指定CONTENT_TYPE頭消息  
        httpPost.setHeader(HTTP.CONTENT_TYPE, "application/x-www-form-urlencoded; charset=" + encodeCharset);  
        try{  
            httpPost.setEntity(new StringEntity(reqData==null?"":reqData, encodeCharset));  
            HttpResponse response = httpClient.execute(httpPost);  
            HttpEntity entity = response.getEntity();  
            if (null != entity) {  
                reseContent = EntityUtils.toString(entity, ContentType.getOrDefault(entity).getCharset());  
                EntityUtils.consume(entity);  
            }  
        } catch (ConnectTimeoutException cte){  
            LogUtil.getLogger().error("請求通信[" + reqURL + "]時連接超時,堆棧軌跡如下", cte);  
        } catch (SocketTimeoutException ste){  
            LogUtil.getLogger().error("請求通信[" + reqURL + "]時讀取超時,堆棧軌跡如下", ste);  
        }catch(Exception e){  
            LogUtil.getLogger().error("請求通信[" + reqURL + "]時偶遇異常,堆棧軌跡如下", e);  
        }finally{  
            httpClient.getConnectionManager().shutdown();  
        }  
        return reseContent;  
    }  
    
    
    /** 
     * 發送HTTP_POST_SSL請求 
     * @see 1)該方法會自動關閉連接,釋放資源 
     * @see 2)該方法亦可處理普通的HTTP_POST請求 
     * @see 3)當處理HTTP_POST_SSL請求時,默認請求的是對方443端口,除非reqURL參數中指明了SSL端口 
     * @see 4)方法內設置了連接和讀取超時時間,單位為毫秒,超時或發生其它異常時方法會自動返回"通信失敗"字符串 
     * @see 5)請求參數含中文等特殊字符時,可直接傳入本方法,并指明其編碼字符集encodeCharset參數,方法內部會自動對其轉碼 
     * @see 6)方法內部會自動注冊443作為SSL端口,若實際使用中reqURL指定的SSL端口非443,可自行嘗試更改方法內部注冊的SSL端口 
     * @see 7)該方法在解碼響應報文時所采用的編碼,取自響應消息頭中的[Content-Type:text/html; charset=GBK]的charset值 
     * @see   若響應消息頭中未指定Content-Type屬性,則會使用HttpClient內部默認的ISO-8859-1 
     * @param reqURL        請求地址 
     * @param params        請求參數 
     * @param encodeCharset 編碼字符集,編碼請求數據時用之,當其為null時,則取HttpClient內部默認的ISO-8859-1編碼請求參數 
     * @return 遠程主機響應正文 
     */  
    public static String sendPostSSLRequest(String reqURL, Map<String, String> params, String encodeCharset){  
        String responseContent = "通信失敗";  
        HttpClient httpClient = new DefaultHttpClient();  
        httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);  
        httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 20000);  
        //創建TrustManager()  
        //用于解決javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated  
        X509TrustManager trustManager = new X509TrustManager(){  
            @Override  
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}  
            @Override  
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}  
            @Override  
            public X509Certificate[] getAcceptedIssuers() {return null;}  
        };  
        //創建HostnameVerifier  
        //用于解決javax.net.ssl.SSLException: hostname in certificate didn't match: <123.125.97.66> != <123.125.97.241>  
        X509HostnameVerifier hostnameVerifier = new X509HostnameVerifier(){  
            @Override  
            public void verify(String host, SSLSocket ssl) throws IOException {}  
            @Override  
            public void verify(String host, X509Certificate cert) throws SSLException {}  
            @Override  
            public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {}  
            @Override  
            public boolean verify(String arg0, SSLSession arg1) {return true;}  
        };  
        try {  
            //TLS1.0與SSL3.0基本上沒有太大的差別,可粗略理解為TLS是SSL的繼承者,但它們使用的是相同的SSLContext  
            SSLContext sslContext = SSLContext.getInstance(SSLSocketFactory.TLS);  
            //使用TrustManager來初始化該上下文,TrustManager只是被SSL的Socket所使用  
            sslContext.init(null, new TrustManager[]{trustManager}, null);  
            //創建SSLSocketFactory  
            SSLSocketFactory socketFactory = new SSLSocketFactory(sslContext, hostnameVerifier);  
            //通過SchemeRegistry將SSLSocketFactory注冊到HttpClient上  
            httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory));  
            //創建HttpPost  
            HttpPost httpPost = new HttpPost(reqURL);  
            //由于下面使用的是new UrlEncodedFormEntity(....),所以這里不需要手工指定CONTENT_TYPE為application/x-www-form-urlencoded  
            //因為在查看了HttpClient的源碼后發現,UrlEncodedFormEntity所采用的默認CONTENT_TYPE就是application/x-www-form-urlencoded  
            //httpPost.setHeader(HTTP.CONTENT_TYPE, "application/x-www-form-urlencoded; charset=" + encodeCharset);  
            //構建POST請求的表單參數  
            if(null != params){  
                List<NameValuePair> formParams = new ArrayList<NameValuePair>();  
                for(Map.Entry<String,String> entry : params.entrySet()){  
                    formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));  
                }  
                httpPost.setEntity(new UrlEncodedFormEntity(formParams, encodeCharset));  
            }  
            HttpResponse response = httpClient.execute(httpPost);  
            HttpEntity entity = response.getEntity();  
            if (null != entity) {  
                responseContent = EntityUtils.toString(entity, ContentType.getOrDefault(entity).getCharset());  
                EntityUtils.consume(entity);  
            }  
        } catch (ConnectTimeoutException cte){  
            LogUtil.getLogger().error("請求通信[" + reqURL + "]時連接超時,堆棧軌跡如下", cte);  
        } catch (SocketTimeoutException ste){  
            LogUtil.getLogger().error("請求通信[" + reqURL + "]時讀取超時,堆棧軌跡如下", ste);  
        } catch (Exception e) {  
            LogUtil.getLogger().error("請求通信[" + reqURL + "]時偶遇異常,堆棧軌跡如下", e);  
        } finally {  
            httpClient.getConnectionManager().shutdown();  
        }  
        return responseContent;  
    }  
    

    } </pre>下面是測試代碼

        public static void main(String[] args) {  
            Map<String, String> params = new HashMap<String, String>();  
            params.put("merNo", "301100100001630");  
            params.put("signType", "MD5");  
            params.put("merBindAgrNo", "00003018007000006450000013866742");  
            params.put("interfaceVersion", "1.0.0.0");  
            params.put("amount", "1000");  
            params.put("orderDate", "20120823");  
            params.put("orderNo", "UDP1208230917531231111");  
            params.put("merReqTime", "20120823091802");  
            params.put("goodsDesc", "為號碼交費充值元");  
            params.put("goodsName", "中國聯通交費充值");  
            params.put("userIdeMark", "3");  
            params.put("bankAgrMode", "9");  
            params.put("signMsg", "3ced24a118461043901d47815e6905a9");  
            System.out.println(HttpClientUtil.sendPostSSLRequest("https://123.125.97.66:8085/pay/servlet/CreditPayReqServlte", params, "UTF-8"));  
        }  
    </strong></span>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!