基于Spring Boot+Vue的博客系统 10——后端异常的设计与处理

废弃说明:
这个专栏的文章本意是记录笔者第一次搭建博客的过程,文章里里有很多地方的写法都不太恰当,现在已经废弃,关于SpringBoot + Vue 博客系列,笔者重新写了这个系列的文章,不敢说写的好,但是至少思路更加清晰,还在看SpringBoot + Vue 博客系列文章的朋友可以移步:https://blog.csdn.net/li3455277925/category_10341110.html,文章中有错误的地方或者大家有什么意见或建议都可以评论或者私信交流。

处理在Controller产生的异常
  • 定义com.qianyucc.blog.exception.ApiException类,表示在Controller产生异常的超类
package com.qianyucc.blog.exception;

import lombok.*;

/**
 * @author lijing
 * @date 2019-10-12 10:25
 * @description api异常的超类
 */
@Data
@NoArgsConstructor
public class ApiException extends RuntimeException {
    protected Long errorCode;
    protected Object data;

    public ApiException(Long errorCode, String message, Object data, Throwable e) {
        super(message, e);
        this.errorCode = errorCode;
        this.data = data;
    }

    public ApiException(Long errorCode, String message, Object data) {
        this(errorCode, message, data, null);
    }

    public ApiException(Long errorCode, String message) {
        this(errorCode, message, null, null);
    }

    public ApiException(String message, Throwable e) {
        this(null, message, null, e);
    }

    public ApiException(Throwable e) {
        super(e);
    }
}
  • 自定义枚举类型com.qianyucc.blog.model.enums.ErrorCode,用来表示全局错误码
package com.qianyucc.blog.model.enums;

/**
 * @author lijing
 * @date 2019-10-12 10:30
 * @description 全局错误码
 */
public enum ErrorCode {
    TOKEN_EXPIRED(10001L),
    UNKNOW_ERROR(50000L),
    SUCCESS(20000L),
    RESOURCES_NOT_FOUND(40004L);
    private Long code;

    ErrorCode(Long code) {
        this.code = code;
    }

    public Long getCode() {
        return code;
    }

    public void setCode(Long code) {
        this.code = code;
    }
}
  • 自定义异常com.qianyucc.blog.exception.ApiTokenExpiredException,用来处理token过期异常
package com.qianyucc.blog.exception;

import com.qianyucc.blog.model.enums.*;

/**
 * @author lijing
 * @date 2019-10-08 19:55
 * @description token过期异常
 */
public class ApiTokenExpiredException extends ApiException {
    public ApiTokenExpiredException() {
        super(ErrorCode.TOKEN_EXPIRED.getCode(),"token已过期");
    }
}
  • 新建com.qianyucc.blog.advice.ApiExceptionHandlerAdvice类,用来处理全局异常,只要程序出现异常都会走以下代码,这里自定义了一个RespDataVO类,用于封装返回信息
package com.qianyucc.blog.advice;

import com.qianyucc.blog.exception.*;
import com.qianyucc.blog.model.enums.*;
import com.qianyucc.blog.model.vo.*;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.*;

import javax.servlet.http.*;
import java.time.*;

/**
 * @author lijing
 * @date 2019-10-12 10:37
 * @description 全局异常处理
 */
@ControllerAdvice
class ApiExceptionHandlerAdvice {
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseEntity<RespDataVO> exception(Exception exception, HttpServletResponse response) {
        RespDataVO respDataVO = new RespDataVO();
        if (exception instanceof ApiException) {//api异常
            ApiException apiException = (ApiException) exception;
            respDataVO.setCode(apiException.getErrorCode());
            if (apiException instanceof ApiTokenExpiredException) {
                response.setStatus(403);
            } else {
                response.setStatus(500);
            }
        } else if (exception instanceof NoHandlerFoundException) {// 没有匹配的api
            respDataVO.setCode(ErrorCode.RESOURCES_NOT_FOUND.getCode());
            response.setStatus(404);
        } else {//未知异常
            respDataVO.setCode(ErrorCode.UNKNOW_ERROR.getCode());
            response.setStatus(500);
        }
        respDataVO.setTimestamp(Instant.now().toEpochMilli());
        respDataVO.setMsg(exception.getMessage());
        ResponseEntity<RespDataVO> responseEntity = new ResponseEntity<>(respDataVO, HttpStatus.valueOf(response.getStatus()));
        return responseEntity;
    }
}

这里注意token过期我们返回的状态码为403,而不是500,因为我们在前端会拦截状态码问为500的响应,并跳转到error500界面。

package com.qianyucc.blog.model.vo;

import lombok.*;
import java.time.*;

/**
 * @author lijing
 * @date 2019-10-12 10:40
 * @description 封装返回信息
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RespDataVO {
    private Long code;
    private String msg;
    private Object data;
    private Long timestamp;

    public static RespDataVO ok(String msg, Object data) {
        return new RespDataVO(ErrorCode.SUCCESS.getCode(), msg, data, Instant.now().toEpochMilli());
    }

    public static RespDataVO ok(String msg) {
        return new RespDataVO(ErrorCode.SUCCESS.getCode(), msg, null, Instant.now().toEpochMilli());
    }

    public static RespDataVO error(String msg, Object obj) {
        return new RespDataVO(ErrorCode.UNKNOW_ERROR.getCode(), msg, obj, Instant.now().toEpochMilli());
    }

    public static RespDataVO error(String msg) {
        return new RespDataVO(ErrorCode.UNKNOW_ERROR.getCode(), msg, null, Instant.now().toEpochMilli());
    }
}
  • 这样我们在UserController里面发现token过期的时候就可以抛出异常

抛出异常

  • application.yml里面添加如下配置:

添加异常配置

  • 测试:
  • 启动项目,打开浏览器控制台可以看到有在浏览器缓存中token的存在,所以在项目加载的时候会向后端发送请求校验token是否过期。
    存在token
  • 打开NetWork可以看到getUserInfo的状态码为500,打开可以看到服务端返回信息如下:
    服务端返回信息
    当我们输入一个不存在的地址时:
    地址不存在
处理在业务层产生的异常
  • 由于业务层异常不用将信息返回至前台,所以不用定义错误码,于是新建com.qianyucc.blog.exception.TokenInvalidException类,来处理业务层的token无效异常
package com.qianyucc.blog.exception;

/**
 * @author lijing
 * @date 2019-10-12 11:34
 * @description 业务层token无效异常
 */
public class TokenInvalidException extends RuntimeException {
    public TokenInvalidException() {
        super("非法token!");
    }

    public TokenInvalidException(String message) {
        super(message);
    }
}
  • 这时在UserService中即可抛出异常

抛出异常

  • 测试业务层异常:

编写TestController用来制造一个异常

package com.qianyucc.blog.controller;

import com.qianyucc.blog.model.entity.*;
import com.qianyucc.blog.service.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.web.bind.annotation.*;

/**
 * @author lijing
 * @date 2019-10-12 11:58
 * @description 用户测试的控制器
 */
@RestController
public class TestController {
    @Autowired
    private UserService userService;

    @GetMapping("test")
    public Object test() {
        UserDO abc = userService.getUserInfo("abc");
        return abc;
    }
}

在浏览器里访问http://localhost:8886/test,发现前台可以接受到错误信息

前台错误信息

参考文章:这样设计 Java 异常更优雅,赶紧学!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值