Spring Boot数据校验validation实战:写少一半代码,还更优雅!
🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志
🎐 个人CSND主页——Micro麦可乐的博客
🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战
🌺《RabbitMQ》专栏19年编写主要介绍使用JAVA开发RabbitMQ的系列教程,从基础知识到项目实战
🌸《设计模式》专栏以实际的生活场景为案例进行讲解,让大家对设计模式有一个更清晰的理解
🌛《开源项目》本专栏主要介绍目前热门的开源项目,带大家快速了解并轻松上手使用
🍎 《前端技术》专栏以实战为主介绍日常开发中前端应用的一些功能以及技巧,均附有完整的代码示例
✨《开发技巧》本专栏包含了各种系统的设计原理以及注意事项,并分享一些日常开发的功能小技巧
💕《Jenkins实战》专栏主要介绍Jenkins+Docker的实战教程,让你快速掌握项目CI/CD,是2024年最新的实战教程
🌞《Spring Boot》专栏主要介绍我们日常工作项目中经常应用到的功能以及技巧,代码样例完整
👍《Spring Security》专栏中我们将逐步深入Spring Security的各个技术细节,带你从入门到精通,全面掌握这一安全技术
如果文章能够给大家带来一定的帮助!欢迎关注、评论互动~
Spring Boot 数据校验validation实战:写少一半代码,还更优雅
- 1. 前言
- 2. 没有使用 Validation 的传统写法
- 3. 使用 Validation 的优雅写法
- 4. 全局异常处理(友好返回错误信息)
- 5. 常用校验注解
- 6. 分组校验
- 7. 结语
1. 前言
在我们日常开发中,后端经常需要对请求参数进行校验。比如注册用户时,用户名不能为空、密码长度要在 6~16 之间、邮箱必须符合格式等等,如果我们不做校验,脏数据就可能进入数据库,造成业务问题;如果校验方式不合理,代码又会变得臃肿
相信很多小伙伴还在 Controller
代码中写大量重复的 if-else
判断,既冗余又难维护!(如果你也是这样操作,那一定要看完本篇文章)
下面博主介绍一下Spring Boot
提供的 Validation
(基于 JSR 303/380 规范)让我们能通过注解的方式优雅地完成参数校验,极大地提升了开发效率和代码可读性
2. 没有使用 Validation 的传统写法
下面我们先看看没用 Validation
的“土法校验”,再对比一下用了注解后的优雅写法。当不使用数据校验框架时,我们通常会在 Controller 中手动校验参数,代码会像这样:
场景:创建用户接口
定义接受参数对象UserDto
// UserDTO实体类
class UserDto {private String name;private Integer age;private String email;// getter和setter省略
}
要求:用户名不能为空,长度5-10;邮箱格式必须正确;年龄在18-60之间
@RestController
@RequestMapping("/user")
public class UserController {@PostMapping("/add")public String addUser(UserDto user) {// 手动校验参数if (user.getName() == null || user.getName().trim().isEmpty()) {return "用户名不能为空";}if (user.getName().length() < 5 || user.getName().length() > 10) {return "用户名长度必须在5-10之间";}if (user.getAge() == null) {return "年龄不能为空";}if (user.getAge() < 18 || user.getAge() > 60) {return "年龄必须在18-60之间";}if (user.getEmail() == null || user.getEmail().trim().isEmpty()) {return "邮箱不能为空";}if (!user.getEmail().matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$")) {return "邮箱格式不正确";}// 业务逻辑处理return "用户添加成功";}
}
可以看出上述写法的缺点:
代码冗长,不利于维护
每个接口都要写重复的校验逻辑
校验逻辑和业务逻辑耦合,不够优雅
3. 使用 Validation 的优雅写法
我们可以在实体类上加注解,把校验规则声明在模型上,让 Spring 自动完成校验
Maven 依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>
UserDto对象加上注解
import javax.validation.constraints.*;public class UserDto {@NotBlank(message = "用户名不能为空")@Size(min = 2, max = 10, message = "用户名长度必须在{min}-{max}之间")private String username;@NotBlank(message = "邮箱不能为空")@Email(message = "邮箱格式不正确") // 自带邮箱格式校验,无需自己写正则!private String email;@NotNull(message = "年龄不能为空")@Min(value = 0, message = "年龄最小为{value}")@Max(value = 150, message = "年龄最大为{value}")private Integer age;// 省略 Getter 和 Setter...
}
在Controller参数前加@Valid或@Validated注解
import javax.validation.Valid;@RestController
@RequestMapping("/user")
public class UserController {@PostMapping("/add")// 关键一步:在 @RequestBody 前加上 @Valid 注解public String addUser(@Valid @RequestBody UserDto user) {// 只需关注核心业务System.out.println("用户创建成功: " + user);return "success";}
}
通过上述使用 validation
改造,Spring 会自动对 UserDto
的字段进行校验,当请求参数不满足规则时,Spring Boot
会自动抛出 MethodArgumentNotValidException
异常,不会进入这个方法体。但我们不能直接给用户返回异常栈,需要统一处理
4. 全局异常处理(友好返回错误信息)
刚才我们已经说过了参数校验不满足规则,系统会抛出MethodArgumentNotValidException
,那么我们就可以通过 @RestControllerAdvice
捕获 MethodArgumentNotValidException
,来实现统一返回错误信息
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;@RestControllerAdvice
public class GlobalExceptionHandler {/*** 处理实体校验异常*/@ExceptionHandler(MethodArgumentNotValidException.class)public Map<String, Object> handleValidException(MethodArgumentNotValidException e) {Map<String, Object> errorResult = new HashMap<>();errorResult.put("code", 400);errorResult.put("message", "参数校验失败");// 从异常对象中拿到具体的错误信息// 这里只取第一个错误信息,也可以全部返回String defaultMessage = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();errorResult.put("data", defaultMessage);return errorResult;}
}
最后我们可以使用Postman或curl测试,观察接口返回的JSON异常数据
5. 常用校验注解
注解 | 功能说明 |
---|---|
@NotNull | 值不能为null |
@NotBlank | 字符串不能为空(trim后长度>0) |
@NotEmpty | 集合、数组、Map、String不能为空 |
@Size(min=, max=) | 检查字符串、集合、数组大小 |
@Min(value) | 数字最小值 |
@Max(value) | 数字最大值 |
@Email | 校验邮箱格式 |
@Pattern(regexp=) | 正则表达式匹配 |
@Positive | 正数 |
@Future | 日期必须在未来 |
@Past | 日期必须在过去 |
6. 分组校验
当同一个实体类在不同场景下有不同的校验规则时,比如新增时ID应为空,而更新时ID不能为空,这时就需要分组校验
定义分组接口(标记接口)
public interface CreateGroup {} // 创建分组
public interface UpdateGroup {} // 更新分组
在实体上指定分组
继续改造一下我们的UserDto,这时候需要增加id字段
public class UserDto {@Null(groups = CreateGroup.class, message = "创建时ID必须为空")@NotNull(groups = UpdateGroup.class, message = "更新时ID不能为空")private Long id;@NotBlank(message = "用户名不能为空", groups = {CreateGroup.class, UpdateGroup.class})private String username;// ... 其他字段
}
在Controller中使用@Validated指定分组
@PostMapping("/create")
public String create(@Validated(CreateGroup.class) @RequestBody UserDto user) {// ... 创建逻辑
}@PostMapping("/update")
public String update(@Validated(UpdateGroup.class) @RequestBody UserDto user) {// ... 更新逻辑
}
7. 结语
通过使用 Spring Boot Validation
,我们可以告别繁琐的手动参数校验,让代码更加简洁、优雅、易维护。希望本文能帮助你在项目中更好地应用数据校验机制,提升开发效率和代码质量,是开发中必不可少的利器!
如果你在实践过程中有任何疑问或更好的扩展思路,欢迎在评论区留言,最后希望大家 一键三连 给博主一点点鼓励!
专栏回顾:
01 Spring Boot 整合 spring-boot-starter-mail 实现邮件发送和账户激活
02 使用Spring Boot自定义注解 + AOP实现基于IP的接口限流和黑白名单
03 Spring Boot 使用自定义注解和自定义线程池实现异步日志记录
04 Spring Boot整合Jasypt 库实现配置文件和数据库字段敏感数据的加解密
05 Spring Boot中整合Jasypt 使用自定义注解+AOP实现敏感字段的加解密
06 Spring Boot整合WebSocket和Redis实现直播间在线人数统计功能
07 Spring Boot通过自定义注解和Redis+Lua脚本实现接口限流
08 Spring Boot整合Redis通过Zset数据类型+定时任务实现延迟队列
09 Spring Boot整合Redis实现发布/订阅功能
10 Spring Boot集成 Spring Retry 实现容错重试机制并附源码
11 Spring Boot 3 整合 SpringDoc OpenAPI 生成接口文档
12 Spring Boot 整合开源 Tess4J库 实现OCR图片文字识别
13 Spring Boot 实现 AOP 动态热插拔功能并附DEMO源码
14 Spring Boot中@Async注解的使用及原理 + 常见问题及解决方案
15 Spring Boot集成OpenPDF和Freemarker实现PDF导出功能并附水印
16 使用Spring Boot整合ip2region获取客户端IP地理位置信息
17 SpringBoot中MyBatis使用自定义TypeHandler
18 Spring Boot 集成 PDFBox 实现PDF电子签章的简单应用
19 实现重试只知道Spring Retry?试试Spring Boot 整合 Fast Retry 来实现重试机制
20 在Spring Boot中使用SeeEmitter类实现EventStream流式编程将实时事件推送至客户端
21 Spring Boot 整合 ShedLock 处理定时任务重复执行的问题
22 视频续播功能实现 - 断点续看从前端到 Spring Boot 后端
23 前端与 Spring Boot 后端无感 Token 刷新 - 从原理到全栈实践
24 一文让你测底明白如何在 Spring Boot 上传中将 MultipartFile 转 File 对象