You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中BindingResult失效,如何让Spring绑定不抛出异常?

解决Spring Boot @RequestBody绑定不填充BindingResult的问题

这个问题我之前也踩过坑,核心原因是:@RequestBody的参数绑定是由Jackson这类HttpMessageConverter在参数解析阶段完成的,如果此时发生类型不匹配(比如给Integer字段传字符串),错误会直接抛出HttpMessageNotReadableException,根本到不了控制器方法执行的环节,所以BindingResult自然不会被填充。

要实现你想要的“用BindingResult自行处理绑定错误”的需求,我们需要把Jackson的反序列化错误转化为Spring的校验错误,让BindingResult能捕获到这些问题。下面是具体的实现步骤:

1. 自定义Jackson反序列化错误处理器

首先创建一个DeserializationProblemHandler的子类,用来捕获Jackson反序列化时的各种类型错误,并把这些错误存起来:

import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;

import java.util.ArrayList;
import java.util.List;

public class ValidationDeserializationProblemHandler extends DeserializationProblemHandler {

    private final List<ObjectError> errors = new ArrayList<>();

    // 捕获类型不匹配、意外Token等常见反序列化错误
    @Override
    public boolean handleUnexpectedToken(DeserializationContext ctxt, Class<?> targetType, com.fasterxml.jackson.core.JsonToken t, com.fasterxml.jackson.core.JsonParser p, String failureMsg) {
        String fieldName = p.getCurrentName();
        errors.add(new FieldError(targetType.getSimpleName(), fieldName, failureMsg));
        return true; // 告诉Jackson我们已处理错误,继续完成反序列化
    }

    @Override
    public boolean handleWrongTokenException(DeserializationContext ctxt, com.fasterxml.jackson.databind.exc.WrongTokenException e) {
        String fieldName = e.getPath().isEmpty() ? "" : e.getPath().get(0).getFieldName();
        errors.add(new FieldError(e.getTargetType().getSimpleName(), fieldName, e.getMessage()));
        return true;
    }

    // 可以根据需要添加更多错误处理方法,比如handleInstantiationProblem等

    public List<ObjectError> getErrors() {
        return errors;
    }
}

2. 创建自定义校验器,将Jackson错误注入BindingResult

接下来写一个Validator,把上面处理器捕获的错误添加到Spring的Errors(也就是BindingResult)中:

import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

public class JacksonValidationValidator implements Validator {

    private final ValidationDeserializationProblemHandler problemHandler;

    public JacksonValidationValidator(ValidationDeserializationProblemHandler problemHandler) {
        this.problemHandler = problemHandler;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true; // 支持所有类型的校验
    }

    @Override
    public void validate(Object target, Errors errors) {
        // 将Jackson捕获的错误全部添加到BindingResult里
        problemHandler.getErrors().forEach(errors::addError);
    }
}

3. 配置WebMvc,整合Jackson和校验器

在Spring配置类中,把我们的Jackson处理器和自定义校验器注册进去,让Spring在校验阶段把Jackson的错误整合到BindingResult:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    // 实例化我们的Jackson错误处理器
    private final ValidationDeserializationProblemHandler deserializationProblemHandler = new ValidationDeserializationProblemHandler();

    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper()
                .addHandler(deserializationProblemHandler); // 注册错误处理器
    }

    @Override
    public Validator getValidator() {
        // 获取默认的JSR-380校验器(处理@NotNull、@Size等注解)
        LocalValidatorFactoryBean defaultValidator = new LocalValidatorFactoryBean();
        // 组合默认校验器和我们的Jackson错误校验器
        return (target, errors) -> {
            defaultValidator.validate(target, errors);
            new JacksonValidationValidator(deserializationProblemHandler).validate(target, errors);
        };
    }
}

4. 修改控制器方法,开启校验

最后,在你的控制器方法中,给@RequestBody参数加上@Valid注解,这样Spring会自动触发校验,包括Jackson反序列化产生的错误:

@PostMapping
public ResponseEntity<Book> saveBook(
        @Valid @RequestBody Book book, 
        BindingResult bindingResult, 
        HttpServletRequest request
) {
    if (bindingResult.hasErrors()) {
        throw new RestArgumentValidationException(bindingResult, request.getRequestURI());
    }
    return new ResponseEntity<>(bookRepository.save(book), HttpStatus.CREATED);
}

这样配置后,当你发送无效的类型输入时,Jackson的反序列化错误会被转化为校验错误并填充到BindingResult里,你就可以通过bindingResult.hasErrors()自行处理这些错误,而不会再直接抛出HttpMessageNotReadableException了。

内容的提问来源于stack exchange,提问作者user335870

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:40:05