使用initBinder添加多个验证器

时间:2013-01-26 03:15:36

标签: validation spring-mvc

我正在使用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方法中添加多个验证器。

我该如何解决这个问题?

8 个答案:

答案 0 :(得分:93)

您需要将@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());
}

答案 1 :(得分:8)

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

以下是解释如何操作的教程:using multiple validators

答案 2 :(得分:5)

我没有看到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()));

}

答案 3 :(得分: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

答案 4 :(得分:2)

有一个简单的黑客,总是在true方法中返回supports,并将类检查委托给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");
        }

    }

}

答案 5 :(得分:0)

Spring MVC 4.x现在支持一个命令上的多个验证器。您可以使用此代码段:

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

答案 6 :(得分:0)

最安全的方法是添加处理Controller的通用验证器:

    @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仍需要实现Validator的{​​{1}}接口 另外,请不要忘记在控制器方法调用中使用org.springframework.validation@Valid ModeRequestOne

答案 7 :(得分:-3)

将请求声明为

(... , Model model,HttpServletRequest request)

并更改

model.addAttribute(customerPayment);

request.setAttribute("customerPayment",customerPayment);