Spring Boot中@Valid验证@NotBlank与@Size失效问题排查
问题描述
调用POST接口创建Package时,传入不符合校验规则的数据(比如startingPoint的country为"a"、destinationPoint缺少country字段),但@NotBlank和@Size校验未生效,Package仍被创建,更换版本、调整导入后问题依旧,求原因及解决办法。
相关代码
请求JSON
{ "startingPoint": { "country": "a", "city": "ab", "postalCode": "00-001" }, "destinationPoint": { "city": "Berlin", "postalCode": "10115" }, "expectedShippingTime": "2023-09-20T10:00:00.000Z", "trackingCode": "12345678901" }
Package实体类
package com.courier.application.model; import lombok.*; import org.bson.types.ObjectId; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; @Document(collection = "packages") @Data @AllArgsConstructor @NoArgsConstructor @Builder public class Package { @Id private ObjectId packageId; private Point startingPoint; private Point destinationPoint; private String expectedShippingTime; private String trackingCode; }
Point实体类
package com.courier.application.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import jakarta.validation.constraints.Size; import jakarta.validation.constraints.NotBlank; @Data @AllArgsConstructor @NoArgsConstructor public class Point { @NotBlank @Size(min = 2, max = 25) private String country; @NotBlank @Size(min = 2, max = 25) private String city; @NotBlank @Size(min = 3, max = 7) private String postalCode; }
Controller类
package com.courier.application.controller; import com.courier.application.error.PackageAlreadyExistException; import com.courier.application.error.PackageBadTrackingCodeException; import com.courier.application.error.PackageNotFoundException; import com.courier.application.model.Package; import com.courier.application.service.PackageService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; import java.util.Optional; @RestController @Validated public class PackageController { @Autowired PackageService packageService; @GetMapping("/find-package") public Optional<Package> findPackageByTrackingCode(String trackingCode) throws PackageNotFoundException, PackageBadTrackingCodeException { return packageService.findByTrackingCode(trackingCode); } @PostMapping("/create-package") public Package createPackage(@Valid @RequestBody Package createdPackage) throws PackageAlreadyExistException, PackageBadTrackingCodeException { return packageService.saveCreatedPackage(createdPackage); } }
pom.xml
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.3</version> <relativePath/> </parent> <groupId>com.courier.application</groupId> <artifactId>courier-system-application</artifactId> <version>0.0.1-SNAPSHOT</version> <name>courier-system-application</name> <description>System designed for tracking Your package!</description> <properties> <java.version>20</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> <version>3.1.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </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> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>
问题原因及解决办法
原因
Package实体类中的startingPoint和destinationPoint字段未添加@Valid注解,导致Spring Validation无法递归校验嵌套的Point对象。即使Point类本身定义了校验规则,外层对象没有触发嵌套校验的情况下,这些规则不会生效。
解决办法
在Package类的startingPoint和destinationPoint字段上添加@Valid注解,触发Spring对嵌套对象的递归校验:
修改后的Package实体类:
package com.courier.application.model; import lombok.*; import org.bson.types.ObjectId; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import jakarta.validation.Valid; @Document(collection = "packages") @Data @AllArgsConstructor @NoArgsConstructor @Builder public class Package { @Id private ObjectId packageId; @Valid private Point startingPoint; @Valid private Point destinationPoint; private String expectedShippingTime; private String trackingCode; }
额外确认以下几点(你的代码已满足):
- 已引入
spring-boot-starter-validation依赖 - Controller方法的
@RequestBody参数已添加@Valid注解 - 使用的是Jakarta Validation规范的注解(
jakarta.validation.constraints.*)
修改完成后,当传入不符合规则的数据时,校验会触发,接口返回400 Bad Request,且不会创建Package。
内容的提问来源于stack exchange,提问作者GPPiotr
相关产品推荐
相关产品推荐

