【问题标题】:Convert JSR-303 validation errors to Spring's BindingResult将 JSR-303 验证错误转换为 Spring 的 BindingResult
【发布时间】:2013-01-31 08:06:45
【问题描述】:

我在 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);

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

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

【问题讨论】:

    标签: java spring validation spring-mvc bean-validation


    【解决方案1】:

    一个更简单的方法是使用 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);
    

    【讨论】:

    • 谢谢!这肯定更简洁。
    • 我没有看到方法 validate 接受 BindingResult
    • 如果您没有看到接受绑定结果的 validate 方法,则表示您的 Validator 验证器是 javax.validation.Validator。将其更改为 org.springframework.validation.Validator 就可以了。无需更改 bean 定义。
    【解决方案2】:

    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】:

      扩展 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 会为您构建正确的结果。

      【讨论】:

        【解决方案4】:
        @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);
            }
        }
        

        与此问题的其他答案不同,此解决方案处理已经存在需要转换为 BindingResultSet&lt;ConstraintViolation&lt;?&gt;&gt; 的情况。

        说明

        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);
        

        但是,在已经存在需要转换为 BindingResultSet&lt;ConstraintViolation&lt;?&gt;&gt; 的情况下,这无济于事。

        实现这个目标仍然是可能的,尽管它确实需要多跳几圈。 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,实现从Set&lt;ConstraintViolation&lt;?&gt;&gt; 创建BindingResult 的目标。

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

        【讨论】:

          【解决方案5】:

          我遇到过类似的问题,我就是这样解决的。

          根据你的例子,这就是我实现它的方式

          首先,我使用了一个智能验证器,在方法中我让 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);
          }
          

          【讨论】:

            猜你喜欢
            • 2018-08-15
            • 2014-09-28
            • 2013-11-23
            • 2013-12-27
            • 2011-07-21
            • 2011-07-24
            • 1970-01-01
            • 1970-01-01
            • 2012-11-24
            相关资源
            最近更新 更多