SpringBoot项目rest接口的全局异常处理

SpringBoot项目rest接口的全局异常处理

当使用SpringBoot开发api接口时,常常需要和前端约定项目的返回码,返回体等数据,同时还要定义后台的异常处理时的返回逻辑。spring web模块提供了全局异常处理的方法,即ControllerAdvice 和 RestControllerAdvice ,由于本文讲述的是rest接口的异常处理,这里使用的是 RestControllerAdvice来做 rest接口的全局异常处理。

物料准备:

1.一个全局异常处理类

2.一个自定义的通用业务异常类

3.一个公共的返回体泛型类

4.系统返回码枚举类

全局异常处理类RestGlobalExceptionHandler.java

package cn.ath.knowwikibackend.sys;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolationException;

/**
 * 根据需要,此处可以全局处理
 * 各种异常应该返回的 code/msg信息
 */
@Slf4j
@RestControllerAdvice(annotations = RestController.class)
public class RestGlobalExceptionHandler {
    /**
     * 运行时业务异常
     *
     * @param e APIException
     * @return String
     */
    @ExceptionHandler({APIException.class})
    public ResultVO<String> APIExceptionHandler(APIException e) {
        // 注意这里返回类型是自定义响应体
        return new ResultVO<>(e.getCode(), e.getMsg());
    }

    /**
     * @param e HttpMessageNotReadableException
     * @return ResultVO
     * @RequestBody @Valid put/post请求体缺少异常
     */
    @ExceptionHandler({HttpMessageNotReadableException.class})
    public ResultVO reqBodyIsNullException(HttpMessageNotReadableException e) {
        log.error("requestBody请求体异常",e);
        return new ResultVO(ResultCodeEnum.REQ_BODY_IS_NULL, e.getMessage());
    }
    
       /**
     * 对 jsr303 参数校验失败抛出的MethodArgumentNotValidException全局处理
     *
     * @param e MethodArgumentNotValidException
     * @return String
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public ResultVO<String> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e)     {
        log.error("自动抛的MethodArgumentNotValidException",e);
        // 从异常对象中拿到ObjectError对象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        log.error("objectError:{}", JSONObject.toJSONString(objectError, true));
        String field = String.valueOf(JSONPath.eval(objectError, "$.field"));
        log.error("field:{}", field);
        String objName = objectError.getObjectName();
        System.err.println(objName);
        // 然后提取错误提示信息进行返回
        return new ResultVO<>(ResultCodeEnum.VALIDATE_FAILED,
                objName + "." + field + objectError.getDefaultMessage());
    }
    
    /**
     * 处理 ArithmeticException 类的异常 (如除0异常)
     */
    @ExceptionHandler({ArithmeticException.class})
    public ResultVO arithmeticExceptionHandler(ArithmeticException e) {
        return new ResultVO(ResultCodeEnum.ARITHMETIC_FAILED, e.getMessage());
    }

    /**
     * 手动抛出的或 Assert断言的 非法参数异常
     *
     * @param e IllegalArgumentException
     * @return ResultVO
     */
    @ExceptionHandler({IllegalArgumentException.class})
    public ResultVO illegalArgumentExceptionHandler(IllegalArgumentException e) {
        log.error("人工抛出异常的IllegalArgumentException",e);
        return new ResultVO<>(ResultCodeEnum.IllEGAL_ARG_EXCEPTION,
                e.getMessage());
    }
    
    /**
     * 处理 数组越界 异常
     */
     @ExceptionHandler({ArrayIndexOutOfBoundsException.class})
    public ResultVO arrayIndexOutOfBoundsExceptionHandler(ArrayIndexOutOfBoundsException e) {
        return new ResultVO(ResultCodeEnum.ARRAY_INDEX_OUT_OF_BOUNDS_FAILED, e.getMessage());
    }
    
    /**
     * Validated全局自动校验,要@Validated加在Controller类名上,同时手动使用BindingResult
     */
    @ExceptionHandler({ConstraintViolationException.class})
    public ResultVO constraintViolationExceptionHandler(ConstraintViolationException e) {
        log.error("Validated全局自动校验出的异常",e);
        //校验框架会自动返回多条约束提示信息的拼接
        //格式如 test1234.req.company.name: 长度需要在4-30之间,test1234.req.account: 长度需要在2-30之间
        //(逗号拼接的字符串  接口方法名.参数名.子参数对象.子参数属性:描述)
        return new ResultVO<>(ResultCodeEnum.VALIDATE_FAILED,
                e.getMessage());
    }
    
    /**
     * 未知异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler({Exception.class})
    public ResultVO otherExceptionHandle(Exception e) {
        log.error("otherExceptionHandle", e);
        return new ResultVO(ResultCodeEnum.ERROR, e.getMessage());
    }
    
}

通用业务异常类APIException.java

package cn.ath.knowwikibackend.sys;

import lombok.Getter;

@Getter
public class APIException extends RuntimeException {
    int code;
    String msg;

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

    public APIException(String msg) {
        this(1001, msg);
    }

    public APIException() {
        this(ResultCodeEnum.FAILED.getCode(), ResultCodeEnum.FAILED.getMsg());
    }

    public APIException(ResultCodeEnum resultCodeEnum){
        this(resultCodeEnum.getCode(),resultCodeEnum.getMsg());
    }
}

公共返回体泛型类ResultVO.java

package cn.ath.knowwikibackend.sys;

import cn.hutool.core.date.DateTime;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class ResultVO<T> {
    int code;
    String msg;
    T content;
    Long timestamp;
    String traceDateTime;
    public ResultVO(int code,String msg,T content){
        this.code = code;
        this.msg = msg;
        this.content = content;
        this.timestamp = System.currentTimeMillis();
        this.traceDateTime = DateTime.now().toString();
    }

    public ResultVO(int code,String msg){
        this.code = code;
        this.msg = msg;
        this.content = null;
        this.timestamp = System.currentTimeMillis();
        this.traceDateTime = DateTime.now().toString();
    }

    public ResultVO(ResultCodeEnum resultCode, T content) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
        this.content = content;
        this.timestamp = System.currentTimeMillis();
        this.traceDateTime = DateTime.now().toString();
    }

    public ResultVO(T content){
        this(ResultCodeEnum.SUCCESS, content);
    }
}

系统返回码枚举类ResultCodeEnum.java

package cn.ath.knowwikibackend.sys;

import lombok.Getter;

@Getter
public enum ResultCodeEnum {
    SUCCESS(0, "操作成功"),
    VERITY_GEN_ERROR(9801,"验证码生成失败"),
    VERITY_IDENTITY_ERROR(9803,"验证码验证失败"),
    TOKEN_NULL(9777, "请求头里没有token"),
    TOKEN_TIME_OUT(9776, "token已过期,请重新登录"),
    TOKEN_USER_ALREADY_SIGN(9775, "token用户已登录,请退出重登后再请求本接口"),
    FAILED(9500, "响应失败"),
    BUSY(9600, "系统繁忙"),
    ACCOUNT_PASS_FAILED(9301, "密码错误"),
    ACCOUNT_NOT_EXIST(9302, "用户名不存在"),
    ACCOUNT_DISABLED(9303, "用户没有权限/已禁用"),
    NOT_LOGIN(9403,"未登录"),
    REQ_BODY_IS_NULL(9995,"requestBody请求体异常"),
    VALIDATE_FAILED(9996, "参数校验异常"),
    ARITHMETIC_FAILED(9997, "算术异常"),
    ARRAY_INDEX_OUT_OF_BOUNDS_FAILED(9998, "数组越界异常"),
    USER_IS_USING(7701,"目标用户正在使用中"),
    IllEGAL_ARG_EXCEPTION(9988,"非法参数异常"),
    ERROR(9999, "未知错误");

    private int code;
    private String msg;

    ResultCodeEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ThinkPet

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

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

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

打赏作者

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

抵扣说明:

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

余额充值