构建方便安全的HttpClient工具类

本文详细介绍了如何在Java中构建一个方便、安全的HttpClient工具类,包括依赖引入、异常处理、IO操作、URL解析、单例模式以及各种HTTP请求方法的实现。示例代码展示了如何进行GET、POST请求,文件上传,以及处理JSON和表单数据。通过这个工具类,可以简化第三方API的调用过程。
摘要由CSDN通过智能技术生成

前言

在Java开发中经常遇见需要调用第三方接口的情况,这种情况下往往需要一个接口调用工具,常用的有hutool restTemplate httpClient。但是不管哪一种,在使用便捷性方面都不太方便。不如自己再去封装。
常见的二进制提交,formdata提交等需要做不少的代码转换。

开始

引入核心依赖
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpmime</artifactId>
      <version>4.5.3</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.73</version>
    </dependency>
其它依赖
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>28.1-jre</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.10</version>
      <scope>provided</scope>
    </dependency>
    <!-- log start -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.25</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.25</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-nop</artifactId>
      <version>1.7.25</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>1.7.5</version>
    </dependency>
    <!-- log end -->

封装工具类

1.UrlEntity
    import java.util.HashMap;
    import java.util.Map;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    /**
     * url对象
     *
     * @author LiTing
     * @date 2021/2/26 11:18
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class UrlEntity {
      /** 基础url */
      public String baseUrl;
      /** url参数 */
      public Map<String, Object> params = new HashMap<>();
    
      public String toUrl() {
        int i = 0;
        StringBuilder text = new StringBuilder(baseUrl);
        for (Map.Entry<String, Object> entry : params.entrySet()) {
          if (i == 0) {
            text = text.append("?" + entry.getKey() + "=" + entry.getValue());
          } else {
            text = text.append("&" + entry.getKey() + "=" + entry.getValue());
          }
          i++;
        }
        return text.toString();
      }
    
      public UrlEntity addParam(Map<String, Object> map) {
        if (map != null) {
          params.putAll(map);
        }
        return this;
      }
    }
2.HttpClientException
    import lombok.Data;

    /**
     * 第三方服务调用异常
     *
     * @author LiTing
     * @date 2021/6/8 10:43
     */
    @Data
    public class HttpClientException extends Exception {
    
      public HttpClientException(String message) {
        super(message);
      }
    }   
3.IoUtil
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    /**
     * 统一的io处理工具类
     *
     * @author liting
     */
    public class IoUtil {
      /** 默认的字节读取大小 */
      private static final int SIZE = 4 * 1024;
    
      public static File inputStream2File(
          InputStream inputStream, String targetFile, boolean closeStream) throws   IOException {
        File file = new File(targetFile);
        OutputStream outStream = null;
        try {
          outStream = new FileOutputStream(file);
          File parentFile = file.getParentFile();
          if (!parentFile.exists()) {
            parentFile.mkdirs();
          }
          byte[] buffer = new byte[SIZE];
          int bytesRead;
          while ((bytesRead = inputStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, bytesRead);
          }
          outStream.flush();
        } finally {
          if (outStream != null) {
            outStream.close();
          }
          if (closeStream && inputStream != null) {
            inputStream.close();
          }
        }
        return file;
      }
    
      /** 目的是检查目录存不存在,不存在则创建. */
      public static void checkPath(String path) {
        File file = new File(path);
        if (file != null && file.isDirectory() && !file.exists()) {
          file.mkdirs();
        }
        if (file == null || !file.exists()) {
          file.mkdirs();
        }
      }
      
    
      /** byte数组转文件 */
      public static byte[] file2Byte(File file) throws IOException {
        byte[] buffer;
        FileInputStream fis = null;
        ByteArrayOutputStream bos = null;
        try {
          checkPath(file.getParent());
          fis = new FileInputStream(file);
          bos = new ByteArrayOutputStream();
          byte[] b = new byte[SIZE];
          int n;
          while ((n = fis.read(b)) != -1) {
            bos.write(b, 0, n);
          }
        } finally {
          if (fis != null) {
            fis.close();
          }
          if (bos != null) {
            bos.close();
          }
        }
        buffer = bos.toByteArray();
        return buffer;
      }
    }
4.UrlUtil
    import java.util.HashMap;
    import org.hhdbb.hhpclient.entity.UrlEntity;
    
    /**
     * url解析工具
     *
     * @author LiTing
     * @date 2021/2/26 11:12
     */
    public class UrlUtil {
    
      /**
       * 解析url
       *
       * @param url
       * @return
       */
      public static UrlEntity parse(String url) {
        UrlEntity entity = new UrlEntity();
        if (url == null) {
          return entity;
        }
        url = url.trim();
        if ("".equals(url)) {
          return entity;
        }
        String[] urlParts = url.split("\\?");
        entity.baseUrl = urlParts[0];
        // 没有参数
        if (urlParts.length == 1) {
          return entity;
        }
        // 有参数
        String[] params = urlParts[1].split("&");
        entity.params = new HashMap<>(8);
        for (String param : params) {
          String[] keyValue = param.split("=");
          entity.params.put(keyValue[0], keyValue[1]);
        }
    
        return entity;
      }
    }
5.SingletonUtil
    import com.google.common.util.concurrent.ThreadFactoryBuilder;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadFactory;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    import org.apache.http.client.config.CookieSpecs;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.impl.client.HttpClientBuilder;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
    
    /**
     * 单例工具类
     *
     * @author LiTing
     * @date 2021/3/26 18:22
     */
    public class SingletonUtil {
      private static volatile PoolingHttpClientConnectionManager   poolingHttpClientConnectionManager;
      private static volatile HttpClientBuilder httpClientBuilder;
      private static RequestConfig globalConfig =
          RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
      private static Lock lock1 = new ReentrantLock();
      private static Lock lock2 = new ReentrantLock();
      public static ThreadFactory namedThreadFactory =
          new ThreadFactoryBuilder().setNameFormat("thread-call-runner-%d").build();
      public static ExecutorService executorService =
          new ThreadPoolExecutor(
              5, 100, 200L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(),   namedThreadFactory);
      /** 保证全局只使用提个连接池管理对象 */
      public static PoolingHttpClientConnectionManager
          getSingletonPoolingHttpClientConnectionManager() {
        if (poolingHttpClientConnectionManager == null) {
          lock1.lock();
          try {
            PoolingHttpClientConnectionManager cm = new     PoolingHttpClientConnectionManager();
            cm.setDefaultMaxPerRoute(800);
            cm.setMaxTotal(1000);
            poolingHttpClientConnectionManager = cm;
          } finally {
            lock1.unlock();
          }
        }
        return poolingHttpClientConnectionManager;
      }
    
      /** 保证全局只通过一个builder去创建client */
      public static HttpClientBuilder getSingletonHttpClientBuilder() {
        if (httpClientBuilder == null) {
          lock2.lock();
          try {
            httpClientBuilder =
                HttpClients.custom()
                    .setConnectionManager(
                        SingletonUtil.getSingletonPoolingHttpClientConnectionManager    ())
                    .setDefaultRequestConfig(globalConfig);
          } finally {
            lock2.unlock();
          }
        }
        return httpClientBuilder;
      }
    }   

开始构建

6.HttpClientUtil
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.UnsupportedEncodingException;
    import java.nio.charset.StandardCharsets;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    import java.util.Map;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpHost;
    import org.apache.http.NameValuePair;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.entity.UrlEncodedFormEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.client.methods.HttpRequestBase;
    import org.apache.http.entity.ByteArrayEntity;
    import org.apache.http.entity.ContentType;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.entity.mime.HttpMultipartMode;
    import org.apache.http.entity.mime.MultipartEntityBuilder;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.util.EntityUtils;
    import org.hhdbb.hhpclient.entity.UrlEntity;
    import org.hhdbb.hhpclient.exception.HttpClientException;
    
    /**
     * http请求工具类
     *
     * @author LiTing
     * @date 2021/3/26 16:39
     */
    @Slf4j
    public class HttpClientUtil {
    
      private static final String DEFAULT_CHARSET = "UTF-8";
    
      private static class HttpClientUtilHolder {
        private static HttpClientUtil httpClientUtil = new HttpClientUtil();
      }
    
      public static final HttpClientUtil getSingleInstance() {
        return HttpClientUtilHolder.httpClientUtil;
      }
    
      public static void httpResponseToFile(CloseableHttpResponse httpResponse,   String path)
          throws IOException {
        HttpEntity httpEntity = httpResponse.getEntity();
        InputStream inStream = httpEntity.getContent();
        IoUtil.inputStream2File(inStream, path, true);
      }
    
      /**
       * 发送Get请求
       *
       * @param url
       * @param header
       * @param tClass
       * @return T
       * @throws
       * @version V1.0.0
       * @date 2021/3/26 17:06
       */
      public static <T> T doGet(String url, Map<String, String> header, Class<T>   tClass)
          throws HttpClientException {
        CloseableHttpClient httpClient = getHttpClient();
        HttpGet get = new HttpGet(url);
        setHeader(get, header);
        return getResult(httpClient, get, tClass);
      }
    
      /**
       * 发送Get请求
       *
       * @param url
       * @param header
       * @param tClass
       * @return T
       * @throws
       * @version V1.0.0
       * @date 2021/3/26 17:06
       */
      public static <T> T doGetWithProxy(
          String url, Map<String, String> header, Class<T> tClass, String ip, int   port)
          throws HttpClientException {
        CloseableHttpClient httpClient = getHttpClient();
        HttpGet get = new HttpGet(url);
        RequestConfig c = get.getConfig();
        if (c == null) {
          c = RequestConfig.DEFAULT;
        }
        RequestConfig requestConfig = RequestConfig.copy(c).setProxy(new     HttpHost(ip, port)).build();
        get.setConfig(requestConfig);
        setHeader(get, header);
        return getResult(httpClient, get, tClass);
      }
    
      /**
       * 发送get请求 带参
       *
       * @param url
       * @param params
       * @param header
       * @param tClass
       * @return T
       * @throws
       * @version V1.0.0
       * @date 2021/3/26 17:06
       */
      public static <T> T doGet(
          String url, Map<String, Object> params, Map<String, String> header,   Class<T> tClass)
          throws HttpClientException {
        CloseableHttpClient httpClient = getHttpClient();
        HttpGet get = new HttpGet(dealUrl(url, params));
        setHeader(get, header);
        return getResult(httpClient, get, tClass);
      }
    
      /**
       * 下载文件
       *
       * @param url
       * @param header
       * @param filePath
       * @return void
       * @throws
       * @version V1.0.0
       * @date 2021/3/26 17:06
       */
      public static void doGetFile(String url, Map<String, String> header, String   filePath)
          throws HttpClientException {
        CloseableHttpClient httpClient = getHttpClient();
        HttpGet get = new HttpGet(url);
        setHeader(get, header);
        try (CloseableHttpResponse res = doExecute(httpClient, get)) {
          httpResponseToFile(res, filePath);
        } catch (IOException e) {
          e.printStackTrace();
          // 换成自己的统一异常处理方法,省得到处抛HttpClientException
          throw new HttpClientException("HTTP工具类IO异常");
        }
      }
    
      /**
       * 获取文件输入流
       *
       * @param url
       * @param header
       * @return java.io.InputStream
       * @throws
       * @version V1.0.0
       * @date 2021/3/26 17:05
       */
      public static InputStream doGetFileInputStream(String url, Map<String, String>   header)
          throws HttpClientException {
        CloseableHttpClient httpClient = getHttpClient();
        HttpGet get = new HttpGet(url);
        setHeader(get, header);
        try (CloseableHttpResponse res = doExecute(httpClient, get)) {
          return res.getEntity().getContent();
        } catch (IOException e) {
          e.printStackTrace();
          // aa//换成自己的统一异常处理方法,省得到处抛HttpClientException
          throw new HttpClientException("HTTP工具类IO异常");
        }
      }
    
      /**
       * 发送 application/x-www-form-urlencoded请求
       *
       * @param url
       * @param param
       * @param header
       * @param tClass
       * @return T
       * @throws
       * @version V1.0.0
       * @date 2021/3/26 17:05
       */
      public static <T> T doPost(
          String url, Map<String, Object> param, Map<String, String> header,   Class<T> tClass)
          throws HttpClientException {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost(url);
        setHeader(httpPost, header);
        try {
          setUrlFormEntity(httpPost, param);
          return getResult(httpClient, httpPost, tClass);
        } catch (IOException e) {
          e.printStackTrace();
          // 换成自己的统一异常处理方法,省得到处抛HttpClientException
          throw new HttpClientException("HTTP工具类IO异常");
        }
      }
    
      /**
       * 发送application/json请求
       *
       * @param url
       * @param json
       * @param header
       * @param t
       * @return T
       * @throws
       * @version V1.0.0
       * @date 2021/3/26 17:04
       */
      public static <T> T doPostJson(String url, String json, Map<String, String>   header, Class<T> t)
          throws HttpClientException {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost(url);
        setHeader(httpPost, header);
        setJsonEntity(httpPost, json);
        return getResult(httpClient, httpPost, t);
      }
    
      /**
       * form表单文件上传
       *
       * @param url
       * @param params
       * @param headers
       * @param t
       * @return T
       * @throws
       * @version V1.0.0
       * @date 2021/3/26 17:23
       */
      public static <T> T doPostFormData(
          String url, Map<String, Object> params, Map<String, String> headers,   Class<T> t)
          throws HttpClientException {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost(url);
        setHeader(httpPost, headers);
        setFormDataEntity(httpPost, params);
        return getResult(httpClient, httpPost, t);
      }
    
      /**
       * 仅上传文件
       *
       * @param url
       * @param file
       * @param headers
       * @param tClass
       * @return T
       * @throws
       * @version V1.0.0
       * @date 2021/3/26 17:30
       */
      public static <T> T doPostFile(
          String url, File file, Map<String, String> headers, Class<T> tClass)
          throws HttpClientException {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost(url);
        setHeader(httpPost, headers);
        ByteArrayEntity byteArrayEntity = null;
        try {
          if (file != null) {
            byteArrayEntity = new ByteArrayEntity(IoUtil.file2Byte(file));
          }
          httpPost.setEntity(byteArrayEntity);
          return getResult(httpClient, httpPost, tClass);
        } catch (IOException e) {
          e.printStackTrace();
          // 换成自己的统一异常处理方法,省得到处抛HttpClientException
          throw new HttpClientException("HTTP工具类IO异常");
        }
      }
    
      public static CloseableHttpClient getHttpClient() {
        return SingletonUtil.getSingletonHttpClientBuilder().build();
      }
    
      public static void setHeader(HttpRequestBase hb, Map<String, String> headers)   {
        if (headers != null) {
          for (String key : headers.keySet()) {
            hb.setHeader(key, headers.get(key));
          }
        }
      }
    
      private static CloseableHttpResponse doExecute(CloseableHttpClient hc,   HttpRequestBase hb)
          throws IOException {
        CloseableHttpResponse ch = hc.execute(hb);
        return ch;
      }
    
      private static String dealUrl(String url, Map<String, Object> params) {
        UrlEntity urlEntity = UrlUtil.parse(url);
        urlEntity.addParam(params);
        return urlEntity.toUrl();
      }
    
      public static void setUrlFormEntity(HttpPost httpPost, Map<String, Object>   param)
          throws UnsupportedEncodingException {
        if (param != null) {
          List<NameValuePair> paramList = new ArrayList<>();
          for (String key : param.keySet()) {
            paramList.add(new BasicNameValuePair(key,     String.valueOf(param.get(key))));
          }
          UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
          httpPost.setEntity(entity);
        }
      }
    
      public static void setJsonEntity(HttpPost httpPost, String json) {
        StringEntity entity = new StringEntity(json, StandardCharsets.UTF_8);
        entity.setContentType("application/json;charset=utf-8");
        httpPost.setEntity(entity);
      }
    
      public static void setFormDataEntity(HttpPost httpPost, Map<String, Object>   params) {
        MultipartEntityBuilder mEntityBuilder = MultipartEntityBuilder.create();
        mEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        mEntityBuilder.setCharset(StandardCharsets.UTF_8);
        ContentType textContent = ContentType.create("text/plain",     StandardCharsets.UTF_8);
        params.forEach(
            (k, v) -> {
              if (v instanceof File) {
                File file = (File) v;
                mEntityBuilder.addBinaryBody(k, file);
              } else if (v instanceof Collection) {
                Collection cl = (Collection) v;
                cl.stream()
                    .forEach(
                        c -> {
                          File f = (File) c;
                          mEntityBuilder.addBinaryBody(k, f);
                        });
              } else {
                mEntityBuilder.addTextBody(k, v.toString(), textContent);
              }
            });
        httpPost.setEntity(mEntityBuilder.build());
      }
    
      public static <T> boolean checkStringResult(Class<T> t) {
        if (String.class.isAssignableFrom(t)) {
          return true;
        }
        return false;
      }
    
      public static <T> boolean checkJsonObjectResult(Class<T> t) {
        return JSONObject.class.isAssignableFrom(t);
      }
    
      public static <T> boolean checkJsonArrayResult(Class<T> t) {
        return JSONArray.class.isAssignableFrom(t);
      }
    
      public static <T> T getResult(CloseableHttpClient hc, HttpRequestBase hb,   Class<T> t)
          throws HttpClientException {
        String s;
        try (CloseableHttpResponse res = doExecute(hc, hb)) {
          s = EntityUtils.toString(res.getEntity(), DEFAULT_CHARSET);
          log.info("URI:{}", hb.getURI().toString());
          log.info("RESULT:{}", s);
          if (checkStringResult(t)) {
            return (T) s;
          } else if (checkJsonObjectResult(t)) {
            return (T) JSONObject.parse(s);
          } else if (checkJsonArrayResult(t)) {
            return (T) JSONArray.parse(s);
          }
        } catch (IOException e) {
          e.printStackTrace();
          // 换成自己的统一异常处理方法,省得到处抛HttpClientException
          throw new HttpClientException("HTTP工具类IO异常");
        }
        return JSON.parseObject(s, t);
      }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我的孤独与美酒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值