Spring Boot 入参校验及全局异常处理

news2025/1/17 21:42:00

版本依赖

  • JDK 17
  • Spring Boot 3.2.0

源码地址:Gitee

Spring Boot validation

spring-boot-starter-validation是基于hibernate-validator的实现,在Spring Boot项目中直接导入spring-boot-starter-validation即可。

@Valid 和 @Validated 的区别

  1. 适用范围:
    • @Valid 是 Java 校验(JSR-303)的一部分,通常用于标注在方法参数或方法返回值上,以触发参数校验或返回结果的校验。
    • @Validated 是 Spring 提供的,用于在方法级别进行校验。它支持分组校验,并且可以标注在类或方法上。
  2. 分组校验:
    • @Valid 支持分组校验,可以通过定义校验接口的不同分组来控制不同情况下的校验规则。
    • @Validated 也支持分组校验,但是它的分组校验是通过在校验注解上指定分组来实现的。
  3. 校验方式:
    • @Valid 主要用于标注在方法参数或返回值上,触发 Bean Validation 校验。
    • @Validated 主要用于方法级别的校验,并且支持 Spring 提供的校验方式,例如 Spring 的 @NotEmpty@Range 等。
  4. 引入依赖:
    • 使用 @Valid 需要引入 Java Bean Validation 的相关依赖,比如 Hibernate Validator。
    • 使用 @Validated 需要引入 Spring 的相关依赖,它是 Spring 提供的一个校验框架。
  5. 支持的校验注解:
    • @Valid 支持 JSR-303(Bean Validation)提供的注解,比如 @NotNull@Size 等。
    • @Validated 支持 Spring 提供的校验注解,例如 @NotEmpty@Range@Email 等。
  6. 参数校验异常类
    • @Valid 异常类为 org.springframework.web.bind.MethodArgumentNotValidException
    • Validated 异常类为 jakarta.validation.ConstraintViolationException

参数校验常用注解

@Null验证对象是否为null
@NotNull验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty检查约束元素是否为NULL或者是EMPTY.
@AssertTrue验证 Boolean 对象是否为 true
@AssertFalse验证 Boolean 对象是否为 false
@Size(min=, max=)验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=)验证注解的元素值长度在min和max区间内
@Past验证 Date 和 Calendar 对象是否在当前时间之前
@Future验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern验证 String 对象是否符合正则表达式的规则
@Min验证 Number 和 String 对象是否大等于指定的值
@Max验证 Number 和 String 对象是否小等于指定的值
@DecimalMax被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
@DecimalMin被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
@Digits验证 Number 和 String 的构成是否合法
@Digits(integer=,fraction=)验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
@Range(min=, max=)验证注解的元素值在最小值和最大值之间

用例代码

导入依赖
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
定义对象参数
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.hibernate.validator.constraints.Length;

import java.io.Serial;
import java.io.Serializable;

@Data
public class RequestVO implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    @NotBlank(message = "用户名不能为空")
    private String username;


    @NotBlank(message = "手机号码不能为空")
    @Length(min = 11, max = 11, message = "手机号码格式错误")
    private String phoneNumber;
}
定义测试Controller
package com.yiyan.study.controller;

import com.yiyan.study.model.RequestVO;
import com.yiyan.study.model.Result;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 参数校验及全局异常处理测试Controller
 */
@RestController
@RequestMapping("/ex")
@Validated
public class ExController {

    @GetMapping("/param_ex")
    public Result<RequestVO> paramEx(@Valid RequestVO request) {
        return Result.success();
    }

    @GetMapping("/param_in_query")
    public Result<String> paramInQuery(@NotBlank(message = "PARAM 不能为空") String param,
                                       @Min(value = 1, message = "number不能小于1") Integer number) {
        return Result.success();
    }
}

Spring Boot 全局异常处理

参数注释

  • @RestControllerAdvice: 能够捕获应用中所有控制器抛出的异常
  • @ExceptionHandler:方法中定义统一的返回格式,比如将异常信息封装成一个标准的 JSON 对象,并设置响应状态码等。
  • @ResponseStatus:定义响应的HttpStatus,如400,401,403等

自定义业务异常类

import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serial;
import java.io.Serializable;

/**
 * 自定义业务异常
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class BizException extends RuntimeException implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    /**
     * 错误码
     */
    private final String errorCode;
    /**
     * 错误信息
     */
    private final String errorMessage;

    public BizException(String errorCode, String errorMessage) {
        super(errorCode);
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }

    public BizException(String errorCode, String errorMessage, Throwable cause) {
        super(errorCode, cause);
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }

    @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }
}

自定义API统一返回结构

示例

package com.yiyan.study.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;

import java.io.Serial;
import java.io.Serializable;

/**
 * 接口统一返回
 */
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Result<T> implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    /**
     * 请求Status
     */
    private String code;

    /**
     * 业务信息
     */
    private String message;
    /**
     * 返回数据
     */
    private T data;

    /**
     * Instantiates a new Result.
     */
    public Result() {
    }

    public Result(String code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public static <T> Result<T> success() {
        return new Result<>("200", "请求成功", null);
    }

    public static <T> Result<T> success(String code, String message, T data) {
        return new Result<>(code, message, data);
    }

    public static <T> Result<T> error(String code, String message, T data) {
        return new Result<>(code, message, data);
    }

    public static <T> Result<T> error(String code, String message) {
        return error(code, message, null);
    }

}

自定义全局异常处理类

import com.yiyan.study.model.Result;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * 全局异常处理
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    /**
     * 处理自定义的业务异常
     *
     * @param req the req
     * @param e   the e
     * @return result
     */
    @ExceptionHandler(value = BizException.class)
    public Result<BizException> bizExceptionHandler(HttpServletRequest req, BizException e) {
        log.error("[ {} ] {} 请求异常: {}", req.getMethod(), req.getRequestURL(), e.getErrorCode());
        return Result.error(e.getErrorCode(), e.getErrorMessage());
    }

    /**
     * 参数异常信息返回
     *
     * @param req the req
     * @param e   the e
     * @return result
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result<Map<String, String>> methodArgumentNotValidExceptionHandler(HttpServletRequest req, MethodArgumentNotValidException e) {
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        log.error("[ {} ] {} 请求参数校验错误", req.getMethod(), req.getRequestURL());
        Map<String, String> paramExceptionInfo = new TreeMap<>();
        for (ObjectError objectError : allErrors) {
            FieldError fieldError = (FieldError) objectError;
            log.error("参数 {} = {} 校验错误:{}", fieldError.getField(), fieldError.getRejectedValue(), fieldError.getDefaultMessage());
            paramExceptionInfo.put(fieldError.getField(), fieldError.getDefaultMessage());
        }
        return Result.error(HttpStatus.BAD_REQUEST.toString(), "PARAM_EXCEPTION", paramExceptionInfo);
    }

    /**
     * 参数异常信息返回
     *
     * @param req the req
     * @param e   the e
     * @return result
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = ConstraintViolationException.class)
    public Result<String> constraintViolationExceptionHandler(HttpServletRequest req, ConstraintViolationException e) {
        log.error("[ {} ] {} 请求参数校验错误", req.getMethod(), req.getRequestURL());
        return Result.error(HttpStatus.BAD_REQUEST.toString(), "PARAM_EXCEPTION", e.getMessage());
    }

    /**
     * 处理其他异常
     *
     * @param req the req
     * @param e   the e
     * @return result
     */
    @ExceptionHandler(value = Exception.class)
    public Result<String> exceptionHandler(HttpServletRequest req, Exception e) {
        log.error("[ {} ] {} 未定义异常: {}", req.getMethod(), req.getRequestURL(), e.getMessage());
        return Result.error("500", e.getMessage());
    }
}

测试

springboot3-全局异常处理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1340362.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

事务的简介

一、什么是事务 事务是一组数据库的操作序列&#xff0c;包含一个或多个sql操作命令&#xff08;增删改&#xff09;&#xff0c;事务将所有的操作命令看做一个不可分割的整体&#xff0c;向数据库系统提交或撤销操作&#xff0c;所有操作要么执行要么不执行。 ●事务是一种机…

P1019 [NOIP2000 提高组] 单词接龙 刷题笔记

P1019 [NOIP2000 提高组] 单词接龙 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路来自 大佬 Chardo 的个人中心 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 匹配 &#xff1a; 将 第一个字符串末尾 和第二个字符串第一个开始匹配 如果 j<i这段走完了 flag还没…

Vue学习day_01

指令: v-html: 设置元素的innerHTML v-show和v-if: 二者都是控制元素去隐藏起来&#xff0c;但是二者的原理是不一样的&#xff0c; v-show底层的原理是在切换底层css里面的display为none v-if底层的原理是移除节点或者是创建节点&#xff0c;"成本"是比较高的…

linux 防火墙查看放行端口,追加放行端口命令

linux 查看防火墙已经放行端口列表 firewall-cmd --list-ports 运行结果如下&#xff1a; linux 追加防火墙经放行端口&#xff08;如追加443&#xff09; firewall-cmd --zonepublic --add-port443/tcp --permanent 亲测有效&#xff01;

力扣刷题记录(20)LeetCode:198、213、337

198. 打家劫舍 我们从第一个开始分析&#xff1a; dp[i]:i表示索引&#xff0c;dp表示当前索引可以拿到的最高金额 索引为0时&#xff0c;可以拿到的最高金额为1&#xff1b; 索引为1时&#xff0c;可以拿到的最高金额就是在索引[0,1]之间取&#xff0c;为2 索引为2时&…

deepin系统安装达梦数据库

deepin系统安装达梦数据库 1.下载安装包和执行可执行文件2.解压缩可执行文件3.运行安装程序 2.初始化3.达梦管理工具 deepin系统安装达梦数据库 1.下载安装包和执行可执行文件 进入deepin系统桌面, 打开终端, 输入命令uname -a 检查cpu架构,前往达梦官网下载合适的安装包, 目前…

【linux】touch的基本使用

碎碎念 刚接触linux时候的几个最基础的命令之一&#xff0c;用来创建文件。如果使用touch --help的时候会发现作者对于touch的简介&#xff1a;Update the access and modification times of each FILE to the current time.用于修改文件的访问和时间戳 带我的leader属于那种…

使用Python Flask搭建一个简单的Web站点并发布到公网上访问

文章目录 前言1. 安装部署Flask并制作SayHello问答界面2. 安装Cpolar内网穿透3. 配置Flask的问答界面公网访问地址4. 公网远程访问Flask的问答界面 前言 Flask是一个Python编写的Web微框架&#xff0c;让我们可以使用Python语言快速实现一个网站或Web服务&#xff0c;本期教程…

深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第二节 栈基本工作原理

深入浅出图解C#堆与栈 C# HeapingVS Stacking第二节 栈基本工作原理 [深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第一节 理解堆与栈](https://mp.csdn.net/mdeditor/101021023)[深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第二节 栈基本工作原理](https://mp.cs…

Python五子棋程序实现详解

Python五子棋程序实现详解 引言功能实现显示棋盘点击落子判断胜负游戏结束判断交替落子 运行结果完整代码总结 引言 五子棋是一种广泛传播的策略棋类游戏&#xff0c;两人对弈&#xff0c;通过在棋盘上落子&#xff0c;以先形成连续的相同颜色的五子棋为胜利条件。本文将介绍如…

关于Zoom ZTP和AudioCodes Ltd桌面电话缺陷暴露,导致用户遭受窃听的动态情报

一、基本内容 近期SySS安全研究员发布分析报告显示&#xff0c;Zoom的零接触&#xff08;ZTP&#xff09;和AudioCodes Ltd桌面电话配置功能中发现高危漏洞&#xff0c;可以获得对设备的完全远程控制并不受限制的访问可以被武器化&#xff0c;以窃听房间或电话、通过设备并攻击…

JAVA语言—AOP基础

1、AOP概述 AOP&#xff1a;AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff0c;即面向切面编程&#xff0c;可以说是OOP&#xff08;Object Oriented Programming&#xff0c;面向对象编程&#xff09;的补充和完善。 场景&#xff1a;案例部分功能运行较慢&…

Linux操作系统——进程(六) 进程地址空间

进程地址空间 C/C程序员一般将我们所写的程序看成如下这种结构&#xff1a; 我们所写的程序通过编译编译之后就可以以这样的方式进行分布. 我们先通过编写一段C语言代码来进行验证&#xff1a; 运行结果&#xff1a; 我们可以看出来上述地址遵循的就是我们上面画的一种结构。…

TSINGSEE青犀智能分析网关V4人体行为检测算法在视频监控中的应用

旭帆科技智能分析网关的算法十分繁多&#xff0c;其中可分为人体事件、车辆事件、环境事件、行为检测、着装检测等等&#xff0c;可覆盖绝大多数场景&#xff0c;如智慧校园、智慧工地、智慧景区等&#xff0c;今天小编就TSINGSEE青犀智能分析网关的行为检测算法和大家进行研讨…

Leetcode算法系列| 8. 字符串转换整数 (atoi)

目录 1.题目2.题解C# 解法一&#xff1a;及其臃肿的代码C# 解法二&#xff1a;DFA&#xff08;确定有穷自动机&#xff09; 1.题目 请你来实现一个 myAtoi(string s) 函数&#xff0c;使其能将字符串转换成一个 32 位有符号整数&#xff08;类似 C/C 中的 atoi 函数&#xff09…

2024年中职“网络安全“—数字调查取证(attack817.pcapng)

目录 ​1.通过分析数据包找出恶意用户最初访问HTTP服务的包号&#xff0c;将该值作为Flag值提交, Flag格式为flag{xxx}&#xff1b; 2.继续查看数据包文件分析出恶意用户扫描了哪些端口&#xff0c;将全部的端口号按照一定顺序作为Flag值&#xff0c;提示&#xff1a;注意端口…

Spring Boot整合 EasyExcel 实现复杂 Excel 表格的导入与导出功能

文章目录 1. 简介2. 引入依赖3. 导入功能实现3.1 创建实体类3.2 编写导入 Controller3.3 编写导入页面 4. 导出功能实现4.1 编写导出 Controller4.2 编写导出页面 5. 启动应用 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &…

2023年中职“网络安全”——B-5:网络安全事件响应(Server2216)

B-5&#xff1a;网络安全事件响应 任务环境说明&#xff1a; 服务器场景&#xff1a;Server2216&#xff08;开放链接&#xff09; 用户名:root密码&#xff1a;123456 1、黑客通过网络攻入本地服务器&#xff0c;通过特殊手段在系统中建立了多个异常进程&#xff0c;找出启…

NFC物联网智能锁安全测试研究

针对短距离无线通信在物联网智能锁实际运用中的安全机制问题&#xff0c;通过理论分析和实际操作演示潜在的攻击流程&#xff0c;发现其存在的安全漏洞并提出可行的加固方法&#xff0c;并对加固后的通信系统进行CPN建模与安全性分析&#xff0c;对无线通信协议的安全性能提升、…

数据结构学习 Leetcode474 一和零

关键词&#xff1a;动态规划 01背包 一个套路&#xff1a; 01背包&#xff1a;空间优化之后dp【target1】&#xff0c;遍历的时候要逆序遍历完全背包&#xff1a;空间优化之后dp【target1】&#xff0c;遍历的时候要正序遍历 目录 题目&#xff1a; 思路&#xff1a; 复杂…