我想使用 SpringBoot @Valid 来验证 http 请求字段,但基于同一个 http 请求的另一个字段。
我有以下代码:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<artifactId>question</artifactId>
<properties>
<maven.compiler.source>23</maven.compiler.source>
<maven.compiler.target>23</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
class FieldValidationApplication {
public static void main(String[] args) {
SpringApplication.run(FieldValidationApplication.class, args);
}
}
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 question(@Valid @RequestBody SomeRequest someRequest) {
return "please validate the field";
}
}
record SomeRequest(int score,
String fieldPositive,
String fieldZeroAndNegative
)
{ }
验证规则非常简单:
请求负载包含字段 score。如果字段 score 的值严格为正数,则我需要检查字段 fieldPositive 是否为有效字符串,以及 fieldZeroAndNegative 是否为 null。
例如:
{
"score": 1,
"fieldPositive": "thisisok"
}
但那些不是:
{
"score": 1
}
{
"score": 1,
"fieldPositive": ""
}
{
"score": 1,
"fieldPositive": "below fieldZeroAndNegative should be null",
"fieldZeroAndNegative": "not ok"
}
其他字段的规则类似(代码就在下面)。
这是我尝试过的,我创建了自定义注释:
record SomeRequest(int score,
@ValidateThisFieldOnlyIfScoreIsPositive String fieldPositive,
@ValidateThisFieldOnlyIfScoreIsZeroOrNegative String fieldZeroAndNegative
)
{ }
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 = ValidateThisFieldOnlyIfScoreIsPositiveValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface ValidateThisFieldOnlyIfScoreIsPositive
{
String message() default "Field is invalid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
class ValidateThisFieldOnlyIfScoreIsPositiveValidator implements ConstraintValidator<ValidateThisFieldOnlyIfScoreIsPositive, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
System.out.println("hello, the value of the field fieldPositive is " + value);
System.out.println("However, I cannot get the value of the field score");
if (" SomeRequest score " > 0) { //how to get the value of the field score here?
return value != null && !value.isEmpty() && value.length() > 3;
}
if (" SomeRequest score" <= 0) {
return value == null;
}
...
}
}
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 = ValidateThisFieldOnlyIfScoreIsZeroOrNegativeValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface ValidateThisFieldOnlyIfScoreIsZeroOrNegative
{
String message() default "Field is invalid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
class ValidateThisFieldOnlyIfScoreIsZeroOrNegativeValidator implements ConstraintValidator<ValidateThisFieldOnlyIfScoreIsZeroOrNegative, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
System.out.println("hello, the value of the field fieldZeroAndNegative is " + value);
System.out.println("However, I cannot get the value of the field score");
if (" SomeRequest score " <= 0) { //how to get the value of the field score here?
return value != null && !value.isEmpty() && value.length() > 3;
}
if (" SomeRequest score" > 0) {
return value == null;
}
}
}
我不确定每个字段使用一个注释是否是可行的方法。
问题:
如何在验证器中获取同一请求的两个字段(或多个字段)?
要根据同一请求对象中另一个字段的值来验证一个字段,您需要在类级别而不是字段级别应用验证。这样,验证器就可以访问对象的所有字段。
解决方案:使用类级约束
创建可应用于整个类的自定义注释:
创建一个可以访问SomeRequest的所有字段的验证器:
修改
SomeRequest
以使用新的验证注释:Spring Boot 在处理请求时将自动使用注释验证 SomeRequest:
为什么有效
score
在一个地方检查两个字段及其依赖字段。addPropertyNode()
,从而提高 API 响应的清晰度。这种方法可确保您的请求根据分数得到正确验证,而无需每个字段进行单独的注释。