当前位置: 首页 > news >正文

Validation - Spring Boot项目中参数检验的利器

Validation - Spring Boot项目中参数检验的利器

什么是Validation

Sping Boot官方原文:

When it comes to validating user input, Spring Boot provides strong support for this common, yet critical, task straight out of the box.

Although Spring Boot supports seamless integration with custom validators, the de-facto standard for performing validation is Hibernate Validator, the Bean Validation framework’s reference implementation.

Validation是Spring Boot官方提供的一个参数检验的工具,通常负责验证用户输入,也就是在从前端获取请求时对请求中包含的信息参数进行检验,避免了进入后端检验这个流程的耗时与逻辑编写

为什么要使用Validation

Validation在Spring Boot架构的分布式项目当中比较常见,其优点我总结为:

  • 与后端逻辑分离,做到了解耦
  • 使用简单,只需要在需要进行参数检验的参数(DTO层)上增加一个注释即可,不需要编写大量接口和实现代码
  • 官方支持,使用稳定

接下来我们将通过前两个角度来看看Validation是怎样简单上手使用的,我将使用我的学习项目作为示例

如何使用Validation

在一种情况下,比如我们设计数据库表的时候,往往会有一些字段包含实际意义,比如邮箱或电话号码,这些字段与分配的随机id不同,在现实中有一定的规范;另一种情况下,比如我们的用户设置初始密码时,我们也通常会要求用户至少输入6位,有时还会要求至少有一位大写字母等,这时我们就需要对用户输入的数据进行检验,检查这些数据是否符合实际规范或者我们的要求,此时就可以用上Validation

比如在我的项目中,作为一个电商平台,有用户表如下:

public class User {private Long id;private String username;private String password;private String phone;private String email;private Date registerTime;private Integer status;private String realName;private String address;
}

电商平台会出现用户注册的场景,于是我们就要定义用户注册时返回的数据的DTO类

public class UserRegisterDTO {@NotBlank(message = "用户名不能为空")private String username;@NotBlank(message = "密码不能为空")@Size(message = "密码长度至少为6", min = 6)private String password;@NotBlank(message = "电话号码不能为空")@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")private String phone;@NotBlank(message = "邮箱不能为空")@Email(message = "邮箱格式不正确")private String email;
}

在注册时,我们就需要规范用户输入的这些信息,所有信息都是必要的不能为空,同时密码有长度限制,电话号码有其固定格式,邮箱同理,我们在这里就可以通过Validation的注释来告诉系统,在把用户返回的这些数据包装成对应的DTO类时,需要进行检查

再具体一点,我们怎么通知系统呢

首先我们需要添加依赖,比如我的项目中:

        <dependency><groupId>jakarta.validation</groupId><artifactId>jakarta.validation-api</artifactId><version>3.1.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>

我做的是一个微服务项目,目前上述的代码都位于用户模块,因此这个依赖我也是添加在我用户模块的子pom中,有了这个依赖,我们就可以开始使用Validation

接着我们需要在对应的API中声明,告诉系统我们在处理这个请求之前需要对其参数进行检验,比如我注册的Restful API:

    // 注册用户@PostMapping("/register")public Result<Boolean> register(@Valid @RequestBody UserRegisterDTO dto) {return userService.register(dto) ?Result.ok(true) :Result.fail("Register Failed");}

只有在这里指明了@Valid这个注释,系统才会对其进行Validation相关处理

接着我们就要进一步对参数进行注释,正如我之前给出的代码,在DTO层对应的参数前添加相应的注释即可,一般来说,validation注释有以下几种:

  • @AssertFalse:被注解的元素必须为false
  • @AssertTrue:被注解的元素必须为true
  • @DecimalMax(value):被注解的元素必须是一个数字,其值必须小于等于指定的最大值
  • @DecimalMin(value):被注解的元素必须是一个数字,其值必须大于等于指定的最小值
  • @Digits(integer, fraction):被注解的元素必须是一个数字,其值必须在可接受的范围内
  • @Future:被注解的元素必须是一个将来的日期
  • @Max(value):被注解的元素必须是一个数字,其值必须小于等于指定的最大值
  • @Min(value):被注解的元素必须是一个数字,其值必须大于等于指定的最小值
  • @NotNull:被注解的元素必须不为null
  • @Null:被注解的元素必须为null
  • @Past:被注解的元素必须是一个过去的日期
  • @Pattern(regex, flag):被注解的元素必须符合指定的正则表达式
  • @Size(min, max):被注解的元素的大小必须在指定的范围内
  • @Email:被注解的元素必须是电子邮件地址
  • @NotEmpty:被注解的元素不能为null,并且长度必须大于0
  • @NotBlank:被注解的元素为字符串,并且被trim()以后length要大于0
  • @Range(min, max):被注解的元素必须在合适的范围内
  • @URL:被注解的元素必须是一个有效的URL

在元素之前添加相应的注释,项目启动后,把用户返回的参数包装成DTO类对象时就会进行检查

但是只检查出来了,最多也就会返回400的错误代码,前端只是了解到有错误,但是无法对错误进行定位,于是此时我们就需要进一步处理

自定义Result类,用来统一管理前端请求的处理结果

public class Result<T> {private Integer code;private String msg;private T data;public static <T> Result<T> ok(T data) {Result<T> r = new Result<>();r.setCode(200);r.setMsg("success");r.setData(data);return r;}public static <T> Result<T> fail(String msg) {Result<T> r = new Result<>();r.setCode(500);r.setMsg(msg);return r;}
}

自定义异常处理类,用于对运行时错误进行处理

@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(MethodArgumentNotValidException.class)public Result<?> handleValidationException(MethodArgumentNotValidException ex) {String errorMsg = ex.getBindingResult().getFieldError().getDefaultMessage();return Result.fail(errorMsg);}
}

有了这两段代码,项目启动后就有了自定义的对运行时错误进行定位处理的能力,比如用户注册时输入的初始密码小于6位,通过Validation注释检查出了这一错误,然后汇报给GlobalExceptionHandler类,它就将错误信息包装,并最终包装成Result类,返回给前端,这样前端就能看见具体的错误信息,比如我测试如下:

我在Postman中进行用户注册测试,这是注册的body json部分:

{"username": "testValid6","password": "five","email": "12345678","phone": "12345678"
}

最后返回了结果:

{"timestamp": "2025-07-19T04:59:28.091+00:00","status": 400,"error": "Bad Request","trace": 略"message": "Validation failed for object='userRegisterDTO'. Error count: 3","errors": [{"codes": ["Email.userRegisterDTO.email","Email.email","Email.java.lang.String","Email"],"arguments": [{"codes": ["userRegisterDTO.email","email"],"arguments": null,"defaultMessage": "email","code": "email"},[],{"arguments": null,"defaultMessage": ".*","codes": [".*"]}],"defaultMessage": "邮箱格式不正确","objectName": "userRegisterDTO","field": "email","rejectedValue": "12345678","bindingFailure": false,"code": "Email"},{"codes": ["Pattern.userRegisterDTO.phone","Pattern.phone","Pattern.java.lang.String","Pattern"],"arguments": [{"codes": ["userRegisterDTO.phone","phone"],"arguments": null,"defaultMessage": "phone","code": "phone"},[],{"arguments": null,"defaultMessage": "^1[3-9]\\d{9}$","codes": ["^1[3-9]\\d{9}$"]}],"defaultMessage": "手机号格式不正确","objectName": "userRegisterDTO","field": "phone","rejectedValue": "12345678","bindingFailure": false,"code": "Pattern"},{"codes": ["Size.userRegisterDTO.password","Size.password","Size.java.lang.String","Size"],"arguments": [{"codes": ["userRegisterDTO.password","password"],"arguments": null,"defaultMessage": "password","code": "password"},2147483647,6],"defaultMessage": "密码长度至少为6","objectName": "userRegisterDTO","field": "password","rejectedValue": "five","bindingFailure": false,"code": "Size"}],"path": "/user/register"
}

可以看出,这里按照DTO设计的参数的顺序,通通给出了错误的信息,这样就能够精准地定位错误,前端也就可以对其做相应的处理

总结

整体来说,Validation将原先需要一个API一个API编写的参数检验逻辑简化至如此方便,可以说是Spring Boot项目中官方提供的一个参数检验的利器了,对参数标注只需要注释,错误处理可以统一编写错误处理类,前端也可以清晰地看见具体的错误,能够极大地简化这部分逻辑的开发,省去繁琐的重复性工作

http://www.xdnf.cn/news/1148005.html

相关文章:

  • 打造高效订单处理!ZKmall开源商城的统一履约中心架构解析
  • UGUI 性能优化系列:第三篇——渲染与像素填充率优化
  • Vue3生命周期函数
  • ABP VNext + Kubernetes Istio:微服务网格实战指南
  • Word快速文本对齐程序开发经验:从需求分析到实现部署
  • 深度学习Depth Anything V2神经网络实现单目深度估计系统源码
  • Spring AI 项目实战(十八):Spring Boot + AI + Vue3 + OSS + DashScope 实现高效语音识别系统(附完整源码)
  • 市场数据+幸存者偏差提问,有趣的思考?
  • [论文阅读] 人工智能 + 软件工程 | 强化学习在软件工程中的全景扫描:从应用到未来
  • 异世界历险之数据结构世界(二叉树-leetcode)
  • 【2025最新】 .NET FrameWork微软离线运行库合集,一键安装版
  • 【C# in .NET】19. 探秘抽象类:具体实现与抽象契约的桥梁
  • 《Electron应用性能深耕:资源加载与内存治理的进阶路径》
  • 辛普森悖论
  • 用虚拟机体验纯血鸿蒙所有机型!
  • OpenCV 官翻7 - 对象检测
  • 13.5 Meta LLaMA 2核心技术拆解:4T数据训练+30%显存优化,70B模型准确率82.6%
  • 文件搜索的工具
  • Rust Web 全栈开发(十):编写服务器端 Web 应用
  • Flink实时流量统计:基于窗口函数与Redis Sink的每小时PV监控系统(学习记录)
  • rust实现的快捷补全到剪贴板的实用工具
  • Zara和网易云音乐仿写总结
  • 【c++】提升用户体验:问答系统的交互优化实践——关于我用AI编写了一个聊天机器人……(12)
  • 使用 Gunicorn 部署 Django 项目
  • AI编程工具对比:Cursor、GitHub Copilot与Claude Code
  • Oracle Database 23ai 技术细节与医疗 AI 应用
  • Lock4j 使用说明
  • 【Linux服务器】-mysql数据库数据目录迁移
  • 安全事件响应分析--基础命令
  • 【机器学习深度学习】为什么要将模型转换为 GGUF 格式?