【SpringBoot3】全局异常处理
在 Controller 处理请求过程中发生了异常,DispatcherServlet 将异常处理委托给异常处理器(处理异常的类)。实现 HandlerExceptionResolver 接口的都是异常处理类。
项目的异常一般集中处理,定义全局异常处理器。在结合框架提供的注解,诸如:@ExceptionHandler,@ControllerAdvice ,@RestControllerAdvice 一起完成异常的处理。@ControllerAdvice 与@RestControllerAdvice 区别在于:@RestControllerAdvice 加了@RepsonseBody。
一、全局异常处理器
- 需求:应用计算两个数字相除,当用户被除数为 0 ,发生异常。使用自定义异常处理器代替默认的异常处理程序
step1:创建收入数字的页面
- 在 static 目录下创建 input.html , static 目录下的资源浏览器可以直接访问
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="divide" method="post">
除 数:<input type="text" name="n1"> <br/>
被除数:<input type="text" name="n2"> <br/>
<input type="submit" value="计算">
</form>
</body>
</html>
step2:创建控制器,计算两个整数相除
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class NumberController {
@PostMapping("/divide")
public String some(Integer n1,Integer n2){
int result = n1/n2;
return "n1/n2 = " + result;
}
}
step3:创建自定义异常处理器
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//控制器增强
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler({ArithmeticException.class})
public String handlerArtithmecticException(ArithmeticException e, Model model){
String error = e.getMessage();
model.addAttribute("error",error);
return "exp";
}
/* @ExceptionHandler({ArithmeticException.class})
@ResponseBody
public Map<String,String> handlerArtithmecticException(ArithmeticException e){
Map<String,String> errors = new HashMap<>();
errors.put("msg",e.getMessage());
errors.put("tips","被除数不能为0");
return errors;
}*/
}
step5:创建给用提示的页面
- 在 resources/templates/ 创建 exp.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>显示异常信息:<div th:text = "${error}"></div> </h3>
</body>
</html>
step6:测试输入(10/0)
二、BeanValidator 异常处理
使用 JSR-303 验证参数时,我们是在 Controller 方法,声明BindingResult 对象获取校验结果。Controller 的方法很多,每个方法都加入 BindingResult 处理检验参数比较繁琐。 校验参数失败抛出异常给框架,异常处理器能够捕获到 MethodArgumentNotValidException,它是 BindException 的子类。
BindException 异常实现了 BindingResult 接口,异常类能够得到 BindingResult 对象,进一步获取 JSR303 校
验的异常信息。
- 需求:全局处理 JSR-303 校验异常
step1:添加 JSR-303 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
step2:创建 Bean 对象,属性加入 JSR-303 注解
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
@Data
public class OrderVO {
@NotBlank(message = "订单名称不能为空")
private String name;
@NotNull(message = "商品必须有数量")
@Range(min = 1,max = 99,message = "一个订单商品数量在{min} -- {max}")
private Integer amount;
@NotNull(message = "用户不能为空")
@Min(value = 1,message = "从1开始")
private Integer userId;
}
step3:Controlller 接收请求
import com.bjpowernode.exp.vo.OrderVO;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
@PostMapping("/order/new")
public String createOrder(@Validated @RequestBody OrderVO orderVO){
return "订单信息:" + orderVO.toString();
}
}
step4:创建异常处理器
import org.springframework.ui.Model;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//控制器增强
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler({ArithmeticException.class})
public String handlerArtithmecticException(ArithmeticException e, Model model){
String error = e.getMessage();
model.addAttribute("error",error);
return "exp";
}
/* @ExceptionHandler({ArithmeticException.class})
@ResponseBody
public Map<String,String> handlerArtithmecticException(ArithmeticException e){
Map<String,String> errors = new HashMap<>();
errors.put("msg",e.getMessage());
errors.put("tips","被除数不能为0");
return errors;
}*/
//处理JSR303 验证参数的异常
//@ExceptionHandler({BindException.class})
@ExceptionHandler({MethodArgumentNotValidException.class})
@ResponseBody
public Map<String,Object> handlerJSR303Exception(MethodArgumentNotValidException e){
System.out.println("=============JSR303===========");
Map<String,Object> map = new HashMap<>();
BindingResult result = e.getBindingResult();
if(result.hasErrors()){
List<FieldError> fieldErrors = result.getFieldErrors();
for (int i = 0; i < fieldErrors.size(); i++) {
FieldError fieldError = fieldErrors.get(i);
map.put(i + "-" + fieldError.getField(), fieldError.getDefaultMessage());
}
}
return map;
}
}
- 核心代码:
step5:测试
POST http://localhost:8080/order/new
Content-Type: application/json
{
"name": "每日订单",
"amount": 0,
"userId": 0
}
显示:
{
“1-amount”: “一个订单商品数量在1 – 99”,
“0-userId”: “从1开始”
}