SpringBoot优雅的全局异常处理(二)——@RestControllerAdvice@ExceptionHandler方式

前言

SpringBoot版本:2.1.9.RELEASE
Mybatis Plus版本:3.3.0

上篇文章主要是讲的SpringBoot非web项目的全局异常处理方式,现在来讲一下web注解的方式。

一、新建自定义异常处理类GlobalExceptionHandler。

package com.junya.util.exception;

import com.junya.util.result.ResponseMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;

/**
 * @program sweet-dream
 * @description: 自定义全局异常处理类
 * @author: zhangchao
 * @date: 2020/02/26 22:05
 * @since: 1.0.0
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 全局异常拦截处理
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler
    public ResponseMessage exceptionHandler(HttpServletRequest request, Exception e){
        String requestUrl = request.getRequestURL().toString();//得到请求的URL地址
        String queryString = request.getQueryString();//得到请求的URL地址中附带的参数
        String remoteAddr = request.getRemoteAddr();//得到来访者的IP地址
        String method = request.getMethod();//得到请求URL地址时使用的方法
        //异常堆栈信息放入日志中
        String api = requestUrl.replaceFirst("[\\s\\S]*\\d(.+)","$1");
        ExceptionUtil.getFullStackTrace(e,api);
        ResponseMessage responseMessage = new ResponseMessage();
        String eStr = e.toString();
        for (GlobalErrorCodeEnum global : GlobalErrorCodeEnum.values()){
            if (eStr.toUpperCase().contains(global.toString().replaceAll("_",""))){
                responseMessage.setCode(global.getCode());
                responseMessage.setMsg(global.getMsg());
                break;
            }
        }
        if (eStr.contains("GlobalException")){
            for (GlobalErrorCodeEnum statusCodeEnum : GlobalErrorCodeEnum.values()){
                if (eStr.contains(statusCodeEnum.getMsg())){
                    if (eStr.contains("Data too long for column")){
                        String field = eStr.replaceAll("[\\s\\S]+Data too long for column '(.+)' at[\\s\\S]+","$1");
                        responseMessage.setCode(statusCodeEnum.getCode());
                        responseMessage.setMsg(statusCodeEnum.getMsg()+",字段长度超过限制:"+field);
                        break;
                    }
                    if (eStr.contains("Unknown column")){
                        String field = eStr.replaceAll("[\\s\\S]+Unknown column '(.+)' in[\\s\\S]+","$1");
                        responseMessage.setCode(statusCodeEnum.getCode());
                        responseMessage.setMsg(statusCodeEnum.getMsg()+",未知的字段:"+field);
                        break;
                    }
                    if (eStr.contains("doesn't have a default value")){
                        String field = eStr.replaceAll("[\\s\\S]+Field '(.+)' doesn't have a default value[\\s\\S]+","$1");
                        responseMessage.setCode(statusCodeEnum.getCode());
                        responseMessage.setMsg(statusCodeEnum.getMsg()+",此字段必须有值:"+field);
                        break;
                    }
                    responseMessage.setCode(statusCodeEnum.getCode());
                    responseMessage.setMsg(statusCodeEnum.getMsg());
                    break;
                }
            }
        }
        if ("".equals(responseMessage.getCode()) || "".equals(responseMessage.getMsg())){
            responseMessage.setCode(GlobalErrorCodeEnum.UNKNOWN_EXCEPTION.getCode());
            responseMessage.setMsg(GlobalErrorCodeEnum.UNKNOWN_EXCEPTION.getMsg());
        }
        responseMessage.setData("{请求PATH:"+api+",请求参数:"+queryString+",来访者IP:"+remoteAddr+",请求方法类型:"+method+"}");
        return responseMessage;
    }

}

二、 自定义异常类,继承RuntimeException。

package com.junya.util.exception;

/**
 * 自定义全局异常类
 *
 * @author ZhangChao
 * @date 2019/10/18 13:23
 * @since 1.0.0
 */
public class GlobalException extends RuntimeException {
    private static final long serialVersionUID = 6958499252468627021L;

    /**
     * 错误码
     */
    private String code;

    public GlobalException(String code, String msg) {
        super(msg);
        this.code = code;
    }

    public GlobalException(GlobalErrorCodeEnum errorCode) {
        super(errorCode.getMsg());
        this.code = errorCode.getCode();
    }

    public GlobalException(GlobalErrorCodeEnum errorCode, String msg){
        super(errorCode.getMsg()+msg);
        this.code = errorCode.getCode();
    }

    public GlobalException(String code, String msg, Throwable throwable){
        super(msg,throwable);
        this.code = code;
    }
    public GlobalException(GlobalErrorCodeEnum errorCode, Throwable throwable){
        super(errorCode.getMsg(),throwable);
        this.code = errorCode.getCode();
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

三、异常枚举类

package com.junya.util.exception;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 自定义全局异常枚举
 *
 * @author ZhangChao
 * @date 2019/10/18 13:15
 * @since 1.0.0
 */
public enum GlobalErrorCodeEnum {

    /** 未知异常 */
    UNKNOWN_EXCEPTION("CSEP001","未知异常,请联系管理员"),
    /** 系统错误 */
    SYSTEM_ERROR("CSEP002","系统错误"),
    /** 类转换异常 */
    CLASS_CAST_EXCEPTION("CSEP003","类型强制转换异常"),
    /** 算术条件异常 */
    ARITHMETIC_EXCEPTION("CSEP004","算术条件异常"),
    /** 空指针异常 */
    NULL_POINTER_EXCEPTION("CSEP005","空指针异常"),
    /** 字符串转换为数字异常 */
    NUMBER_FORMAT_EXCEPTION("CSEP006","字符串转换为数字异常"),
    /** 数组下标越界异常 */
    ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION("CSEP007","数组下标越界异常"),
    /** 方法未找到异常 */
    NO_SUCH_METHOD_EXCEPTION("CSEP008","方法未找到异常"),
    /** 未找到类定义错误 */
    NO_CLASS_DEF_FOUND_ERROR("CSEP009","未找到类定义错误"),
    /** 未找到类定义错误 */
    CLASS_NOT_FOUND_EXCEPTION("CSEP010","找不到类异常"),
    /** 索引越界异常 */
    INDEX_OUT_OF_BOUNDS_EXCEPTION("CSEP011","索引越界异常"),
    /** 数据库异常 */
    DB_ERROR("CSEP012","数据库异常")
    ;

    private String code;
    private String msg;

    GlobalErrorCodeEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

}

四、异常工具类

package com.junya.util.exception;

import com.junya.util.result.ResponseMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * 保存异常堆栈信息
 * -- 两种方式 Throwable和Exception
 *
 * @author ZhangChao
 * @date 2019/9/6 9:56
 * @since 1.0.0
 */
public class ExceptionUtil {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionUtil.class);

    /**
     * 异常堆栈信息保存到日志中
     * @param ex
     */
    public static void getFullStackTrace(Exception ex) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PrintStream pout = new PrintStream(out);
        ex.printStackTrace(pout);
        String ret = new String(out.toByteArray());
        pout.close();
        try {
            out.close();
        } catch (Exception e) {
        }
        ex.printStackTrace();
        logger.error(ret);
    }

    /**
     * 异常堆栈信息保存到日志中,并保存消息记录
     * @param ex
     * @param msg
     */
    public static void getFullStackTrace(Exception ex, String msg) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PrintStream pout = new PrintStream(out);
        ex.printStackTrace(pout);
        String ret = new String(out.toByteArray());
        pout.close();
        try {
            out.close();
        } catch (Exception e) {
        }
        ex.printStackTrace();
        logger.error("出现异常==>: "+msg+" ==> \n"+ret);
//        logger.error(ret);
    }

    /**
     * 参数是Throwable
     * @param e
     * @return
     */
    public static void getFullStackTrace(Throwable e){
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        try {
            e.printStackTrace(pw);
            pw.flush();
            sw.flush();
            logger.error(sw.toString());
        } finally {
            pw.close();
        }
    }

}

五、测试
在这里插入图片描述

总结

通过@RestControllerAdvice@ExceptionHandler方式还是很简单的。

PS: 更多技术干货,欢迎大家来我的个人博客 yak33的技术人生

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值