使用initBinder添加多个验证器

Rav*_*avi 30 validation spring-mvc

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

@InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.setValidator(new UserValidator());
    }
Run Code Online (Sandbox Code Playgroud)

这里是 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
    }
}
Run Code Online (Sandbox Code Playgroud)

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";
}
Run Code Online (Sandbox Code Playgroud)

但是当我回到付款界面时,我收到了这个错误:

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(Native Method)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.annota tion.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:414)

这可能是因为我正在返回a CustomerPayment并且没有为此定义验证器.

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

我怎样才能解决这个问题?

Ann*_*lle 95

您需要将@InitBinder注释的值设置为您要验证的命令的名称.这告诉Spring应用绑定器的内容; 没有它,Spring会尝试将它应用到一切.这就是为什么你会看到这个异常的原因:Spring正在尝试将binder - 与你的UserValidator- 应用于类型的参数CustomerPayment.

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

@InitBinder("user")
protected void initBinder(WebDataBinder binder) {
    binder.setValidator(new UserValidator());
}
Run Code Online (Sandbox Code Playgroud)

对于第二个问题,正如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());
}
Run Code Online (Sandbox Code Playgroud)

  • 它有一个有趣的规则:我必须匹配要以小写形式验证的类的名称:@InitBinder("payment")付款:应该是类的名称 (6认同)
  • 好的信息 - 这是Spring的参考文档中没有的 (5认同)
  • 真的没有办法注册所有 Validator 实现并让 Spring 利用 `supports` 方法吗? (3认同)

Rig*_*802 8

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

这是一个解释如何操作的教程:使用多个验证器


Hry*_*nko 6

您可以通过遍历 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

有关示例和简单的基准测试,请参阅我的项目。https://github.com/LyashenkoGS/spring-mvc-and-jms-validation-POC/tree/benchamark


Kon*_*gin 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()));
Run Code Online (Sandbox Code Playgroud)

}