我在Spring控制器中有以下代码:
@Autowired
private javax.validation.Validator validator;
@RequestMapping(value = "/submit", method = RequestMethod.POST)
public String submitForm(CustomForm form) {
Set<ConstraintViolation<CustomForm>> errors = validator.validate(vustomForm);
...
}
是否可以将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) {
...
}
答案 0 :(得分:8)
更简单的方法可能是使用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);
答案 1 :(得分: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);
}
}
与此问题的其他答案不同,此解决方案处理已经存在需要转换为 Set<ConstraintViolation<?>>
的 BindingResult
的情况。
Spring 提供了 SpringValidatorAdapter
类来执行 bean 验证,将结果存储在 Errors
实例中(注意 BindingResult
扩展 Errors
)。此类的正常手动使用是通过 validate
方法使用它来执行验证:
Validator beanValidator = Validation.buildDefaultValidatorFactory().getValidator();
SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter(beanValidator);
BindException bindException = new BindException(form, "form");
validatorAdapter.validate(form, bindException);
但是,这在已经存在需要转换为 Set<ConstraintViolation<?>>
的 BindingResult
的情况下无济于事。
仍然有可能实现这个目标,尽管它确实需要跳过几个额外的箍。 SpringValidatorAdapter
包含一个 processConstraintViolations
方法,该方法将 ConstraintViolation
对象转换为适当的 Spring ObjectError
子类型,并将它们存储在 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;
}
答案 2 :(得分:0)
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上下文。
答案 3 :(得分: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会为您构建合适的结果。