zoukankan      html  css  js  c++  java
  • 通用HttpClient请求工具类

    前言

    现在大多数Java项目开发中,经常都用通过HTTP协议来调用网络资源数据(1、爬虫爬取网页数据;2、请求第三方系统进行数据交互等),虽然JDK8及以前的版本,也提供了响应的请求工具包,但是使用起来很不灵活,所以大多数都是采用Apache的HttpClient包来封装自己的请求工具类,方便整个项目开发使用。

    使用流程

    1、引入maven依赖

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.13</version>
    </dependency>
    
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.75</version>
    </dependency>

     2、HttpClient使用

    2.1 创建请求客户端对象

    /*
     1、创建自定义请求客户端构建对象
     */
    HttpClientBuilder httpClientBuilder = HttpClients.custom();
    // 创建连接池,并对连接池进行设置后赋值给请求对象构造器
    HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
    httpClientBuilder.setConnectionManager(cm);
    CloseableHttpClient build = httpClientBuilder.build();
    /*
     2、创建默认的请求客户端构建对象
     */
    CloseableHttpClient request2 = HttpClients.createDefault();

    2.2 创建连接池管理对象

    // 创建请求连接池管理,可创建空的,也可以创建带注册对象(注册对象示例:https的连接认证注册)
    PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
    // 设置连接池最大连接数(设置时可以设置部署机器的CPU线程数)
    cm.setMaxTotal(8);
    // 设置每个路由最大默认连接数(可以认为一个域名就是路由)
    cm.setDefaultMaxPerRoute(8);

    2.3 请求方式对象

    // get请求对象
    HttpGet httpGet = new HttpGet(url);
    // post请求对象
    HttpPost httpPost = new HttpPost(url);

    2.4 请求头设置

    // 设置用户代理为浏览器
    httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38");
    // 设置请求的数据类型
    httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
    httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");

    2.5 参数设置

    注意:随着请求方式的不同,传参方式也大不相同

    /*
     1、get请求
     */
    String url = "url";
    Map<String, String> params = new HashMap<>();
    params.put("username", "chris");
    URIBuilder uriBuilder = new URIBuilder(url);
    if (params!=null) { // 判空操作要有,避免空指针异常
        for (String paramKey : params.keySet()) {
            uriBuilder.addParameter(paramKey, params.get(paramKey));
        }
    }
    HttpGet httpGet = new HttpGet(uriBuilder.build());
    /*
     2、post请求(表单形式:application/x-www-form-urlencoded)
     */
    String url = "url";
    Map<String, String> params = new HashMap<>();
    params.put("username", "chris");
    HttpPost httpPost = new HttpPost(url);
    if (params!=null) { // 判空操作要有,避免空指针异常
        List<NameValuePair> nvList = new ArrayList<>(params.size());
        NameValuePair nv = null;
        for (String paramKey : params.keySet()) {
         // 构建参数键值对象
            nv = new BasicNameValuePair(paramKey, params.get(paramKey));
            nvList.add(nv);
        }
        // 传送参数的对象
        HttpEntity paramsEntity = new UrlEncodedFormEntity(nvList, StandardCharsets.UTF_8);
        // 设置参数
        httpPost.setEntity(paramsEntity);
    }
    /*
     3、post请求(json形式:application/json)
     */
    String url = "url";
    Map<String, String> params = new HashMap<>();
    params.put("username", "chris");
    HttpPost httpPost = new HttpPost(url);
    if (params!=null) { // 判空操作要有,避免空指针异常
        // 需要将请求对象转换为json字符串形式
        HttpEntity paramEntity = new StringEntity(JSON.toJSONString(params), StandardCharsets.UTF_8);
        httpPost.setEntity(paramEntity);
    }

    2.6 执行请求并处理返回对象

    CloseableHttpResponse response = null;
    try {
        // 执行请求
        response = closeableHttpClient.execute(httpPost);
        int statusCode = response.getStatusLine().getStatusCode();
        // 判断请求响应是否成功
        if (statusCode== HttpStatus.SC_OK) { 
            // 处理响应数据并返回
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity, StandardCharsets.UTF_8);
        } else {
            log.error("请求地址({})失败:{}", url, statusCode);
        }
    } catch (IOException e) {
        log.error("请求地址({})失败", url, e);
        throw new RuntimeException("请求地址("+url+")失败");
    } finally { // 确认数据消费并关闭http响应对象
        HttpClientUtils.closeQuietly(response);
    }

    3、书写请求工具类

    /**
     * http请求工具类
     */
    @Slf4j
    public class HttpUtil {
    
        /**
         * 请求连接构造对象
         */
        private static final HttpClientBuilder httpClientBuilder = HttpClients.custom();
    
        /**
         * 连接池最大连接数
         */
        private static final int MAX_TOTAL = 8;
    
        /**
         * 每个路由最大默认连接数
         */
        private static final int DEFAULT_MAX_RER_ROUTE = 8;
    
        /**
         * 获取连接获取超时时间
         */
        private static final int CONNECTION_REQUEST_TIMEOUT = 2000;
    
        /**
         * 连接超时时间
         */
        private static final int CONNECTION_TIMEOUT = 2000;
    
        /**
         * 数据响应超时时间
         */
        private static final int SOCKET_TIMEOUT = 10000;
    
    
    
        static {
            /*
             1、绕开不安全的https请求的证书验证(不需要可以注释,然后使用空参数的PoolingHttpClientConnectionManager构造连接池管理对象)
             */
            Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", trustHttpsCertificates())
                    .build();
    
            /*
             2、创建请求连接池管理
             */
            PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
            // 设置连接池最大连接数
            cm.setMaxTotal(MAX_TOTAL);
            // 设置每个路由最大默认连接数
            cm.setDefaultMaxPerRoute(DEFAULT_MAX_RER_ROUTE);
            httpClientBuilder.setConnectionManager(cm);
    
            /*
            3、设置默认请求配置
             */
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT) // 设置获取连接获取超时时间
                    .setConnectTimeout(CONNECTION_TIMEOUT) // 设置连接超时时间
                    .setSocketTimeout(SOCKET_TIMEOUT) // 设置数据响应超时时间
                    .build();
            httpClientBuilder.setDefaultRequestConfig(requestConfig);
        }
    
    
        /**
         *  执行get请求(网页)
         * @param url 请求地址(含有特殊符号需要URLEncoder编码)
         * @param headers 请求头参数
         * @return 响应数据
         */
        public static String getPage(String url, Map<String, String> headers) {
    
            CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
            HttpGet httpGet = new HttpGet(url);
    
            // 请求头设置,如果常用的请求头设置,也可以写死,特殊的请求才传入
            httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38");
            if (headers != null) {
                for (String headerKey : headers.keySet()) {
                    httpGet.setHeader(headerKey, headers.get(headerKey));
                }
            }
    
            CloseableHttpResponse response = null;
            try {
                response = closeableHttpClient.execute(httpGet);
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode== HttpStatus.SC_OK) { // 请求响应成功
                    HttpEntity entity = response.getEntity();
                    return EntityUtils.toString(entity, StandardCharsets.UTF_8);
                } else {
                    log.error("请求地址({})失败:{}", url, statusCode);
                }
            } catch (Exception e) {
                log.error("请求地址({})失败", url, e);
                throw new RuntimeException("请求地址("+url+")失败");
            } finally {
                HttpClientUtils.closeQuietly(response);
            }
            return null;
        }
    
        /**
         *  执行post请求(form表单)
         * @param url 请求地址
         * @param headers 请求头参数
         * @return 响应数据
         */
        public static String postForm(String url, Map<String, String> headers, Map<String, String> params) {
            CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
            HttpPost httpPost = new HttpPost(url);
             // 请求头设置,如果常用的请求头设置,也可以写死,特殊的请求才传入
            httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38");
            httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            if (headers != null) {
                for (String headerKey : headers.keySet()) {
                    httpPost.setHeader(headerKey, headers.get(headerKey));
                }
            }
    
            // 设置请求参数
            if (params!=null) {
                List<NameValuePair> nvList = new ArrayList<>(params.size());
                for (String paramKey : params.keySet()) {
                    NameValuePair nv = new BasicNameValuePair(paramKey, params.get(paramKey));
                    nvList.add(nv);
                }
                HttpEntity paramsEntity = new UrlEncodedFormEntity(nvList, StandardCharsets.UTF_8);
                httpPost.setEntity(paramsEntity);
            }
    
            CloseableHttpResponse response = null;
            try {
                response = closeableHttpClient.execute(httpPost);
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode== HttpStatus.SC_OK) { // 请求响应成功
                    HttpEntity entity = response.getEntity();
                    return EntityUtils.toString(entity, StandardCharsets.UTF_8);
                } else {
                    log.error("请求地址({})失败:{}", url, statusCode);
                }
            } catch (IOException e) {
                log.error("请求地址({})失败", url, e);
                throw new RuntimeException("请求地址("+url+")失败");
            } finally {
                HttpClientUtils.closeQuietly(response);
            }
            return null;
        }
    
        /**
         *  执行post请求(接口)
         * @param url 请求地址
         * @param headers 请求头参数
         * @return 响应数据
         */
        public static String getJson(String url, Map<String, String> headers) {
            CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
            HttpGet httpGet = new HttpGet(url);
            // 请求头设置,如果常用的请求头设置,也可以写死,特殊的请求才传入
            if (headers != null) {
                for (String headerKey : headers.keySet()) {
                    httpGet.setHeader(headerKey, headers.get(headerKey));
                }
            }
            CloseableHttpResponse response = null;
            try {
                response = closeableHttpClient.execute(httpGet);
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode== HttpStatus.SC_OK) { // 请求响应成功
                    HttpEntity entity = response.getEntity();
                    return EntityUtils.toString(entity, StandardCharsets.UTF_8);
                } else {
                    log.error("请求地址({})失败:{}", url, statusCode);
                }
            } catch (IOException e) {
                log.error("请求地址({})失败", url, e);
                throw new RuntimeException("请求地址("+url+")失败");
            } finally {
                HttpClientUtils.closeQuietly(response);
            }
            return null;
        }
    
        /**
         *  执行post请求(接口)
         * @param url 请求地址
         * @param headers 请求头参数
         * @return 响应数据
         */
        public static String postJson(String url, Map<String, String> headers, Map<String, String> params) {
            CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
            HttpPost httpPost = new HttpPost(url);
            // 请求头设置,如果常用的请求头设置,也可以写死,特殊的请求才传入
            httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
            if (headers != null) {
                for (String headerKey : headers.keySet()) {
                    httpPost.setHeader(headerKey, headers.get(headerKey));
                }
            }
            if (params!=null) {
                HttpEntity paramEntity = new StringEntity(JSON.toJSONString(params), StandardCharsets.UTF_8);
                httpPost.setEntity(paramEntity);
            }
    
            CloseableHttpResponse response = null;
            try {
                response = closeableHttpClient.execute(httpPost);
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode== HttpStatus.SC_OK) { // 请求响应成功
                    HttpEntity entity = response.getEntity();
                    return EntityUtils.toString(entity, StandardCharsets.UTF_8);
                } else {
                    log.error("请求地址({})失败:{}", url, statusCode);
                }
            } catch (IOException e) {
                log.error("请求地址({})失败", url, e);
                throw new RuntimeException("请求地址("+url+")失败");
            } finally {
                HttpClientUtils.closeQuietly(response);
            }
            return null;
        }
    
        /**
         * 构建https安全连接工厂
         * @return 安全连接工厂
         */
        private static ConnectionSocketFactory trustHttpsCertificates() {
            SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
            try {
                sslContextBuilder.loadTrustMaterial(null, new TrustStrategy() {
                    @Override
                    public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                        return true;
                    }
                });
                SSLContext sslContext = sslContextBuilder.build();
                SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
                        new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}, // 支持的https安全认证协议
                        null, NoopHostnameVerifier.INSTANCE);
                return sslConnectionSocketFactory;
            } catch (Exception e) {
                log.error("构建安全连接工厂失败", e);
                throw new RuntimeException("构建安全连接工厂失败");
            }
        }
    }

    4、额外说明

    • 项目中HttpClient基本最好构建请求连接池进行请求,提升性能,加快请求速度;
    • 如果你的应用不请求不信任的https连接,则不需要绕过https安全认证(不需要使用到trustHttpsCertificates方法);
    • 项目请求在工具类一般可以确定请求头,可以写死,从而少传入一个参数;(特殊要求传入的请求才保留该参数设置)
    • 每次请求后一定要确认消费和关闭响应;(HttpClientUtils.closeQuietly(response))
    • 部分爬虫需要设置代理,使用示例如下:
    HttpGet httpGet = new HttpGet(url);
    HttpHost proxy = new HttpHost("49.70.17.48", 8888);
    RequestConfig config = RequestConfig.custom()
            .setProxy(proxy) //设置代理
            .setConnectTimeout(2000) // 设置HTTP连接超时时间
            .setSocketTimeout(3000)  // 设置数据响应超时时间
            .setConnectionRequestTimeout(2000) // 设置从连接池获取连接的超时时间
            .build();
    httpGet.setConfig(config); 

      成功 = 正确的选择 + 实际的行动 + 长期的坚持;

  • 相关阅读:
    CSS定位 position
    时间日期类--显示时间
    【HDOJ】4775 Infinite Go
    【HDOJ】4297 One and One Story
    【HDOJ】4056 Draw a Mess
    【HDOJ】2242 考研路茫茫——空调教室
    【HDOJ】3828 A + B problem
    【HDOJ】3386 Final Kichiku “Lanlanshu”
    【HDOJ】1648 Keywords
    【HDOJ】1699 The comment in cpp
  • 原文地址:https://www.cnblogs.com/gangbalei/p/15399471.html
Copyright © 2011-2022 走看看