【问题标题】:Adding multiple validators using initBinder使用 initBinder 添加多个验证器
【发布时间】:2013-01-10 02:33:17
【问题描述】:

我正在使用initBinder 方法添加用户验证器:

@InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.setValidator(new UserValidator());
    }

这里是UserValidator

public class UserValidator implements Validator {

    public boolean supports(Class clazz) {
        return User.class.equals(clazz);
    }

    public void validate(Object target, Errors errors) {
        User u = (User) target;

        // more code here
    }
}

validate 方法在控制器方法调用期间被正确调用。

@RequestMapping(value = "/makePayment", method = RequestMethod.POST)
public String saveUserInformation(@Valid User user, BindingResult result, Model model){

    // saving User here

    // Preparing CustomerPayment object for the payment page.
    CustomerPayment customerPayment = new CustomerPayment();
    customerPayment.setPackageTb(packageTb);
    model.addAttribute(customerPayment);
    logger.debug("Redirecting to Payment page.");

    return "registration/payment";
}

但在返回付款屏幕时,我收到此错误:

java.lang.IllegalStateException:Validator [com.validator.UserValidator@710db357] 的目标无效:com.domain.CustomerPayment[customerPaymentId=null] org.springframework.validation.DataBinder.setValidator(DataBinder.java:476) com.web.UserRegistrationController.initBinder(UserRegistrationController.java:43) sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) java.lang.reflect.Method.invoke(Method.java:597) org.springframework.web.bind.annotation.support.HandlerMethodInvoker.initBinder(HandlerMethodInvoker.java:393) org.springframework.web.bind.annotation.support.HandlerMethodInvoker.updateModelAttributes(HandlerMethodInvoker.java:222) org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:429) org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:414)

这可能是因为我返回了 CustomerPayment 并且没有为此定义验证器。

我也无法在 initBinder 方法中添加多个验证器。

我该如何解决这个问题?

【问题讨论】:

    标签: validation spring-mvc


    【解决方案1】:

    您需要将@InitBinder 注释的值设置为您希望它验证的命令的名称。这告诉 Spring 将绑定器应用于什么;没有它,Spring 会尝试将它应用到所有东西上。这就是您看到该异常的原因:Spring 正在尝试将绑定器(使用您的 UserValidator)应用于 CustomerPayment 类型的参数。

    在您的具体情况下,您似乎需要以下内容:

    @InitBinder("user")
    protected void initBinder(WebDataBinder binder) {
        binder.setValidator(new UserValidator());
    }
    

    对于您的第二个问题,正如 Rigg802 所解释的,Spring 不支持将多个验证器附加到单个命令。但是,您可以为不同的命令定义多个 @InitBinder 方法。因此,例如,您可以将以下内容放在单个控制器中并验证您的用户和支付参数:

    @InitBinder("user")
    protected void initUserBinder(WebDataBinder binder) {
        binder.setValidator(new UserValidator());
    }
    
    @InitBinder("payment")
    protected void initPaymentBinder(WebDataBinder binder) {
        binder.setValidator(new CustomerPaymentValidator());
    }
    

    【讨论】:

    • 很好的信息 - Spring 的参考文档中没有
    • 真的没有办法注册所有 Validator 实现并让 Spring 利用supports 方法吗?
    • 它有一个有趣的规则:我必须匹配要验证的类的小写名称:@InitBinder("payment") Payment: 应该是类的名称
    • 至少如果你的参数是@RequestBody
    • @JPRLCol 此信息应包含在答案中。
    【解决方案2】:

    这有点棘手,1 个控制器在 1 个命令对象上只有 1 个验证器。 您需要创建一个“复合验证器”,它将获取所有验证器并单独运行它们。

    这里有一个解释如何做的教程:using multiple validators

    【讨论】:

      【解决方案3】:

      我看不出 Spring 没有过滤掉所有默认情况下不适用于当前实体的验证器的原因,这会强制使用 @Rigg802 描述的 CompoundValidator 之类的东西。

      InitBinder 允许您仅指定名称,以便您可以控制但不能完全控制应用自定义验证器的方式和时间。从我的角度来看,这还不够。

      您可以做的另一件事是自己执行检查并仅在实际需要时将验证器添加到活页夹,因为活页夹本身具有绑定上下文信息。

      例如,如果您想添加一个新的验证器,除了内置验证器之外,它还可以与您的 User 对象一起使用,您可以编写如下内容:

      @InitBinder
      protected void initBinder(WebDataBinder binder) {
        Optional.ofNullable(binder.getTarget())
            .filter((notNullBinder) -> User.class.equals(notNullBinder.getClass()))
            .ifPresent(o -> binder.addValidators(new UserValidator()));
      

      }

      【讨论】:

        【解决方案4】:

        您可以通过迭代 ApplicationContext 中的所有 org.springframework.validation.Validator 来添加多个验证器,并在 @InitBinder 中为每个请求设置合适的验证器。

        @InitBinder
        public void setUpValidators(WebDataBinder webDataBinder) {
            for (Validator validator : validators) {
                if (validator.supports(webDataBinder.getTarget().getClass())
                        && !validator.getClass().getName().contains("org.springframework"))
                    webDataBinder.addValidators(validator);
            }
        }
        

        请参阅我的项目以获取示例和简单的基准。 https://github.com/LyashenkoGS/spring-mvc-and-jms-validation-POC/tree/benchamark

        【讨论】:

          【解决方案5】:

          有一个简单的技巧,总是在supports 方法中返回true,并将类检查委托给validate。然后基本上你可以在initBinder 中添加多个验证器而不会出现问题。

          @Component
          public class MerchantRegisterValidator implements Validator {
          
              @Autowired
              private MerchantUserService merchantUserService;
          
              @Autowired
              private MerchantCompanyService merchantCompanyService;
          
              @Override
              public boolean supports(Class<?> clazz) {
                  return true; // always true
              }
          
              @Override
              public void validate(Object target, Errors errors) {
          
                  if (!XxxForm.getClass().equals(target.getClass()))
                      return; // do checking here.
          
                  RegisterForm registerForm = (RegisterForm) target;
          
                  MerchantUser merchantUser = merchantUserService.getUserByEmail(registerForm.getUserEmail());
          
                  if (merchantUser != null) {
                      errors.reject("xxx");
                  }
          
                  MerchantCompany merchantCompany = merchantCompanyService.getByRegno(registerForm.getRegno());
          
                  if (merchantCompany != null) {
                      errors.reject("xxx");
                  }
          
              }
          
          }
          

          【讨论】:

            【解决方案6】:

            Spring MVC 4.x 现在支持一个命令上的多个验证器。你可以使用这个 sn-p 代码:

            @InitBinder
            protected void initBinder(WebDataBinder binder) {
                binder.addValidators(new UserValidator(), new CustomerPaymentValidator());
            }
            

            【讨论】:

            • 不起作用。所有通过的验证器都应用于绑定。
            【解决方案7】:

            最安全的方法是添加一个通用的验证器来处理该控制器:

                @InitBinder
                public void initBinder(WebDataBinder binder) {
                    binder.setValidator(new GenericControllerOneValidator());
                }
            

            然后,在通用验证器中,您可以支持多个请求主体模型,并且基于对象的实例,您可以调用适当的验证器:

             public class GenericValidator implements Validator {
                    @Override
                    public boolean supports(Class<?> aClass) {
                        return ModelRequestOne.class.equals(aClass) 
                              || ModelRequestTwo.class.equals(aClass);
                    }
                
                        @Override
                        public void validate(Object body, Errors errors) {
                            if (body instanceof ModelRequestOne) {
                                ValidationUtils.invokeValidator(new ModelRequestOneValidator(), body, errors);
                            }
                            if (body instanceof ModelRequestTwo) {
                                ValidationUtils.invokeValidator(new ModelRequestTwoValidator(), body, errors);
                            }
                            
                        }
                    }
            

            然后为每个模型验证器实现添加自定义验证。 ModeRequestOneValidatorModeRequestTwoValidator还需要实现org.springframework.validationValidator接口 另外,不要忘记在控制器方法调用中使用@Valid ModeRequestOne@Valid ModeRequestTwo

            【讨论】:

              【解决方案8】:

              Annabelle 回答的一个补充:

              如果控制器有这个方法参数并且你想专门验证那个

               @RequestMapping(value = "/users", method = RequestMethod.POST)
               public String findUsers(UserRequest request){..}
              

              那么绑定应该是类名的小写(但只是第一个字母,而不是其他所有字母)

              @InitBinder("userRequest")
              protected void initUserBinder(WebDataBinder binder) {
                  binder.setValidator(new YourValidator());
              }
              

              【讨论】:

                【解决方案9】:

                将请求声明为

                (... , Model model,HttpServletRequest request)

                改变

                model.addAttribute(customerPayment);
                

                request.setAttribute("customerPayment",customerPayment);
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2012-10-08
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-11-27
                  • 2013-04-15
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-12-28
                  相关资源
                  最近更新 更多