• 通用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); 

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

  • 相关阅读:
    阿里DatatX mysql8往 Elasticsearch 7 插入时间数据 时区引发的问题
    通俗易懂 k8s (3):kubernetes 服务的注册与发现
    ReplicaSet 和 ReplicationController 的区别
    使用Go module导入本地包
    k8s之statefulset控制器
    终于成功部署 Kubernetes HPA 基于 QPS 进行自动伸缩
    Atitit drmmr outline org stat vb u33.docx Atitit drmmr outline org stat v0 taf.docx Atitit drmmr out
    Atitit all diary index va u33 #alldiary.docx Atitit alldiaryindex v1 t717 目录 1. Fix 1 2. Diary deta
    Atitit path query 路径查询语言 数据检索语言 目录 1.1. List map >> spel 1 1.2. Html数据 》》Css选择符 1 1.3. Json 》map》
    Atitit prgrmlan topic--express lan QL query lan表达式语言 目录 1. 通用表达语言(CEL) 1 1.1. 8.2 功能概述 1 1.2. Ongl
  • 原文地址:https://www.cnblogs.com/gangbalei/p/15399471.html
Copyright © 2020-2023  润新知