SpringBoot配置全局异常捕获

全局异常捕获返回json格式

前后端分离开发中,后台接口返回给前端的数据一般都是json格式,所以这种情况下,异常捕获之后也是返回前端json格式
1. 自定义异常类
注:
异常类里定义了一个参数为ResultCode 枚举类的构造方法,这种方法可以直接在枚举类里面配置各种状态的code和message,非常直观地就能知道返回的code和message信息,同时枚举类里面的信息也可以复用。

public class MyException extends RuntimeException {
    protected Integer code;
    protected String message;

    public MyException(ResultCode resultCode) {
        this.code = resultCode.code();
        this.message = resultCode.message();
    }
}

2. 异常提示枚举类
简单的配置了几种情况做测试,可以随意添加。

public enum ResultCode {
	/* 成功状态码 */
	SUCCESS(0, "成功"),

	/* 失败状态码 */
	FAIL(200, "失败"),

	/* 系统500错误*/
	SYSTEM_ERROR(10000, "系统异常,请稍后重试"),
	UNAUTHORIZED(10401, "签名验证失败"),
	TEST(500, "测试异常"),

	/* 参数错误:10001-19999 */
	PARAM_IS_INVALID(10001, "参数无效"),

	/* 用户错误:20001-29999*/
	USER_HAS_EXISTED(20001, "用户名已存在"),
	USER_NOT_FIND(20002, "用户名不存在");
	private Integer code;

	private String message;

	ResultCode(Integer code, String message) {
		this.code = code;
		this.message = message;
	}

	public Integer code() {
		return this.code;
	}

	public String message() {
		return this.message;
	}

}

3. 异常信息封装VO类
这里单独定义了一个专门返回异常的VO类,里面有异常的信息,这样如果后台报异常了,返回前端的信息里面能够直观的看到报错的信息。

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ErrorResult {
	/**
	 * 异常状态码
	 */
	private Integer status;

	/**
	 * 用户看得见的异常,例如 用户名重复!!,
	 */
	private String message;

	/**
	 * 异常的名字
	 */
	private String exception;

	/**
	 * 对异常提示语进行封装
	 */
	public static ErrorResult fail(ResultCode resultCode, Throwable e, String message) {
		ErrorResult result = ErrorResult.fail(resultCode, e);
		result.setMessage(message);
		return result;
	}

	/**
	 * 对异常枚举进行封装
	 */
	public static ErrorResult fail(ResultCode resultCode, Throwable e) {
		ErrorResult result = new ErrorResult();
		result.setMessage(resultCode.message());
		result.setStatus(resultCode.code());
		result.setException(e.getClass().getName());
		return result;
	}
}

4. 全局异常捕获类
注:
类上面用了@RestControllerAdvice注解,表示所有的信息都会直接返回前端json格式。
在异常处理里面使用log的方式把出现异常的地址打印出来,能够方便我们及时排查出出现异常的位置。

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler  {
    /**
     * 处理运行时异常
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Throwable.class)
    public ErrorResult handleThrowable(Throwable e, HttpServletRequest request) {
        //TODO 运行是异常,可以在这里记录,用于发异常邮件通知
        ErrorResult error =ErrorResult.fail(ResultCode.SYSTEM_ERROR, e);
        log.error("URL:{} ,系统异常: ",request.getRequestURI(), e);
        return error;
    }
    /**
     *处理@notNull绑定异常
     */
   @ExceptionHandler(BindException.class)
    public ErrorResult exceptionHandler(BindException e, HttpServletRequest request) {
        String failMsg = e.getBindingResult().getFieldError().getDefaultMessage();
        ErrorResult error = ErrorResult.fail(ResultCode.SYSTEM_ERROR, e, failMsg);
        log.error("URL:{} ,绑定异常:{} ", request.getRequestURI(),failMsg);
        return error;
    }

	/**
     * 处理自定义异常
     */
	//捕获指定异常
	@ExceptionHandler(BusinessException.class)
	public ErrorResult handleBusinessException(BusinessException e, HttpServletRequest request) {
        ErrorResult error = ErrorResult.builder().status(e.code)
                .message(e.message)
                .exception(e.getClass().getName())
                .build();
        //在控制台会打印出异常的url地址和详细信息
        log.warn("URL:{} ,业务异常:{}", request.getRequestURI(),error);
        return error;
	}

    /**
     * validator 统一异常封装
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ErrorResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        String msgs = this.handle(e.getBindingResult().getFieldErrors());
        ErrorResult error = ErrorResult.fail(ResultCode.PARAM_IS_INVALID, e,  msgs);
        log.warn("URL:{} ,参数校验异常:{}", request.getRequestURI(),msgs);
        return error;
    }

    private String handle(List<FieldError> fieldErrors) {
        StringBuilder sb = new StringBuilder();
        for (FieldError obj : fieldErrors) {
            sb.append(obj.getField());
            sb.append("=[");
            sb.append(obj.getDefaultMessage());
            sb.append("]  ");
        }
		return sb.toString();
    }

    /**
     * Assert的异常统一封装
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public ErrorResult illegalArgumentException(IllegalArgumentException e, HttpServletRequest request) {
        ErrorResult error = ErrorResult.builder().status(4000)
                .message(e.getMessage())
                .exception(e.getClass().getName())
                .build();
        log.warn("URL:{} ,业务校验异常:{}", request.getRequestURI(),e);
        return error;
    }
}

除了全局异常捕获异常外,也可以在controller里面自定义捕获service里面的异常,拿到异常code和message,然后返回给前端

    @PostMapping("/login")
    @ResponseBody
    public AjaxResult ajaxLogin(String username, String password, Boolean rememberMe) {
        UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
            return success();
        } catch (AuthenticationException e) {
            String msg = "用户或密码错误";
            if (StringUtils.isNotEmpty(e.getMessage())) {
                msg = e.getMessage();
            }
            return error(msg);
        }
    }

你可能感兴趣的:(SpringBoot)