【SpringBoot3】全局异常处理

【SpringBoot3】全局异常处理

在 Controller 处理请求过程中发生了异常,DispatcherServlet 将异常处理委托给异常处理器(处理异常的类)。实现 HandlerExceptionResolver 接口的都是异常处理类。

项目的异常一般集中处理,定义全局异常处理器。在结合框架提供的注解,诸如:@ExceptionHandler,@ControllerAdvice ,@RestControllerAdvice 一起完成异常的处理。@ControllerAdvice 与@RestControllerAdvice 区别在于:@RestControllerAdvice 加了@RepsonseBody。

一、全局异常处理器

  • 需求:应用计算两个数字相除,当用户被除数为 0 ,发生异常。使用自定义异常处理器代替默认的异常处理程序

step1:创建收入数字的页面

  • 在 static 目录下创建 input.html , static 目录下的资源浏览器可以直接访问
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="divide" method="post">
  除&nbsp;&nbsp;&nbsp;数:<input type="text" name="n1"> <br/>
  被除数:<input type="text" name="n2"> <br/>
  <input type="submit" value="计算">
</form>
</body>
</html>

step2:创建控制器,计算两个整数相除

java 复制代码
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:创建自定义异常处理器

java 复制代码
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
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 依赖

xml 复制代码
   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
   </dependency>

step2:创建 Bean 对象,属性加入 JSR-303 注解

java 复制代码
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 接收请求

java 复制代码
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:创建异常处理器

java 复制代码
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:测试

java 复制代码
POST http://localhost:8080/order/new
Content-Type: application/json

{
  "name": "每日订单",
  "amount": 0,
  "userId": 0
}

显示:

{

"1-amount": "一个订单商品数量在1 -- 99",

"0-userId": "从1开始"

}

相关推荐
小飞Coding3 小时前
Spring Boot 中关于 Bean 加载、实例化、初始化全生命周期的扩展点
spring boot
小飞Coding4 小时前
彻底搞懂 Spring 容器导入配置类:@EnableXXX 与 spring.factories 核心原理
spring boot
悟空码字1 天前
Spring Boot 整合 MongoDB 最佳实践:CRUD、分页、事务、索引全覆盖
java·spring boot·后端
皮皮林5513 天前
拒绝写重复代码,试试这套开源的 SpringBoot 组件,效率翻倍~
java·spring boot
用户908324602735 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户8307196840826 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解6 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解6 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记6 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者7 天前
Kafka 基础介绍
spring boot·kafka·消息队列