J'ajoute un validateur utilisateur à l'aide de la méthode initBinder
:
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new UserValidator());
}
Voici la 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
}
}
La méthode validate
est correctement appelée lors de l'appel de la méthode du contrôleur.
@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";
}
Mais en revenant à l'écran de paiement, j'obtiens cette erreur:
Java.lang.IllegalStateException: cible non valide pour 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 (Méthode native) 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)
C'est peut-être parce que je retourne une CustomerPayment
et qu'il n'y a pas de validateur défini pour cela.
Je ne parviens pas non plus à ajouter plusieurs validateurs dans la méthode initBinder
.
Comment puis-je réparer cela?
Vous devez définir la valeur de l'annotation @InitBinder
sur le nom de la commande à valider. Cela indique au printemps à quoi appliquer le classeur; sans cela, Spring essaiera de l'appliquer à tout. C'est pourquoi vous voyez cette exception: Spring tente d'appliquer le classeur - avec votre UserValidator
- à un paramètre de type CustomerPayment
.
Dans votre cas particulier, il semble que vous ayez besoin de quelque chose comme:
@InitBinder("user")
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new UserValidator());
}
Pour répondre à votre deuxième question, comme l'a expliqué Rigg802, Spring ne prend pas en charge l'association de plusieurs validateurs à une seule commande. Vous pouvez cependant définir plusieurs méthodes @InitBinder
pour différentes commandes. Ainsi, vous pouvez par exemple mettre les éléments suivants dans un seul contrôleur et valider vos paramètres d'utilisateur et de paiement:
@InitBinder("user")
protected void initUserBinder(WebDataBinder binder) {
binder.setValidator(new UserValidator());
}
@InitBinder("payment")
protected void initPaymentBinder(WebDataBinder binder) {
binder.setValidator(new CustomerPaymentValidator());
}
C'est un peu difficile à faire, 1 contrôleur n'a qu'un validateur sur 1 objet de commande . Vous devez créer un "validateur composite" qui obtiendra tous les validateurs et les exécutera séparément.
Voici un tutoriel qui explique comment faire: en utilisant plusieurs validateurs
Je ne vois pas pourquoi Spring ne filtre pas tous les validateurs qui ne sont pas applicables par défaut à l'entité actuelle, ce qui oblige à utiliser des éléments tels que CompoundValidator décrit par @ Rigg802.
InitBinder
vous permet de spécifier un nom uniquement, ce qui vous permet de contrôler, mais pas totalement, comment et quand appliquer votre validateur personnalisé. Ce qui de mon point de vue ne suffit pas.
Vous pouvez également vérifier vous-même et ajouter un validateur au classeur uniquement si cela est réellement nécessaire, car le classeur lui-même contient des informations de contexte contraignantes.
Par exemple, si vous souhaitez ajouter un nouveau validateur qui fonctionnera avec votre objet Utilisateur en plus des validateurs intégrés, vous pouvez écrire quelque chose comme ceci:
@InitBinder
protected void initBinder(WebDataBinder binder) {
Optional.ofNullable(binder.getTarget())
.filter((notNullBinder) -> User.class.equals(notNullBinder.getClass()))
.ifPresent(o -> binder.addValidators(new UserValidator()));
}
Vous pouvez ajouter plusieurs validateurs en effectuant une itération sur tous les org.springframework.validation.Validator dans un ApplicationContext et configurer ceux appropriés dans @InitBinder pour chaque demande.
@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);
}
}
Voir mon projet pour des exemples et des repères simples. https://github.com/LyashenkoGS/spring-mvc-and-jms-validation-POC/tree/benchamark
Il existe un hack simple, retourne toujours true
dans la méthode supports
et délègue la vérification de classe à validate
. Vous pouvez ensuite ajouter plusieurs validateurs dans la initBinder
sans problème.
@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");
}
}
}
Plusieurs validateurs sur une commande sont désormais pris en charge avec Spring MVC 4.x. Vous pouvez utiliser ce code de code:
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new UserValidator(), new CustomerPaymentValidator());
}