Valider la combinaison de plusieurs champs

Le problème exposé dans ce sujet a été résolu.

Bonjour,

Je suis en train (pour le boulot), de créer une API rest en java avec jetty, hk2 comme IOC, et java 8.

Je me trouve coincé lorsqu’il faut que je valide mes ressources d’entrée. En effet fonctionnellement parlant il m’est spécifié que deux champs peuvent être nuls mais pas les deux en même temps. J’aimerais pouvoir intégrer cette contrainte dans le flux de validation que permet JAX-RS avec l’API javax.validation.constraints.

Voici ma ressource de base (StatusType est une énum):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class AnalysisInput {
    @javax.validation.constraints.Pattern(regexp = "^(?:(?:https?|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?!10(?:\\.\\d{1,3}){3})" +
            "(?!127(?:\\.\\d{1,3}){3})(?!169\\.254(?:\\.\\d{1,3}){2})(?!192\\.168(?:\\.\\d{1,3}){2})" +
            "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
            "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
            "|(?:(?:[a-z\\x{00a1}-\\x{ffff}0-9]+-?)*[a-z\\x{00a1}-\\x{ffff}0-9]+)" +
            "(?:\\.(?:[a-z\\x{00a1}-\\x{ffff}0-9]+-?)*[a-z\\x{00a1}-\\x{ffff}0-9]+)*(?:\\.(?:[a-z\\x{00a1}-\\x{ffff}]{2,})))" +
            "(?::\\d{2,5})?(?:/[^\\s]*)?$_iuS", message = "Url is not valid.")
    private String url;
    private String document;
    private List<TokenInput> token;
// constructeur + accesseur 
// ...
//

    public boolean hasDocument() {
        return document != null && !"".equals(document);
    }

    public boolean hasToken() {
        return !isDocument() && !token.isEmpty();
    }
}

Globalement, je veux que hasDocument et hasToken ne puisse pas être faux en même temps.

Comment puis-je faire?

Globalement, je veux que hasDocument et hasToken ne puisse pas être faux en même temps.

C’est la définition du ou logique (||). Juste pour être sur, ce que tu demandes, c’est comment utiliser l’API javax.validation.constraints pour ajouter cette contrainte ?

Edit : une solution pourrait être d’écrire une troisième méthode checkSpecs qui rende un booléen (hasDocument || hasToken) et d’utiliser la contrainte AssertTrue pour vérifier son retour…

Je ne connais pas cette API, les résultats sont checkés à l’appel de la fonction annotée ?

+1 -0

Juste pour être sur, ce que tu demandes, c’est comment utiliser l’API javax.validation.constraints pour ajouter cette contrainte ?

En effet.

Je ne connais pas cette API, les résultats sont checkés à l’appel de la fonction annotée ?

je continue mes recherches et il semblerait, en effet que l’utilisation d’annotation sur une fonction soit possible, j’attends néanmoins qu’une personne qui a plus l’habitude réponde. Mais merci pour la piste lthms :)

Pas de souci, c’est normal. J’ai regardé rapidement sur Github, je n’ai pas trouvé d’exemples concrets qui utilisent l’approche naïve que je propose, mais je n’ai pas non plus beaucoup cherché.

+0 -0

ça donne quelque chose comme ça (j’ai enlevé les fonctions qui servent à rien)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class AnalysisInput {
    @ValidUrl(message = "Url is not valid.")
    @NotNull(message = "Url must be set.")
    @Size(min=4)
    private String url;
    private String document;
    private List<TokenInput> token;

    public boolean hasDocument() {
        return document != null && !"".equals(document);
    }

    public boolean hasToken() {
        return !hasDocument() && !token.isEmpty();
    }

    @AssertTrue(message = "You need at least a document or a list of tokens.")
    public boolean hasEnoughData(){
        return hasDocument() || hasToken();
    }
}

Bon, en fait ça marchait pas vraiment, néanmoins j’ai trouvé la solution :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@DocumentOrToken
public class AnalysisInput implements DocumentContainerEntity{
    @ValidUrl(message = "You need at least a document or a list of tokens.")
    @NotNull(message = "Url must be set.")
    @Size(min=4)
    private String url;
    private String document;
    private List<TokenInput> tokens;

    public AnalysisInput() {
        this(null, null, null);
    }

    public AnalysisInput(String url, String document, List<TokenInput> token) {
        this.url = url;
        this.document = document;
        this.tokens = token;
    }

    public String getUrl() {
        return url;
    }

    @Override
    public boolean hasDocument() {
        return document != null && !"".equals(document);
    }
    @Override
    public boolean hasToken() {
        return tokens != null && !tokens.isEmpty();
    }
    @Override
    public boolean hasEnoughData(){
        return hasDocument() || hasToken();
    }
}

L’interface m’oblige simplement à avoir un "hasToken", "hasDocument" et "hasEnoughData".

Ensuite je crée l’annoation :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
        validatedBy = {ValidContentConstraintValidator.class}
)
public @interface DocumentOrToken {
    String message() default "${constraint.validation.document_or_token_needed}";
    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

pour terminer par le validateur en lui-même :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class ValidContentConstraintValidator implements ConstraintValidator<DocumentOrToken, DocumentContainerEntity> {
    @Override
    public void initialize(DocumentOrToken documentOrToken) {
        // Nothing to do
    }

    @Override
    public boolean isValid(DocumentContainerEntity documentContainerEntity, ConstraintValidatorContext constraintValidatorContext) {
        return documentContainerEntity.hasEnoughData();
    }
}
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte