web-dev-qa-db-fra.com

Annotation du validateur Spring MVC + validation personnalisée

Je travaille sur l'application Spring MVC, où je devrais appliquer la validation basée sur le validateur Spring MVC. J'ai la première étape pour cela, j'ai ajouté une annotation pour la classe et le contrôleur de configuration et cela fonctionne très bien. Et maintenant, je dois implémenter un validateur personnalisé pour effectuer une logique complexe, mais je veux utiliser les annotations existantes et ajouter simplement une vérification supplémentaire.

Ma classe d'utilisateurs:

public class User
{
    @NotEmpty
    private String name;

    @NotEmpty
    private String login; // should be unique
}

Mon validateur:

@Component
public class UserValidator implements Validator
{

    @Autowired
    private UserDAO userDAO;

    @Override
    public boolean supports(Class<?> clazz)
    {
        return User.class.equals(clazz) || UsersForm.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors)
    {
        /*
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "NotEmpty.user");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "login", "NotEmpty.user");
        */
        User user = (User) target;
        if (userDAO.getUserByLogin(user.getLogin()) != null) {
            errors.rejectValue("login", "NonUniq.user");
        }
    }
}

Mon contrôleur:

@Controller
public class UserController
{
    @Autowired
    private UserValidator validator;

    @InitBinder
    protected void initBinder(final WebDataBinder binder)
    {
        binder.setValidator(validator);
    }

    @RequestMapping(value = "/save")
    public ModelAndView save(@Valid @ModelAttribute("user") final User user,
            BindingResult result) throws Exception
    {
        if (result.hasErrors())
        {
            // handle error
        } else
        {
            //save user
        }
    }
}

Alors, est-il possible d'utiliser ensemble un validateur et une annotation personnalisés? Et si oui comment?

31
Vartlok

Je sais que c'est une sorte de vieille question mais, pour les googleurs ...

vous devez utiliser addValidators au lieu de setValidator. Comme suivant:

@InitBinder
protected void initBinder(final WebDataBinder binder) {
    binder.addValidators(yourCustomValidator, anotherValidatorOfYours);
}

PS: addValidators accepte plusieurs paramètres (points de suspension)

si vous extrayez la source de org.springframework.validation.DataBinder tu verras:

public class DataBinder implements PropertyEditorRegistry, TypeConverter {

    ....

    public void setValidator(Validator validator) {
        assertValidators(validator);
        this.validators.clear();
        this.validators.add(validator);
    }

    public void addValidators(Validator... validators) {
        assertValidators(validators);
        this.validators.addAll(Arrays.asList(validators));
    }

    ....

}

comme vous voyez setValidator efface le validateur existant (par défaut) donc @Valid l'annotation ne fonctionnera pas comme prévu.

58
destan

Si je comprends bien votre problème, dès que vous utilisez votre validateur personnalisé, validation par défaut pour @NotEmpty l'annotation ne se produit plus. Cela est courant lors de l'utilisation de spring: si vous surchargez une fonctionnalité donnée par défaut, vous devez l'appeler explicitement.

Vous devez générer un LocalValidatorFactoryBean et l'injecter avec votre source de message (le cas échéant). Ensuite, vous injectez ce validateur de base dans votre validateur personnalisé et lui déléguez la validation des annotations.

En utilisant Java, cela pourrait ressembler à:

@Configuration
public class ValidatorConfig {
    @Autowired
    private MessageSource messageSource;

    @Bean
    public Validator basicValidator() {
        LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
        validator.setValidationMessageSource(messageSource);
        return validator;
    }
}

Ensuite, vous modifiez UserValidator pour l'utiliser:

@Component
public class UserValidator implements Validator
{

    @Autowired
    @Qualifier("basicValidator")
    private Validator basicValidator;

    @Autowired
    private UserDAO userDAO;

    // ...

    @Override
    public void validate(Object target, Errors errors)
    {
        basicValidator.validate(target, errors);
        // eventually stop if any errors
        //  if (errors.hasErrors()) { return; }
        User user = (User) target;
        if (userDAO.getUserByLogin(user.getLogin()) != null) {
            errors.rejectValue("login", "NonUniq.user");
        }
    }
}
9
Serge Ballesta

Eh bien, pour moi, vous devez supprimer le

 @InitBinder
protected void initBinder(final WebDataBinder binder)
{
    binder.setValidator(validator);
}

Quitter le

@Valid @ModelAttribute("user") final User user,
        BindingResult result

Et après dans la fonction faire

validator.validate(user,result)

De cette façon, vous utiliserez la validation de base avec @Valid et après vous mettrez la validation la plus complexe.

Parce qu'avec initBinder vous définissez la validation avec votre logique complexe et mettez en place la logique de base.

C'est peut-être faux, j'utilise toujours le @Valid sans aucun validateur.

7
nicearma