Spring Web开发异常的统一处理

310 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情

前言

本篇文章介绍如何实现全局的统一异常处理并返回统一的接口格式,便于前后端的交互。请观众老爷们多多支持,并在评论区批评指正!

1. 为什么要对异常进行统一处理?

前面我们通过拦截器校验 token 时,对 token 解析或者超时异常只是简单的向前端返回一个错误响应码。

返回简单的响应码不利于前后端的交互,我们希望出现异常时,前端可以跳转到一个异常界面提醒用户(如用户未登录等),那么就需要对异常进行统一处理,我们只需要抛出异常,然后由异常统一处理方案进行处理,将错误封装成 json 返回到响应体中。

2. SpringBoot 中实现异常统一处理

我们在实际项目中 Dao层和 Service层的异常都会抛出到 Controller 层。但是如果我们在 Controller的方法中都添加异常处理会会显得非常繁琐。

SpringMVC为我们提供了统一的异常处理方式。可以把 Controller 层的异常进行统一处理。这样既提高了代码的复用性也让异常处理代码和我们的业务代码解耦。

一种是实现 HandlerExceptionResolver 接口的方式,一种是使用 @ControllerAdvice的方式。

2.1. 实现 HandlerExceptionResolver 接口的方式

  1. 实现 HandlerExceptionResolver 接口
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
}
  1. 重写方法
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
    
    //如果出现异常,就会调用该方法,我们可以在本方法中进行统一的异常处理
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        return null;
    }
}

但是这样返回的是一个 ModelAndView 对象,该对象主要用于服务端渲染的项目,用于向域对象添加数据和要跳转的页面。而我们前后端项目中,服务器只是用来提供接口的,所以这种方式已经不合适了。

2.2. @ControllerAdvice

  1. 创建类加上 @ControllerAdvice注解进行标识
@ControllerAdvice
public class ApplicationExceptionHandler{
    
}
  1. 定义异常处理方法,使用 @ExceptionHandler标识可以处理的异常。

注意这个异常处理方法我们可以自定义,也就是说我们可以指定返回值为我们封装的接口格式 ResponseResult,并且在接口上标注 @ResponseBody注解,然后在方法中对异常进行处理,封装异常信息并返回,前端就可以接收到返回异常信息了。

@ControllerAdvice
public class ApplicationExceptionHandler {

    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public ResponseResult handlerException(Exception e){ //e 接收异常信息
        //获取异常信息,存放到 ResponseResult 的 msg 属性中
        String message = e.getMessage();
        //把 ResponseResult 作为返回值返回,要求到时候转换 json 存入到响应体中
        return new ResponseResult<>(300, message);
    }

}

注意这里的状态码 300只是模拟一下,实际业务开发中,我们不同的异常对应不同的响应码,我们需要把异常状态码封装成一个枚举类,使其语义化,这样更加清晰。

  1. 注意我们不需要将该对象注入到容器中,因为 @ControllerAdvice中包含了 @Componet

2.3. 如何触发异常统一处理机制

我们只需要手动抛出异常,异常统一处理机制就可以捕获到抛出的异常自动返回给前端了,而不需要我们手动响应。

测试

重启我们的项目,访问非登录接口,不携带 token。然后就提示封装后的异常信息了。