SpringBoot | 统一异常处理

1、定义返回结构

/**
 * 返回结果实体类
 */
@Getter
public class R implements Serializable {

    private int code;
    private String message;
    private Object data;

    private R setResult(int code, String message, Object data) {
        this.code = code;
        this.message = message;
        this.data = data;
        return this;
    }

    public static final int SUCCESS_CODE = ResponseCode.SUCCESS.getCode();
    public static final String SUCCESS_MESSAGE = ResponseCode.SUCCESS.getMessage();
    public static final int ERROR_CODE = ResponseCode.ERROR.getCode();
    public static final String ERROR_MESSAGE = ResponseCode.ERROR.getMessage();

    public R result(int code, String message, Object data) {
        return setResult(code, message, data);
    }

    public static R ok() {
        return new R().result(SUCCESS_CODE, SUCCESS_MESSAGE, new JSONObject());
    }

    public static R ok(Object data) {
        return new R().result(SUCCESS_CODE, SUCCESS_MESSAGE, data);
    }

    public static R okList(Object data) {
        JSONObject result = new JSONObject();
        result.put("list", data);
        return new R().result(SUCCESS_CODE, SUCCESS_MESSAGE, result);
    }

    public static R error() {
        return new R().result(ERROR_CODE, ERROR_MESSAGE, new JSONObject());
    }

    public static R error(ResponseCode resultEnum) {
        return new R().result(resultEnum.getCode(), resultEnum.getMessage(), new JSONObject());
    }

    public static R error(String message, Object data) {
        return new R().result(ERROR_CODE, message, data);
    }

    public static R error(int code, String message, Object data) {
        return new R().result(code, message, data);
    }

    public static R error(int code, String message) {
        return new R().result(code, message, new JSONObject());
    }

    public static R error(String message) {
        return new R().result(ResponseCode.ERROR.getCode(), message, new JSONObject());
    }
}

2、定义响应结果枚举类

/**
 * @Author: zhang
 * @Date: 2019/8/2 10:33
 * @Description: 响应结果枚举类
 */
public enum ResponseCode {

    SUCCESS(0, "success"),

    ERROR(500, "服务出错,请联系管理员"),

    USER_NOT_EXIST(101, "该用户不存在!"),

    USER_DISABLE(102, "该用户已停用!"),

    TOKEN_AUTHENTICATION_FAILED(201, "Token认证失败!"),

    TOKEN_EMPTY(202, "Token为空!"),

    MISS_HEADER(400, "请求header出错:"),

    NO_PERMISSION(401, "您没有权限访问!"),

    PARAMETER_ERROR(403, "参数异常:"),

    NOT_FOUND(404, "路径出错:"),

    METHOD_NOT_SUPPORTED(405, "请求方法不支持:");


    private int code;
    private String message;

    ResponseCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

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

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

3、自定义异常类

/**
 * 服务异常类
 *
 */
@Getter
public class ServiceException extends RuntimeException {

    private int code;
    private String message;
    private Object data;

    public ServiceException(Object data, String message) {
        this.code = 500;
        this.message = message;
        this.data = data;
    }

    public ServiceException(Object data, String message, int code) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public ServiceException(String message) {
        this.code = 500;
        this.message = message;
    }

    public ServiceException(String message, int code) {
        this.code = code;
        this.message = message;
    }

    public ServiceException(ResponseCode response) {
        this.code = response.getCode();
        this.message = response.getMessage();
    }

}

4、全局异常捕获

/**
 * @Author: zhang
 * @Date: 2019/3/8 18:22
 * @Description:
 * 当我们写的接口拥有@RequiresRoles, @RequiresPermissions注解时,
 * 如果请求没有带有 token 或者带了 token 但权限认证不通过,
 * 则会报 UnauthenticatedException 异常,
 *  ExceptionController 类对这些异常进行了集中处理
 */
@Slf4j
@RestControllerAdvice
public class ExceptionsHandler {


    /**
     * 捕捉shiro的异常
     *
     * @return
     */
    @ExceptionHandler(ShiroException.class)
    public R handle401() {
        return R.error(ResponseCode.NO_PERMISSION.getCode(),
                ResponseCode.NO_PERMISSION.getMessage());
    }

    /**
     * header错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MissingRequestHeaderException.class)
    public R noHeaher(Throwable ex) {
        return R.error(ResponseCode.MISS_HEADER.getCode(),
                ResponseCode.MISS_HEADER.getMessage() + ex.getMessage());
    }

    /**
     * 请求参数异常
     */
    @ExceptionHandler({HttpMessageNotReadableException.class,
            MissingServletRequestParameterException.class,
            MissingServletRequestPartException.class, BindException.class})
    public R parameterException(Throwable ex) {
        return R.error(ResponseCode.PARAMETER_ERROR.getCode(),
                ResponseCode.PARAMETER_ERROR.getMessage() + ex.getMessage());
    }

    /**
     * 请求路径无法找到异常
     *
     * @return
     */
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException.class)
    public R notFoundException(Throwable ex) {
        return R.error(ResponseCode.NOT_FOUND.getCode(),
                ResponseCode.NOT_FOUND.getMessage() + ex.getMessage());
    }

    /**
     * 请求方法不支持异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R httpRequestMethodNotSupportedException(Throwable ex) {
        return R.error(ResponseCode.METHOD_NOT_SUPPORTED.getCode(),
                ResponseCode.METHOD_NOT_SUPPORTED.getMessage() + ex.getMessage());
    }

    /**
     * 方法参数校验
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        return R.error(ResponseCode.PARAMETER_ERROR.getCode(),
                e.getBindingResult().getFieldError().getDefaultMessage());
    }

    /**
     * 捕捉其他所有异常
     *
     * @param request
     * @return
     */
    @ExceptionHandler(Exception.class)
    public R globalException(HttpServletRequest request, Throwable ex) {
        log.error("系统异常:", ex);
        System.out.println("系统异常 = " + ex);
        return R.error(getStatus(request).value(), ex.getMessage());
    }

    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }

    /**
     * 自定义服务异常
     */
    @ExceptionHandler(ServiceException.class)
    public R serviceException(ServiceException e) {
        log.error("发生业务异常!原因是:{}",e);
        return R.error(e.getCode(), e.getMessage(), e.getData());
    }

}

5、测试异常

    @GetMapping("/test/{num}")
    public R test(@PathVariable int num) {
        if (num == 10){
            throw new ServiceException(ResponseCode.ERROR);
        }
        int result = 10 / num;
        return R.ok(result);
    }
传入2,正常返回 `{"code":0,"message":"success","data":5}`
传入0,异常拦截,返回 `{"code":500,"message":"/ by zero","data":{}}`
传入10,抛出自定义异常,返回 `{"code":500,"message":"服务出错,请联系管理员","data":null}`

参考

SpringBoot优雅的全局异常处理
Spring Boot @RestControllerAdvice 统一异常处理

本文链接: https://jianz.xyz/index.php/archives/82/

1 + 4 =
快来做第一个评论的人吧~