Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

ASP.NET Mvc 3 unobtrusive validation - erweitern mit eigenen jQuery Adaptern und Validatoren

  1. Teil 1: ASP.NET Mvc 3 unobtrusive validation
  2. Teil 2: Unobtrusive validation - Clientseitige Adapter
  3. Teil 3: Unobtrusive validation - Eigene Adapter erstellen
Grundsätzlich gibt es serverseitig zwei Möglichkeiten die eigenen Attribute für die Validierung zu Gestalten:

Implementierung einer Basisklasse wie RegularExpressionAttribute sowie des Interfaces IClientValidatable
Implementierung einer Basisklasse wie RegularExpressionAttribute und den Einbau eines DataAnnotationsModelValidator

Im Prinzip hat der letzte Post gezeigt, dass ein jQuery Adapter die Konvertierung von HTML5 Attributen in die für jQuery kompatiblen Metadaten vornimmt.

Diese HTML5 Attribute haben alle die selbe Form: data-val-<xxx>

Als Beispiel gibt es bereits vordefinierte Konstellationen wie data-val-regex und data-val-regex-pattern.

Somit würde der im Hintergrund stehende Adapter nichts anderes machen, als zu überprüfen, ob ein Feld mit dem Attribut data-val-regex gesetzt ist und wenn ja, das dazugehörige pattern auslesen und validieren.

Im nachstehenden Beispiel wird ein eigenes, triviales Email Attribut erstellt, um die Umsetzung etwas deutlicher zu erklären.

Möglichkeit 1) IClientValidatable

Das Attribut
public class EmailWithoutValidatorAttribute : RegularExpressionAttribute, IClientValidatable 
{ 
    public EmailWithoutValidatorAttribute() : base(@"<emailregex>")
    {

    }
 
    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, 
                                                                             ControllerContext context)
    {
        var rule = new ModelClientValidationRule(...);
        return new[] { rule };
    }
}
Eigentlich nur ein RegularExpressionAttrubite wie von früheren Zeiten gewohnt, sowie eine zusätzliche Methode GetClientValidationRules, die die Rückgabe einer Liste von Regeln erwartet. Ob eine oder mehrere Regeln zurückgegeben werden, ist irrelevant.

Nun reicht eine normale ModelClientValidationRule für das Vorhaben nicht aus, somit wird eine eigene erstellt. Diese hat den Vorteil, dass ein eigener validationType (Der eigentliche Name des Adapters, der später benötigt wird) angegeben werden kann.
public class NamedRegexValidationRule : ModelClientValidationRule
{
    public NamedRegexValidationRule(string errorMessage,
                               string pattern, string validationType)
    {
        this.ErrorMessage = errorMessage;
        this.ValidationType = validationType;
        this.ValidationParameters.Add("pattern", pattern);
    }
}
Die im Code gezeigten ValidationParemeters können beliebig gefüllt werden, benötigt wird hier ausschließlich das Pattern.

Hinweis: Die <string, object> collection kann beliebig gefüllt werden. Letztendlich  wird jedes Element als ein html Attribut data-val-<string>="<value>" gerendert. Es ist somit egal, ob "pattern" oder "blubr" vorkommt.

Die angepasste Methode im Attribut würde somit wie folgt aussehen (Wichtig ist der Name „customemail“, dieser wird als Adaptername verwendet und kann ausschließlich mit Kleinbuchstaben angegeben werden)
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, 
                                                                         ControllerContext context)
{
    var rule = new NamedRegexValidationRule(this.ErrorMessage, this.Pattern, "customemail");
    return new[] { rule };
}
Angenommen es ist ein Model "Person" mit einer Eigenschaft "Email" vorhanden, die mit dem eben erstellen Attribut versehen ist:
public class Person
{
    [EmailWithoutValidator(ErrorMessage = "Not a valid email.")]
    public string Email { get; set; }
}
Das dazugehörige Feld als gerenderter Version @Html.TextBoxFor(p => p.Email)
<input type="text" value="" name="Email" id="Email" class="input-validation-error"
    data-val-customemail-pattern="<EmailPattern>" 
    data-val-customemail="Not a valid email." 
    data-val="true">
Zwei Sachen wurden damit erreicht:
  • data-val-customemail steht für die angegebene Fehlermeldung.
  • data-val-customemail-pattern steht für das angegebene Email Pattern.

Möglichkeit 2) DataAnnotationsModelValidator
public class EmailWithValidatorAttribute : RegularExpressionAttribute
{
    public EmailWithValidatorAttribute() : base(@"<EmailRegex>")
    {
    }
}
Nichts anderes als bei Möglichkeit 1, ohne der Implementierung des IClientValidatable Interfaces und somit der Erstellung der Regeln.

Diese werden bei dieser Variante in den Validator ausgelagert:
public class EmailValidator : DataAnnotationsModelValidator<EmailWithValidatorAttribute>
{
    private readonly string _errorMessage;
    private readonly string _pattern;

    public EmailValidator(ModelMetadata metadata, ControllerContext context, EmailWithValidatorAttribute attribute) 
                       : base(metadata, context, attribute)
    {
        this._errorMessage = attribute.ErrorMessage;
        this._pattern = attribute.Pattern;
    }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        var rule = new NamedRegexValidationRule(this._errorMessage, this._pattern, "customemail");
        return new[] { rule };
    }
}
Auch hier gibt es eine Methode GetClientValidationRules(), die eigentlich die selbe Logik beinhaltet, wie in Möglichkeit 1.

Zusätzlich muss der Validator nun in der Global.asax (In der application start Methode) noch mit Angabe des Attributes registriert werden:
DataAnnotationsModelValidatorProvider.RegisterAdapter(
                            typeof(EmailWithValidatorAttribute), 
                            typeof(EmailValidator));
Im Prinzip bringen beide Möglichkeiten das selbe Ergebnis, Möglichkeit 1 hat den Nachteil (zumindest für den ein oder anderen), dass die Logik direkt im Attribut steht und Möglichkeit 2 lagert dies in einen externen Validator aus. Somit ist das Attribut nicht MVC abhängig und könnte auch für andere Zwecke verwendet werden, bzw. bereits vorhandene Attribute könnten problemlos wiederverwendet werden.


Last but not least, der clientseitige Aufbau des Adapters.

Die jQuery unobtrusive validation Bibliothek stellt eine Methode Add() zur Verfügung, über welche neue, eigene Adapter registiert werden können. Diese nimmt als Parameter einmal den Adapternamen, den/die Attributnamen die benötig werden, sowie eine Funktion, über die die eigene Logik implementiert werden kann.
jQuery.validator.unobtrusive.adapters.add("customemail", ["pattern"], function (options) {

    if (options.element.tagName.toUpperCase() == "INPUT") {

        // Set the regex rule with the given pattern.
        options.rules["regex"] = options.params.pattern;

        if (options.message) {
            options.messages["regex"] = options.message;
        }
    }
});
Wie im letzten Post gezeigt, beinhaltet das options Objekt Regeln, Parameter und Mitteilungen, über die die Validierung gesteuert wird.

In Falle einer Email Validierung reicht eine regex Regel aus, es können aber beliebig viele hinzugefügt werden. Wichtig ist hierbei nur, dass diese als Paremter angegeben werden ["pattern", "<anderes Attribut">, ...]

Natürlich macht es nicht wirklich viel Sinn, ein Email Attribut so kompliziert zu gestalten, viel mehr geht es darum zu zeigen, wie es im Verhältnis doch einfach ist, eigene Adapter zu erstellen. Im Web 2.0 Zeitalter ist eine kombination aus client und server Validierung eher Standard als eine Ausnahme. Mit ein bisschen Mühe können tolle Sachen, wie bspw.  eine Überprüfung, ob ein Username einzigartig ist entwickelt werden. Der Große Vorteil liegt auch hier in der Entkoppelung der Validierungslogik vom restlichen Teil der Anwendung.

Anbei gibt’s wie immer das Beispiel zum Post.

DotNetKicks-DE Image
Veröffentlicht Donnerstag, 13. Januar 2011 08:30 von Roberto

Kommentare

# Roberto's Blog : ASP.NET unobtrusive validation - Clientseitige Adapter

Anonyme Kommentare sind nicht zugelassen