jeecg统一异常处理根据不同模块返回指定响应信息

实际项目中由于需要与不同客户、不同协议的平台进行对接,响应信息也不近相同,通过改造现有统一异常处理逻辑,让jeecgboot的统一异常返回不同格式、不同协议的响应信息。

目录

一、核心改造思路

二、具体实现方案

[1. 先定义核心枚举和上下文工具](#1. 先定义核心枚举和上下文工具)

[2. 新增拦截器识别请求所属平台](#2. 新增拦截器识别请求所属平台)

[3. 改造统一异常处理器](#3. 改造统一异常处理器)

[4. 扩展说明](#4. 扩展说明)

三、实际对接场景示例

四、总结


一、核心改造思路

在 JeecgBoot 默认的统一异常处理基础上,我们需要实现异常响应的动态适配,核心思路分为三步:

  1. 定义多协议 / 多格式的响应模板(如 JSON、XML、自定义协议格式);
  2. 新增上下文标识,用于识别当前请求对应的客户 / 协议类型;
  3. 改造全局异常处理器,根据上下文标识动态选择响应格式和协议。

二、具体实现方案

1. 先定义核心枚举和上下文工具

首先定义协议 / 客户标识枚举,以及用于存储当前请求上下文的工具类:

java 复制代码
/**
 * 对接平台/协议枚举
 */
public enum PlatformEnum {
    // 客户A-JSON格式
    CUSTOMER_A_JSON("customerA", "application/json"),
    // 客户B-XML格式
    CUSTOMER_B_XML("customerB", "application/xml"),
    // 客户C-自定义协议格式
    CUSTOMER_C_CUSTOM("customerC", "application/custom"),
    // 默认-JeecgBoot原生格式
    DEFAULT("default", "application/json");

    private String code;
    private String contentType;

    // 构造方法、getter/setter
    PlatformEnum(String code, String contentType) {
        this.code = code;
        this.contentType = contentType;
    }

    public static PlatformEnum getByCode(String code) {
        for (PlatformEnum platform : values()) {
            if (platform.getCode().equals(code)) {
                return platform;
            }
        }
        return DEFAULT;
    }

    // getter/setter省略
}

/**
 * 请求上下文工具类(存储当前请求对应的平台标识)
 */
public class RequestContextHolderUtil {
    private static final ThreadLocal<PlatformEnum> PLATFORM_THREAD_LOCAL = new ThreadLocal<>();

    // 设置当前平台标识
    public static void setPlatform(PlatformEnum platform) {
        PLATFORM_THREAD_LOCAL.set(platform);
    }

    // 获取当前平台标识
    public static PlatformEnum getPlatform() {
        return PLATFORM_THREAD_LOCAL.get() == null ? PlatformEnum.DEFAULT : PLATFORM_THREAD_LOCAL.get();
    }

    // 清除上下文
    public static void clear() {
        PLATFORM_THREAD_LOCAL.remove();
    }
}

2. 新增拦截器识别请求所属平台

通过拦截器解析请求头 / 参数中的平台标识,存入上下文:

java 复制代码
/**
 * 平台标识拦截器
 */
@Component
public class PlatformIdentifyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 方式1:从请求头获取平台标识(推荐)
        String platformCode = request.getHeader("X-Platform-Code");
        // 方式2:从请求参数获取(备用)
        if (StringUtils.isEmpty(platformCode)) {
            platformCode = request.getParameter("platformCode");
        }
        // 设置到上下文
        PlatformEnum platform = PlatformEnum.getByCode(platformCode);
        RequestContextHolderUtil.setPlatform(platform);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 清除上下文,避免线程复用导致数据污染
        RequestContextHolderUtil.clear();
    }
}

/**
 * 注册拦截器
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private PlatformIdentifyInterceptor platformIdentifyInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 拦截所有对接外部的接口路径(根据实际项目调整)
        registry.addInterceptor(platformIdentifyInterceptor)
                .addPathPatterns("/api/third/**");
    }
}

3. 改造统一异常处理器

JeecgBoot 默认的GlobalExceptionHandler通常在org.jeecg.common.exception包下,改造后支持多格式响应:

java 复制代码
/**
 * 改造后的全局异常处理器
 */
@RestControllerAdvice
@Slf4j
public class CustomGlobalExceptionHandler {

    /**
     * 处理JeecgBoot基础异常
     */
    @ExceptionHandler(value = JeecgBootException.class)
    public Object handleJeecgBootException(JeecgBootException e, HttpServletRequest request, HttpServletResponse response) {
        return buildMultiFormatResponse(e.getCode(), e.getMessage(), response);
    }

    /**
     * 处理通用业务异常
     */
    @ExceptionHandler(value = BusinessException.class)
    public Object handleBusinessException(BusinessException e, HttpServletRequest request, HttpServletResponse response) {
        return buildMultiFormatResponse(e.getCode(), e.getMessage(), response);
    }

    /**
     * 处理系统异常
     */
    @ExceptionHandler(value = Exception.class)
    public Object handleException(Exception e, HttpServletRequest request, HttpServletResponse response) {
        log.error("系统异常", e);
        return buildMultiFormatResponse(500, "系统内部错误", response);
    }

    /**
     * 核心方法:根据平台标识构建不同格式的响应
     */
    private Object buildMultiFormatResponse(Integer code, String msg, HttpServletResponse response) {
        PlatformEnum platform = RequestContextHolderUtil.getPlatform();
        // 设置响应ContentType
        response.setContentType(platform.getContentType() + ";charset=UTF-8");

        switch (platform) {
            case CUSTOMER_A_JSON:
                // 客户A:自定义JSON格式
                return buildCustomerAJsonResponse(code, msg);
            case CUSTOMER_B_XML:
                // 客户B:XML格式
                return buildCustomerBXmlResponse(code, msg, response);
            case CUSTOMER_C_CUSTOM:
                // 客户C:自定义协议格式(如固定分隔符的字符串)
                return buildCustomerCCustomResponse(code, msg, response);
            default:
                // 默认:JeecgBoot原生响应格式
                return Result.error(code, msg);
        }
    }

    /**
     * 客户A - 自定义JSON响应
     */
    private Map<String, Object> buildCustomerAJsonResponse(Integer code, String msg) {
        Map<String, Object> response = new HashMap<>();
        response.put("retCode", code); // 客户A要求的码名字段
        response.put("retMsg", msg);   // 客户A要求的消息字段
        response.put("timestamp", System.currentTimeMillis());
        return response;
    }

    /**
     * 客户B - XML响应
     */
    private String buildCustomerBXmlResponse(Integer code, String msg, HttpServletResponse response) {
        StringBuilder xml = new StringBuilder();
        xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        xml.append("<response>");
        xml.append("<code>").append(code).append("</code>");
        xml.append("<message>").append(msg).append("</message>");
        xml.append("</response>");
        return xml.toString();
    }

    /**
     * 客户C - 自定义协议响应(如 "code|msg|timestamp")
     */
    private String buildCustomerCCustomResponse(Integer code, String msg, HttpServletResponse response) {
        return code + "|" + msg + "|" + System.currentTimeMillis();
    }
}

4. 扩展说明

  • 如果需要支持更多协议(如 Protobuf、FastJSON2 等),只需在PlatformEnum中新增枚举值,在buildMultiFormatResponse中新增分支即可;
  • 可以将响应模板配置到 yml 文件中,通过@Value或配置类读取,减少硬编码;
  • 对于复杂的 XML/Protobuf 响应,可结合 JAXB、Protobuf 插件生成对应的实体类,简化序列化操作。

三、实际对接场景示例

假设对接客户 A 的接口请求:

html 复制代码
POST /api/third/customerA/query
Headers:
X-Platform-Code: customerA
Content-Type: application/json

Body:
{"orderNo":"123456"}

当接口抛出异常时,返回的响应格式为客户 A 指定的 JSON:

html 复制代码
{
  "retCode": 500,
  "retMsg": "订单查询失败",
  "timestamp": 1736889600000
}

对接客户 B 的接口请求:

html 复制代码
POST /api/third/customerB/query
Headers:
X-Platform-Code: customerB
Content-Type: application/json

Body:
{"orderNo":"123456"}

异常响应为 XML 格式:

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<response>
  <code>500</code>
  <message>订单查询失败</message>
</response>

四、总结

  1. 核心是通过线程上下文存储当前请求的平台标识,让异常处理器能识别对接方;
  2. 异常处理器通过分支判断实现不同格式 / 协议的响应构建,保持统一入口的同时适配多场景;
  3. 扩展时只需新增枚举值和响应构建方法,符合 "开闭原则",便于后续对接更多客户 / 协议。

这种改造方式既保留了 JeecgBoot 统一异常处理的优势,又解决了多平台对接响应格式不统一的问题

相关推荐
PyHaVolask2 小时前
CSRF跨站请求伪造
android·前端·csrf
程序员海军2 小时前
我的2025:做项目、跑副业、见人、奔波、搬家、维权、再回上海
前端·程序员·年终总结
我来整一篇2 小时前
[Razor] ASP.NET Core MVC 前端组件快速使用总结
前端·asp.net·mvc
P7Dreamer2 小时前
微信小程序处理Range分片视频播放问题:前端调试全记录
前端·微信小程序
RedHeartWWW2 小时前
初识next-auth,和在实际应用中的几个基本场景(本文以v5为例,v4和v5的差别主要是在个别显式配置和api,有兴趣的同学可以看官网教程学习)
前端·next.js
C_心欲无痕2 小时前
前端页面中,如何让用户回到上次阅读的位置
前端
C_心欲无痕2 小时前
前端本地开发构建和更新的过程
前端
Mintopia3 小时前
🌱 一个小而美的核心团队能创造出哪些奇迹?
前端·人工智能·团队管理
蚊道人3 小时前
Nuxt 4 学习文档
前端·vue.js