一、粗谈统一异常处理
先看个案例:
1、写了一个接口:
实体类如下:
@Data
public class Emp {
@NotNull(message = "id 不能为 null")
private Integer id;
@NotNull(message = "name 不能为 null")
private String name;
}
接口如下:
@RestController
public class EmpController {
@PostMapping("/emp")
public String createEmp(@Valid @RequestBody Emp emp) {
return "ok";
}
}
测试时,假如不传 id、name,会抛出 MethodArgumentNotValidException 异常。使用 BindingResult 可以处理异常信息,但 通常使用统一异常处理。
① 使用 BindingResult:
@RestController
public class EmpController {
@PostMapping("/emp")
public Map createEmp(@Valid @RequestBody Emp emp, BindingResult result) {
if (result.hasErrors()) {
Map<String, String> map = new HashMap<>();
// 获取校验结果,遍历获取捕获到的每个校验结果
result.getFieldErrors().forEach(item -> {
// 获取校验的信息
String message = item.getDefaultMessage(); // 也获取message的值
String field = item.getField(); //获取属性名
// 存储得到的校验结果
map.put(field, message);
});
return map;
}
return "ok";
}
}
问题:通过上面的步骤,已经可以捕获异常、处理异常,但是每次都是在业务方法中手动处理逻辑,这样的实现,代码肯定会冗余。可以将其抽出,使用 统一异常处理,每次异常发生时,将其捕获。
② 全局统一异常处理:@RestControllerAdvice、@ExceptionHandler
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map handlerValidException(MethodArgumentNotValidException e) {
BindingResult result = e.getBindingResult();
Map<String, String> map = new HashMap<>();
// 获取校验结果,遍历获取捕获到的每个校验结果
result.getFieldErrors().forEach(item ->{
// 存储得到的校验结果
map.put(item.getField(), item.getDefaultMessage());
});
return map;
}
}
Controller 中不需要再用 BindingResult 去处理数据了。
二、细谈统一异常处理:结合统一结果返回类
提示:统一结果返回类,可查看这篇文章:点击查看
为什么使用统一异常处理?
使用统一结果处理时,有些异常我们可以提前预知并处理,但是一个运行时异常,我们不一定能预知并处理,这时可以使用统一异常处理,当异常发生时,触发该处理操作,从而保证程序的健壮性。
怎么做?如何处理?代码实现?
-
使用 @ControllerAdvice 或者 @RestControllerAdvice 注解作为统一异常处理的核心。
这两个注解都是 Spring MVC 提供的
@ControllerAdvice 和 **@RestControllerAdvice **的区别:
@RestControllerAdvice 注解包含了 @ControllerAdvice 与 @ResponseBody 注解。类似于 @Controller 与 @RestController 的区别。
步骤: 使用 @ControllerAdvice 或者 @RestControllerAdvice 注解标记一个 全局异常处理类。全局异常处理类内部使用 @ExceptionHandler 注解去捕获异常。
可选操作:可以自定义一个异常信息收集类,用于处理项目中的异常,并收集异常信息。
代码实现:
1、可选操作:自定义一个异常类,用于处理项目中的异常,并收集异常信息。
/**
* 自定义异常类
* 可以自定义 异常信息message、响应状态码 code(默认为500)
*/
@Data
public class GlobalException extends RuntimeException {
// 保存异常信息
private String message;
// 保存响应状态码(默认为500)
private Integer code = HttpStatus.SC_INTERNAL_SERVER_ERROR;
// 默认构造方法,根据异常信息 构建一个异常实例对象
public GlobalException(String message) {
super(message);
this.message = message;
}
// 根据异常信息、响应状态码 构建一个异常实例对象
public GlobalException(String message, Integer code) {
super(message);
this.message = message;
this.code = code;
}
// 根据异常信息、异常对象 构建一个异常实例对象
public GlobalException(String message, Throwable e) {
super(message, e);
this.message = message;
}
// 根据异常信息、响应状态码、异常对象 构建一个异常实例对象
public GlobalException(String message, Integer code, Throwable e) {
super(message, e);
this.message = message;
this.code = code;
}
}
2、全局异常处理类:
- 定义一个全局的异常处理类 GlobalExceptionHandler。使用 @RestControllerAdvice 注解标记这个类。
- 内部使用 @ExceptionHandler 注解去捕获异常。
// 使用统一结果返回类:Result
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
// 处理 Exception 异常
@ExceptionHandler(Exception.class)
public Result handlerException(Exception e) {
log.error(e.getMessage(), e);
return Result.error().message("系统异常");
}
// 处理空指针异常
@ExceptionHandler(NullPointerException.class)
public Result handlerNullPointerException(NullPointerException e) {
log.error(e.getMessage(), e);
return Result.error().message("空指针异常");
}
// 处理自定义异常
@ExceptionHandler(GlobalException.class)
public Result handlerGlobalException(GlobalException e) {
log.error(e.getMessage(), e);
return Result.error().message(e.getMessage()).code(e.getCode());
}
}
3、使用:修改某个 controller 如下所示:
@GetMapping("/test")
public Result test(Integer id) {
if (id == 0) {
throw new NullPointerException();
}
if (id == -1) {
throw new GlobalException("参数异常", 400);
}
if (id == 1) {
throw new GlobalException("未查询到结果,请确认输入是否正确");
}
return Result.ok().data("items", 123456).message("查询成功");
}
参数为 0 时,抛出 空指针异常。
{
"success": false,
"code": 500,
"message": "空指针异常",
"data": {}
}
参数为 -1 时,抛出自定义异常并处理。
{
"success": false,
"code": 400,
"message": "参数异常",
"data": {}
}
参数为 1 时,抛出自定义异常并处理。
{
"success": false,
"code": 500,
"message": "未查询到结果,请确认输入是否正确",
"data": {}
}
查询成功时,正确处理并返回。
至此,文章结束!!!记得关注哦!!!!