• HttpClient工具类v1.7


      1 package com.jadyer.util;
      2 
      3 import java.io.IOException;
      4 import java.net.SocketTimeoutException;
      5 import java.nio.charset.Charset;
      6 import java.security.cert.CertificateException;
      7 import java.security.cert.X509Certificate;
      8 import java.util.ArrayList;
      9 import java.util.List;
     10 import java.util.Map;
     11 
     12 import javax.net.ssl.SSLContext;
     13 import javax.net.ssl.SSLException;
     14 import javax.net.ssl.SSLSession;
     15 import javax.net.ssl.SSLSocket;
     16 import javax.net.ssl.TrustManager;
     17 import javax.net.ssl.X509TrustManager;
     18 
     19 import org.apache.http.Header;
     20 import org.apache.http.HttpEntity;
     21 import org.apache.http.HttpResponse;
     22 import org.apache.http.NameValuePair;
     23 import org.apache.http.ParseException;
     24 import org.apache.http.client.ClientProtocolException;
     25 import org.apache.http.client.HttpClient;
     26 import org.apache.http.client.entity.UrlEncodedFormEntity;
     27 import org.apache.http.client.methods.HttpGet;
     28 import org.apache.http.client.methods.HttpPost;
     29 import org.apache.http.conn.ConnectTimeoutException;
     30 import org.apache.http.conn.scheme.Scheme;
     31 import org.apache.http.conn.ssl.SSLSocketFactory;
     32 import org.apache.http.conn.ssl.X509HostnameVerifier;
     33 import org.apache.http.entity.ContentType;
     34 import org.apache.http.entity.StringEntity;
     35 import org.apache.http.impl.client.DefaultHttpClient;
     36 import org.apache.http.message.BasicNameValuePair;
     37 import org.apache.http.params.CoreConnectionPNames;
     38 import org.apache.http.protocol.HTTP;
     39 import org.apache.http.util.EntityUtils;
     40 
     41 /**
     42  * 封装了采用HttpClient发送HTTP请求的方法
     43  * @see 本工具所采用的是HttpComponents-Client-4.2.1
     44  * @see ===================================================================================================
     45  * @see 开发HTTPS应用时,时常会遇到两种情况
     46  * @see 1、测试服务器没有有效的SSL证书,客户端连接时就会抛异常
     47  * @see    javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
     48  * @see 2、测试服务器有SSL证书,但可能由于各种不知名的原因,它还是会抛一堆烂码七糟的异常,诸如下面这两种
     49  * @see    javax.net.ssl.SSLException: hostname in certificate didn't match: <123.125.97.66> != <123.125.97.241>
     50  * @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
     51  * @see ===================================================================================================
     52  * @see 这里使用的是HttpComponents-Client-4.2.1创建的连接,所以就要告诉它使用一个不同的TrustManager
     53  * @see 由于SSL使用的模式是X.509,对于该模式,Java有一个特定的TrustManager,称为X509TrustManager
     54  * @see TrustManager是一个用于检查给定的证书是否有效的类,所以我们自己创建一个X509TrustManager实例
     55  * @see 而在X509TrustManager实例中,若证书无效,那么TrustManager在它的checkXXX()方法中将抛出CertificateException
     56  * @see 既然我们要接受所有的证书,那么X509TrustManager里面的方法体中不抛出异常就行了
     57  * @see 然后创建一个SSLContext并使用X509TrustManager实例来初始化之
     58  * @see 接着通过SSLContext创建SSLSocketFactory,最后将SSLSocketFactory注册给HttpClient就可以了
     59  * @see ===================================================================================================
     60  * @version v1.7
     61  * @history v1.0-->新建<code>sendGetRequest()</code>和<code>sendPostRequest()</code>方法
     62  * @history v1.1-->新增<code>sendPostSSLRequest()</code>方法,用于发送HTTPS的POST请求
     63  * @history v1.2-->新增<code>sendPostRequest()</code>方法,用于发送HTTP协议报文体为任意字符串的POST请求
     64  * @history v1.3-->新增<code>java.net.HttpURLConnection</code>实现的<code>sendPostRequestByJava()</code>
     65  * @history v1.4-->所有POST方法中增加连接超时限制和读取超时限制
     66  * @history v1.5-->重组各方法,并补充自动获取HTTP响应文本编码的方式,移除<code>sendPostRequestByJava()</code>
     67  * @history v1.6-->整理GET和POST请求方法,使之更为适用
     68  * @history v1.7-->修正<code>sendPostRequest()</code>请求的CONTENT_TYPE头信息,并优化各方法参数及内部处理细节
     69  * @create Feb 1, 2012 3:02:27 PM
     70  * @update Jul 23, 2013 1:18:35 PM
     71  * @author 玄玉<http://blog.csdn.net/jadyer>
     72  */
     73 public class HttpClientUtil {
     74     private HttpClientUtil(){}
     75     
     76     /**
     77      * 发送HTTP_GET请求
     78      * @see 1)该方法会自动关闭连接,释放资源
     79      * @see 2)方法内设置了连接和读取超时时间,单位为毫秒,超时或发生其它异常时方法会自动返回"通信失败"字符串
     80      * @see 3)请求参数含中文时,经测试可直接传入中文,HttpClient会自动编码发给Server,应用时应根据实际效果决定传入前是否转码
     81      * @see 4)该方法会自动获取到响应消息头中[Content-Type:text/html; charset=GBK]的charset值作为响应报文的解码字符集
     82      * @see   若响应消息头中无Content-Type属性,则会使用HttpClient内部默认的ISO-8859-1作为响应报文的解码字符集
     83      * @param requestURL 请求地址(含参数)
     84      * @return 远程主机响应正文
     85      */
     86     public static String sendGetRequest(String reqURL){
     87         String respContent = "通信失败"; //响应内容
     88         HttpClient httpClient = new DefaultHttpClient(); //创建默认的httpClient实例
     89         //设置代理服务器
     90         //httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, new HttpHost("10.0.0.4", 8080));
     91         httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000); //连接超时10s
     92         httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 20000);         //读取超时20s
     93         HttpGet httpGet = new HttpGet(reqURL); //创建org.apache.http.client.methods.HttpGet
     94         try{
     95             HttpResponse response = httpClient.execute(httpGet); //执行GET请求
     96             HttpEntity entity = response.getEntity();            //获取响应实体
     97             if(null != entity){
     98                 //respCharset=EntityUtils.getContentCharSet(entity)也可以获取响应编码,但从4.1.3开始不建议使用这种方式
     99                 Charset respCharset = ContentType.getOrDefault(entity).getCharset();
    100                 respContent = EntityUtils.toString(entity, respCharset);
    101                 //Consume response content
    102                 EntityUtils.consume(entity);
    103             }
    104             System.out.println("-------------------------------------------------------------------------------------------");
    105             StringBuilder respHeaderDatas = new StringBuilder();
    106             for(Header header : response.getAllHeaders()){
    107                 respHeaderDatas.append(header.toString()).append("
    ");
    108             }
    109             String respStatusLine = response.getStatusLine().toString(); //HTTP应答状态行信息
    110             String respHeaderMsg = respHeaderDatas.toString().trim();    //HTTP应答报文头信息
    111             String respBodyMsg = respContent;                            //HTTP应答报文体信息
    112             System.out.println("HTTP应答完整报文=[" + respStatusLine + "
    " + respHeaderMsg + "
    
    " + respBodyMsg + "]");
    113             System.out.println("-------------------------------------------------------------------------------------------");
    114         } catch (ConnectTimeoutException cte){
    115             //Should catch ConnectTimeoutException, and don`t catch org.apache.http.conn.HttpHostConnectException
    116             LogUtil.getLogger().error("请求通信[" + reqURL + "]时连接超时,堆栈轨迹如下", cte);
    117         } catch (SocketTimeoutException ste){
    118             LogUtil.getLogger().error("请求通信[" + reqURL + "]时读取超时,堆栈轨迹如下", ste);
    119         }catch(ClientProtocolException cpe){
    120             //该异常通常是协议错误导致:比如构造HttpGet对象时传入协议不对(将'http'写成'htp')or响应内容不符合HTTP协议要求等
    121             LogUtil.getLogger().error("请求通信[" + reqURL + "]时协议异常,堆栈轨迹如下", cpe);
    122         }catch(ParseException pe){
    123             LogUtil.getLogger().error("请求通信[" + reqURL + "]时解析异常,堆栈轨迹如下", pe);
    124         }catch(IOException ioe){
    125             //该异常通常是网络原因引起的,如HTTP服务器未启动等
    126             LogUtil.getLogger().error("请求通信[" + reqURL + "]时网络异常,堆栈轨迹如下", ioe);
    127         }catch (Exception e){
    128             LogUtil.getLogger().error("请求通信[" + reqURL + "]时偶遇异常,堆栈轨迹如下", e);
    129         }finally{
    130             //关闭连接,释放资源
    131             httpClient.getConnectionManager().shutdown();
    132         }
    133         return respContent;
    134     }
    135     
    136     
    137     /**
    138      * 发送HTTP_POST请求
    139      * @see 1)该方法允许自定义任何格式和内容的HTTP请求报文体
    140      * @see 2)该方法会自动关闭连接,释放资源
    141      * @see 3)方法内设置了连接和读取超时时间,单位为毫秒,超时或发生其它异常时方法会自动返回"通信失败"字符串
    142      * @see 4)请求参数含中文等特殊字符时,可直接传入本方法,并指明其编码字符集encodeCharset参数,方法内部会自动对其转码
    143      * @see 5)该方法在解码响应报文时所采用的编码,取自响应消息头中的[Content-Type:text/html; charset=GBK]的charset值
    144      * @see   若响应消息头中未指定Content-Type属性,则会使用HttpClient内部默认的ISO-8859-1
    145      * @param reqURL        请求地址
    146      * @param reqData       请求参数,若有多个参数则应拼接为param11=value11&22=value22&33=value33的形式
    147      * @param encodeCharset 编码字符集,编码请求数据时用之,此参数为必填项(不能为""或null)
    148      * @return 远程主机响应正文
    149      */
    150     public static String sendPostRequest(String reqURL, String reqData, String encodeCharset){
    151         String reseContent = "通信失败";
    152         HttpClient httpClient = new DefaultHttpClient();
    153         httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);
    154         httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 20000);
    155         HttpPost httpPost = new HttpPost(reqURL);
    156         //由于下面使用的是new StringEntity(....),所以默认发出去的请求报文头中CONTENT_TYPE值为text/plain; charset=ISO-8859-1
    157         //这就有可能会导致服务端接收不到POST过去的参数,比如运行在Tomcat6.0.36中的Servlet,所以我们手工指定CONTENT_TYPE头消息
    158         httpPost.setHeader(HTTP.CONTENT_TYPE, "application/x-www-form-urlencoded; charset=" + encodeCharset);
    159         try{
    160             httpPost.setEntity(new StringEntity(reqData==null?"":reqData, encodeCharset));
    161             HttpResponse response = httpClient.execute(httpPost);
    162             HttpEntity entity = response.getEntity();
    163             if (null != entity) {
    164                 reseContent = EntityUtils.toString(entity, ContentType.getOrDefault(entity).getCharset());
    165                 EntityUtils.consume(entity);
    166             }
    167         } catch (ConnectTimeoutException cte){
    168             LogUtil.getLogger().error("请求通信[" + reqURL + "]时连接超时,堆栈轨迹如下", cte);
    169         } catch (SocketTimeoutException ste){
    170             LogUtil.getLogger().error("请求通信[" + reqURL + "]时读取超时,堆栈轨迹如下", ste);
    171         }catch(Exception e){
    172             LogUtil.getLogger().error("请求通信[" + reqURL + "]时偶遇异常,堆栈轨迹如下", e);
    173         }finally{
    174             httpClient.getConnectionManager().shutdown();
    175         }
    176         return reseContent;
    177     }
    178     
    179     
    180     /**
    181      * 发送HTTP_POST_SSL请求
    182      * @see 1)该方法会自动关闭连接,释放资源
    183      * @see 2)该方法亦可处理普通的HTTP_POST请求
    184      * @see 3)当处理HTTP_POST_SSL请求时,默认请求的是对方443端口,除非reqURL参数中指明了SSL端口
    185      * @see 4)方法内设置了连接和读取超时时间,单位为毫秒,超时或发生其它异常时方法会自动返回"通信失败"字符串
    186      * @see 5)请求参数含中文等特殊字符时,可直接传入本方法,并指明其编码字符集encodeCharset参数,方法内部会自动对其转码
    187      * @see 6)方法内部会自动注册443作为SSL端口,若实际使用中reqURL指定的SSL端口非443,可自行尝试更改方法内部注册的SSL端口
    188      * @see 7)该方法在解码响应报文时所采用的编码,取自响应消息头中的[Content-Type:text/html; charset=GBK]的charset值
    189      * @see   若响应消息头中未指定Content-Type属性,则会使用HttpClient内部默认的ISO-8859-1
    190      * @param reqURL        请求地址
    191      * @param params        请求参数
    192      * @param encodeCharset 编码字符集,编码请求数据时用之,当其为null时,则取HttpClient内部默认的ISO-8859-1编码请求参数
    193      * @return 远程主机响应正文
    194      */
    195     public static String sendPostSSLRequest(String reqURL, Map<String, String> params, String encodeCharset){
    196         String responseContent = "通信失败";
    197         HttpClient httpClient = new DefaultHttpClient();
    198         httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);
    199         httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 20000);
    200         //创建TrustManager()
    201         //用于解决javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    202         X509TrustManager trustManager = new X509TrustManager(){
    203             @Override
    204             public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
    205             @Override
    206             public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
    207             @Override
    208             public X509Certificate[] getAcceptedIssuers() {return null;}
    209         };
    210         //创建HostnameVerifier
    211         //用于解决javax.net.ssl.SSLException: hostname in certificate didn't match: <123.125.97.66> != <123.125.97.241>
    212         X509HostnameVerifier hostnameVerifier = new X509HostnameVerifier(){
    213             @Override
    214             public void verify(String host, SSLSocket ssl) throws IOException {}
    215             @Override
    216             public void verify(String host, X509Certificate cert) throws SSLException {}
    217             @Override
    218             public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {}
    219             @Override
    220             public boolean verify(String arg0, SSLSession arg1) {return true;}
    221         };
    222         try {
    223             //TLS1.0与SSL3.0基本上没有太大的差别,可粗略理解为TLS是SSL的继承者,但它们使用的是相同的SSLContext
    224             SSLContext sslContext = SSLContext.getInstance(SSLSocketFactory.TLS);
    225             //使用TrustManager来初始化该上下文,TrustManager只是被SSL的Socket所使用
    226             sslContext.init(null, new TrustManager[]{trustManager}, null);
    227             //创建SSLSocketFactory
    228             SSLSocketFactory socketFactory = new SSLSocketFactory(sslContext, hostnameVerifier);
    229             //通过SchemeRegistry将SSLSocketFactory注册到HttpClient上
    230             httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory));
    231             //创建HttpPost
    232             HttpPost httpPost = new HttpPost(reqURL);
    233             //由于下面使用的是new UrlEncodedFormEntity(....),所以这里不需要手工指定CONTENT_TYPE为application/x-www-form-urlencoded
    234             //因为在查看了HttpClient的源码后发现,UrlEncodedFormEntity所采用的默认CONTENT_TYPE就是application/x-www-form-urlencoded
    235             //httpPost.setHeader(HTTP.CONTENT_TYPE, "application/x-www-form-urlencoded; charset=" + encodeCharset);
    236             //构建POST请求的表单参数
    237             if(null != params){
    238                 List<NameValuePair> formParams = new ArrayList<NameValuePair>();
    239                 for(Map.Entry<String,String> entry : params.entrySet()){
    240                     formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
    241                 }
    242                 httpPost.setEntity(new UrlEncodedFormEntity(formParams, encodeCharset));
    243             }
    244             HttpResponse response = httpClient.execute(httpPost);
    245             HttpEntity entity = response.getEntity();
    246             if (null != entity) {
    247                 responseContent = EntityUtils.toString(entity, ContentType.getOrDefault(entity).getCharset());
    248                 EntityUtils.consume(entity);
    249             }
    250         } catch (ConnectTimeoutException cte){
    251             LogUtil.getLogger().error("请求通信[" + reqURL + "]时连接超时,堆栈轨迹如下", cte);
    252         } catch (SocketTimeoutException ste){
    253             LogUtil.getLogger().error("请求通信[" + reqURL + "]时读取超时,堆栈轨迹如下", ste);
    254         } catch (Exception e) {
    255             LogUtil.getLogger().error("请求通信[" + reqURL + "]时偶遇异常,堆栈轨迹如下", e);
    256         } finally {
    257             httpClient.getConnectionManager().shutdown();
    258         }
    259         return responseContent;
    260     }
    261 }

    下面是测试代码

    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"));
    }
  • 相关阅读:
    redis安装
    VMware安装Centos
    Nacos简单配置
    RAS非对称加密
    uLua Unity工作机制
    一个长期主义者的内与外
    MacOSX 运行Unity卡顿 [gethostname]
    着眼于长远,走的更稳
    物质趋于无穷, 人群趋于发散.符合熵增加的规律
    论PM与团队与敏捷开发
  • 原文地址:https://www.cnblogs.com/lr393993507/p/5310566.html
Copyright © 2020-2023  润新知