
spring 默认不会因 json 字段类型不匹配(如数字赋值给 string 字段)而拒绝请求,需结合 `@valid` 与 `bindingresult` 捕获反序列化后的校验错误,并手动处理。
在 Spring Web 应用中,当客户端发送如下非法 JSON:
{
"Person": {
"name": 5
}
}尽管 Person.name 被声明为 String 并标注了 @NotBlank 和 @JsonProperty("name"),Spring MVC 仍会静默将数字 5 转换为字符串 "5"(得益于 Jackson 的宽松类型转换策略),导致 @NotBlank 校验通过,@Valid 无法触发类型层面的拦截——因为此时字段已是合法 String 类型,而非“类型错误”。
⚠️ 关键认知:@Valid 仅校验对象属性值是否满足约束注解(如非空、长度等),不负责 JSON 到 Java 类型的反序列化合法性检查。类型转换发生在 @RequestBody 绑定阶段,早于 @Valid 执行;若 Jackson 成功完成转换(如 5 → "5"),校验便不会感知原始类型冲突。
✅ 正确做法是显式捕获绑定与校验全过程的错误:
- 在 Controller 方法中添加 BindingResult 参数(必须紧跟 @Valid 参数之后);
- 检查 result.hasErrors(),并返回适当的 HTTP 状态(如 400 Bad Request)及错误详情;
- (可选)配合全局异常处理器统一响应格式。
示例修正代码:
@PostMapping("/register")
public ResponseEntity> register(@Valid @RequestBody Person person, BindingResult result) {
if (result.hasErrors()) {
List errors = result.getFieldErrors().stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.toList());
return ResponseEntity.badRequest().body(Map.of("errors", errors));
}
// 处理有效请求
service.register(person);
return ResponseEntity.ok().build();
} ? 补充建议:
- 若需严格禁止数字转字符串等隐式转换,可配置 Jackson DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS(对枚举)或自定义 JsonDeserializer
抛出异常,但通常不推荐——这会破坏 REST API 的健壮性。更合理的方案是:接受 Jackson 的默认转换行为,专注业务级校验,并通过 BindingResult 暴露所有验证失败。 - 确保 spring-boot-starter-validation 已引入(Spring Boot 2.3+ 后需显式添加)。
- @NotBlank 对 null 或纯空白字符串生效,但对 "5"(字符串形式的数字)不报错——这是预期行为;如需禁止数字字符串,应使用正则 @Pattern(regexp = "^[a-zA-Z\\s]+$") 或自定义约束。
总结:Spring 的 @Valid 不是万能类型守门员。要实现端到端请求质量控制,必须组合 @RequestBody + @Valid + BindingResult 三要素,并主动处理校验结果——这是构建高可靠性 REST API 的标准实践。










