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

SpringBoot 接口参数校验(Bean Validation)实战

在开发接口时,参数校验是必不可少的环节:前端传参是否为空、格式是否正确、数值是否合法,都需要后端严格校验,否则很容易出现脏数据、程序异常。

传统的if-else判空不仅代码臃肿,还容易遗漏,维护成本极高。

SpringBoot 官方推荐使用Bean Validation(JSR-380)实现优雅的参数校验,通过注解一键完成校验,配合全局异常处理,让接口更健壮、代码更简洁。

今天就来介绍一下基础注解、实战使用、分组校验、自定义注解、嵌套校验、全局异常处理


一、为什么要用 Bean Validation?

  1. 告别繁琐 if-else

    :不用写大量判空、判断逻辑,代码更简洁

  2. 注解式开发

    :一个注解完成一类校验,可读性极强

  3. 校验规则统一

    :团队协作无歧义

  4. 配合全局异常

    :校验失败自动返回友好提示,无需手动处理

  5. 支持复杂场景

    :分组校验、自定义校验、嵌套对象校验


二、核心依赖引入

SpringBoot 2.x 版本直接引入以下依赖即可:

1 2<dependency> 3<groupId>org.springframework.bootgroupId> 4<artifactId>spring-boot-starter-validationartifactId> 5dependency> 6 7 8<dependency> 9<groupId>org.springframework.bootgroupId> 10<artifactId>spring-boot-starter-webartifactId> 11dependency>

三、最常用校验注解

1. 空与非空校验

  • @NotBlank

    :字符串不能为null,且去除空格后长度大于0(专用字符串)

  • @NotNull

    :不能为null,但可以是空字符串、空集合

  • @NotEmpty

    :不能为null,且长度/大小大于0(字符串、集合、数组)

2. 数值校验

  • @Min

    :数值最小值

  • @Max

    :数值最大值

  • @Positive

    :正数

  • @PositiveOrZero

    :正数或0

  • @Negative

    :负数

  • @NegativeOrZero

    :负数或0

3. 格式校验

  • @Email

    :邮箱格式

  • @Pattern

    :正则表达式自定义格式

  • @Length

    :字符串长度限制

4. 日期与时间

  • @Past

    :必须是过去时间

  • @PastOrPresent

    :过去或当前时间

  • @Future

    :必须是未来时间

  • @FutureOrPresent

    :未来或当前时间


四、单对象参数校验

1. 封装实体类 + 校验注解

1importlombok.Data; 2importjavax.validation.constraints.*; 3 4/** 5* 用户参数接收类 6*/ 7@Data 8publicclassUserDTO{ 9 10@NotBlank(message ="用户ID不能为空") 11privateString userId; 12 13@NotBlank(message ="用户名不能为空") 14@Length(min =2, max =10, message ="用户名长度必须在2-10位之间") 15privateString username; 16 17@NotNull(message ="年龄不能为空") 18@Min(value =18, message ="年龄必须大于等于18岁") 19@Max(value =60, message ="年龄必须小于等于60岁") 20privateInteger age; 21 22@Email(message ="邮箱格式不正确") 23@NotBlank(message ="邮箱不能为空") 24privateString email; 25 26@Pattern(regexp ="^1[3-9]\\d{9}$", message ="手机号格式不正确") 27privateString phone; 28}

2. Controller 开启校验(@Valid)

@Valid是开启校验的核心注解,必须添加在参数前:

1importorg.springframework.validation.BindingResult; 2importorg.springframework.web.bind.annotation.PostMapping; 3importorg.springframework.web.bind.annotation.RequestBody; 4importorg.springframework.web.bind.annotation.RequestMapping; 5importorg.springframework.web.bind.annotation.RestController; 6 7importjavax.validation.Valid; 8 9@RestController 10@RequestMapping("/user") 11publicclassUserController{ 12 13/** 14* 新增用户 15*/ 16@PostMapping("/add") 17publicResult<String>addUser(@Valid@RequestBodyUserDTO userDTO){ 18// 校验通过,执行业务逻辑 19returnResult.success("用户新增成功"); 20} 21}

五、配合全局异常处理

参数校验失败会抛出MethodArgumentNotValidException,我们在全局异常处理器中捕获,统一返回格式:

1importlombok.extern.slf4j.Slf4j; 2importorg.springframework.web.bind.MethodArgumentNotValidException; 3importorg.springframework.web.bind.annotation.ExceptionHandler; 4importorg.springframework.web.bind.annotation.RestControllerAdvice; 5importjava.util.stream.Collectors; 6 7@Slf4j 8@RestControllerAdvice 9publicclassGlobalExceptionHandler{ 10 11/** 12* 捕获参数校验异常 13*/ 14@ExceptionHandler(MethodArgumentNotValidException.class) 15publicResult<String>handleValidException(MethodArgumentNotValidException e){ 16// 拼接所有错误提示 17String errorMsg = e.getBindingResult().getFieldErrors().stream() 18.map(error -> error.getField()+":"+ error.getDefaultMessage()) 19.collect(Collectors.joining(";")); 20 21 log.error("参数校验异常:{}", errorMsg); 22returnResult.fail(400, errorMsg); 23} 24}

校验失败返回示例

1{ 2"code":400, 3"msg":"年龄必须大于等于18岁;邮箱格式不正确", 4"data":null 5}

六、分组校验

实际业务中,新增修改的校验规则不同:

  • 新增:不需要传 ID

  • 修改:必须传 ID

使用分组校验可完美解决。

1. 定义分组接口

1/** 2* 新增分组 3*/ 4publicinterfaceAddGroup{ 5} 6 7/** 8* 修改分组 9*/ 10publicinterfaceUpdateGroup{ 11}

2. 实体类标注分组

1@Data 2publicclassUserDTO{ 3 4// 修改时必须传ID,新增时不需要 5@NotBlank(message ="用户ID不能为空", groups =UpdateGroup.class) 6privateString userId; 7 8@NotBlank(message ="用户名不能为空", groups ={AddGroup.class,UpdateGroup.class}) 9privateString username; 10}

3. Controller 指定分组

使用@Validated注解指定分组:

1@RestController 2@RequestMapping("/user") 3@Validated 4publicclassUserController{ 5 6// 新增:使用 AddGroup 分组校验 7@PostMapping("/add") 8publicResult<String>add(@Validated(AddGroup.class)@RequestBodyUserDTO userDTO){ 9returnResult.success("新增成功"); 10} 11 12// 修改:使用 UpdateGroup 分组校验 13@PostMapping("/update") 14publicResult<String>update(@Validated(UpdateGroup.class)@RequestBodyUserDTO userDTO){ 15returnResult.success("修改成功"); 16} 17}

七、自定义校验注解

当内置注解不满足业务时,可自定义注解,例如校验性别只能是男/女

1. 自定义注解@Gender

1importjavax.validation.Constraint; 2importjavax.validation.Payload; 3importjava.lang.annotation.*; 4 5@Target({ElementType.FIELD}) 6@Retention(RetentionPolicy.RUNTIME) 7@Constraint(validatedBy =GenderValidator.class)// 绑定校验器 8public@interfaceGender{ 9 10Stringmessage()default"性别只能输入:男/女"; 11 12Class<?>[]groups()default{}; 13 14Class<?extendsPayload>[]payload()default{}; 15}

2. 自定义校验器

1importjavax.validation.ConstraintValidator; 2importjavax.validation.ConstraintValidatorContext; 3 4publicclassGenderValidatorimplementsConstraintValidator<Gender,String>{ 5 6@Override 7publicbooleanisValid(String value,ConstraintValidatorContext context){ 8// 校验逻辑 9return"男".equals(value)||"女".equals(value); 10} 11}

3. 使用自定义注解

1@Gender 2privateString gender;

八、嵌套对象校验

如果参数是嵌套对象,需要在嵌套对象上添加 @Valid才能开启校验:

1@Data 2publicclassUserDTO{ 3@NotBlank 4privateString username; 5 6@Valid// 开启嵌套校验 7@NotNull(message ="地址信息不能为空") 8privateAddressDTO address; 9} 10 11@Data 12classAddressDTO{ 13@NotBlank(message ="详细地址不能为空") 14privateString detail; 15 16@NotBlank(message ="城市不能为空") 17privateString city; 18}

九、单个参数校验(非实体类)

如果接口是零散参数,在类上添加@Validated,直接给参数加注解:

1@RestController 2@RequestMapping("/user") 3@Validated 4publicclassUserController{ 5 6@GetMapping("/get") 7publicResult<String>getUser( 8@NotBlank(message ="用户ID不能为空")String userId, 9@NotNull(message ="状态不能为空")Integer status 10){ 11returnResult.success("查询成功"); 12} 13}

十、总结

  1. @Valid

    :开启实体类参数校验

  2. @Validated

    :开启分组校验、单个参数校验

  3. 常用注解

    @NotBlank@NotNull@Min@Max@Email

  4. 全局异常捕获

    :校验失败自动返回友好提示

  5. 分组校验

    :适配新增/修改不同规则

  6. 自定义注解

    :满足复杂业务校验

学会这套参数校验方案,你的接口健壮性、规范性、安全性直接拉满


http://www.cnnetsun.cn/news/1534701.html

相关文章:

  • 丹青幻境Z-Image Atelier功能全解析:从LoRA切换参数调节到作品保存
  • 【技术解析】UNet++:深度监督与密集跳跃连接如何提升医学图像分割精度
  • JMM内存模型与三大并发问题:从底层原理到问题根治,读懂Java并发核心
  • 保姆级教程:用DDNS-Go搞定动态域名解析,让IPv6远程访问不再掉线
  • 如何通过MetPy实现气象数据的高效处理与可视化
  • Ollama本地模型管理:配置国内镜像源并对比Qwen3-14B-Int4-AWQ部署方案
  • 从MAX3232到SM712:手把手设计一个带防雷保护的RS485工业节点电路
  • YAML2ModelGraph进阶:自定义模块与交互式模型可视化
  • Harmonyos应用实例227:平面向量的坐标运算
  • 显存稳定性测试权威指南:使用memtest_vulkan保障GPU健康
  • BG3ModManager高级配置:从基础设置到专业定制的完全指南
  • OpenClaw语音控制方案:Qwen3-32B镜像实现本地语音指令解析
  • Awesome-Dify-Workflow:多平台内容自动化的效率革命
  • 3分钟掌握Mermaid:用代码思维绘制专业图表的核心技巧
  • 国际电工委员会(IEC)国际标准数据
  • Qt图形视图框架性能调优指南:从QGraphicsScene的ItemIndexMethod到视图更新策略
  • CH224芯片:解锁Type-C接口的PD快充潜能
  • SDMatte镜像CI/CD实践:GitHub Actions自动构建、镜像签名、Harbor仓库推送
  • MTools开发进阶:自定义AI模型接入指南
  • openIot:面向ESP32的嵌入式IoT应用框架深度解析
  • TlbbGmTool:重构游戏管理体验的全栈解决方案
  • AI绘画提示词高级技巧:用Disco Diffusion生成赛博朋克风格壁纸的实战案例
  • 我的网站被安全扫描工具警告了?Nginx这些安全头你配齐了吗
  • 【2026 职场洗牌系列 03】华尔街的冷汗:当算法比你更懂财报,金融人路在何方?
  • 保姆级教程:手把手教你用PX4源码中的Mahony算法搞定无人机姿态解算(附代码逐行解析)
  • 树莓派+Python+OpenCV:从安装到调用摄像头实时处理视频的完整项目流程
  • 解决显存难题!CogVideoX-2b优化版实测,8G显卡流畅生成视频
  • ECharts地图可视化进阶:当官方数据源不够用时,如何自己‘造’一份GeoJSON?
  • SenseVoice-Small语音识别模型内网穿透部署方案:实现远程调用
  • 脑机接口数据集处理知识体系重构:从信号解码到临床转化