• HttpClient 4.5.x 工具类设计与实现


    最近,业务需要在java服务端发起http请求,需要实现"GET","POST","PUT"等基本方法。于是想以 "HttpClient" 为基础,封装基本实现方法。在github上面看到一个项目cn-umbrella/httpclientDemo,里面实现简单的 "GET","POST"。一方面,demo中实现的逻辑不能满足业务需要,同时实现过程中,存在使用过期的类,代码封装不足等问题。也是我就重新封装了httpclient-util,抱着大家相互学习的心态,将代码上传到github上面,相关链接,错误的地方欢迎大家指正。

    回归正题,下面讲讲工具类实现的基本思路。

    1.主要依赖

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.2</version>
    </dependency>
    
    <!--httpmime主要用于构建multi-part/form-data 请求-->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpmime</artifactId>
        <version>4.5.2</version>
    </dependency>
    

    2.HttpClientUtil 主要方法

    /**
     * Do http request
     * @param request request object
     * @return response object
     */
    public static Response doRequest(Request request)
    

    其中,Request 封装了请求参数,Response 封装了返回内容。

    3.Request 说明

    支持方法:"GET","OPTIONS","HEAD","POST","PUT","PATCH"。支持请求体: "application/x-www-form-urlencoded","json字符串","multipart/form-data"。

    Request的继承关系
    
    Request (没有请求body的请求,支持方法:"GET", "OPTIONS", "HEAD")
          |---BaseEntityRequest (抽象类,定义有请求body的请求, 支持方法:"POST", "PUT", "PATCH")
                          |--- UrlEncodedFormRequest (application/x-www-form-urlencoded)
                          |--- JsonRequest (请求body为json字符串)
                          |--- MultiPartFormRequest (multipart/form-data)
    

    同时,Request 支持使用 ssl/tls,使用request.setUseSSL(true);设置即可。

    HttpEntity就是对应请求body相关实体对象。

    3.BaseEntityRequest 介绍

    任何实现具有请求body的请求,需要实现 BaseEntityRequest 以下方法

    /**
     * Get HttpEntity about request body
     * @return HttpEntity
     */
    public abstract HttpEntity getEntity();
    

    3.1 UrlEncodedFormRequest 核心代码

    这个实现比较简单,"K-V" 的形式,返回 UrlEncodedFormEntity,注意参数编码。

    @Override
    public HttpEntity getEntity() {
        List<NameValuePair> pairList = new ArrayList<>(params.size());
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry
                    .getValue().toString());
            pairList.add(pair);
        }
    
        return new UrlEncodedFormEntity(pairList, Charset.forName(getRequestCharset())); //参数编码
    }
    

    3.2 JsonRequest 核心代码

    请求body就是一个 "json字符串",返回一个StringEntity 实体即可。

    @Override
    public HttpEntity getEntity() {
        StringEntity stringEntity = new StringEntity(jsonObject.toString(), getRequestCharset()); //解决中文乱码问题,需要注意编码,如使用"utf-8"
        stringEntity.setContentEncoding(getResponseDefaultCharset());
        stringEntity.setContentType(JSON_CONTENT_TYPE);
        return stringEntity;
    }
    

    3.3 MultiPartFormRequest 核心代码

    httpclient 类库中不直接支持构造 "multi-part/form-data" ,需要引入 httpmime,并使用 MultipartEntityBuilder 构造请求body。如果是文件类型的参数对象,使用 FileBody 构建 part,否则,使用 StringBody,注意编码。

    @Override
    public HttpEntity getEntity()  {
        try {
            Charset charset = CharsetUtils.get(getRequestCharset());
            //使用浏览器兼容模式,并且设置编码,防止文件名乱码问题
            MultipartEntityBuilder builder = MultipartEntityBuilder.create()
                    .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
                    .setCharset(charset); //编码
            parts.forEach((key, value) -> {
                if (null != value && value instanceof File) { //for file
                    FileBody fileBody = new FileBody((File) value);
                    builder.addPart(key, fileBody);
                } else {
                    StringBody stringBody = new StringBody(null == value ? "" : value.toString()
                            , ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), charset)); //编码
                    builder.addPart(key, stringBody);
                }
            });
            return builder.build();
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("Charset '" + getRequestCharset() + "' is unsupported!");
        }
    }
    

    4.注意地方

    使用 https 过程中,如果出现下述异常:

    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
        at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
        at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
    ...
    Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
        at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
        at sun.security.validator.Validator.validate(Validator.java:260)
        at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
        at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
    
    

    可以参考我的上篇博文解决 java 使用ssl过程中出现"PKIX path building failed:...,当然在包 com.xiaobenma020.http.cert 下面可以找到类 InstallCert

    关键步骤

    % java InstallCert _web_site_hostname_
    
    显示相关的证书信息
    
    此时输入'q' 则为'退出', '1' 则将添加CA证书。
    
    将新生成的 "jssecacerts" 移到"$JAVA_HOME/jre/lib/security"
    

    5.使用demo

    @Test
    public void doResponse() throws Exception {
        UrlEncodedFormRequest request = new UrlEncodedFormRequest("https://xxx.com/login", RequestMethod.POST);
    
        //url form param
        request.addParam("loginId", "loginId");
        request.addParam("password", "password");
    
        //query string param
        request.addUrlParam("version", "v1");
    
        //ssl
        request.setUseSSL(true);
    
        Response response = HttpClientUtil.doRequest(request);
        System.out.println(response.getResponseText()); //response text
        System.out.println(response.getCode()); //response code
        System.out.println(response.getHeader("Set-Cookie"));
    }
    
  • 相关阅读:
    as3工程和flex工程的区别
    Timer的repeatCount和currentCount的区别
    mouseChildren为false后,
    flex编译时,会把trace语句也编译进去
    stage和root的区别
    flex编译时,会把trace语句也编译进去
    水瓶座(1.202.19)更多星座运程
    如何更改titleWindow组件上的title字体大小?
    转贴关于AsWing和MXML 选项
    Eclipse中的文本编辑器使用技巧
  • 原文地址:https://www.cnblogs.com/javaDeveloper/p/6129874.html
Copyright © 2020-2023  润新知