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

SpringMVC 数据校验和BindingResult以及自定义校验注解

数据校验和BindingResult

    • BindingResult
      • 概念:
      • 用法:
      • 常用方法:
    • 校验概述
      • 标准注解
      • 核心使用示例
      • 在 Spring 中使用
      • 用法总结
    • 数据类型校验
      • 一、字符串类型校验
        • 基础验证
        • 长度和格式验证
      • 二、数值类型校验
        • 基础数值验证-整数、浮点、正负数
      • 三、日期时间类型校验
      • 四、集合类型校验
      • 五、布尔类型校验
      • 六、枚举类型校验
      • 七、嵌套对象校验
      • 八、★ 自定义注解校验复杂逻辑
        • 1. 自定义注解
        • 2. 自定义注解校验器
        • 3. 在实体类中使用
        • 4. 访问并验证
      • 九、完整综合示例
      • 十、最佳实践总结
        • 1. **按数据类型选择合适注解**
        • 2. **验证层次**
        • 3. **组合验证策略**
    • 数据校验注解使用示例
      • 1. 在POM文件中导入jar包
      • 2. 创建一个实体类,并使用参数校验注解。
      • 3. 在控制器中开启校验。
    • 数据校验错误加入到全局统一异常

示例代码地址:https://gitee.com/hua5h6m/framework-java/tree/master/spring-validated-exception

BindingResult

BindingResultSpring MVC中用于处理数据绑定的一个接口,它继承自Errors接口。
在表单验证中,我们通常使用BindingResult来存储和获取数据绑定 errors 和验证 errors

概念:

  • 当我们在Controller的方法中使用@Valid@Validated注解对模型对象进行验证时,紧跟在被验证模型对象参数之后可以添加一个BindingResult参数。

  • BindingResult参数会包含验证结果,包括所有错误信息(如字段错误、全局错误等)。

  • 如果不存在BindingResult参数,那么当验证失败时,Spring MVC会抛出异常(如MethodArgumentNotValidException)。而如果有BindingResult,则异常不会被抛出,我们可以通过BindingResult对象来自定义处理错误。

用法:

  • 在Controller方法中,将BindingResult放在被验证的模型对象参数后面

  • 通过BindingResult的方法来判断是否有错误,并处理这些错误。

常用方法:

  • hasErrors(): 判断是否有错误。

  • hasFieldErrors(String field): 判断指定字段是否有错误。

  • getFieldError(String field): 获取指定字段的错误。

  • getAllErrors(): 获取所有错误(包括字段错误和全局错误)。

校验概述

在WEB应用三层架构体系中,表述层负责接口搜浏览器提交的数据,业务逻辑层负责数据的处理。为了能够让业务逻辑层基于正确的数据进行处理,我们需要在表述层对数据进行检查,将错误的信息隔绝在业务逻辑层之外。
JSR303是JAVA为Bean数据合法性校验提供的标准框架,他已经包含在JavaEE 6.0标准中。JSR303通过在Bean属性上标注类似于@NotNull@Max等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。

  1. Spring 4.0版本已经拥有自己独立的数据校验框架,同时支持JSR303标准的校验框架。Spring在进行数据绑定时,可同时调用校验框架完成数据校验工作。
  2. 在SpringMVC中,可直接通过注解驱动mvc:annotation-driven的方式进行数据校验。
  3. Spring的LocalValidatorFactoryBean,即可将其注入到需要数据校验的Bean中。
  4. Spring本身并没有提供JSR303的实现,所以必须将JSR303的实现者的jar包放到类路径下。

配置mvc:annotation-driven后,SpringMVC会默认装配好一个LocalValidatorFactoryBean,通过在处理方法的入参上标注@Validated注解,即可让SpringMVC在完成数据绑定后执行数据校验的工作。

标准注解

这些是核心规范中包含的注解,适用于大多数场景。

注解功能描述示例
@NotNull验证注解的元素值不能为 null。但可以是空字符串或 0。@NotNull private String name;
@Null验证注解的元素值必须为 null@Null private String unusedField;
@NotBlank验证字符串不能为 null,且必须至少包含一个非空白字符(即修剪后长度 > 0)。@NotBlank private String username;
@NotEmpty验证元素不能为 null 或空。支持 CharSequence, Collection, Map, Array。@NotEmpty private List<String> roles;
@Size验证元素大小在指定范围内(包括边界)。支持字符串、集合、数组、Map。@Size(min=2, max=10) private String password;
@Min验证数字值大于或等于指定的最小值。@Min(18) private Integer age;
@Max验证数字值小于或等于指定的最大值。@Max(100) private Integer score;
@DecimalMin验证 BigDecimal/BigInteger/字符串等大于或等于指定的最小值(值包含在字符串内,可指定是否包含边界)。@DecimalMin("0.0") private BigDecimal price;
@DecimalMax验证 BigDecimal/BigInteger/字符串等小于或等于指定的最大值。@DecimalMax("10000.00") private BigDecimal budget;
@Digits验证数字的整数部分和小数部分的位数是否符合要求。@Digits(integer=3, fraction=2) // 如 123.45
@Positive验证数字必须是正数(大于 0)。@Positive private Integer quantity;
@PositiveOrZero验证数字必须是正数或零@PositiveOrZero private Integer stock;
@Negative验证数字必须是负数(小于 0)。@Negative private BigDecimal balance;
@NegativeOrZero验证数字必须是负数或零@NegativeOrZero private BigDecimal delta;
@Email验证字符串是否是合法的电子邮件地址(默认正则表达式,可通过 regexp 覆盖)。@Email private String contactEmail;
@Pattern验证字符串是否匹配指定的正则表达式@Pattern(regexp = "^[a-zA-Z0-9_]+$")
@Future验证日期或时间是否在当前时间之后@Future private LocalDate startDate;
@FutureOrPresent验证日期或时间是否在当前时间之后或等于当前时间@FutureOrPresent private LocalDateTime eventTime;
@Past验证日期或时间是否在当前时间之前@Past private Date birthDate;
@PastOrPresent验证日期或时间是否在当前时间之前或等于当前时间@PastOrPresent private LocalDate reportDate;
@AssertTrue验证布尔值必须为 true。常用于依赖其他字段的逻辑验证。@AssertTrue private Boolean isAgreedToTerms;
@AssertFalse验证布尔值必须为 false@AssertFalse private Boolean isSuspended;

核心使用示例

以下是一个综合使用这些注解的实体类示例:

import jakarta.validation.constraints.*;
import org.hibernate.validator.constraints.Length;public class User {@NotNull(message = "用户ID不能为空")private Long id;@NotBlank(message = "用户名不能为空")@Length(min = 3, max = 20, message = "用户名长度必须在3-20个字符之间")@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")private String username;@NotBlank(message = "邮箱不能为空")@Email(message = "邮箱格式不正确")private String email;@Min(value = 18, message = "年龄必须大于等于18岁")@Max(value = 100, message = "年龄必须小于等于100岁")private Integer age;@Future(message = "会员有效期必须是一个将来的日期")private LocalDate membershipExpiry;// ... 省略 getter 和 setter
}

在 Spring 中使用

在 Spring Boot 中,Bean Validation 被无缝集成。你只需要:

  1. 在依赖中引入 spring-boot-starter-validation
  2. 在 Controller 的方法参数上使用 @Valid@Validated 注解来触发验证。
@RestController
public class UserController {@PostMapping("/users")public ResponseEntity<User> createUser(@RequestBody @Valid User user) {// 如果 user 对象的验证失败,Spring 会自动抛出 MethodArgumentNotValidException// 业务逻辑...return ResponseEntity.ok(user);}
}

用法总结

  • 基础三剑客@NotNull, @NotBlank, @NotEmpty 用于非空检查。
  • 范围与大小@Size, @Min, @Max, @Range 用于控制大小和范围。
  • 格式与模式@Email, @Pattern 用于验证特定格式。
  • 时间相关@Future(未来), @Past(过去) 等用于日期验证。
  • 逻辑验证@AssertTrue/False 用于复杂的业务逻辑验证。

数据类型校验

一、字符串类型校验

基础验证
public class StringValidation {@NotNull                    // 不能为nullprivate String requiredField;@NotBlank                   // 非null且至少一个非空白字符private String username;@NotEmpty                   // 非null且非空字符串private String notEmptyString;
}
长度和格式验证
public class StringFormatValidation {@Size(min = 2, max = 50)                    // 长度范围private String name;@Length(min = 8, max = 20)                  // Hibernate扩展,专用于字符串private String password;@Email                                      // 邮箱格式private String email;@Pattern(regexp = "^[A-Za-z0-9+_.-]+@(.+)$") // 自定义邮箱正则private String customEmail;@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{8,}$") // 密码强度private String strongPassword;@URL                                        // URL格式验证private String websiteUrl;
}

二、数值类型校验

基础数值验证-整数、浮点、正负数
public class NumberValidation {// 整数验证@Min(0) @Max(100)                          // 整数范围private Integer percentage;@Range(min = 1, max = 150)                 // Hibernate扩展,整数范围private Integer age;// 小数/高精度验证@DecimalMin("0.0")                         // 最小值(字符串形式)@DecimalMax("9999.99")private BigDecimal price;@Digits(integer = 4, fraction = 2)         // 位数控制private BigDecimal amount;// 正负验证@Positive                                  // 必须为正数private Integer positiveNumber;@PositiveOrZero                            // 正数或零private Long nonNegativeId;@Negative                                  // 必须为负数private Double negativeValue;@NegativeOrZero                            // 负数或零private Integer nonPositiveCount;
}

三、日期时间类型校验

public class DateTimeValidation {@Future                                    // 将来时间private LocalDate startDate;@FutureOrPresent                           // 将来或现在private LocalDateTime eventTime;@Past                                      // 过去时间private Date birthDate;@PastOrPresent                             // 过去或现在private LocalDateTime lastLogin;// 自定义日期范围验证(需要自定义注解或方法验证)// 例如:确保结束日期在开始日期之后
}

四、集合类型校验

public class CollectionValidation {@NotNull@NotEmpty                                  // 集合不能为空private List<String> roles;@Size(min = 1, max = 10)                  // 集合大小范围private Set<Long> userIds;@NotEmpty@Size(max = 5)                            // Map不能为空且最多5个元素private Map<String, String> properties;@UniqueElements                           // Hibernate扩展,元素必须唯一private List<String> uniqueTags;// 数组验证@Size(min = 1)                            // 数组至少一个元素private String[] items;
}

五、布尔类型校验

public class BooleanValidation {@AssertTrue                               // 必须为trueprivate Boolean termsAccepted;@AssertFalse                              // 必须为falseprivate Boolean isExpired;@NotNull                                  // 不能为nullprivate Boolean activeFlag;
}

六、枚举类型校验

public class EnumValidation {@NotNull                                  // 枚举不能为nullprivate Status status;// 自定义枚举值验证(如果需要特定值)
}enum Status {PENDING, ACTIVE, INACTIVE, DELETED
}

七、嵌套对象校验

public class NestedValidation {@NotNull@Valid                                     // 启用嵌套对象验证private Address address;@Valid                                     // 集合内的对象也会被验证private List<@Valid Product> products;
}public class Address {@NotBlankprivate String street;@NotBlankprivate String city;@Size(min = 5, max = 10)private String postalCode;
}

八、★ 自定义注解校验复杂逻辑

自定义校验需要:自定义注解和自定义注解校验器。

1. 自定义注解
  • 我们自定义一个性别校验注解,规定数据值只能是男和女。
  • 重点在:@Constraint(validatedBy = {GenderValidation.class})约束注解,我们必须在这个地方填写我们自定义的校验器GenderValidation.class
/*** 自定义数据校验注解*/
// 这个注解只能用在类的字段(成员变量)上
@Target(ElementType.FIELD)
// 表示注解在运行时保留,可以通过反射读取
@Retention(RetentionPolicy.RUNTIME)
// 元注解,这个注解应该被包含在JavaDoc中
@Documented
// 校验器,声明这是一个Bean Validation约束注解
@Constraint(validatedBy = {GenderValidation.class})
public @interface Gender {// 定义验证失败时的错误消息,默认提示信息String message() default "性别只能是男和女";// 用于分组验证,创建用户时验证A组字段,更新用户时验证B组字段Class<?>[] groups() default {};// 用于传递元数据给验证过程Class<? extends Payload>[] payload() default {};
}
2. 自定义注解校验器

步骤:

  1. 实现ConstraintValidator接口,接口参数类型必须为注解名称准备接收的参数类型。注意:我们@Gender注解,规定只能用在String类型的字段上,因此接口准备接收的参数类型为String
  2. 实现接口isValid(),在这个接口中编写判定逻辑。
/*** 自定义数据校验注解实现类*/
public class GenderValidation implements ConstraintValidator<Gender, String> {@Overridepublic void initialize(Gender constraintAnnotation) {ConstraintValidator.super.initialize(constraintAnnotation);}@Overridepublic boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {// 只有当参数s,为男或女时为真return "男".equals(s) || "女".equals(s);}
}
3. 在实体类中使用
  • @Gender注解,是我们自定义的注解。只允许添加男和女。
@Data
public class User {@NotNull@Min(1)private Integer id;@NotBlank(message = "名称不能为空")private String name;@NotBlank@Size(min = 6, max = 12, message = "The password must be 6 to 12 characters in length.")private String password;@NotNull@AssertTrue(message = "必须同意注册协议!")private Boolean active;@Gender(message = "只允许为男性或女性!")private String gender;}
4. 访问并验证
    1. 准备JSON字符串:
{"id": 1,"name":"张三","password": "111111","active": true,"gender": "9999"
}
    1. 访问地址。
    /*** 全局异常处理,捕获数据校验异常,查看数据异常类型。* @param user 实体类* @return*/@PostMapping("data2")public R data2(@Validated @RequestBody User user) {return R.success(user.toString());}
  • 结果返回:自定义校验注解,成功生效!
{"code": 500,"msg": "数据校验异常","data": {"gender": "只允许为男性或女性!"}
}

九、完整综合示例

public class UserRegistrationDto {// 字符串验证@NotBlank(message = "用户名不能为空")@Size(min = 3, max = 20, message = "用户名长度3-20字符")@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字、下划线")private String username;@NotBlank@Email(message = "邮箱格式不正确")private String email;// 数值验证@Min(value = 18, message = "必须年满18岁")@Max(value = 100, message = "年龄不能超过100岁")private Integer age;@DecimalMin("0.0")@Digits(integer = 6, fraction = 2)private BigDecimal balance;// 日期验证@Past(message = "生日必须是过去日期")private LocalDate birthDate;@Futureprivate LocalDateTime membershipExpiry;// 集合验证@Size(min = 1, max = 5, message = "至少选择1个,最多5个兴趣")private List<String> interests;// 布尔验证@AssertTrue(message = "必须接受服务条款")private Boolean termsAccepted;// 嵌套对象验证@NotNull@Validprivate Address address;// 自定义验证@FieldMatch(first = "password", second = "confirmPassword")private String password;private String confirmPassword;
}

十、最佳实践总结

1. 按数据类型选择合适注解
  • 字符串@NotBlank, @Size, @Pattern, @Email
  • 数值@Min/@Max(整数), @DecimalMin/@DecimalMax(小数)
  • 日期@Past, @Future 系列
  • 集合@NotEmpty, @Size
  • 布尔@AssertTrue/@AssertFalse
2. 验证层次
public class ValidationHierarchy {@NotNull                    // 第一层:非空检查@NotBlank                   // 第二层:内容检查  @Size(max = 100)            // 第三层:格式/大小检查@Email                      // 第四层:业务规则检查private String email;
}
3. 组合验证策略
// 基础数据完整性
@NotNull + @NotBlank/@NotEmpty// 格式和范围  
@Size + @Pattern + @Min/@Max// 业务规则
@AssertTrue + 自定义注解 + 类级别验证

数据校验注解使用示例

若想校验用户传递过来的数据,必须使用@Validated@Valid注解开启校验功能。

1. 在POM文件中导入jar包

        <!--导入java规范包,web包是一个精简包--><dependency><groupId>jakarta.platform</groupId><artifactId>jakarta.jakartaee-web-api</artifactId><version>10.0.0</version><scope>provided</scope></dependency><!--扩展规范--><!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator --><dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><version>8.0.2.Final</version></dependency><!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator-annotation-processor --><!--构建时会有报错警告--><dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator-annotation-processor</artifactId><version>8.0.2.Final</version></dependency>

2. 创建一个实体类,并使用参数校验注解。

@Data
public class User {@NotNull@Min(1)private Integer id;@NotBlank(message = "名称不能为空")private String name;@NotBlank@Size(min = 6, max = 12, message = "The password must be 6 to 12 characters in length.")private String password;@NotNull@AssertTrue(message = "必须同意注册协议!")private Boolean active;}

3. 在控制器中开启校验。

  • 必须使用@Validated@Valid注解,放在模型对象的前面,方能开启校验对象功能
  • 必须放在最前面。
@RestController
@RequestMapping("user")
public class UserController {@PostMapping("info")public String info(@Validated @RequestBody User user) {return "user info信息: " + user.toString();}@PostMapping("data")public String data(@Validated @RequestBody User user, BindingResult result) {if (result.hasErrors()) {return result.getFieldError().toString();}return "user info信息: " + user.toString();}
}
  • 不需要@Valid@Validated注解的情况,在方法参数中使用
    @PostMapping("param")public String param(@RequestParam(value = "name") @Size(min = 2) String name) {return "user info信息: " + name;}

数据校验错误加入到全局统一异常

  • 当我们使用数据校验注解是,如果数据校验失败报错则会导致页面跳转到400页面,并不会返回我么之前定义的全局统一异常格式。
  • 因此,若想将数据校验失败的异常错误加入到全局异常中,需要定义一个异常处理器。

我们通常将数据校验错误纳入全局统一异常处理,是为了统一处理校验失败的情况,避免在每个控制器方法中重复处理绑定结果(BindingResult),从而提高代码的复用性和可维护性。

在Spring MVC中,我们通常使用@Valid@Validated注解来触发数据校验。如果校验失败,默认会抛出MethodArgumentNotValidException(对于@RequestBody)或BindException(对于@ModelAttribute)。我们可以通过全局异常处理器(@ControllerAdvice)来捕获这些异常,并统一返回错误信息。

这样做的好处:

  • 避免在每个控制器方法中编写重复的BindingResult处理代码。
  • 统一错误响应格式,便于前端处理。
  • 将业务逻辑与参数校验错误处理分离,使代码更清晰。
http://www.dtcms.com/a/512082.html

相关文章:

  • [明道云专栏·里程碑] 从第一篇到第一百篇:这是一场属于“低代码实战者”的长跑
  • Ubuntu 安装 Harbor
  • 网站屏蔽ip地址河南网站备案系统短信
  • 中科院网站做的好的院所双鸭山网站建设公司
  • Linux配置Samba文件共享并访问Windows文件
  • Cursor配置markdown转Word的MCP工具教程
  • 常见springboot相关注解
  • ◆comfyUI教程◆第2章13节 XL模型专用工作流与refiner精炼
  • PostIn V1.3.1版本发布,新增在线更新程序命令,新增请求体json支持引用变量
  • asp网站作业下载二级建造师报名时间2022年官网
  • 信息平台网站建设微信商城网站方案
  • OpenCV计算机视觉库
  • 区块链的效率引擎:梅克尔树原理解析与应用
  • TF-A CMake构建系统
  • PowerShell下载和安装图解(附安装包,适合新手)
  • 分析网易严选网站开发wordpress类别图标
  • 元服务上架自检
  • 8款企业微信SCRM工具功能对比分析
  • 个人网站建设小江网站建设做网站需要多少钱
  • 蚂蚁集团开源的万亿参数模型Ling-1T和Ring-1T-preview
  • 南宁希噢网站开发工作室wordpress打造官网
  • 区块链的基石:深入解析哈希指针与链表数据结构
  • Windows 系统下使用 Docker 安装 Milvus 向量数据库
  • 【TDengine TSDB】使用DBeaver客户端访问
  • Dockerfile及其部署镜像步骤
  • 建设部网站资质标准昌平电子网站建设
  • 大模型-7种大模型微调方法 上
  • (三).Net, NextJS(NextJs初始化/图片闪烁/定义types/分页/过滤/)
  • 中国建设银行网站密码忘了怎么办企业网站设计策划
  • 【JavaScript】every 方法的详解与实战