【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开始"

}

相关推荐
学长毕设18 分钟前
基于SpringBoot的野生动物公益保护平台设计与实现-附源码
java·spring boot·后端
她的男孩1 小时前
三人会签只批了一个就往下走:Forge Admin 把 all / any / ratio 写成 completionCondition
spring boot·低代码·工作流引擎
vipxieliang3 小时前
ValidX vs 手写验证代码:开发效率对比实测
java·spring boot·后端
vipxieliang3 小时前
虚拟线程与 GraalVM:Spring Boot 3.x 云原生性能跃迁
java·spring boot·后端
头发还在的女程序员4 小时前
基于 Spring Boot + Vue 3 + uni-app 的灵活用工系统:四端架构与业务流程设计
vue.js·spring boot·uni-app·灵活用工·灵工系统
CoderYanger10 小时前
Java EE 进阶:8.MyBatis 操作数据库(入门)(第二弹)
java·数据库·spring boot·spring·面试·java-ee·mybatis
计算机毕设定制辅导-无忧学长20 小时前
《基于微信小程序的居家医疗陪护预约系统设计与实现》
java·spring boot·微信小程序·uniapp·居家医疗陪护预约系统
小郎君6581 天前
注解式脱敏:用 Jackson 的 ContextualSerializer 做字段级策略(一)
spring boot·spring cloud
jktantan1 天前
注解式脱敏:用 Jackson 的 ContextualSerializer 做字段级策略(一)
spring boot·后端·算法·spring cloud·系统架构
露天赏雪2 天前
第3篇:Sentinel熔断降级实战:大模型接口抖动时的服务兜底方案
java·开发语言·人工智能·spring boot·spring·servlet·sentinel