Spring Boot 入参校验及全局异常处理
版本依赖
- 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 的区别
- 适用范围:
@Valid是 Java 校验(JSR-303)的一部分,通常用于标注在方法参数或方法返回值上,以触发参数校验或返回结果的校验。@Validated是 Spring 提供的,用于在方法级别进行校验。它支持分组校验,并且可以标注在类或方法上。
- 分组校验:
@Valid支持分组校验,可以通过定义校验接口的不同分组来控制不同情况下的校验规则。@Validated也支持分组校验,但是它的分组校验是通过在校验注解上指定分组来实现的。
- 校验方式:
@Valid主要用于标注在方法参数或返回值上,触发 Bean Validation 校验。@Validated主要用于方法级别的校验,并且支持 Spring 提供的校验方式,例如 Spring 的@NotEmpty、@Range等。
- 引入依赖:
- 使用
@Valid需要引入 Java Bean Validation 的相关依赖,比如 Hibernate Validator。 - 使用
@Validated需要引入 Spring 的相关依赖,它是 Spring 提供的一个校验框架。
- 使用
- 支持的校验注解:
@Valid支持 JSR-303(Bean Validation)提供的注解,比如@NotNull、@Size等。@Validated支持 Spring 提供的校验注解,例如@NotEmpty、@Range、@Email等。
- 参数校验异常类:
@Valid异常类为org.springframework.web.bind.MethodArgumentNotValidExceptionValidated异常类为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 {@Serialprivate 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 {@Serialprivate 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;}@Overridepublic 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 {@Serialprivate 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());}
}
测试

相关文章:
Spring Boot 入参校验及全局异常处理
版本依赖 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 的区别 适用范围…...
MySQL 和 MySQL2 的区别
MySQL是最流行的开源关系型数据库管理系统,拥有大量的使用者和广泛的应用场景。而MySQL2是MySQL官方团队推出的新一代MySQL驱动,用于取代老版的MySQL模块,提供更好的性能和更丰富的功能。 本文将介绍MySQL2相较于MySQL有哪些优势以及具体的技术区别。 …...
AutoCAD图纸打印后内容不见
用户反映,在CAD里的对象打印出来不显示。其实,这是在CAD的打印对象颜色的问题。(在9.2以下版本有这种问题,9.2及以上版本已默认此种颜色) 1.当背景色为黑色的时候,这里的颜色是白,如下图 2.当C…...
ASUS华硕ROG幻16 2023款GU603VU VV VI笔记本电脑原厂Win11.22H2系统
链接:https://pan.baidu.com/s/1AgevUZleCHBJgCBcIp5CFQ?pwdhjxy 提取码:hjxy 华硕笔记本2023款幻16原厂Windows11系统自带所有驱动、出厂主题壁纸、Office办公软件、MyASUS华硕电脑管家、Armoury Crate奥创控制中心等预装程序 文件格式࿱…...
学习笔记 k8s常用kubectl命令
k8s常用kubectl命令 pod 相关强制删除pod查看 Pod 中指定容器的日志pod 扩容 etcd 备份集群设置集群上下文配置文件切换集群 节点cordondrain pod 相关 强制删除pod pod 状态terminal了,需要强制删除 kubectl delete pod <pod_name> --grace-period0 --force…...
企业数据可视化-亿发数据化管理平台提供商,实现一站式数字化运营
近些年来,国内企业数据化管理升级进程持续加速,以物联网建设、人工智能、大数据和5G网络等新技术的发展,推动了数字经济的蓬勃发展,成为维持经济持续稳定增长的重要引擎。如今许多国内中小型企业纷纷摒弃传统管理模式,…...
网络通信-Linux 对网络通信的实现
Linux 网络 IO 模型 同步和异步,阻塞和非阻塞 同步和异步 关注的是调用方是否主动获取结果 同步:同步的意思就是调用方需要主动等待结果的返回 异步:异步的意思就是不需要主动等待结果的返回,而是通过其他手段比如,状态通知࿰…...
mysql修改密码
mysql -u root -p ALTER USER USER() IDENTIFIED BY root; FLUSH PRIVILEGES; 其它cmd: ①查看端口,找到占用3306端口的进程:命令行输入 netstat -aon ,找到端口号为3306的对应的PID ②结束占用端口3306的进程:命令…...
深入解析C语言中void (*signal(int ,void(*)(int) ) ) (int)
目录 深入解析C语言中的signal函数声明 1. signal函数声明 1.1 signal是一个函数 1.2 返回类型是一个函数指针 2. 函数指针的理解 3. 简化声明使用typedef 为啥不这么写typedef void (*)(int) acc? 代码: 结论 深入解析C语言中的signal函数声明…...
网站显示不安全警告怎么办?消除网站不安全警告超全指南
网站显示不安全警告怎么办?当用户访问你的网站,而您的网站没有部署SSL证书实现HTTPS加密时,网站就会显示不安全警告,这种警告,不仅有可能阻止用户继续浏览网站,影响网站声誉,还有可能影响网站在…...
[SWPUCTF 2021 新生赛]finalrce
[SWPUCTF 2021 新生赛]finalrce wp 注:本文参考了 NSSCTF Leaderchen 师傅的题解,并修补了其中些许不足。 此外,参考了 命令执行(RCE)面对各种过滤,骚姿势绕过总结 题目代码: <?php highlight_file(__FILE__); …...
如何底层调用最快地复制OPC数据到关系数据库
计算机上的二大应用,一是从WEB服务器上获得数据,另一种是向关系数据库中写入数据。在上集我已提出了一个从WEB上获得OPC数据的独创方法,现在谈谈第二种如何快速地把OPC数据写进到数据库中,这也是Calssic OPC最典型的一个应用场景。…...
接口测试工具——ApiFox使用初体验 postman导出和ApiFox导入
目录 ApiFox使用初体验初步使用从postman导出到apifox导入 IDEA简单测试Postman测试工具post请求 接口测试工具swaggerKnife4j1.引入依赖2.配置3.常用注解4.接口测试 JMeter什么是JMeter?JMeter安装配置1.官网下载2.下载后解压3.汉语设置 JMeter的使用方法1.新建线程组2.设置参…...
搜维尔科技:经脉腧穴虚拟针灸VR虚拟教学平台AcuMap软件案例分享
北京中医药大学经脉腧穴VR虚拟教学平台案例 主要产品 HTCvive ,AcuMap; 实施内容 一、项目说明 (1)穴位取穴与体表解剖标志关系;(2)穴下层次解剖及周围解剖结构展示; …...
Jenkins的shared library相关
Jenkins的shared library是一种用于在多个Jenkins流水线项目中共享和重用代码的机制。它可以将常用的构建逻辑、工具函数或自定义步骤封装为可复用的库,并以插件的形式提供给Jenkins。 Shared library的作用主要包括以下几个方面: 代码复用:…...
文件IO
文章目录 文章目录 前言 一 . 文件 文件路径 文件类型 Java中操作文件 File 概述 属性 构造方法 方法 createNewFile mkdir 二 . 文件内容的读写 - IO InputStream 概述 FileInputStream 概述 利用 Scanner 进行字符读取 OutputStream 概述 PrintWriter封装O…...
【日常聊聊】编程语言的未来:趋势、多样性、人工智能融合、教育与生态系统
🍎个人博客:个人主页 🏆个人专栏: 日常聊聊 ⛳️ 功不唐捐,玉汝于成 目录 前言: 正文 1. 编程语言的发展趋势 1.1 新语言和编程范式的涌现 1.2 影响和挑战 2. 编程语言的多样性 2.1 互操作性和可移…...
无需手动搜索!轻松创建IntelliJ IDEA快捷方式的Linux教程
轻松创建IntelliJ IDEA快捷方式的Linux教程 一、IntelliJ IDEA简介二、在Linux系统中创建快捷方式的好处三、命令行创建IntelliJ IDEA快捷方式四、图形界面创建IntelliJ IDEA快捷方式五、常见问题总结 一、IntelliJ IDEA简介 IntelliJ IDEA是一个由JetBrains搞的IDE࿰…...
如何去掉微博水印?用它一键去除三秒出图
微博是一款非常流行的社交媒体平台,许多人都在上面分享自己的生活点滴和心得体会。但是,有时候我们会发现,在上传图片时,微博会自动添加水印,这会影响到图片的美观度。那么,如何去掉微博水印呢?…...
Golang 泛型实现原理
文章目录 1.什么是泛型?2.有 interface{} 为什么还要有泛型?3.泛型有哪些特性?3.1 类型参数泛型函数泛型类型 3.2 类型约束3.3 类型集3.4 约束元素任意类型约束元素近似约束元素联合约束元素约束中的可比类型 3.5 类型推断 4.实现原理4.1 类型擦除虚方法…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...
ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...
DiscuzX3.5发帖json api
参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下,适配我自己的需求 有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...
路由基础-路由表
本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中,往往存在多个不同的IP网段,数据在不同的IP网段之间交互是需要借助三层设备的,这些设备具备路由能力,能够实现数据的跨网段转发。 路由是数据通信网络中最基…...
嵌入式面试常问问题
以下内容面向嵌入式/系统方向的初学者与面试备考者,全面梳理了以下几大板块,并在每个板块末尾列出常见的面试问答思路,帮助你既能夯实基础,又能应对面试挑战。 一、TCP/IP 协议 1.1 TCP/IP 五层模型概述 链路层(Link Layer) 包括网卡驱动、以太网、Wi‑Fi、PPP 等。负责…...
