38

我正在使用以下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(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.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java: 414)

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

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

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

4

9 回答 9

107

You need to set the value of the @InitBinder annotation to the name of the command you want it to validate. This tells Spring what to apply the binder to; without it, Spring will try to apply it to everything. This is why you're seeing that exception: Spring is trying to apply the binder - with your UserValidator - to a parameter of type CustomerPayment.

In your specific case, it looks like you need something like:

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

To your second question, as Rigg802 explained, Spring does not support attaching multiple validators to a single command. You can, however, define multiple @InitBinder methods for different commands. So, for example, you could put the following in a single controller and validate your user and payment parameters:

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

@InitBinder("payment")
protected void initPaymentBinder(WebDataBinder binder) {
    binder.setValidator(new CustomerPaymentValidator());
}
于 2013-04-03T19:40:28.560 回答
8

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

这是一个解释如何做到这一点的教程:使用多个验证器

于 2013-01-26T11:40:50.460 回答
6

我看不出为什么 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()));

}

于 2015-11-04T16:16:05.250 回答
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);
    }
}

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

于 2017-06-14T09:14:53.837 回答
3

有一个简单的技巧,总是truesupports方法中返回,并将类检查委托给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 (!RegisterForm.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");
        }

    }

}
于 2018-01-22T04:21:21.407 回答
2

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

@InitBinder
protected void initBinder(WebDataBinder binder) {
    binder.addValidators(new UserValidator(), new CustomerPaymentValidator());
}
于 2014-01-26T07:13:40.810 回答
2

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

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

然后为每个模型验证器实现添加自定义验证。ModeRequestOneValidator并且ModeRequestTwoValidator还需要实现的Validator接口org.springframework.validation 还有,别忘了在控制器里面使用@Valid ModeRequestOneand@Valid ModeRequestTwo方法调用。

于 2020-07-02T10:12:00.980 回答
0

安娜贝尔回答的一个补充:

如果控制器具有此方法参数并且您想专门验证该方法参数

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

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

@InitBinder("userRequest")
protected void initUserBinder(WebDataBinder binder) {
    binder.setValidator(new YourValidator());
}
于 2021-10-27T16:04:29.723 回答
-3

将请求声明为

(... , Model model,HttpServletRequest request)

和改变

model.addAttribute(customerPayment);

request.setAttribute("customerPayment",customerPayment);
于 2014-04-01T17:26:29.043 回答