持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情
一、springboot异常的统一处理
之前在spirngmvc的文章中有介绍使用@ControllerAdvice注解,本篇文章着重介绍springboot的异常处理机制。如果不进行统一异常处理那在和前端同学进行配合的时候就会出现一些问题,如下:
首先在浏览器访问接口地址,并传入错误的参数如下:
但是使用接口测试工具访问传入错误的接口地址如下:
由此可以看出,两种方式返回的错误接口信息是不统一的,这样就给后面对接使用接口的地方造成一些影响。我们之前一直使用Resut:{code,data,message}的方式,那么我们要是可以将接口在任意的情况下都返回这样的数据,就完美了
二、springboot内置错误自动配置类
springboot帮我们内置了错误异常自动配置类:ErrorMvcAutoConfiguration,下面就通过解读这个自动配置类,来了解springboot的统一异常处理。他是怎么在浏览器访问的时候显示错误页面,在ajax请求访问的时候,响应json错误数据。
2-1、ErrorMvcAutoConfiguration的三个重要组件
在ErrorMvcAutoConfiguration自动配置类中,最核心的三个Bean为:
- DefaultErrorAttributes
- BasicErrorController
- DefaultErrorViewResolver
2-1-1、BasicErrorController错误页面控制器
首先进入这个BasicErrorController,看名字知道应该是一个控制器,如下:
在@RequestMapping注解中用到了一个表达式,srver.error.path:${error.path:/error},含义为如果在配置文件中server.error.path未获取到值,则使用${error.path},还没有的话就走/error这样就指定了错误页面。
大概处理机制如下:
客户端发送请求到DispatcherServlet进行处理,然后跳转到指定的handler处理方法,如果遇到异常则会转发/error的请求再次回到中央调度器里面,然后再将这个/error发送到统一异常处理BaseErrorController中进行处理,这样我们一般遇到的4xx/5xx等都就会交由这个统一异常控制器来进行处理。
2-1-1-1、BasicErrorController中的两个RequestMapper
如下在这个BasicErrorController中有两个requestMapping,那这两个是怎么处理的呢?
- 当使用浏览器发送请求时 请求头是Accept:text/html
- 所以如果是浏览器请求会交给errorHtml方法处理
- 那除了text/html的其他请求都会交给error方法处理
如果使用Accept:text/html,走errorHtml方法的时候,怎么去定制它的返回页面呢?下面继续看errorHtml这个方法。
2-1-1-1-1、通过debug的方式解读errorHtml方法
如果在看代码无法看懂的情况下,可以通过debug的方式看一下代码的流程及作用。下面就在errorHtml打断点的方式看下程序是如何运行的。
首先获得了HttpStatus,具体值如下:
然后看下model,获取到了如下相关信息:
而model是通过getErrorAttributes方法获得的,我们就可以得知,这个方法就是用来获取异常相关信息的方法。
2-1-1-1-1-1、resolveErrorView获得异常视图
下面就是获得了ModelAndView modelAndView=resolveErrorView(),这就可以得知是在resolveErrorView方法进行视图解析的,下面进入这个方法。
通过下图,我们就可以到,DefaultErrorViewResolver就是解析错误视图页面的
然后又通过resolver.resolveErrorView进行是视图解析,再进入这个方法
然后又接着调用了resolve方法
通过viewName就获得了errorViewName的值
接着去视图模板中,看errorViewName是否匹配到(先从模板视图去解析(由于没有配置模板视图所以并不解析出来))
如果匹配到则返回ModelAndView,未匹配到则会进入resolveResource方法
进入方法后就会迭代四个根目录路径,找对应viewName的页面,如下:
因为在项目中没有创建这个错误的html,因此最终此方法返回null
后面再进行SERIES_VIEWS.containsKey(status.series())判断的时候,SERIES_VIEWS值如下:
status.series的值如下
这样条件就成立,然后继续走resolve方法,和上面走过的逻辑一致,就不继续往下走了
通过上面代码的逐步调试,就可以得知,如果想自定义错误页面就可以在如下目录中创建错误页面即可,在查找的时候错误页面会拼接:/error/400.html因此在四个目录下还需要创建error文件。
2-1-1-1-2、通过debug的方式解读error方法
通过上面的调试,已经了解错误页面跳转的实现方式了,下面就看一下agax请求是如何处理的,这次就需要看error方法了。了解了这个方法,看它是怎么返回json数据的, 从而要定制自己的json数据
这个方法只有ajax请求才会进入,因此我们这边用一个chrome插件进行请求,如下:
这就可以进入error方法了,进入之后获得的HttpStatus值为
因为状态不是CONTENT,因此下面进入到getErrorAttributes方法,运行之后获得body的值,可以看到body的值和我们通过ajax请求的值返回的值一样,因此可以知道getErrorAttributes即为获得错误信息的核心方法,如下:
然后再查看getErrorAttributeOptions方法,是如何动态控制返回的异常信息的,熟悉如下:
可以看到这个方法里面有四个if判断,这四个判断可以通过application.properties中机型配置,来控制错误信息的返回内容,如下:
2-1-2、创建自定义错误页面
访问测试,就可以跳转到我们自定的错误页面了,如下:
2-2、通过上面的源码阅读,定制自己的异常处理类
首先创建自己的异常处理Controller,并且和系统内置的BasicErrorController一样,继承AbstractErrorController。
2-2-1、创建自己的异常Controller
2-2-1-1、重写构造函数
创建了MyErrorController并继承AbstractErrorController,同时将构造函数重写一下。
2-2-1-2、拷贝errorHtml和error两个方法
上面文章提到,处理页面请求和ajax请求的两个方法,因为我们要自己实现,因此也需要将那两个方法重写一下,首先将那两个方法拷贝到自己的Controller中
2-2-1-3、自定义getErrorAttributeOptions方法
上图中,可以看到getErrorAttributeOptions因为未拷贝这个方法,当前方法就报错了,上面提到getErrorAttributeOptions这个方法的作用,就是为了控制返回那些异常信息字段的,而我们自己的异常类返回的是固定的,因此我们自己重写这个方法,如下:
通过这个方法,异常信息就会全部返回这三个信息了。
2-2-1-4、error方法返回值修改
因为我们定义了自己的返回Result类,因此这个地方我们根据Result进行一下处理,如下:
2-2-1-4-1、测试一下返回值
做测试访问之前,需要先给我们自定义的Controller加上@Controller以及@RequestMapping("/error")注解
其中@Controller为了将自定义类交由IOC容器管理,并且我们自定义这个类有页面访问和ajax访问同时支持。
其中@RequestMapping("/error"),主要为了接收返回错误请求,这样所有/error的请求就可以到我们自定义这个Controller中了。
另外需要给error方法添加@ResponseBody,让方法返回Json数据。
测试访问结果:
三、通过注解@ControllerAdvice实现统一异常处理
这个地方就不再叙述了,之前在springmvc的文章中有介绍,可以去看下我之前的文章juejin.cn/post/708521…
通过以上统一异常处理,我们就不需要在dao/service/controller层进行try-catch处理,全部交由统一异常处理来处理,当然如果因为某些业务需要在try-catch中处理,还可以添加try-catch。
到此统一异常处理就结束了。