HttpClient工具类

 1. 前言

在现代软件开发中,HTTP客户端库是与Web服务交互的关键组件。本文将介绍一个功能丰富的HttpClientUtils工具类,它不仅简化了发送HTTP请求的过程,还提供了绕过HTTPS证书验证的能力,这对于调试和测试环境尤其有用。此工具类基于Apache HttpClient库构建,结合Spring框架的便利性,以及Lombok简化代码的特点,旨在提高开发者的工作效率。

2. 工具类代码

注意:

1. 如果请求体为空,请不要传null,而是传入空字符串"";

功能支持:

1. 支持http和https协议;

2. 支持GET、POST、PUT、DELETE请求;

3. 支持图片下载到本地。

1. 所需依赖:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

 2. 代码:

import lombok.extern.slf4j.Slf4j;
import org.apache.http.Header;
import org.apache.http.HeaderIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.*;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.springframework.http.HttpHeaders;

import javax.imageio.ImageIO;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Map;

@Slf4j
public class HttpClientUtils {
    /**
     * 发送HTTP请求到指定URL,并绕过HTTPS证书验证。
     *
     * @param method          HTTP方法(GET、POST、PUT等)
     * @param url             请求URL
     * @param requestBodyJson 请求体(JSON格式字符串,对于非POST/PUT请求可为空)
     * @param headers         可选的额外请求头(键值对)
     * @return 响应体(JSON格式字符串),如果请求失败则返回null
     * @throws IOException 如果发生网络错误
     */
    public static String sendRequest(HttpMethod method, String url, String requestBodyJson, Map<String, String> headers) throws IOException, NoSuchAlgorithmException, KeyManagementException {
        // 创建一个信任所有证书的SSLContext
        SSLContext sslContext = createTrustAllSSLContext();

        // 创建一个使用该SSLContext的SSLConnectionSocketFactory
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);

        // 构建一个包含SSL连接工厂的Registry
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", sslSocketFactory).build();

        // 创建一个支持HTTPS的PoolingHttpClientConnectionManager,使用上述Registry
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);

        try (CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build()) {
            HttpUriRequest request = createHttpRequest(method, url, requestBodyJson, headers);
            printHttpRequestDetails(request);
            HttpResponse response = httpClient.execute(request);

            // 打印响应状态行、响应头和响应体
            String result = printHttpResponseDetails(response);

            // 处理响应
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                return result;
            } else {
                log.error("Error: Request failed with status code " + statusCode);
                return null;
            }
        } catch (IOException e) {
            log.error("An exception occurred while executing the request:");
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 发送获取验证码的HTTP请求到指定URL,并绕过HTTPS证书验证。
     *
     * @param method          HTTP方法(GET、POST、PUT等)
     * @param url             请求URL
     * @param requestBodyJson 请求体(JSON格式字符串,对于非POST/PUT请求可为空)
     * @param headers         可选的额外请求头(键值对)
     * @param filePath        图片保存路径
     * @return 响应体(JSON格式字符串),如果请求失败则返回null
     * @throws IOException 如果发生网络错误
     */
    public static void sendImageRequest(HttpMethod method, String url, String requestBodyJson, Map<String, String> headers, String filePath, String fileType) throws IOException, NoSuchAlgorithmException, KeyManagementException {
        // 创建一个信任所有证书的SSLContext
        SSLContext sslContext = createTrustAllSSLContext();

        // 创建一个使用该SSLContext的SSLConnectionSocketFactory
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);

        // 构建一个包含SSL连接工厂的Registry
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", sslSocketFactory).build();

        // 创建一个支持HTTPS的PoolingHttpClientConnectionManager,使用上述Registry
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);

        try (CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build()) {
            HttpUriRequest request = createHttpRequest(method, url, requestBodyJson, headers);
            printHttpRequestDetails(request);
            HttpResponse response = httpClient.execute(request);

            // 打印响应状态行、响应头和响应体
            downloadImages(response, filePath, fileType);

            // 处理响应
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                log.info("图片下载成功,下载地址为:{}", filePath);
            } else {
                log.error("Error: Request failed with status code " + statusCode);
            }
        } catch (IOException e) {
            log.error("An exception occurred while executing the request:");
            e.printStackTrace();
        }
    }


    /**
     * 打印HTTP请求的详细信息
     *
     * @param request HTTP请求对象
     */
    private static void printHttpRequestDetails(HttpUriRequest request) {
        log.info("\n----------------------------------------------- Request Details -------------------------------------------------------");

        // 打印请求方法
        log.info("Method: " + request.getMethod());

        // 打印请求URI
        log.info("URI: " + request.getURI());

        // 打印请求头
        log.info("Headers:");
        HeaderIterator headerIterator = request.headerIterator();
        while (headerIterator.hasNext()) {
            Header header = headerIterator.nextHeader();
            log.info(header.getName() + ": " + header.getValue());
        }

        // 如果是POST或PUT请求,打印请求体
        if (request instanceof HttpEntityEnclosingRequestBase) {
            HttpEntity entity = ((HttpEntityEnclosingRequestBase) request).getEntity();
            if (entity != null) {
                try {
                    String requestBody = EntityUtils.toString(entity, StandardCharsets.UTF_8);
                    log.info("Request Body: " + requestBody);
                } catch (IOException e) {
                    log.error("Failed to print request body:");
                    e.printStackTrace();
                }
            } else {
                log.info("Request Body: (empty)");
            }
        }

        log.info("\n----------------------------------------------------------------------------------------------------------------\n");
    }


    /**
     * 打印HTTP响应的详细信息
     *
     * @return
     */
    private static String printHttpResponseDetails(HttpResponse response) {
        log.info("\n----------------------------------------------- Response Details -------------------------------------------------------");

        // 打印状态行
        log.info("Status Line: " + response.getStatusLine());

        // 打印响应头
        System.out.println("Headers:");
        HeaderIterator headerIterator = response.headerIterator();
        while (headerIterator.hasNext()) {
            Header header = headerIterator.nextHeader();
            log.info(header.getName() + ": " + header.getValue());
        }

        // 打印响应体
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            try {
                String responseBody = EntityUtils.toString(entity, "UTF-8");
                log.info("Response Body: " + responseBody);
                log.info("\n----------------------------------------------------------------------------------------------------------------\n");
                return responseBody;
            } catch (IOException e) {
                log.error("Failed to print response body:");
                log.info("\n----------------------------------------------------------------------------------------------------------------\n");
                e.printStackTrace();
            }
        } else {
            log.info("Response Body: (empty)");
        }

        return null;
    }

    /**
     * 下载图片
     *
     * @param response HttpResponse
     * @param filePath 下载的文件路径
     * @param fileType 文件类型,如png
     */
    private static void downloadImages(HttpResponse response, String filePath, String fileType) {
        log.info("\n----------------------------------------------- Response Details -------------------------------------------------------");

        // 打印状态行
        log.info("Status Line: " + response.getStatusLine());

        // 打印响应头
        System.out.println("Headers:");
        HeaderIterator headerIterator = response.headerIterator();
        while (headerIterator.hasNext()) {
            Header header = headerIterator.nextHeader();
            log.info(header.getName() + ": " + header.getValue());
        }

        // 打印响应体
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            try {
                // 获取响应实体内容
                byte[] imageBytes = EntityUtils.toByteArray(response.getEntity());

                // 创建输入流
                InputStream inputStream = new ByteArrayInputStream(imageBytes);

                // 将字节数组转换为BufferedImage
                BufferedImage image = ImageIO.read(inputStream);
                File outputFile = new File(filePath);
                // 如果父路径没有则创建
                if (!outputFile.getParentFile().exists()) {
                    outputFile.getParentFile().mkdirs();
                }
                ImageIO.write(image, fileType, outputFile);
                log.info("图片下载成功");
                log.info("\n----------------------------------------------------------------------------------------------------------------\n");
            } catch (IOException e) {
                log.error("图片下载失败");
                log.info("\n----------------------------------------------------------------------------------------------------------------\n");
                e.printStackTrace();
            }
        } else {
            log.info("Response Body: (empty)");
        }
    }

    /**
     * 创建一个HTTP请求对象
     */
    private static HttpUriRequest createHttpRequest(HttpMethod method, String url, String requestBodyJson, Map<String, String> headers) throws IOException {
        HttpUriRequest request;
        switch (method) {
            case GET:
                request = new HttpGet(url);
                break;
            case POST:
                request = new HttpPost(url);
                ((HttpEntityEnclosingRequestBase) request).setEntity(new StringEntity(requestBodyJson, "UTF-8"));
                break;
            case PUT:
                request = new HttpPut(url);
                ((HttpEntityEnclosingRequestBase) request).setEntity(new StringEntity(requestBodyJson, "UTF-8"));
                break;
            case DELETE:
                request = new HttpDelete(url);
                break;
            default:
                throw new IllegalArgumentException("Unsupported HTTP method: " + method);
        }

        // 设置默认请求头
        request.setHeader(HttpHeaders.ACCEPT, "application/json");
        request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");

        // 添加额外请求头(如果有)
        if (headers != null) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                request.setHeader(entry.getKey(), entry.getValue());
            }
        }

        return request;
    }

    /**
     * 创建一个信任所有证书的SSLContext
     */
    private static SSLContext createTrustAllSSLContext() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }}, new SecureRandom());
        return sslContext;
    }

    /**
     * HTTP方法枚举
     */
    public enum HttpMethod {
        GET, POST, PUT, DELETE
    }
}

3. 核心功能

3.1 安全连接管理

  • 信任所有证书:通过自定义SSLContext,实现了对所有SSL证书的信任,避免了因证书问题导致的请求失败,特别适合内部测试环境。
  • 连接池管理:利用PoolingHttpClientConnectionManager来复用连接,提高性能,减少资源消耗。

3.2 发送HTTP请求

  • 灵活的HTTP方法支持:支持GET、POST、PUT、DELETE四种基本HTTP方法。
  • JSON请求体处理:自动处理JSON格式的请求体,适应RESTful API的普遍需求
  • 自定义请求头:允许用户添加额外的HTTP头部信息,满足特定API调用需求。
  • 响应内容处理:不仅能获取JSON格式的响应体,还能下载图片资源到指定路径。

3.3 日志记录

  • 详尽的请求/响应日志:通过printHttpRequestDetails和printHttpResponseDetails方法,全面记录请求和响应的细节,便于调试和监控

3.4 异常处理

  • 优雅的错误处理:对执行请求过程中可能发生的异常进行了捕获和日志记录,确保程序的健壮性。

4. 使用示例

4.1 发送普通JSON请求

String url = "https://api.example.com/data";
String jsonBody = "{\"key\":\"value\"}";
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer your_token_here");
try {
    String response = HttpClientUtils.sendRequest(HttpClientUtils.HttpMethod.GET, url, jsonBody, headers);
    System.out.println(response);
} catch (Exception e) {
    e.printStackTrace();
}

4.2 下载图片

String imageUrl = "https://example.com/image.png";
String savePath = "/path/to/save/image.png";
try {
    HttpClientUtils.sendImageRequest(HttpClientUtils.HttpMethod.GET, imageUrl, "", null, savePath,"png");
} catch (Exception e) {
    e.printStackTrace();
}

 5. 拓展HTTP方法(HTTP Methods)

1. HttpMethod枚举类中,新增新的HTTP方法(HTTP Methods)

2. createHttpRequest方法中,新增case方法。

6. 总结

HttpClientUtils工具类通过集成了Apache HttpClient的强大功能,结合了简化证书验证、请求响应日志记录、灵活的HTTP方法支持等特性,极大地提升了开发人员在进行HTTP通信时的效率和体验。无论是日常的API调用,还是复杂的数据交互场景,该工具类都能提供稳定可靠的解决方案。在实际应用中,根据具体需求,可以进一步扩展其功能,比如增加请求重试机制、超时配置等,以适应更多样化的应用场景。

  • 32
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值