Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Atteint de JavaScriptite Aiguë [Cyril Durand]

Expert ASP.net Ajax et WCF, Cyril Durand parle dans son blog de point techniques sur ASP.net, ASP.net Ajax, JavaScript, WCF et .net en général. Cyril est également consultant indépendant, n'hésitez pas à le contacter pour de l'assistance sur vos projets

Actualités

  • Blog de Cyril DURAND, passionné de JavaScript, Ajax, ASP.net et tout ce qui touche au developpement Web Client-Side.

    N'hésitez pas à me contacter pour vos projets .net : architecture, accompagnement, formation, ...

    View Cyril Durand's profile on LinkedIn
    hit counters


    Expertise Commerce server et BizTalk

ASP.net - tout savoir sur la validation des entrées utilisateurs | les controles de validation

Trop souvent, les entrées utilisateurs ne sont pas vérifiées. Dans le meilleur des cas, le thread exécutant la requête s’arrête à cause d’une exception lors de son traitement dans la couche métier ou dans la couche base de données, mais dans certains cas cela peut avoir des incidences graves : corruption de données, ou pire encore : injection SQL.

Dans cet article, nous allons voir les bonnes pratiques afin de valider la saisie utilisateur dans une application ASP.net.

Les contrôles de validation

Les contrôles de validation fournis avec ASP.net valident la saisie utilisateur à la fois coté client ET coté serveur.

Pourquoi valider les données deux fois ? Coté client et serveur ?
La validation coté client s’effectuera forcément via JavaScript, or il est très simple de désactiver JavaScript ou alors d’envoyer ses propres requêtes HTTP, par conséquent il est nécessaire de toujours effectuer une vérification coté serveur.

La classe BaseValidator

La classe BaseValidator est le contrôle de base dont héritent tous les autres contrôles de validation, cette classe hérite de Label elle possède donc toutes ses propriétés mais aussi quelques autres propriétés intéressantes afin de configurer les différents contrôles de validation :

  • ControlToValidate : Indique l’ID du contrôle sur lequel s’effectue la validation
  • Text : Message d’erreur du contrôle, il s’agit de la propriété par défaut
  • ErrorMessage : Message d’erreur affiché dans le ValidationSummary
  • Display : Indique comment le message d’erreur est affiché, les valeurs possibles sont :
    • None : Le message d’erreur n’est jamais affiché.
    • Static : Le contrôle réserve la place au message d’erreur mais n’est affiché que lorsque le contrôle est invalide ; dans ce cas l’affichage ou non du message d’erreur se fait grâce à la propriété CSS visibility.
    • Dynamic : Le contrôle ne réserve pas de place dans la page lorsque le contrôle est valide ; l’affichage du message d’erreur ce fait via la propriété CSS display.
  • ValidationGroup : Indique le groupe de validation
  • SetFocusOnError : Indique qu’il faut positionner le curseur sur le contrôle à valider si celui-ci est invalide.
  • IsValid : Indique si l’état du contrôle est valide ou non

La plupart du temps on renseigne la propriété Text avec un astérisque et la propriété ErrorMessage avec le détail du problème, ainsi nous avons un astérisque à coté des contrôles invalide et un résumé des erreurs dans le ValidationSummary que nous verrons plus tard.

La propriété ValidationGroup est très utile, elle permet d’avoir des zones de formulaire au sein d’une même page, cette propriété est également disponible sur les boutons, liste déroulante, ou tout autre contrôle effectuant un postback, cela permet ainsi de valider le formulaire coté client avant de faire un postback.

D’une manière générale chaque contrôle de validation doit avoir sa propriété ValidationGroup renseignée.

La validation s’effectue de façon automatique, tout d’abord coté client via JavaScript puis côté serveur. Lorsque vous utilisez des ValidationGroup, il vous sera alors nécessaire de vérifier si votre groupe de validation est valide avant de rentrer dans le code critique ! Si la page n’est pas valide, alors les contrôles invalides afficheront leur message d’erreur.

protected void btnSubmit_Click(object sender, EventArgs e) { Page.Validate("MyValidationGroup"); if (Page.IsValid) { // critical code goes here } }

Si vous voulez qu’un contrôle précis ne valide pas le formulaire, ce qui est généralement le cas pour les boutons « annuler », il faut alors renseigner la propriété CausesValidation du contrôle faisant le postback (Button, ImageButton, …) à false.

Il existe 5 contrôles de validation fournis par ASP.net

RequiredFieldValidator

Le RequiredFieldValidator permet de vérifier qu’une valeur ait bien été saisie. Voici un exemple d’utilisation :

<asp:label runat="server" AssociatedControlID="tbFirstName">FirstName :</asp:label> <asp:textbox id="tbFirstName" runat="server"/> <asp:RequiredFieldValidator runat="server" ControlToValidate="tbFirstName" Display="dynamic" ErrorMessage="You must enter a first name" Text="*" ValidationGroup="MyValidationGroup" />

A noter que l’on peut aussi renseigner la propriété Text de tous les contrôles de validation directement à l’intérieur de ceux-ci :

<asp:label runat="server" AssociatedControlID="tbFirstName">FirstName :</asp:label> <asp:textbox id="tbFirstName" runat="server"/> <asp:RequiredFieldValidator runat="server" ControlToValidate="tbFirstName" ErrorMessage="You must enter a first name" Display="dynamic" ValidationGroup="MyValidationGroup" >*</asp:RequiredFieldValidator>

Ce contrôle possède également  une propriété DefaultValue qui lui permet de vérifier que la valeur saisie est différente d’une valeur par défaut. Cela est très utile dans le cas d’une liste déroulante, en effet on renseigne souvent la première valeur avec « Sélectionnez un élément » ou autre.

<asp:DropDownList ID="ddlCountry" runat="server" AppendDataBoundItems="true"> <asp:ListItem Value="-1">Sélectionnez un élément</asp:ListItem> </asp:DropDownList> <asp:RequiredFieldValidator runat="server" ControlToValidate="ddlCountry" InitialValue="-1" Text="Choisissez un élément" Display="Dynamic" ValidationGroup="MyValidationGroup"/>

CompareValidator et RangeValidator

Ces deux contrôles sont assez proches et ont d’ailleurs un parent commun BaseCompareValidator. Ils permettent de vérifier la validité d’une entrée par rapport à une autre valeur.

Le CompareValidator vérifie l’entrée en fonction d’une autre valeur, pour cela il dispose de différentes propriétés :

    • Equal, NotEqual, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual
    • DataTypeCheck : Permet de vérifier que la valeur est du type voulu. s’utilise en coordination avec la propriété Type.
  • Type : String, Integer, Double, Date, Currency
  • ValueToCompare, ControlToCompare : indique avec quelle valeur le contrôle doit comparer la valeur du contrôle renseigné par la propriété ControlToValidate

Le RangeValidator permet de vérifier qu’une valeur est comprise entre deux autres, voici les propriétés du contrôle :

Toutes les comparaisons s’effectuent avec la culture du thread en cours, si vous avez un problème pour valider une date dans un autre format, pensez à vérifier la culture du thread ! Cette culture se spécifie dans le web.config ou dans la directive de page.

<globalization culture="fr-FR" uiCulture="fr-FR">

Vous pouvez aussi mettre auto comme valeur, dans ce cas là, la culture sera suivant la configuration du navigateur.

Voici quelques exemples d’utilisation du CompareValidator et RangeValidator.

Validation d'un nombre entier :

<asp:label runat="server" AssociatedControlID="tbInteger" Text="Nombre entier" /> <asp:TextBox runat="server" ID="tbInteger" /> <asp:CompareValidator runat="server" ControlToValidate="tbInteger" Type="Integer" Operator="DataTypeCheck" Text="*" />

Vérifie que le nombre saisit est compris entre int.MinValue et int.MaxValue.

Validation d'un nombre à virgule :

<asp:Label runat="server" AssociatedControlID="tbDouble" Text="Nombre à virgule" /> <asp:TextBox ID="tbDouble" runat="server" /> <asp:CompareValidator runat="server" ControlToValidate="tbDouble" Text="*" ValidationGroup="MyValidationGroup" Operator="DataTypeCheck" Type="Double" />

Vérifie que le nombre est compris entre Double.MinValue et Double.MaxValue. La virgule est dépendante de la culture du thread en cours.

Validation d'une date :

<asp:Label runat="server" AssociatedControlID="tbDate" Text="Date" /> <asp:TextBox ID="tbDate" runat="server" /> <asp:CompareValidator runat="server" ControlToValidate="tbDate" Text="*" ValidationGroup="MyValidationGroup" Operator="DataTypeCheck" Type="date" />

Là encore le format de la date sera en fonction de la culture en cours. Afin de rendre la saisie beaucoup plus agréable pour l'utilisateur, il est possible d’utiliser le contrôle Calendar des Ajax toolkits,

Validation d'une valeur par rapport à une autre valeur fixe :

Comme son nom l'indique, le CompareValidator peut aussi vérifier qu'une valeur est inférieure, égale ou supérieure à une autre ...

<asp:Label runat="server" AssociatedControlID="tbCompare" Text="Comparaison" /> <asp:TextBox ID="tbCompare" runat="server" /> <asp:CompareValidator runat="server" ControlToValidate="tbCompare" Text="*" Type="Integer" ValueToCompare="5" Operator="LessThan" ValidationGroup="MyValidationGroup" />

Les opérateurs disponibles sont LessThanLessThanEqualNotEqualEqualGreaterThan, GreaterThanEqual. Vous pouvez faire la même chose avec le type Date, Double, String. Dans le cas d'une comparaison avec un String ce sera la méthode String.Compare qui sera utilisé, pour la plupart des cultures, c'est l'ordre alphabétique qui est pris en compte.

Validation d'une valeur via un intervalle de valeur fixe :

On utilise cette fois-ci le RangeValidator :

<asp:Label runat="server" AssociatedControlID="tbCompare2" Text="Comparaison 2" /> <asp:TextBox ID="tbCompare2" runat="server" /> <asp:RangeValidator runat="server" ControlToValidate="tbCompare2" Type="Date" Text="*" ValidationGroup="MyValidationGroup" MinimumValue="01/01/2007" MaximumValue="31/12/2007" />

Là encore on peut tester les dates, les entiers, les nombres à virgules et les strings, les comparaisons se feront suivant la culture courante.

Validation d'une valeur par rapport à une autre valeur définit par l'utilisateur

Lorsque vous utilisez un CompareValidator avec la propriété ValueToCompare vous pouvez la remplacer par la propriété ControlToCompare afin de laisser le choix à l'utilisateur sur quel valeur il doit comparer. C'est ce qu'on utilise lorsqu'on veut vérifier que 2 mots de passe sont identiques.

<asp:TextBox ID="tbCompare" runat="server" /> <asp:TextBox ID="tbCompare2" runat="server" /> <asp:CompareValidator runat="server" ControlToValidate="tbCompare" Text="*" Type="String" ValidationGroup="MyValidationGroup" ControlToCompare="tbCompare2" Operator="LessThan" />

RegularExpressionValidator

Le RegularExpressionValidator permet de vérifier que la valeur d’un contrôle est conforme avec une expression régulière.

<asp:TextBox ID="tbMail" runat="server" /> <asp:RegularExpressionValidator runat="server" ControlToValidate="tbMail" Display="Dynamic" ValidationGroup="newUser" ErrorMessage="Email invalide veuillez vérifier la saisie de votre e-mail" ValidationExpression="^[a-zA-Z0-9-+.'_]+@\w+([-.]\w+)*\.\w+([-.]\w+)*$" />

CustomValidator

Le CustomValidator est à utiliser lorsqu’aucun autre contrôle n’est utilisable. Grâce à lui vous pouvez écrire votre code de validation à la fois côté client et/ou côté serveur. Pour cela le CustomValidator possède une propriété et un événement :

  • ServerValidate : événement se déclenchant lorsque la page valide le contrôle coté serveur, le retour de la validation se fait via la propriété IsValid de l’EventArgs
  • ClientValidationFunction : nom de la fonction JavaScript à exécuter lors de la validation cliente. La méthode JavaScript doit prendre 2 arguments, le premier correspond à l’élément HTML représentant le contrôle de validation, le second argument possède une propriété IsValid permettant de renseigner l’état de validation.

Voici un exemple de l’utilisation du CustomValidator afin de valider que l’utilisateur ait bien coché une checkbox

<script type="text/javascript"> var validateAcceptCondition = function(source, args){ args.IsValid = $get('<%=cbAcceptCondition.ClientID%>').checked; } </script> <asp:CustomValidator ID="cvAcceptCondition" runat="server" ClientValidationFunction="validateAcceptCondition" Text="Vous devez accepter les conditions" ValidationGroup="Condition" /> <asp:CheckBox ID="cbAcceptCondition" runat="server" ValidationGroup="Condition" Text="En cochant cette case, j'accepte les conditions ci-dessus" />

// Coté serveur protected void cvAcceptCondition_ServerValidate(object source, ServerValidateEventArgs args) { args.IsValid = cbAcceptCondition.Checked; }

Contrôle de validation personnalisé

Si vous utilisez souvent le même CustomValidator, je ne peux que vous conseiller de créer un contrôle de validation personnalisé qui hérite de BaseValidator ou de CustomValidator, ou encore de IValidator, en effet cela vous permettra de mutualiser le code de vérification.

Voici le même exemple que le précédent mais écrit sous la forme d’un contrôle de validation personnalisé.

public class CheckBoxValidator : BaseValidator { private String ClientValidationFunction { get { this.EnsureID(); return String.Format("cbValidate{0}", this.ClientID); } } private ICheckBoxControl CheckBoxToValidate { get { if (String.IsNullOrEmpty(this.ControlToValidate)) throw new ArgumentNullException("ControlToValidate"); ICheckBoxControl cb = this.NamingContainer .FindControl(this.ControlToValidate) as ICheckBoxControl; if (cb == null) throw new ArgumentException("ControlToValidate", String.Format("Control {0} not found", this.ControlToValidate)); return cb; } } protected override bool EvaluateIsValid() { return this.CheckBoxToValidate.Checked; } protected override void OnPreRender(EventArgs e) { String script = String.Format(@"var {0}=function(source,args){{args.IsValid={1}.checked}}", this.ClientValidationFunction, ((Control)this.CheckBoxToValidate).ClientID); ScriptManager.RegisterClientScriptBlock(this, typeof(CheckBoxValidator), this.ClientValidationFunction, script, true); base.OnPreRender(e); } }

Ainsi on pourra vérifier que l’utilisateur a coché une checkbox en n’utilisant que ce code :

<cs:CheckBoxValidator runat="server" ControlToValidate="cbAcceptCondition" Text="Vous devez accepter les conditions" ValidationGroup="Condition" /> <asp:CheckBox ID="cbAcceptCondition" runat="server" Text="En cochant cette case, j'accepte les conditions ci-dessus" ValidationGroup="Condition" />

On voit ici que nous n’avons plus aucune règle métier dans la partie aspx, tout est mutualisé au sein du contrôle personnalisé.

Le contrôle ValidationSummary

Contrairement aux autres contrôles présentés jusqu’ici, le ValidationSummary n’est pas un contrôle qui permet de valider les données, ce contrôle permet d’afficher au même endroit les propriétés ErrorMessage des contrôles de validation non valide.

Voici ses principales propriétés :

  • DisplayMode : indique comment les erreurs sont affichées, les valeurs possibles sont :
    • List : utilise une liste séparée par des retours à la ligne
    • BulletList : utilise une liste (ul/li) pour afficher les erreurs
    • SingleParagraph : affiche les erreurs les unes à la suite des autres
  • HeaderText : message affiché en haut de la liste des erreurs, par exemple « la page contient des erreurs : »
  • ShowMessageBox : indique si on affiche un messagebox lorsqu’il y a des erreurs.

Voici un exemple de ValidationSummary :

<asp:label runat="server" AssociatedControlID="tbFirstName">FirstName :</asp:label> <asp:textbox id="tbFirstName" runat="server"/> <asp:RequiredFieldValidator runat="server" ControlToValidate="tbFirstName" Display="dynamic" ErrorMessage="You must enter a first name" Text="*" ValidationGroup="MyValidationGroup" /> <asp:ValidationSummary runat="server" ValidationGroup="MyValidationGroup" />

Plus loin avec les contrôles de validation

Vous avez créé votre propre contrôle de validation et vous voudriez pouvoir le valider avec un contrôle de validation ? C’est possible !

Tout d’abord il faut que les contrôles de validation sachent quelle propriété de votre contrôle ils doivent valider. Pour cela vous devez rajouter l’attribut ValidationPropertyAttribute au niveau de votre contrôle, celui-ci nécessite un string qui est le nom d’une propriété publique de votre contrôle.

[ValidationProperty("Text")] public class TextBox : WebControl, ... {

Voici les contrôles ayant cet attribut :

  • TextBox et la propriété Text
  • ListBox , Dropdownlist, RadioButtonList  et la propriété SelectedItem.Value
  • FileUpload et la propriété FileBytes
  • D’autres contrôles du namespace System.Web.UI.HtmlControls

Tous ces contrôles peuvent donc être validés par un validateur.

Masque de saisie

Valider les données c'est bien, aider les utilisateurs à les saisir c'est mieux. Pour cela ASP.net 2.0 ne propose rien en natif, on peut utiliser les ajax control toolkit qui est un projet Open Source de Microsoft. Présenter en détail les différents contrôles d'aide à la validation des toolkits est au delà des objectifs de cet article.

  • Calendar : permet d'associer un calendrier à une textbox
    image_thumb8
    ==> démo
  • FilteredTextbox  : permet d'autoriser seulement certains caractères dans une textbox
    ==> démo
  • NumericUpDown : permet d'avoir des boutons "plus" and "moins" à coté d'une textbox afin d'augmenter ou diminuer sa valeur
    image_thumb9 
    ==> démo
  • ValidatorCallout : permet d'avoir un message expliquant l'erreur de validation
    image_thumb4
    ==> démo

Validation des entrées via « QueryString » ou autre entrée utilisateur

Les formulaires ne sont pas les seuls endroits où l’utilisateur peut renseigner des valeurs, toutes les données provenant de l’utilisateur doivent être validées !

La principale autre source d’entrée est le passage de paramètres dans l’url : les arguments en querystring. Pour ces cas, je vous conseille d’utiliser une propriété au niveau de la page afin d’obtenir ces valeurs plutôt que d’accéder directement à la saisie utilisateur dans votre code métier.

Si nous avons une page qui prend en entrée un ID d’article, voici un exemple pour être sur que la saisie soit valide.

Dans ce cas le N° d’article est obligatoire, s’il n’est pas renseigné nous déclenchons une erreur.

public int ArticleID { get { int _articleID = 0; if (!String.IsNullOrEmpty(Request.QueryString["ArticleID"])) { if (!int.TryParse(Request.QueryString["ArticleID"], out _articleID)) { throw new ArgumentException("Le N° d'article saisi n'est pas valide"); } } else { throw new ArgumentException("Le N° d'article n'est pas spécifié"); } return _articleID; } }

Dans le cas, où le Numéro d’article ne soit pas obligatoire, on peut utiliser un type nullable, le précédent exemple devient alors :

public int? ArticleID { get { if (!String.IsNullOrEmpty(Request.QueryString["ArticleID"])) { int _articleID = 0; if (!int.TryParse(Request.QueryString["ArticleID"], out _articleID)) { throw new ArgumentException("Le N° d'article saisi n'est pas valide"); } else { return _articleID; } } else { return null; } } }

Bien sur ces exemples ne sont pas réservés au passage d’arguments dans l’url, il vous faut adapter ces propriétés à votre propre cas.

Conclusion

Pour conclure cet article, j'aimerais rappeler une règle simple. Ne faites jamais confiance à l’utilisateur ! Il faut toujours avoir en tête que l’utilisateur est un pirate potentiel et qu’il en sait plus que vous sur votre code ! Pensez toujours à vérifier les saisies utilisateurs même lorsqu'elles viennent d'une source "sûre", que ce soit via un formulaire, un service web ou wcf, un fichier XML/CSV, etc ...

Vous avez quelque chose à rajouter à propos de la validation ? Des questions ? Des points encore obscurs ? Les commentaires sont là pour ça.

Posted: mardi 2 septembre 2008 23:43 par cyril
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

lgmorand a dit :

je sais pas si c'est exhaustif mais ca a le mérite d'être très complet et très clair surtout grâce aux exemples. bravo ;) (et merci oeuf corse)

# septembre 3, 2008 11:03
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01

- SharePoint Online: Script PowerShell pour supprimer une colonne dans tous les sites d’une collection par Blog Technique de Romelard Fabrice le 11-27-2018, 18:01