Spring Boot JSON 请求类型校验失效的解决方案

spring boot 默认不会因 json 字段类型不匹配(如数字赋值给 string 字段)而自动拒绝请求,需结合 @valid 与 bindingresult 手动捕获类型转换错误,才能实现强类型校验和 400 bad request 响应。

在 Spring MVC 中,JSON 反序列化由 Jackson 负责,而 Bean 校验(如 @NotBlank、@NotNull)仅作用于反序列化成功后的对象字段值。当客户端发送 "name": 5

时,Jackson 默认会尝试将数字 5 强转为字符串 "5"(启用 DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY 或默认字符串强制转换策略),导致校验注解根本未触发——因为字段已“合法”地被设为字符串,而非校验失败。

要真正拦截类型不匹配(如非字符串值赋给 String 字段),必须启用并检查数据绑定错误(如类型转换失败),而非仅依赖字段级约束。正确做法是在 Controller 方法中显式添加 BindingResult 参数:

@PostMapping("/register")
public ResponseEntity register(@Valid @RequestBody Person person, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        // 收集所有错误(含类型转换异常,如 "Failed to convert property 'name' of type java.lang.Integer to java.lang.String")
        List errors = bindingResult.getAllErrors().stream()
            .map(error -> error.getDefaultMessage())
            .collect(Collectors.toList());
        return ResponseEntity.badRequest().body(Map.of("errors", errors));
    }
    // 处理有效请求
    return ResponseEntity.ok().build();
}

⚠️ 注意事项:

  • BindingResult 必须紧跟在被 @Valid 标注的参数之后,否则 Spring 无法关联校验结果;
  • 若使用 Lombok,确保 @Data 或 @Setter 不干扰 Jackson 反序列化逻辑(推荐显式定义无参构造器和 setter);
  • 为提升健壮性,建议全局配置 Jackson 禁用宽松类型转换:
@Configuration
public class JacksonConfig {
    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        // 禁止将数字/布尔等类型自动转为字符串
        mapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);
        mapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
        // ⚠️ 关键:禁止将非字符串值强制转为 String(需配合自定义反序列izer 或使用严格模式)
        // 实际中更推荐:在 DTO 中使用 @JsonCreator + @JsonProperty 配合自定义验证逻辑
        return mapper;
    }
}

✅ 最佳实践补充:若需彻底杜绝类型混淆,可弃用默认字符串转换,改用自定义反序列化器或采用更严格的 DTO 设计(例如用 @JsonCreator 构造函数强制校验输入类型),再辅以 @Valid + BindingResult 统一处理错误响应。这样既保证接口契约清晰,又符合 RESTful API 对 400 Bad Request 的语义要求。