Validez vos données

Les auteurs de ce contenu recherchent un correcteur. N’hésitez pas à les contacter par MP pour proposer votre aide !

S’assurer que nos données sont correctes avant de faire des opérations plus complexes se basant sur elles est primordial. Nous allons voir comment on peut déléguer cette validation à des objets dont c’est la spécialité.

Ajouter des contraintes aux entités

Lorsqu’on a des données qui sont fournies par l’utilisateur, il faut toujours appliquer le principe du Filter Input Escape Output, autrement dit "filtre ce qu’on te donne, adapte ce que tu envoies".

Nous avons pu, lors des précédents chapitres utiliser le moteur de templating pour adapter proprement le HTML. De même nous avons utilisé certains attributs pour forcer le système à valider nos entrées.

Ces attributs ont pour point commun de dériver de ValidationAttribute. Lorsque le modèle est construit par ASP.NET grâce aux entrées utilisateurs, le système va générer un objet ModelState qui va dès lors prendre en compte chacun des attributs, et appeler leur méthode IsValid. Dans le cas où la méthode retournerait false, la propriété IsValid de ModelState passe à false et il commence à remplir la liste des erreurs que vous pourrez afficher dans votre template.

Si vous désirez créer votre propre attribut pour valider un modèle, il suffit simplement de suivre ce modèle :

public class StringLengthRangeAttribute : ValidationAttribute
{
    public int Minimum { get; set; }
    public int Maximum { get; set; }

    public StringLengthRangeAttribute()
    {
        this.Minimum = 0;
        this.Maximum = int.MaxValue;
    }

    public override bool IsValid(object value)
    {
        string strValue = value as string;
        if (!string.IsNullOrEmpty(strValue))
        {
            int len = strValue.Length;
            return len >= this.Minimum && len <= this.Maximum;
        }
        return true;
    }
}
un exemple de ValidationAttribute

Notons que dans certains cas vous désirerez obtenir plus d’information afin de gérer la validation. Il faudra alors faire deux choses :

  • plutôt que de surcharger IsValid(object value), il faudra surcharger IsValid(object value, ValidationContext context). Le second paramètre contient pas mal d’informations qui peuvent être utiles.
  • il faudra surcharger la propriété RequiresValidationContext et la passer à true dans le constructeur.

Bonnes pratiques

  • C’est à Required de vérifier qu’un champ n’est pas vide, le retour par défaut, en cas de champ vide pour un validateur personnalisé, doit donc être true.
  • Corrolaire de la première bonne pratique, lors de la conversion de la valeur en le type désiré, utilisé le cast as XXX qui retournera null en cas d’échec.

Un peu plus loin : les MetaType

Le concept de méta données

Nous l’avons vu, la validation des données est une chose très facile à mettre en place dans ASP.NET.

Néanmoins, vous arriverez souvent à un point où vous aurez plusieurs entités qui se ressemblent ou qui partagent certains attributs. Cela peut se trouver dans vos view-model mais aussi plus globalement dans tous les modèles de votre application.

Imaginons que vous permettiez à vos visiteur de vous envoyer un email via un formulaire de contact.

Ce dernier serait alors composé d’un sujet, et d’un texte.

N’est-ce pas aussi à cela que peut ressembler un commentaire de votre blog? Du moins le commentaire de votre blog aura-t-il un point commun avec le formulaire de contact : il contient un texte.

Le problème c’est que dans un cas comme dans l’autre, vous aimeriez que le texte subissent certaines validations, comme par exemple une validation Anti Spam.

Une idée assez simple serait alors de dire à tous les modèles d’hériter d’un modèle qui possède un texte et les attributs idoines.

Malheureusement, cela pose deux problèmes:

  • cela "bloque" votre héritage : en effet en C# on ne peut pas hériter de deux classes en même temps;
  • cela n’a pas forcément de sens : certes on pourrait simplement dire que notre classe de basse s’appelle ElementWithAntiSpamText mais avouons que c’est un peu dommage de créer une classe qui offrirait si peu de fonctionnalités de base qu’on doit créer des classes autour qui ajouteront un vrai sens métier.

Une autre idée serait d’utiliser une fonctionnalité avancée de C# : les méta type. Comme il ne s’agit pas d’une fonctionnalité propre à ASP.NET, je ne vous détaillerai pas le mécanisme complet derrière ce concept, mais voyez le principe de méta type comme la possibilité de définir une base commune à plusieurs sous-type mais sans donner le sens et les contraintes liés à l’héritage. Vous allez offrir une description de votre type de base (le méta type) et le compilateur se chargera d’incorporer cette description dans toutes vos classes réelles.

[MetadataType(typeof(AntiSpamProtectedMetaData))]
public partial class ContactForm
{
    [MaxLength(128)]
    public String Subject {get; set;}
    [EmailAddress]
    public String UserAddress {get; set;}
}

[MetadataType(typeof(AntiSpamProtectedMetaData))]
public partial class Comment
{
    [MaxLength(256)]
    [RegularExpression("/^https?://[a-zA-Z0-9_.-]+\.[a-z]{2,10}(/[a-zA-Z_%0-9.\?/-])*")]
    public String WebSite {get; set;}
    [EmailAddress]
    public String UserAddress {get; set;}
}

public class AntiSpamProtectedMetaData
{
    [Display(Name = "Votre message")]
    [AVeryPowerfulAntiSpam(ErrorMessage = "Votre message a été détecté comme spam.")]
    [Required(ErrorMessage = "Merci d'entrer un message.")]
    public string Text { get; set; }
}
Deux types qui se servent d’un MetaType

Vous pouvez maintenant être assurés de la qualité des données que vous manipulez.