通过RestControllerAdvice和ExceptionHandler实现全局异常捕获

起因:

我么希望通过全局统一的异常处理将自定义错误码以json的形式发送给前端。

步骤:

1.定义一个统一结果返回类BaseRspVo

import com.shizhongcai.business.common.domain.enums.ErrorCodesEnum;
import com.shizhongcai.business.common.exception.BaseException;
import lombok.Data;

@Data
public class BaseRspVo<T> {
    private boolean success;
    private String msg;
    private Integer errorCode;
    private T data;

    public BaseRspVo() {
    }

    public BaseRspVo (T data){
        this.success = true;
        this.msg= ErrorCodesEnum.SUCCESS.getMsg();
        this.errorCode = ErrorCodesEnum.SUCCESS.getCode();
        this.data = data;
    }
    public BaseRspVo (boolean success, String msg, int errorCode) {
        this.success = success;
        this.msg = msg;
        this.errorCode = errorCode;
    }

    public BaseRspVo (boolean success, String msg, int errorCode, T data) {
        this(success,msg,errorCode);
        this.data = data;
    }


    public BaseRspVo(ErrorCodesEnum errorCode) {
        if(errorCode == ErrorCodesEnum.SUCCESS) {
            this.success = true;
        } else {
            this.success = false;
        }
        this.msg = errorCode.getMsg();
        this.errorCode = errorCode.getCode();
    }

    public static BaseRspVo success(Object data){
        return new BaseRspVo(data);
    }
    public static BaseRspVo fail(ErrorCodesEnum errorCode){
        return new BaseRspVo(errorCode);
    }

    public static BaseRspVo fail(BaseException e){
        return new BaseRspVo(false,e.getMsg(), ErrorCodesEnum.DEFAULT_FAIL.getCode());
    }

    public static BaseRspVo fail(int code,String msg){
        return new BaseRspVo(false,msg, code);
    }
    
}

2.定义统一错误枚举类,包含错误码及错误原因

public enum ErrorCodesEnum {

    SUCCESS(10000,"成功"),
    SYS_ERROR(10001,"系统异常,请稍后再试"),
    PARAM_ERROR(10002,"参数异常"),
    NO_HANDLER_FOUND(10003,"路径不存在,请检查路径是否正确"),
    DEFAULT_FAIL(10004,"未知异常,请稍后再试"),
    DECRYPT_ERROR(10005,"解密出错"),
    REQUEST_TOO_FAST(10006,"您的请求太快啦!请在稍后再试"),
    ;

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

    /**
     * 错误描述
     */
    private String msg;

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

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

3.定义基础异常类BaseException

接下来我们需要将业务异常都同意封装到这个异常类中

import com.shizhongcai.business.common.domain.enums.ErrorCodesEnum;
import lombok.Data;

@Data
public class BaseException extends Exception{

    private int code;
    private String msg;


    public BaseException(ErrorCodesEnum errorCode){
        this.code = errorCode.getCode();
        this.msg = errorCode.getMsg();
    }
}

4.创建全局异常处理器GlobalExceptionHandler

@ControllerAdvice可以捕获全局异常 @RestControllerAdvice相对于@ControllerAdvice直接返回JSON @ExceptionHandler 对于捕获的异常进行拦截处理,我们可以通过该注解实现自定义异常处理,配置的 value 指定需要拦截的异常类型。


import com.shizhongcai.business.common.domain.vo.BaseRspVo;
import com.shizhongcai.business.common.domain.enums.ErrorCodesEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

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

@RestControllerAdvice
public class GlobalExceptionHandler {

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

    /**
     * 基础异常捕获
     * @param e
     * @return
     */
    @ExceptionHandler(BaseException.class)
    public BaseRspVo handleTSharkException(BaseException e) {
        logger.error("异常信息:{}",e);
        return BaseRspVo.fail(e);
    }

    /**
     * 未知异常
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public BaseRspVo handleException(Exception e) {
        logger.error("异常信息:{}",e);
        return BaseRspVo.fail(ErrorCodesEnum.DEFAULT_FAIL);
    }

    /**
     * 参数校验异常
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public BaseRspVo handleException(MethodArgumentNotValidException e) {
        logger.error("请求参数不合法:{}",e.getBindingResult());
        //获取所有校验失败项
        List<FieldError> errors = e.getBindingResult().getFieldErrors();
        //处理异常校验项
        Map<String,String> errorMap = new HashMap<>();
        if(!CollectionUtils.isEmpty(errors)) {
            for (FieldError error : errors) {
                errorMap.put(error.getField(),error.getDefaultMessage());
            }
        }
        BaseRspVo baseRspVo =  BaseRspVo.fail(ErrorCodesEnum.PARAM_ERROR);
        baseRspVo.setData(errorMap);
        return baseRspVo;
    }


}