springspring-mvcspring-bootcustom-validators

Spring validation: getting "Invalid target for Validator" when using two validators


I'm having issues implementing two validators using @InitBinder annotation.

Controller code:

@Autowired
private SessionValidator sessionValidator;

@Autowired
private ChannelValidator channelValidator;

@InitBinder
public void initBinder(WebDataBinder binder){
    binder.addValidators(sessionValidator, channelValidator);
}

@RequestMapping(method = RequestMethod.GET)
public UserInfo findBySession(
        @Valid @ModelAttribute Session session,
        @Valid @ModelAttribute Channel channel){
    //...
}

Session validator:

@Component
public class SessionValidator implements Validator {

    @Override
    public boolean supports(Class<?> aClass){
        return Session.class.equals(aClass);
    }

    @Override
    public void validate(Object o, Errors errors){
        //...
    }
}

Channel validator:

@Component
public class ChannelValidator implements Validator {

    @Override
    public boolean supports(Class<?> aClass){
        return Channel.class.equals(aClass);
    }

    @Override
    public void validate(Object o, Errors errors){
        //...
    }
}

I'm receiving the following exception when calling the controller:

Caused by: java.lang.IllegalStateException: Invalid target for Validator [com.almundo.p13n.dna.validators.ChannelValidator@4e642ee1]: Session(null, null)

Anyone knows how to solve it? Thanks in advance!


Solution

  • You binds two validators but for each parameter to validate only one of them will be supported.
    SessionValidator support the Session parameter but doesn't support the Channel parameter and reversely ChannelValidator supports the Channel parameter but doesn't support the Session parameter.
    Whereas the exception.

    As first alternative you could support both parameter types in each Spring Validator subclass. It is a little clumsy but it should work :

    @Override
    public boolean supports(Class<?> aClass){
        return Channel.class.equals(aClass) ||  Session.class.equals(aClass);
    }
    

    You should of course check the type in validate() and perform the validation only if it matched with the validator class.

    As second alternative, use the standard validation API by implementing javax.validation.ConstraintValidator for each class to validate and by validating the parameters explicitly in the controller.

    As third alternative, if it makes sense you could annotate directly the constraints on the Session and the Channel classes. So, you could still use @Valid in the parameter declaration to keep the automate validation.