httpclient工具类(支持泛型转换)

1、网上搜到的httpclient工具类的问题:

1.1、如下图我们都能够发现这种封装的问题:

  • 代码繁杂、充斥了很多重复性代码
  • 返回值单一,无法拿到对应的Java Bean对象及List对象集合
  • 实际场景中会对接大量第三方的OPEN API,下述方法的扩展性差

httpclient工具类(支持泛型转换)_第1张图片

1.2、简洁调用方式演示

本文基于上述问题,通过设计模式、泛型、JSON工具类的方式进行了封装,得到了下述更方便、更简洁的http请求调用方式

Entity params = new Entity();
params.setUrl("/common/postJson");
 Map<String, Object> map = new HashMap<>();
map.put("userId", "13277887788");
map.put("companyId", "87037827534cf");
params.setParams(map);

// 返回对象集合
List<AppEntity> appEntity = thirdHttpProcessorFactory
	.doGetReturnList(ThirdSystemEnum.ION, params, AppEntity.class);
// 返回String类型
String result = thirdHttpProcessorFactory
	.doGetReturnBean(ThirdSystemEnum.ION, params, String.class);
// 返回指定的对象
AppEntity appEntity = thirdHttpProcessorFactory
	.doGetReturnBean(ThirdSystemEnum.ION, params, AppEntity.class);
        

1.3、 目录结构

httpclient工具类(支持泛型转换)_第2张图片

2、引入的maven仓库

 <dependency>
     <groupId>com.fasterxml.jackson.coregroupId>
     <artifactId>jackson-databindartifactId>
	 <version>2.14.2version>
dependency>
<dependency>
    <groupId>cn.hutoolgroupId>
    <artifactId>hutool-allartifactId>
    <version>5.7.16version>
dependency>
  • ThirdHttpProcessorFactory:存储不同三方平台的处理期
  • ThirdHttpProcessor:定义通用的接口
  • AbstractThirdHttpProcessor:定义公共的处理逻辑
  • IonHttpProcessor:定义各对接平台的差异性逻辑
  • AppEntity:返回值
  • Entity:入参
  • ThirdSystemEnum:定义三方平台的枚举

3、代码实现

3.1、获取不同第三方处理器的工厂ThirdHttpProcessorFactory

package com.ionrocking.platform.tripartite;


import com.ionrocking.common.core.exception.ServiceException;
import com.ionrocking.platform.tripartite.entity.Entity;
import com.ionrocking.platform.tripartite.enums.ThirdSystemEnum;
import lombok.experimental.Accessors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author ke
 * Created by on  2023-10-27 15:23
 */
@Component
@Accessors(chain = true)
public class ThirdHttpProcessorFactory {

    @Autowired
    private List<ThirdHttpProcessor> thirdHttpProcessors;

    private Map<String, ThirdHttpProcessor> thirdHttpProcessorMap;

    @PostConstruct
    public void init() {
        if (CollectionUtils.isEmpty(thirdHttpProcessors)) {
            return;
        }
        thirdHttpProcessorMap = new HashMap<>(8);
        for (ThirdHttpProcessor processor : thirdHttpProcessors) {
            thirdHttpProcessorMap.put(processor.getType().getCode(), processor);
        }
    }

    private ThirdHttpProcessor getThirdHttpProcessor(String type) {
        ThirdSystemEnum thirdSystemEnum = ThirdSystemEnum.getByCode(type);
        if (null == thirdSystemEnum) {
            throw new ServiceException("三方OpenApi尚未配置,无法进行请求");
        }
        return thirdHttpProcessorMap.get(type);
    }


    /**
     * http get请求
     *
     * @param thirdSystemEnum 三方系统类型枚举
     * @param entity          参数
     * @param tClass          返回参数类型
     * @return T              返回数据对象
     * @throws Exception 业务执行异常
     */
    public <T> T doGetReturnBean(ThirdSystemEnum thirdSystemEnum, Entity entity, Class<T> tClass) throws Exception {
        return getThirdHttpProcessor(thirdSystemEnum.getCode()).doGetReturnBean(entity, tClass);
    }

    /**
     * http post请求,入参支持application/x-www-form-urlencoded 请求
     *
     * @param thirdSystemEnum 三方系统类型枚举
     * @param entity          参数
     * @param tClass          返回参数类型
     * @return T              返回数据对象
     * @throws Exception 业务执行异常
     */
    public <T> T doPostReturnBean(ThirdSystemEnum thirdSystemEnum, Entity entity, Class<T> tClass) throws Exception {
        return getThirdHttpProcessor(thirdSystemEnum.getCode()).doPostReturnBean(entity, tClass);
    }

    /**
     * http post请求,入参支持application/json请求
     *
     * @param thirdSystemEnum 三方系统类型枚举
     * @param entity          参数
     * @param tClass          返回参数类型
     * @return T              返回数据对象
     * @throws Exception 业务执行异常
     */
    public <T> T doPostJsonReturnBean(ThirdSystemEnum thirdSystemEnum, Entity entity, Class<T> tClass) throws Exception {
        return getThirdHttpProcessor(thirdSystemEnum.getCode()).doPostJsonReturnBean(entity, tClass);
    }

    /**
     * http get请求
     *
     * @param thirdSystemEnum 三方系统类型枚举
     * @param entity          参数
     * @param tClass          返回参数类型
     * @return List        返回数据集合
     * @throws Exception 业务执行异常
     */
    public <T> List<T> doGetReturnList(ThirdSystemEnum thirdSystemEnum, Entity entity, Class<T> tClass) throws Exception {
        return getThirdHttpProcessor(thirdSystemEnum.getCode()).doGetReturnList(entity, tClass);
    }

    /**
     * http post请求,入参支持application/x-www-form-urlencoded 请求
     *
     * @param thirdSystemEnum 三方系统类型枚举
     * @param entity          参数
     * @param tClass          返回参数类型
     * @return List        返回数据集合
     * @throws Exception 业务执行异常
     */
    public <T> List<T> doPostReturnList(ThirdSystemEnum thirdSystemEnum, Entity entity, Class<T> tClass) throws Exception {
        return getThirdHttpProcessor(thirdSystemEnum.getCode()).doPostReturnList(entity, tClass);
    }

    /**
     * http post请求,入参支持application/json请求
     *
     * @param thirdSystemEnum 三方系统类型枚举
     * @param entity          参数
     * @param tClass          返回参数类型
     * @return List        返回数据集合
     * @throws Exception 业务执行异常
     */
    public <T> List<T> doPostJsonReturnList(ThirdSystemEnum thirdSystemEnum, Entity entity, Class<T> tClass) throws Exception {
        return getThirdHttpProcessor(thirdSystemEnum.getCode()).doPostJsonReturnList(entity, tClass);
    }

}

3.2、http请求处理的接口ThirdHttpProcessor

/**
 * Dans.com Inc.
 * Copyright (c) 2011-2020 All Rights Reserved
 */
package com.ionrocking.platform.tripartite;


import com.ionrocking.platform.tripartite.entity.Entity;
import com.ionrocking.platform.tripartite.enums.ThirdSystemEnum;

import java.util.List;

/**
 * 追踪事件处理器
 *
 * @author ke
 * Created by on  2023-06-20 15:23
 */
public interface ThirdHttpProcessor {

    /**
     * 业务执行
     *
     * @param entity
     * @param tClass
     * @return T
     * @throws Exception 业务执行异常
     */
    <T> T doGetReturnBean(Entity entity, Class<T> tClass) throws Exception;

    /**
     * 业务执行
     *
     * @param entity
     * @param tClass
     * @return
     * @throws Exception 业务执行异常
     */
    <T> T doPostReturnBean(Entity entity, Class<T> tClass) throws Exception;

    /**
     * 业务执行
     *
     * @param entity
     * @param tClass
     * @return
     * @throws Exception 业务执行异常
     */
    <T> T doPostJsonReturnBean(Entity entity, Class<T> tClass) throws Exception;
    /**
     * 业务执行
     *
     * @param entity
     * @param tClass
     * @return T
     * @throws Exception 业务执行异常
     */
    <T> List<T> doGetReturnList(Entity entity, Class<T> tClass) throws Exception;

    /**
     * 业务执行
     *
     * @param entity
     * @param tClass
     * @return
     * @throws Exception 业务执行异常
     */
    <T> List<T> doPostReturnList(Entity entity, Class<T> tClass) throws Exception;

    /**
     * 业务执行
     *
     * @param entity
     * @param tClass
     * @return
     * @throws Exception 业务执行异常
     */
    <T> List<T> doPostJsonReturnList(Entity entity, Class<T> tClass) throws Exception;

    /**
     * 获取事件类型
     *
     * @return
     */
    ThirdSystemEnum getType();
}

3.3、通用逻辑处理的抽象类AbstractThirdHttpProcessor

  • 如在通过http client发起HTTP请求时,除了请求头requestHeader、请求入参requestBody不同,其他逻辑都是一样的,则可以进行公共代码的抽取
  • 如果存在特殊的逻辑处理,则可以在子类中重写父类的方法
package com.ionrocking.platform.tripartite;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ionrocking.common.core.exception.ServiceException;
import com.ionrocking.platform.config.SysNacosConfig;
import com.ionrocking.platform.tripartite.entity.Entity;
import com.ionrocking.platform.tripartite.entity.IonResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author ke
 * @Date 2023/10/27
 */
@Slf4j
@Component
public abstract class AbstractThirdHttpProcessor implements ThirdHttpProcessor {
    @Override
    public <T> List<T> doGetReturnList(Entity entity, Class<T> tClass) throws Exception {
        // 构造请求头
        Map<String, String> requestHead = constructRequestHead(entity);
        // 构造请求数据
        Map<String, Object> requestBody = constructRequestBody(entity);
        String result = HttpUtil.createGet(sysNacosConfig.getIonRequestUrl() + entity.getUrl())
                .addHeaders(requestHead)
                .form(requestBody)
                .execute()
                .body();
        return toList(result, tClass);
    }

    @Override
    public <T> List<T> doPostReturnList(Entity entity, Class<T> tClass) throws Exception {
        // 构造请求头
        Map<String, String> requestHead = constructRequestHead(entity);
        // 构造请求数据
        Map<String, Object> requestBody = constructRequestBody(entity);
        String result = HttpUtil.createPost(sysNacosConfig.getIonRequestUrl() + entity.getUrl())
                .addHeaders(requestHead)
                .form(requestBody)
                .execute()
                .body();
        return toList(result, tClass);
    }

    @Override
    public <T> List<T> doPostJsonReturnList(Entity entity, Class<T> tClass) throws Exception {
        // 构造请求头
        Map<String, String> requestHead = constructRequestHead(entity);
        // 构造请求数据
        Map<String, Object> requestBody = constructRequestBody(entity);
        String result = HttpUtil.createPost(sysNacosConfig.getIonRequestUrl() + entity.getUrl())
                .addHeaders(requestHead)
                .body(JSONUtil.toJsonStr(requestBody))
                .execute()
                .body();
        return toList(result, tClass);
    }

    public <T> T toBean(String result, Class<T> tClass) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        IonResponse<Object> response = JSONUtil.toBean(result, IonResponse.class);
        if (CODE != response.getCode()) {
            throw new ServiceException(response.getMsg());
        }
        if (null == response.getData()) {
            return null;
        }
        return objectMapper.readValue(response.getData().toString(), tClass);
    }

    public <T> List<T> toList(String result, Class<T> tClass) throws JsonProcessingException {
        List<T> data = new ArrayList<>();
        IonResponse<List<Object>> response = JSONUtil.toBean(result, IonResponse.class);
        if (CODE != response.getCode()) {
            throw new ServiceException(response.getMsg());
        }
        if (CollUtil.isEmpty(response.getData())) {
            return null;
        }
        for (Object o : response.getData()) {
            ObjectMapper objectMapper = new ObjectMapper();
            data.add(objectMapper.readValue(o.toString(), tClass));
        }
        return data;
    }

    @Resource
    protected SysNacosConfig sysNacosConfig;

    protected static final int CODE = 200;

    @Override
    public <T> T doGetReturnBean(Entity entity, Class<T> tClass) throws Exception {
        // 构造请求头
        Map<String, String> requestHead = constructRequestHead(entity);
        // 构造请求数据
        Map<String, Object> requestBody = constructRequestBody(entity);
        String result = HttpUtil.createGet(sysNacosConfig.getIonRequestUrl() + entity.getUrl())
                .addHeaders(requestHead)
                .form(requestBody)
                .execute()
                .body();
        return toBean(result, tClass);
    }

    /**
     * 执行
     *
     * @return
     */
    @Override
    public <T> T doPostReturnBean(Entity entity, Class<T> tClass) throws Exception {
        // 构造请求头
        Map<String, String> requestHead = constructRequestHead(entity);
        // 构造请求数据
        Map<String, Object> requestBody = constructRequestBody(entity);
        String result = HttpUtil.createPost(sysNacosConfig.getIonRequestUrl() + entity.getUrl())
                .addHeaders(requestHead)
                .form(requestBody)
                .execute()
                .body();
        return toBean(result, tClass);
    }

    @Override
    public <T> T doPostJsonReturnBean(Entity entity, Class<T> tClass) throws Exception {
        // 构造请求头
        Map<String, String> requestHead = constructRequestHead(entity);
        // 构造请求数据
        Map<String, Object> requestBody = constructRequestBody(entity);
        String result = HttpUtil.createPost(sysNacosConfig.getIonRequestUrl() + entity.getUrl())
                .addHeaders(requestHead)
                .body(JSONUtil.toJsonStr(requestBody))
                .execute()
                .body();
        return toBean(result, tClass);
    }


    /**
     * 构造请求头
     *
     * @param entity
     * @return
     */
    public abstract Map<String, String> constructRequestHead(Entity entity);

    /**
     * 构造请求体
     *
     * @param entity
     * @return
     */
    public Map<String, Object> constructRequestBody(Entity entity) {
        if (CharSequenceUtil.isBlank(entity.getUrl())) {
            throw new ServiceException("请求路径不能为空");
        }
        if (null == entity) {
            return null;
        }
        return entity.getParams();
    }

}

3.4、第三方非通用逻辑处理类IonHttpProcessor

  • 比如此处对接的xx公司需要进行access_token获取及验证,然后将access_token放在请求头中
package com.ionrocking.platform.tripartite.impl;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.ionrocking.platform.tripartite.AbstractThirdHttpProcessor;
import com.ionrocking.platform.tripartite.entity.Entity;
import com.ionrocking.platform.tripartite.entity.IonResponse;
import com.ionrocking.platform.tripartite.enums.ThirdSystemEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * @author ke
 * @Date 2023/10/27
 */
@Slf4j
@Component
public class IonHttpProcessor extends AbstractThirdHttpProcessor {

    private String accessTokenUrl = "/token/getAccessToken";


    private static final String ACCESS_TOKEN_KEY = "access_token";

    @Override
    public Map<String, String> constructRequestHead(Entity entity) {
        Map<String, String> header = new HashMap<>(2);
        Map<String, Object> params = new HashMap<>(4);
        params.put("appId", sysNacosConfig.getIonRequestAppId());
        params.put("appSecret", sysNacosConfig.getIonRequestAppSecret());

        String result = HttpUtil.get(sysNacosConfig.getIonRequestUrl() + accessTokenUrl, params);
        if (CharSequenceUtil.isEmpty(result)) {
            return null;
        }
        TypeReference<IonResponse<Map<String, Object>>> typeRef = new TypeReference<IonResponse<Map<String, Object>>>() {
        };
        IonResponse<Map<String, Object>> response = JSONUtil.toBean(result, typeRef, false);
        if (CODE == response.getCode() && null != response.getData()) {
            if (response.getData().containsKey(ACCESS_TOKEN_KEY)) {
                header.put(ACCESS_TOKEN_KEY, response.getData().get(ACCESS_TOKEN_KEY).toString());
            }
        }
        return header;
    }

    @Override
    public ThirdSystemEnum getType() {
        return ThirdSystemEnum.ION;
    }
}

3.5、枚举类ThirdSystemEnum

  • 区分不同的平台,根据枚举获取不同的实现类
package com.ionrocking.platform.tripartite.enums;

import org.apache.commons.lang3.StringUtils;

/**
 * @author ke
 * @Date 2023/10/27
 */
public enum ThirdSystemEnum {
    /**
     * XX科技公司
     */
    ION("ion", "xx科技");

    private final String code;

    private final String name;

    public String getName() {
        return this.name;
    }


    public String getCode() {
        return this.code;
    }

    ThirdSystemEnum(String code, String name) {
        this.code = code;
        this.name = name;
    }

    public static ThirdSystemEnum getByCode(String code) {
        if (StringUtils.isEmpty(code)) {
            return null;
        }
        for (ThirdSystemEnum codeEnum : values()) {
            if (code.equals(codeEnum.getCode())) {
                return codeEnum;
            }
        }
        return null;
    }
}

3.6、第三方返回格式对象IonResponse

package com.ionrocking.platform.tripartite.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author ke
 * @Date 2023/10/27
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class IonResponse<T> implements Serializable {

    private static final long serialVersionUID = -8741972144218822267L;
    private int code;
    /**
     * 消息
     */
    private String msg;
    /**
     * 数据
     */
    private T data;
    /**
     * 总记录数
     */
    private long total;
}

3.7、处理器入参对象Entity

  • 此处定义请求的URL和请求的参数
package com.ionrocking.platform.tripartite.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Map;

/**
 * @author ke
 * @Date 2023/10/27
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Entity implements Serializable {

    private static final long serialVersionUID = -6083780287057302816L;

    private String url;

    /**
     * 参数
     */
    private Map<String, Object> params;
}

3.8、第三方接口返回值对象AppEntity

package com.ionrocking.platform.tripartite.entity;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import javax.validation.constraints.NotBlank;

/**
 * @author ke
 * @Date 2023/10/26
 */

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class AppEntity {
    private long id;

    private String appId;

    @NotBlank(message = "应用名称不能为空")
    private String appName;

    private String appSecret;

    private String accessToken;

    private Integer isFlag;
}

3.9、Nacos配置

  • Nacos中一般存放一些第三方的请求域名、鉴权的appId、appSecret等
tripartite-platform:
  ion:
    request:
      url: http://localhost:8080/api/
      appId: YodeqWwp
      appSecret: 87037827534cf848a570fae3c93a2469fa0262935af531dddfe7a52ae7f98f41
package com.ionrocking.platform.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * nacos配置信息
 * @author ke
 * @Date 2023/9/20
 */
@Component
public class SysNacosConfig {


    @Value("${upload.path}")
    private String uploadPath;

    @Value("${tripartite-platform.ion.request.url}")
    private String ionRequestUrl;

    @Value("${tripartite-platform.ion.request.appId}")
    private String ionRequestAppId;

    @Value("${tripartite-platform.ion.request.appSecret}")
    private String ionRequestAppSecret;

    public String getUploadPath() {
        return uploadPath;
    }

    public String getIonRequestUrl() {
        return ionRequestUrl;
    }

    public String getIonRequestAppId() {
        return ionRequestAppId;
    }

    public String getIonRequestAppSecret() {
        return ionRequestAppSecret;
    }
}

测试

@RestController
@RequestMapping("/test")
public class Test {
    
    @Resource
    private SysNacosConfig sysNacosConfig;
    
    @Resource
    private ThirdHttpProcessorFactory thirdHttpProcessorFactory;
    
    @ResponseBody
    @RequestMapping(value = "/get")
    public AjaxResult get() throws Exception extends BaseController  {
        Entity params = new Entity();
        params.setUrl("/common/get");
        String result = thirdHttpProcessorFactory.doGetReturnBean(ThirdSystemEnum.ION, params, String.class);
        return AjaxResult.success(result);
    }


    @ResponseBody
    @RequestMapping(value = "/list")
    public AjaxResult list() throws Exception {
        Entity params = new Entity();
        params.setUrl("/common/list");
        List<AppEntity> appEntity = thirdHttpProcessorFactory.doGetReturnList(ThirdSystemEnum.ION, params, AppEntity.class);
        return AjaxResult.success(appEntity);
    }

    @ResponseBody
    @RequestMapping(value = "/postJson")
    public AjaxResult postJson() throws Exception {
        Entity params = new Entity();
        params.setUrl("/common/postJson");
        Map<String, Object> map = new HashMap<>();
        map.put("appId", "YodeqWwp");
        map.put("appSecret", "87037827534cf848a570fae3c93a2469fa0262935af531dddfe7a52ae7f98f41");
        params.setParams(map);
        List<AppEntity> list = thirdHttpProcessorFactory.doPostJsonReturnList(ThirdSystemEnum.ION, params, AppEntity.class);
		return AjaxResult.success(list);
    }
}

你可能感兴趣的:(Java相关,java,http,okhttp)