17

我在 Spring 控制器中有以下代码:

@Autowired
private javax.validation.Validator validator;

@RequestMapping(value = "/submit", method = RequestMethod.POST)
public String submitForm(CustomForm form) {
    Set<ConstraintViolation<CustomForm>> errors = validator.validate(form);
    ...
}

是否可以映射errors到 Spring 的BindingResult对象而无需手动检查所有错误并将它们添加到BindingResult? 像这样的东西:

// NOTE: this is imaginary code
BindingResult bindingResult = BindingResult.fromConstraintViolations(errors);

我知道可以注释CustomForm参数@Valid并让 Spring 注入BindingResult作为另一个方法的参数,但在我的情况下它不是一个选项。

// I know this is possible, but doesn't work for me
public String submitForm(@Valid CustomForm form, BindingResult bindingResult) {
    ...
}
4

5 回答 5

10

一个更简单的方法可能是使用 Spring 的抽象org.springframework.validation.Validator,您可以通过在上下文中使用这个 bean 来获取验证器:

<bean id="jsr303Validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />

@Autowired @Qualifier("jsr303Validator") Validator validator;

有了这个抽象,你可以通过这种方式使用验证器,传入你的 bindingResult:

validator.validate(obj, bindingResult);
于 2013-02-15T11:19:27.353 回答
5

Spring 使用 SpringValidatorAdapter 将 javax.validation.ConstraintViolation 对象转换为 ObjectError 或 FieldError 对象,如绑定结果中所示。然后 BindStatus 使用消息源(如 Web 应用程序上下文本身)来转换错误。简而言之,你可以这样做:

SpringValidatorAdapter springValidator = new SpringValidatorAdapter(validator);
BindingResult bindingResult= new BeanPropertyBindingResult(myBeanToValidate, "myBeanName");
springValidator.validate(myBeanToValidate, bindingResult);

这在编写单元测试时更容易,因为您甚至不需要创建 Spring 上下文。

于 2017-06-14T14:26:15.540 回答
1
@RequestMapping(value = "/submit", method = RequestMethod.POST)
public String submitForm(CustomForm form) {
    Set<ConstraintViolation<CustomForm>> errors = validator.validate(form);

    BindingResult bindingResult = toBindingResult(errors, form, "form");
    ...
}

private BindingResult toBindingResult(ConstraintViolationException e, Object object, String objectName) {
    BindingResult bindingResult = new BeanPropertyBindingResult(object, objectName);
    new AddConstraintViolationsToErrors().addConstraintViolations(e.getConstraintViolations(), bindingResult);
    return bindingResult;
}

private static class AddConstraintViolationsToErrors extends SpringValidatorAdapter {
    public AddConstraintViolationsToErrors() {
        super(Validation.buildDefaultValidatorFactory().getValidator()); // Validator is not actually used
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public void addConstraintViolations(Set<? super ConstraintViolation<?>> violations, Errors errors) {
        // Using raw type since processConstraintViolations specifically expects ConstraintViolation<Object>
        super.processConstraintViolations((Set) violations, errors);
    }
}

与此问题的其他答案不同,此解决方案处理已经存在 aSet<ConstraintViolation<?>>需要转换为 a 的情况BindingResult

解释

Spring 提供了SpringValidatorAdapter执行 bean 验证的类,将结果存储在Errors实例中(注意BindingResultextends Errors)。此类的正常手动使用是使用它通过以下validate方法执行验证:

Validator beanValidator = Validation.buildDefaultValidatorFactory().getValidator();
SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter(beanValidator);

BindException bindException = new BindException(form, "form");
validatorAdapter.validate(form, bindException);

但是,这在已经存在 aSet<ConstraintViolation<?>>需要转换为 a的情况下无济于事BindingResult

仍然有可能实现这个目标,尽管它确实需要跳过几个额外的箍。 SpringValidatorAdapter包含processConstraintViolationsConstraintViolation对象转换为适当的 SpringObjectError子类型并将它们存储在Errors对象上的方法。但是,此方法是受保护的,限制了它对子类的可访问性。

这个限制可以通过创建一个自定义子类来解决,该子类SpringValidatorAdapter委托或公开受保护的方法。这不是典型的用法,但它有效。

public class AddConstraintViolationsToErrors extends SpringValidatorAdapter {
    public AddConstraintViolationsToErrors() {
        super(Validation.buildDefaultValidatorFactory().getValidator()); // Validator is not actually used
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public void addConstraintViolations(Set<? super ConstraintViolation<?>> violations, Errors errors) {
        // Using raw type since processConstraintViolations specifically expects ConstraintViolation<Object>
        super.processConstraintViolations((Set) violations, errors);
    }
}

这个自定义类可以用来填充一个新创建BindingResult的,实现BindingResult从一个创建一个的目标Set<ConstraintViolation<?>>

private BindingResult toBindException(ConstraintViolationException e, Object object, String objectName) {
    BindingResult bindingResult = new BeanPropertyBindingResult(object, objectName);
    new AddConstraintViolationsToErrors().addConstraintViolations(e.getConstraintViolations(), bindingResult);
    return bindingResult;
}
于 2021-02-05T20:08:33.237 回答
0

我遇到了类似的问题,这就是我解决它的方法。

鉴于您的示例,这就是我实现它的方式

首先,我使用了一个智能验证器,在方法中我让 spring 注入了 BindingResult

@Autowired
private org.springframework.validation.SmartValidator validator;

@RequestMapping(value = "/submit", method = RequestMethod.POST)
public String submitForm(CustomForm form, BindingResult bindingResult) {
    Set<ConstraintViolation<CustomForm>> errors = validator.validate(form);
    ...
}

然后使用该绑定结果,我将它传递给 SmartValidator,以便将任何错误绑定到 BindingResult。

validator.validate(form, bindingResult);
if(bindingResult.hasErrors()) {
     throw new BindException(bindingResult);
}
于 2021-09-22T04:54:54.250 回答
0

扩展 Kristiaan 的答案,出于测试目的,没有必要创建一个 spring 上下文来使用 Spring 的 bindingResult 进行验证。下面是一个例子:

public class ValidatorTest {

    javax.validation.Validator javaxValidator = Validation.buildDefaultValidatorFactory().getValidator();
    org.springframework.validation.Validator springValidator = new SpringValidatorAdapter(javaxValidator);

    @Test
    public void anExampleTest() {

    JSR303AnnotatedClassToTest   ctt  = new JSR303AnnotatedClassToTest( ..init vars..)

    ... test setup...

    WebDataBinder dataBinder = new WebDataBinder(ctt);
    dataBinder.setValidator(springValidator);
    dataBinder.validate();
    BindingResult bindingResult = dataBinder.getBindingResult(); 

    ... test analysis ...

    }
}

这种方法不需要提前创建绑定结果,dataBinder 会为您构建正确的结果。

于 2018-12-16T21:28:13.140 回答