validation
概述
spring-boot-starter-validation是 Spring Boot 官方提供的用于数据校验的启动器,它基于 Bean Validation API (JSR 380) 标准,并默认使用 Hibernate Validator 作为其实现。这个框架能让你通过声明式的注解,轻松地对控制器接收的参数、服务层的方法参数以及任何 Java Bean 进行校验,确保数据的完整性和有效性。
添加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>常用注解
| 注解 | 适用类型 | 说明 | 示例 |
|---|---|---|---|
| 基础校验 | |||
@NotNull | 任意类型 | 校验字段值不能为null | @NotNull(message = "ID不能为空") |
@NotEmpty | String/Collection/Array/Map | 校验对象不能为null且长度/大小必须大于 0 | @NotEmpty(message = "列表不能为空") |
@NotBlank | String | 校验字符串不能为null且去首尾空格后长度大于 0 | @NotBlank(message = "名称不能为空") |
@Null | 任意类型 | (新增) 校验字段值必须为null | @Null(message = "创建时ID必须为空") |
@Size | String/Collection/Array/Map | 校验长度或大小是否在指定范围内 | @Size(min=6, max=20, message="长度需在6-20之间") |
| 数值校验 | |||
@Min/@Max | 数值类型 | 校验数值是否大于等于/小于等于指定值 | @Min(value=18, message="年龄需大于18") |
@DecimalMin/@DecimalMax | (新增) BigDecimal/String/数值 | 类似于 Min/Max,但支持字符串值,适用于高精度数值(如金额) | @DecimalMin(value="0.01", message="金额不能小于0.01") |
@Digits | (新增) BigDecimal/数值/String | 校验数值必须符合指定的整数位和小数位长度 | @Digits(integer=5, fraction=2, message="金额格式错误") |
@Positive | (新增) 数值类型 | 校验数值必须为正数(> 0) | @Positive(message = "数量必须为正数") |
@PositiveOrZero | (新增) 数值类型 | 校验数值必须为正数或 0(>= 0) | @PositiveOrZero(message = "数量不能为负") |
@Negative | (新增) 数值类型 | 校验数值必须为负数(< 0) | @Negative(message = "数值必须为负") |
@NegativeOrZero | (新增) 数值类型 | 校验数值必须为负数或 0(<= 0) | @NegativeOrZero(message = "数值不能为正") |
| 逻辑校验 | |||
@AssertTrue | (新增) Boolean | 校验字段值必须为true | @AssertTrue(message = "必须同意协议") |
@AssertFalse | (新增) Boolean | 校验字段值必须为false | @AssertFalse(message = "逻辑状态必须为假") |
| 日期校验 | |||
@Future/@Past | 日期时间类型 | 校验日期是否在未来/过去 | @Future(message = "活动日期必须是未来") |
@FutureOrPresent | (新增) 日期时间类型 | 校验日期是否在未来或当前时刻 | @FutureOrPresent(message = "日期不能早于现在") |
@PastOrPresent | (新增) 日期时间类型 | 校验日期是否在过去或当前时刻 | @PastOrPresent(message = "出生日期不能在未来") |
| 格式校验 | |||
@Email | String | 校验是否为有效的邮箱格式 | @Email(message = "邮箱格式不正确") |
@Pattern | String | 校验字符串是否符合指定的正则表达式 | @Pattern(regexp="^1[3-9]\\d{9}$", message="手机号格式错误") |
自定义注解
定义注解
@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@Constraint(validatedBy=PhoneValidator.class)// 指定校验器public@interfacePhone{Stringmessage()default"手机号格式不正确";Class<?>[]groups()default{};Class<?extendsPayload>[]payload()default{};}实现校验逻辑
publicclassPhoneValidatorimplementsConstraintValidator<Phone,String>{privatestaticfinalStringPHONE_REGEX="^1[3-9]\\d{9}$";@OverridepublicbooleanisValid(Stringvalue,ConstraintValidatorContextcontext){returnvalue!=null&&value.matches(PHONE_REGEX);}}使用自定义注解
publicclassUserDTO{@PhoneprivateStringphoneNumber;}实战开发步骤
在DTO中定义校验规则
将校验注解直接添加到数据传输对象(DTO)的字段上,并可以通过message属性自定义错误信息。
publicclassUserCreateRequest{@NotBlank(message="用户名不能为空")@Size(max=50,message="用户名长度不能超过50")privateStringname;@Email(message="邮箱格式不正确")privateStringemail;@Min(value=18,message="年龄必须大于等于18")privateIntegerage;// getters and setters...}在Controller层触发校验
在控制器方法的参数前使用@Valid或@Validated注解,即可触发对该参数的校验。
@RestController@RequestMapping("/users")publicclassUserController{@PostMappingpublicResponseEntity<String>createUser(@RequestBody@ValidUserCreateRequestrequest){// 如果校验通过,代码会执行到这里returnResponseEntity.ok("用户创建成功");}}@Valid与@Validated的区别
| 特性 | @Valid(JSR-303/349 标准) | @Validated(Spring 扩展) |
|---|---|---|
| 分组校验 | 不支持 | 支持 |
| 方法级校验 | 不支持 | 支持 (需配合@Validated在类上) |
| 嵌套对象校验 | 支持 (需在字段上使用) | 支持 (需在字段上使用) |
统一处理校验异常
校验失败会抛出MethodArgumentNotValidException异常。通常使用@RestControllerAdvice进行全局统一处理,向客户端返回结构化的错误信息。
@RestControllerAdvicepublicclassGlobalExceptionHandler{@ExceptionHandler(MethodArgumentNotValidException.class)publicResponseEntity<Map<String,String>>handleValidationException(MethodArgumentNotValidExceptionex){Map<String,String>errors=newHashMap<>();// 收集所有字段错误信息ex.getBindingResult().getFieldErrors().forEach(err->errors.put(err.getField(),err.getDefaultMessage()));returnResponseEntity.badRequest().body(errors);}}分组校验
分组校验允许你在不同场景下应用不同的校验规则,例如创建(Create)和更新(Update)操作。
定义分组接口
publicclassUserGroups{publicinterfaceCreate{}publicinterfaceUpdate{}}在DTO中指定分组
publicclassUserDTO{// 创建时ID必须为null,更新时ID不能为null@Null(groups=UserGroups.Create.class)@NotNull(groups=UserGroups.Update.class)privateLongid;@NotBlank(groups={UserGroups.Create.class,UserGroups.Update.class})privateStringusername;// getters and setters...}在Controller中使用指定分组
@PostMapping("/create")publicStringcreate(@RequestBody@Validated(UserGroups.Create.class)UserDTOuser){...}@PutMapping("/update")publicStringupdate(@RequestBody@Validated(UserGroups.Update.class)UserDTOuser){...}