SpringBoot中基于其他字段值实现@Valid单字段校验的方案
Spring Boot中基于其他字段值的@Valid字段校验方案
问题背景
在Spring Boot项目中,需要使用@Valid注解校验HTTP请求体,但校验规则依赖于请求中的其他字段:
- 当
score严格为正时,fieldPositive必须是非空且长度大于3的有效字符串,同时fieldZeroAndNegative必须为null - 当
score小于等于0时,fieldZeroAndNegative必须是非空且长度大于3的有效字符串,同时fieldPositive必须为null
此前尝试给单个字段添加自定义注解,但字段级别的校验器无法获取同一请求中其他字段(如score)的值,导致校验逻辑无法完成。
解决方案:类级别自定义校验
字段级校验器只能访问当前字段的值,要实现跨字段校验,必须使用类级别的自定义校验注解,这样校验器可以拿到整个请求对象实例,从而访问所有字段的值。
步骤1:创建类级别的校验注解
import jakarta.validation.Constraint; import jakarta.validation.Payload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Constraint(validatedBy = ScoreDependentFieldValidator.class) @Target({ElementType.TYPE}) // 标注在类/record上 @Retention(RetentionPolicy.RUNTIME) public @interface ValidScoreDependentFields { String message() default "字段不符合score对应的校验规则"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
步骤2:实现类级别校验器
校验器的泛型参数指定为SomeRequest,直接拿到整个请求对象,访问所有字段:
import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; public class ScoreDependentFieldValidator implements ConstraintValidator<ValidScoreDependentFields, SomeRequest> { @Override public boolean isValid(SomeRequest request, ConstraintValidatorContext context) { if (request == null) { return true; // 空对象校验逻辑可按需调整,通常配合@NotNull使用 } int score = request.score(); String fieldPositive = request.fieldPositive(); String fieldZeroAndNegative = request.fieldZeroAndNegative(); // 校验score>0的场景 if (score > 0) { boolean positiveFieldValid = fieldPositive != null && !fieldPositive.isEmpty() && fieldPositive.length() > 3; boolean negativeFieldValid = fieldZeroAndNegative == null; return positiveFieldValid && negativeFieldValid; } // 校验score<=0的场景 else { boolean negativeFieldValid = fieldZeroAndNegative != null && !fieldZeroAndNegative.isEmpty() && fieldZeroAndNegative.length() > 3; boolean positiveFieldValid = fieldPositive == null; return negativeFieldValid && positiveFieldValid; } } }
步骤3:在请求类上添加注解
将类级别校验注解添加到SomeRequest这个record上:
@ValidScoreDependentFields // 新增类级别校验注解 public record SomeRequest( int score, String fieldPositive, String fieldZeroAndNegative ) {}
步骤4:保持原有控制器逻辑不变
控制器依然使用@Valid注解触发校验:
import jakarta.validation.Valid; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController class FieldValidationController { @PostMapping("/validate") String validate(@Valid @RequestBody SomeRequest someRequest) { return "校验通过"; } }
自定义错误提示(可选)
如果需要针对不同错误场景返回精准提示,可在校验器中禁用默认消息,添加自定义错误信息:
@Override public boolean isValid(SomeRequest request, ConstraintValidatorContext context) { if (request == null) { return true; } int score = request.score(); String fieldPositive = request.fieldPositive(); String fieldZeroAndNegative = request.fieldZeroAndNegative(); boolean isValid = true; if (score > 0) { if (fieldPositive == null || fieldPositive.isEmpty() || fieldPositive.length() <= 3) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("当score>0时,fieldPositive必须是非空且长度大于3的字符串") .addPropertyNode("fieldPositive") .addConstraintViolation(); isValid = false; } if (fieldZeroAndNegative != null) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("当score>0时,fieldZeroAndNegative必须为null") .addPropertyNode("fieldZeroAndNegative") .addConstraintViolation(); isValid = false; } } else { if (fieldZeroAndNegative == null || fieldZeroAndNegative.isEmpty() || fieldZeroAndNegative.length() <= 3) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("当score<=0时,fieldZeroAndNegative必须是非空且长度大于3的字符串") .addPropertyNode("fieldZeroAndNegative") .addConstraintViolation(); isValid = false; } if (fieldPositive != null) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("当score<=0时,fieldPositive必须为null") .addPropertyNode("fieldPositive") .addConstraintViolation(); isValid = false; } } return isValid; }
内容的提问来源于stack exchange,提问作者PatPanda
相关产品推荐
相关产品推荐

