ASP.NET

Asp.net MVC 4 le système de validation partie 1

Jan 8, 2014

Félix Billon

Je mets sur pause ma série de publications d »articles concernant le Javascripts pour publier une série d »article sur la validation dans Asp.Net MVC 4.

Suite à la réalisation d »un projet (où nous avons eu plusieurs cas sortant de l »utilisation classiques du système de validation imposée par le Framework), je me suis dit que garder une trace des techniques utilisées serait bien utile.

Voici les différents points abordés lors de ce premier article :

1- Remplacement des messages d »erreur par défaut

2- Désactivation des DataAnnotations générées par défaut

3- Extension du système de validation via les Metadatas

1- Remplacement des messages d »erreur par défaut

Lorsqu’2014-02-20 17:02:35’on souhaite utiliser son modèle dans sa vue, on passe souvent par les Html Helpers du moteur Razor :

 @Html.TextBoxFor(m => m.MonText)

 @Html.RadioButtonFor(m => m.MonRadioButton, true, new { id = "idRadio" })
 @Html.Label("idRadio", "blabla")
 @Html.CheckBoxFor(m => m.MaCheckBox)

Lors de l »utilisation de ces Helpers, des DataAnnotations de validation sont automatiquement créées en fonction du Helper utilisé et du type de la variable utilisant le Helper.
Par exemple, si « MonText » est une variable de type int, alors un validator sera automatiquement mis en place pour vérifier que le contenu de la textbox est bien de type int.
De même si « MonText » est une variable de type int non nullable, un validator sera automatiquement mis en place pour vérifier que le contenu de la textbox n »est pas null.

Ces validators créés automatiquement possèdent des messages par défaut :

  • Validator : FieldMustBeDate, Message par défaut : The field {0} must be a date.
  • Validator : FieldMustBeNumeric, Message par défaut : The field {0} must be a number.
  • Validator : PropertyValueInvalid, Message par défaut : The value  »{0} » is not valid for {1}.
  • Validator : PropertyValueRequired, Message par défaut : A value is required.

La solution la plus simple pour modifier ces messages par défaut est de créer un fichier de ressource (par exemple MonFichierDeRessource.resx) dans le dossier App_GlobalResources de votre application.

Pour modifier les messages, éditez le fichier de la façon suivante en remplaçant dans le champ « Valeur » vos messages perso :

Il suffit ensuite de préciser que vous souhaitez utiliser ce fichier pour les messages d »erreur. Pour cela ajouter les lignes suivantes dans la méthode Application_Start() de votre Global.asax :

 ClientDataTypeModelValidatorProvider.ResourceClassKey = "MonFichierDeRessource";
 DefaultModelBinder.ResourceClassKey = "MonFichierDeRessource";

2- Désactivation des DataAnnotations générées par défaut

Comme expliquer dans le paragraphe ci-dessus, lors de l »utilisation des Html Helpers des validators sont automatiquement créés pour contrôler vos champs. Et ce même si vous n »avez précisé aucune DataAnnotation dans votre modèle sur ces propriétés.

Il se peut que vous souhaitiez empêcher la création de ces fameux validators. Dans ce cas rien de plus simple, il vous faut ajouter la ligne suivante dans la méthode Application_Start() de votre Global.asax :

DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

 Et voilà c »est fait !

 3- Extension du système de validation via les Metadatas.

Bon nous rentrons dans un sujet un peu plus complexe : la customisation du système de validation du framework Asp.net MVC 4.

Pour cela, il faut dans un premier temps comprendre le fonctionnement et l »architecture de ce système.

Par défaut, on contrôle le rendu et la validation des champs d »un modèle en les décorant avec des attributs qui sont disponibles dans une assambly séparée appelée : System.ComponentModel.DataAnnotations.

Le fonctionnement par défaut de ces DataAnnoations suffit à la plupart des cas, mais il arrive que l »on ait besoin de d »un système de validation spécifique. Voici quelques exemples :

  • Dans le cadre de notre projet des webservices définissent l »obligation de saisir un champs, l »application du required ce fait donc lors de l »appel aux webservices.
  • On peut également vouloir définir les règles de validation dans une base de données, un fichier xml,… et interroger cette source  pour savoir si l »on applique telle ou telle règles.

Il y a énormément de classes qui composent ce système et passer au peigne fin chacune d »entre elles couvrirai plusieurs articles. Je vais donc essayer d »être bref et de vous donner une vue d »ensemble du fonctionnement du système.

Nous allons dans un premier temps nous attaquer à la création des Metadatas puis dans un second article nous verrons l »intégration de ces Metadatas dans le système de validation.

Le framework MVC 4 a la capacité de créer des Metadatas à partir de notre modèle de données. Ces Metadatas seront utilisées notamment pour la validation mais aussi pour le rendu du modèle. La class ModelMetadata permet de stocker ces données relatives au modèle. C »est une structure récursive ou une instance mère est créée et contient la référence de ses instances filles. Une instance fille est créée pour chaque propriétés du modèle et contient également une référence à ses instances filles si elle en a. Voici un exemple de la structure des instances de ModelMetadata :

Voici la structure de ces Metadatas via un espion placer sur l »instance mère de la classe ModelMetadata  :

Vous remarquerez qu »il ne s »agit pas d »instance de ModelMetadata mais de DataAnnotationsModelMetadata. Cette classe implémente ModelMetadata et les instances sont fournies par le DataAnnotationsModelMetadataProvider. Pour l »instant ne vous en préoccupez pas vous comprendrez par la suite.

Bon, bien sympa ces MetaMachain, mais ça nous sert à quoi ? Patience patience ! Il faut tout d »abords comprendre comment ces instances sont créées. Via la classe  DataAnnotationsModelMetadataProvider qui dérive de la classe abstraite AssociatedMetadataProvider qui elle-même dérive de la classe abstraite ModelMetadataProvider. Cette classe est celle fournit par défaut par le conteneur ModelMedadataProviders. Via la méthode ModelMedadataProviders.Current on peut obtenir le ModelMetadataProvider actuellement utilisé. Voici ce que cela donne schématiquement :

Pour étendre la création des Metadatas et donc personnaliser la validation des champs et leur affichage, une première solution consiste à créer une classe qui dérive de la classe DataAnnotationsModelMetadataProvider. On utilise la méthode de la classe mère et donc de DataAnnotationsModelMetadataProvider pour créer les Metadatas, puis dans la suite du code libre à nous de les modifier, les étendre avec des valeurs additionnel,… Voici un exemple illustrant ce que je viens d »expliquer :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

namespace MonNameSpace
{
    /// 
    /// Gestionnaire de model personnalisé permettant de créer les metadata associé aux propriété du modèle.
    /// 
    public class CustomDataAnnotationsModelMetadataProvider : DataAnnotationsModelMetadataProvider
    {

        /******************************************************************************************
         * Une chose importante à savoir est que l''ensemble des metadatas est crée ici. L''instance mère
         * puis les instances filles ,puis les instances filles des instances filles,...Cela signifie que le model
		 * puis ses propriété, puis le type de chacune des propriétés,... va passez dans cette classe ! Il faut donc filtrer
         * les propriétés, attributs ou modèles que vous ne souhaitez pas modifier afin de ne pas perdre en temps d''éxecution.
         * ****************************************************************************************/

        /// 
        /// Création des metadatas
        /// 
        /// Liste des attributs
        /// Type de la classe conteneur
        /// Accesseur
        /// Type du model
        /// Nom de la propriété
        /// 
        protected override ModelMetadata CreateMetadata(IEnumerable attributes, Type containerType, Func modelAccessor, Type modelType, string propertyName)
        {
            //On execute la méthode de la classe mère DataAnnotationsModelMetadataProvider afin de récupéré la metadata créée
            var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

            //Libre à nous ensuite de modifier cette metadata

            //On peut par exemple agir sur le fait qu''une propriété soit requise ou non
            metadata.IsRequired = false;

            //Modifier le rendu d''une propriété
            metadata.DisplayName = "toto";
            metadata.IsReadOnly = true;

            //Ajouter des valeurs additionelles que l''on pourra récupérer dans les vues pour modifier l''affichage, le comportement,... d''une propriété du modèle
            metadata.AdditionalValues.Add("IsDraggable", true);

            //Dans cette exemple on récupère les propriétés décorés avec l''attribut MonAttributCustom
            var fieldAttr = attributes.FirstOrDefault(c => c.GetType() == typeof(MonAttributCustom)) as MonAttributCustom;
            if (fieldAttr != null)
            {
                //Je charge la liste des mes champs et les règles qui leur sont associés depuis ma BDD, un fichier xml,...(n''importe quelle source de données)
                var listField = MyRepo.GetFields();
                if (listField != null)
                {
                    //Je recherche le champs de ma liste qui correspond à cette propriété
                    var fieldProperty =
                        listField.FirstOrDefault(c => c.FieldName == fieldAttr.FieldName);
                    if (fieldProperty != null)
                    {

                        //Puis j''applique les règles que j''ai récupéré depuis ma source de données
                        if (fieldProperty.IsRequired)
                            metadata.IsRequired = true;
                        else
                            metadata.IsRequired = false;
                        // Pour ensuite activer/désactiver un lien dans la vue
                        if (!fieldProperty.IsEnable)
                            metadata.AdditionalValues.Add("IsEnable", false);
                        // Pour afficher un watermark sur le champ dans la vue
                        if (!string.IsNullOrEmpty(fieldProperty.WaterMark))
                            metadata.Watermark = fieldProperty.WaterMark;
                        //...
                    }
                }
            }

            return metadata;
        }
    }
}

Pour utiliser ce nouveau ModelMetadataProvider il suffit de modifier le current ModelMetadataProvider fournit par le conteneur ModelMedadataProviders en insérant la ligne suivante dans votre Global.asax :

 ModelMetadataProviders.Current = new CustomDataAnnotationsModelMetadataProvider();

Et voilà ! La création des Metadatas pour vos modèles et leurs propriétés passera désormais par votre CustomDataAnnotationsModelMetadataProvider !

Pour créer un attribut custom que l »on souhaite ré-utiliser dans notre CustomDataAnnotationsModelMetadataProvider il suffit de créer une classe implémentant la classe abstraite attribut, exemple :

using System;

namespace MonNameSapce
{
    /// 
    /// Attribut Custom
    /// 
    public class MonAttributCustom : Attribute
    {
        /// 
        /// Propriété de mon attribut custom
        /// 
        public string MyPropertie1 { get; private set; }

        /// 
        /// Propriété de mon attribut custom
        /// 
        public int MyPropertie2 { get; private set; }

        /// 
        /// Constructeur par défaut
        /// 
        /// Propriété définie dans le modèle utilisant cette attribut
        /// Propriété définie dans le modèle utilisant cette attribut
        public MonAttributCustom(string myPropertie1, int myPropertie2)
        {
            MyPropertie1 = myPropertie1;
            MyPropertie2 = myPropertie2;
        }
    }
}

On l »utilise ensuite soit sur une classe soit sur une propriété  :

namespace MonNameSapce
{
    /// 
    /// Classe représentant mon model
    /// 
    public class MonModel
    {
        /// 
        /// Champs utilisant mon attribut custom
        ///         
        [MonAttributCustom("Blabla", 12)]
        public string MonChamp{ get; set; }
    }
}

 Une dernière petite astuce avant de clore ce premier article concernant le système de validation d »ASP.Net MVC 4.

Lors de la création d »un attribut (montré ci-dessus), il est possible d »implémenter une interface qui vous permettra de modifier la Metadata d »une propriété décorée avec un attribut custom. L »interface s »appelle IMetadataAware et possède une méthode : OnMetadataCreated.

Voici un exemple d »utilisation :

using System;
using System.Web.Mvc;

namespace MonNameSapce
{
    /// 
    /// Attribut Custom qui utilise IMetadataAware
    /// 
    public class MonAttributCustom : Attribute, IMetadataAware
    {
        /// 
        /// Propriété de mon attribut custom
        /// 
        public string MyPropertie1 { get; private set; }

        /// 
        /// Propriété de mon attribut custom
        /// 
        public int MyPropertie2 { get; private set; }

        /// 
        /// Constructeur par défaut
        /// 
        /// Propriété définie dans le modèle utilisant cet attribut
        /// Propriété définie dans le modèle utilisant cet attribut
        public MonAttributCustom(string myPropertie1, int myPropertie2)
        {
            MyPropertie1 = myPropertie1;
            MyPropertie2 = myPropertie2;
        }

        /// 
        /// On passe dans cette méthode après que la Metadata soit créée dans notre CustomDataAnnotationsModelMetadataProvider, cela permet donc de définir un comportement spécifique
        /// lorsqu''une propriété est décorée avec cet attribut
        /// 
        /// Instance de la classe metadata
        public void OnMetadataCreated(ModelMetadata metadata)
        {
            //Ajout de valeur additionnel
            metadata.AdditionalValues["MyPropertie1"] = MyPropertie1;
            metadata.AdditionalValues["MyPropertie2"] = MyPropertie2;

            //Libre à nous de modifier la metadata pour modifier l''affichage ou la validation de la propriété de modèle qui sera décoré avec cet attribut
            metadata.DisplayName = "toto";
            metadata.RequestValidationEnabled = false;
            metadata.IsRequired = false;
        }
    }
}

Bon il y a déjà pas mal de matière dans cette première partie concernant la validation en ASP.Net MVC 4. Nous avons vue comment modifier les messages d »erreur par défaut puis comment désactiver les DataAnnotations générées par défaut. Nous somme ensuite passés au système de validation d »ASP.Net MVC 4 où nous avons vu la création des Metadatas.

Dans la seconde partie nous verrons comment récupérer ces Metadatas dans la vue et dans le controller afin d »agir sur la validation et le rendu du model. Puis nous étudierons leur intégration au sein du système de validation.

1 Commentaire

  1. YOVAS

    Excellent article! Merci beaucoup pour tes lumières (mais surveille ton orthographe ^^). Bonne continuation!

    Réponse

Soumettre un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Découvrez nos autres articles

Aller au contenu principal