jakarta.validation.Valid验证范围及控制器场景下的验证逻辑与测试问询
关于jakarta.validation.Valid的验证范围与场景验证说明
一、@Valid的核心验证范围
jakarta.validation.Valid属于Jakarta Validation(原Bean Validation)规范,核心作用是触发对象的嵌套递归验证:
- 当标注在一个对象参数上时,会校验该对象自身字段上的所有验证约束(比如
@NotNull、@Size等); - 如果对象包含嵌套的子对象或集合类型字段,只有当这些字段同时标注了
@Valid时,才会递归校验子对象/集合内每个元素的约束规则。
二、当前场景下@Valid的验证行为
结合你给出的代码来看,此时@Valid不会执行任何实际验证逻辑,原因有两点:
- 所有POJO的字段都没有添加任何具体验证约束(比如
UserDetails.userId没加@NotNull、UserSubscribtion.subscribtionType没加@NotBlank等),没有可校验的规则; User类中的userDetails和subscribtions字段未标注@Valid,即使后续给子对象字段加了约束,@Valid也不会递归校验嵌套内容。
如果要让@Valid生效,需要做两处修改:
- 给嵌套字段添加
@Valid开启递归校验:
public class User { @Valid // 开启对UserDetails的嵌套校验 private UserDetails userDetails; @Valid // 开启对List中每个UserSubscribtion的校验 private List<UserSubscribtion> subscribtions; }
- 给具体字段添加验证约束:
public class UserDetails { @NotNull(message = "用户ID不能为空") private String userId; } public class UserSubscribtion{ private boolean isSubscribed; @NotBlank(message = "订阅类型不能为空") private String subscribtionType; }
修改完成后,调用/saveUser接口时,@Valid会执行以下验证:
- 校验
User自身字段的约束(如果有的话); - 递归校验
userDetails中的userId是否为null; - 遍历
subscribtions列表,校验每个UserSubscribtion的subscribtionType是否为空字符串。
三、测试验证@Valid作用的方法
可以通过Spring Boot Test结合MockMvc或直接调用Validator接口来验证:
方法1:MockMvc接口测试
@SpringBootTest @AutoConfigureMockMvc public class UserControllerTest { @Autowired private MockMvc mockMvc; // 测试不符合验证规则的请求 @Test public void testSaveUser_InvalidRequest() throws Exception { String invalidUserJson = """ { "userDetails": {"userId": null}, "subscribtions": [{"isSubscribed": true, "subscribtionType": ""}] } """; mockMvc.perform(post("/saveUser") .contentType(MediaType.APPLICATION_JSON) .content(invalidUserJson)) .andExpect(status().isBadRequest()) // 验证返回400错误 .andExpect(jsonPath("$.errors").exists()); // 验证返回错误信息 } // 测试符合验证规则的请求 @Test public void testSaveUser_ValidRequest() throws Exception { String validUserJson = """ { "userDetails": {"userId": "12345"}, "subscribtions": [{"isSubscribed": true, "subscribtionType": "PREMIUM"}] } """; mockMvc.perform(post("/saveUser") .contentType(MediaType.APPLICATION_JSON) .content(validUserJson)) .andExpect(status().isOk()); // 验证返回200成功 } }
方法2:直接调用Validator校验
@SpringBootTest public class UserValidationTest { @Autowired private Validator validator; @Test public void testUserValidation() { // 构造不符合约束的User对象 User invalidUser = new User(); UserDetails details = new UserDetails(); details.setUserId(null); invalidUser.setUserDetails(details); UserSubscribtion sub = new UserSubscribtion(); sub.setSubscribtionType(""); invalidUser.setSubscribtions(List.of(sub)); // 执行校验 Set<ConstraintViolation<User>> violations = validator.validate(invalidUser); Assertions.assertFalse(violations.isEmpty()); // 验证存在约束违反 // 可进一步断言错误信息 for (ConstraintViolation<User> violation : violations) { System.out.println(violation.getMessage()); } } }
内容的提问来源于stack exchange,提问作者sunny-java-dev
相关产品推荐
相关产品推荐

