目录
2、@ExceptionHandler和@ControllerAdvice
1、问题
在Controller接口中,程序运行时难免会有很多异常。此时,最简单的做法,是通过try-catch进行异常捕获,将异常信息转换成简洁友好的信息返回给前端。但这种方式,很难看且冗余(每个异常的地方都需要try-catch),如下所示:
@GetMapping("/hello")
public ResponseResult helloWorld() {
try {
// 省略业务代码
} catch (NullPointerException ex) {
log.error(ex.getMessage());
} catch (Exception ex) {
log.error(ex.getMessage());
}
return ResponseResult.success("hello world");
}
当Controller接口特别多的时候,代码中就会充斥大量try-catch代码。
2、@ExceptionHandler和@ControllerAdvice
针对以上情况,可使用@ExceptionHandler和@ControllerAdvice进行Controller接口的全局异常拦截。
- @ExceptionHandler,作用于方法,声明注解方法是一个异常处理方法。
- @ControllerAdvice,作用于类,用于定义一个全局异常处理类。
2.1、Controller层全局异常声明
@ControllerAdvice
@ResponseBody // 将异常以json格式返回给客户端
public class DefaultExceptionHandler {
// 捕获Controller抛出的空指针异常
@ExceptionHandler(NullPointerException.class)
public ResponseResult handlerNullException() {
// 封装异常信息为RespinseResult对象
return ResponseResult.fail("你遇到了空指针异常");
}
// 兜底异常,处理所有代码中未考虑到的异常
@ExceptionHandler(Exception.class)
public ResponseResult handlerException() {
// 封装异常信息为RespinseResult对象
return ResponseResult.fail("这是一个最大的异常");
}
}
2.2、测试
@GetMapping("/hello")
public ResponseResult helloWorld() {
String key = null;
System.out.println(key.toLowerCase(Locale.ROOT));
return ResponseResult.success("hello world");
}
@GetMapping("/hello1")
public ResponseResult helloWorld1() {
int i = 1/0;
return ResponseResult.success("hello world");
}
2.3、测试结果
2.4、小结
优点:将 Controller 层的异常进行统一处理,减少try-catch模板代码,减少编码量,提升扩展性和可维护性。
缺点:只能处理 Controller 层未捕获的异常,无法处理 Interceptor(拦截器)层的异常以及Spring 框架层的异常。
3、ErrorController
@ExceptionHandler和@ControllerAdvice只能处理Controller中未捕获的异常,而对于Spring框架层或者拦截器的异常,却无能为力。此时,可以通过继承ErrorController类来捕获全局异常。
3.1、场景
比如,当我们访问一个不存在的url时,第二小节中的Controller异常处理机制是处理不了的,因为该请求根本就没有进入Controller。此时,页面上就可能返回如下错误:
这种错误信息,极不友好。原因在于当springboot项目出现异常时,默认会跳转到SpringBoot自带的"/error"控制器,而/error默认是由BasicErrorController进行处理。
- BasicErrorController是一个控制器,继承自ErrorController,对/error进行处理;
- BasicErrorController根据Accept头的内容,输出不同格式的错误响应。比如针对浏览器的请求生成html页面,针对其它请求生成json格式的返回。
当然,我们也可以通过自定义ErrorController子类来处理非Controller异常。
3.2、自定义ErrorController子类
@RestController
public class WebErrorController implements ErrorController {
@RequestMapping("/error")
public ResponseEntity error(HttpServletRequest request) {
return ResponseEntity.ok(ResponseResult.fail("这是一个最外层的异常"));
}
}
4、总结
1、Controller中的异常,可通过@ControllerAdvice和@ExceptionHandler来处理,@ExceptionHandler可捕获到Controller中抛出的指定异常。
2、Controller层外的异常(框架层),或者是@ExceptionHandler遗漏的异常,可以通过自定义ErrorController进行统一拦截处理。
3、ErrorController可对全局错误进行处理,但是其获取不到异常的具体信息,不能直接判断异常类型,但是可以通过如下方式进行异常判断。
@RequestMapping("/error")
public ResponseEntity error(HttpServletRequest request) {
if(request.getAttribute("javax.servlet.error.exception") != null){
String msg = "";
Exception exception = (Exception)request.getAttribute("javax.servlet.error.exception");
switch (exception.getCause().getClass().getName()){
case "java.lang.NullPointerException" :
msg = "这个是最外层捕获的空指针异常";
break;
// TODO 这里可以添加其他异常类型
default:
msg = exception.getCause().getMessage();
}
return ResponseEntity.ok(ResponseResult.fail(msg));
}
return ResponseEntity.ok(ResponseResult.fail("这是一个最外层的异常"));
}
以上内容为个人学习理解,如有问题,欢迎在评论区指出。