Modellvalidierung in ASP.NET Core MVC und Razor Pages

Von Kirk Larkin

In diesem Artikel wird erläutert, wie Sie Benutzereingaben in einer ASP.NET Core MVC- oder Razor Pages-App überprüfen.

Zeigen Sie Beispielcode an, oder laden Sie diesen herunter (Vorgehensweise zum Herunterladen).

Modellstatus

Der Modellstatus stellt Fehler dar, die aus zwei Subsystemen kommen: Modellbindung und Modellvalidierung. Fehler, die von der Modellbindung herrühren, sind im allgemeinen Datenkonvertierungsfehler. Beispielsweise wird ein "x" in ein Integerfeld eingegeben. Die Modellvalidierung erfolgt nach der Modellbindung und meldet Fehler, bei denen die Daten nicht den Geschäftsregeln entsprechen. Beispielsweise wird eine 0 in einem Feld eingegeben, das eine Bewertung zwischen 1 und 5 erwartet.

Sowohl die Modellbindung als auch die Modellvalidierung erfolgen vor der Ausführung einer Controlleraktion oder Razor einer Pages-Handlermethode. Bei Web-Apps liegen die Überprüfung von ModelState.IsValid und entsprechende Maßnahmen im Verantwortungsbereich der App. Web-Apps zeigen die Seite normalerweise mit einer Fehlermeldung erneut an:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Web-API-Controller müssen ModelState.IsValid nicht überprüfen, wenn sie das [ApiController]-Attribut haben. In diesem Fall wird eine automatische HTTP 400-Antwort zurückgegeben, die Fehlerdetails beinhaltet, wenn der Modellstatus ungültig ist. Weitere Informationen finden Sie unter Automatische HTTP 400-Antworten.

Validierung erneut ausführen

Die Validierung erfolgt automatisch, aber ggf. möchten Sie sie manuell wiederholen. Eine Szenario wäre, dass Sie einen Wert für eine Eigenschaft berechnen und möchten, dass die Validierung erneut ausgeführt wird, nachdem der berechnete Wert für die Eigenschaft festgelegt wurde. Rufen Sie zum erneuten Ausführen der Validierung auf, um die validierungsspezifische Validierung für das zu ModelStateDictionary.ClearValidationState überprüfende Modell zu löschen, gefolgt von TryValidateModel :

public async Task<IActionResult> OnPostTryValidateAsync()
{
    var modifiedReleaseDate = DateTime.Now.Date;
    Movie.ReleaseDate = modifiedReleaseDate;

    ModelState.ClearValidationState(nameof(Movie));
    if (!TryValidateModel(Movie, nameof(Movie)))
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Validierungsattribute

Mit Validierungsattributen können Sie Validierungsregeln für Modelleigenschaften angeben. Im folgenden Beispiel aus der Beispiel-App sehen Sie eine Modellklasse, die mit Validierungsattributen versehen wurde. Das [ClassicMovie]-Attribut ist ein benutzerdefiniertes Validierungsattribut; die anderen sind integriert. Nicht dargestellt ist [ClassicMovieWithClientValidator]. [ClassicMovieWithClientValidator] zeigt einen alternativen Weg zum Implementieren eines benutzerdefinierten Attributs auf.

public class Movie
{
    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; }

    [ClassicMovie(1960)]
    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; }

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }
}

Integrierte Attribute

Im Folgenden sind einige der integrierten Validierungsattribute aufgeführt:

  • [ValidateNever]: Gibt ValidateNeverAttribute an, dass eine Eigenschaft oder ein Parameter von der Validierung ausgeschlossen werden soll.
  • [CreditCard]: Überprüft, ob die Eigenschaft über ein Kreditkartenformat verfügt. Erfordert zusätzliche jQuery-Validierungsmethoden.
  • [Compare]: Überprüft, ob zwei Eigenschaften in einem Modell übereinstimmen.
  • [EmailAddress]: Überprüft, ob die Eigenschaft über ein E-Mail-Format verfügt.
  • [Phone]: Überprüft, ob die Eigenschaft über ein Telefonnummernformat verfügt.
  • [Range]: Überprüft, ob der Eigenschaftswert innerhalb eines angegebenen Bereichs liegt.
  • [RegularExpression]: Überprüft, ob der Eigenschaftswert einem angegebenen regulären Ausdruck entspricht.
  • [Required]: Überprüft, ob das Feld nicht NULL ist. Details [Required] zum Verhalten dieses Attributs finden Sie unter -Attribut.
  • [StringLength]: Überprüft, ob ein Zeichenfolgeneigenschaftswert ein angegebenes Längenlimit nicht überschreitet.
  • [Url]: Überprüft, ob die Eigenschaft über ein URL-Format verfügt.
  • [Remote]: Überprüft die Eingabe auf dem Client durch Aufrufen einer Aktionsmethode auf dem Server. Details [Remote] zum Verhalten dieses Attributs finden Sie unter -Attribut.

Im System.ComponentModel.DataAnnotations-Namespace finden Sie eine vollständige Liste der Validierungsattribute.

Fehlermeldungen

Mit Validierungsattributen können Sie die Fehlermeldung angeben, die im Fall einer ungültigen Eingabe angezeigt werden soll. Beispiel:

[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]

Intern rufen die Attribute die Methode String.Format mit einem Platzhalter für den Feldnamen und manchmal zusätzliche Platzhalter auf. Beispiel:

[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]

Bei Anwendung auf eine Name-Eigenschaft wäre die Fehlermeldung, die vom vorangehenden Code erstellt wurde, die Folgende: „Name length must be between 6 and 8“ (Die Länge des Namens muss zwischen 6 und 8 Zeichen betragen).

Wenn Sie herausfinden möchten, welche Parameter an die Methode String.Format für die Fehlermeldung eines bestimmten Attributs übergeben werden, sehen Sie sich den DataAnnotations-Quellcode an.

Verweistypen ohne NULL-Werte und [Required]-Attribut

Das Validierungssystem behandelt Parameter, die keine NULL-Werte zu lassen haben, oder gebundene Eigenschaften so, als ob sie ein -Attribut [Required(AllowEmptyStrings = true)] hätten. Durch das Aktivieren von Nullable Kontextenbeginnt MVC implizit mit der Validierung von Eigenschaften oder Parametern, die keine NULL-Werte zu akzeptieren, als ob sie mit dem -Attribut attributiert [Required(AllowEmptyStrings = true)] worden wäre. Betrachten Sie folgenden Code:

public class Person
{
    public string Name { get; set; }
}

Wenn die App mit erstellt wurde, führt ein fehlender Wert für in einem JSON- oder <Nullable>enable</Nullable> Name Formularbeitrag zu einem Validierungsfehler. Verwenden Sie einen Nullable-Verweistyp, damit NULL- oder fehlende Werte für die Eigenschaft angegeben Name werden können:

public class Person
{
    public string? Name { get; set; }
}

Dieses Verhalten kann deaktiviert werden, indem SuppressImplicitRequiredAttributeForNonNullableReferenceTypes in Startup.ConfigureServices konfiguriert wird:

services.AddControllers(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

[Required]-Validierung auf dem Server

Auf dem Server wird ein erforderlicher Wert als fehlend betrachtet, wenn für eine Eigenschaft NULL festgelegt wurde. Ein Feld, das keine NULL-Werte zulässt, ist immer gültig, und die Fehlermeldung des [Required]-Attributs wird nie angezeigt.

Die Modellbindung für eine Eigenschaft, die keine NULL-Werte zulässt, schlägt jedoch möglicherweise fehl, und führt zu einer Fehlermeldung wie The value '' is invalid (Der Wert „“ ist ungültig). Wenn Sie eine benutzerdefinierte Fehlermeldung für die serverseitige Validierung von Typen, die nicht NULL zulassen, angeben möchten, gibt es die folgenden Optionen:

  • Lassen Sie für das Feld NULL-Werte zu (z. B. decimal? statt decimal). Nullable <T> Werttypen werden wie Standardtypen behandelt, die NULL-Werte zu verwenden.

  • Geben Sie die Standardfehlermeldung an, die von der Modellbindung verwendet werden soll, wie es im folgenden Beispiel gezeigt wird:

    services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    services.AddSingleton<IValidationAttributeAdapterProvider,
        CustomValidationAttributeAdapterProvider>();
    

    Weitere Informationen zu Fehlern bei der Modellbindung, für die Sie Standardmeldungen festlegen können, finden Sie unter DefaultModelBindingMessageProvider.

[Required]-Validierung auf dem Client

Typen und Zeichenfolgen, für die keine NULL-Werte festgelegt werden können, werden auf dem Client anders behandelt wie auf dem Server. Auf dem Client:

  • Ein Wert gilt nur als vorhanden, wenn eine Eingabe für ihn erfolgt ist. Deshalb werden bei der clientseitigen Validierung Typen, für die keine NULL-Werte festgelegt werden können, gleich behandelt wie Nullable-Typen.
  • Leerzeichen in einem Zeichenfolgenfeld werden von der jQuery Validation-Required-Methode als gültige Eingabe betrachtet. Die serverseitige Validierung betrachtet ein erforderliches Zeichenfolgenfeld als ungültig, wenn nur Leerzeichen eingegeben werden.

Wie bereits gesagt wurde, werden Typen, die keine NULL-Werte zulassen, so behandelt, als hätten sie ein [Required(AllowEmptyStrings = true)]-Attribut. Das heißt, die clientseitige Validierung erfolgt, auch wenn Sie das [Required(AllowEmptyStrings = true)]-Attribut nicht anwenden. Wenn Sie das Attribut jedoch nicht anwenden, erhalten Sie eine Standardfehlermeldung. Wenn Sie eine benutzerdefinierte Fehlermeldung angeben möchten, verwenden Sie das Attribut.

[Remote]-Attribut

Das [Remote]-Attribut implementiert die clientseitige Validierung, für die eine Methode auf dem Server aufgerufen werden muss, um zu bestimmen, ob die Feldeingabe gültig ist. So muss die App z. B. überprüfen, ob eine Benutzername bereits verwendet wird.

So wird die Remotevalidierung implementiert:

  1. Erstellen Sie eine Aktionsmethode für JavaScript, die aufgerufen werden soll. Die jQuery Validation-Remotemethode erwartet eine JSON-Antwort:

    • true bedeutet, die Eingabedaten sind gültig.
    • false, undefined oder null bedeutet, dass die Eingabe ungültig ist. Verwenden Sie die Standardfehlermeldung.
    • Jede andere Zeichenfolge bedeutet, dass die Eingabe ungültig ist. Verwenden Sie die Zeichenfolge als benutzerdefinierte Fehlermeldung.

    Hier finden Sie ein Beispiel einer Aktionsmethode, die eine benutzerdefinierte Fehlermeldung zurückgibt:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. Versehen Sie in der Modellklasse die Eigenschaft mit einem [Remote]-Attribut, das auf die Validierungsaktionsmethode zeigt, wie es im folgenden Beispiel gezeigt wird:

    [Remote(action: "VerifyEmail", controller: "Users")]
    public string Email { get; set; }
    

    Das [Remote]-Attribut ist im Microsoft.AspNetCore.Mvc-Namespace enthalten.

Zusätzliche Felder

Die AdditionalFields-Eigenschaft des [Remote]-Attributs ermöglicht Ihnen die Validierung von Kombinationen von Feldern für Daten auf dem Server. Wenn z. B. das User-Modell die Eigenschaften FirstName und LastName hätte, sollten Sie überprüfen, dass kein bereits vorhandener Benutzer diese Namenskombination verwendet. Das folgende Beispiel veranschaulicht die Verwendung von AdditionalFields:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; }

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; }

AdditionalFields könnte explizit auf die Zeichenfolgen „FirstName“ und „LastName“ festgelegt werden, aber die Verwendung des nameof-Operators vereinfacht das spätere Refactoring. Die Aktionsmethode für diese Validierung muss sowohl firstName- als auch lastName-Argumente akzeptieren:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
    if (!_userService.VerifyName(firstName, lastName))
    {
        return Json($"A user named {firstName} {lastName} already exists.");
    }

    return Json(true);
}

Wenn der Benutzer einen Vor- oder einen Nachnamen eingibt, führt JavaScript einen Remoteaufruf durch, um zu prüfen, ob dieses Paar Namen bereits verwendet wurde.

Wenn Sie mindestens zwei weitere Felder überprüfen möchten, geben Sie sie als eine mit Kommas getrennte Liste an. Wenn Sie z. B. dem Modell eine MiddleName-Eigenschaft hinzufügen möchten, legen Sie das [Remote]-Attribut wie im folgenden Code veranschaulicht fest:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }

AdditionalFields muss wie alle anderen Attributargumente ein konstanter Ausdruck sein. Aus diesem Grund verwenden Sie keine interpolierte Zeichenfolge oder rufen Join auf, um AdditionalFields zu initialisieren.

Alternativen zu integrierten Attributen

Wenn Sie eine Validierung benötigen, die nicht von integrierten Attributen bereitgestellt wird, haben Sie die folgenden Möglichkeiten:

Benutzerdefinierte Attribute

Für Szenarios, die die integrierten Validierungsattribute nicht verarbeiten können, können Sie benutzerdefinierte Validierungsattribute erstellen. Erstellen Sie eine Klasse, die von ValidationAttribute erbt, und überschreiben Sie die IsValid-Methode.

Die IsValid-Methode akzeptiert ein Objekt namens value. Dies ist die Eingabe, die überprüft werden soll. Ein Überladen akzeptiert auch ein ValidationContext-Objekt, das zusätzliche Informationen bereitstellt, z. B. die Modellinstanz, die von der Modellbindung erstellt wurde.

Im folgenden Beispiel wird überprüft, ob das Veröffentlichungsdatum eines Films aus dem Genre Classic (Klassiker) zeitlich nicht hinter einer angegebenen Jahreszahl liegt. Das [ClassicMovie]-Attribut:

  • wird nur auf dem Server ausgeführt.
  • Für Filmklassiker wird das Veröffentlichungsdatum überprüft:
public class ClassicMovieAttribute : ValidationAttribute
{
    public ClassicMovieAttribute(int year)
    {
        Year = year;
    }

    public int Year { get; }

    public string GetErrorMessage() =>
        $"Classic movies must have a release year no later than {Year}.";

    protected override ValidationResult IsValid(object value,
        ValidationContext validationContext)
    {
        var movie = (Movie)validationContext.ObjectInstance;
        var releaseYear = ((DateTime)value).Year;

        if (movie.Genre == Genre.Classic && releaseYear > Year)
        {
            return new ValidationResult(GetErrorMessage());
        }

        return ValidationResult.Success;
    }
}

Die obenstehende movie-Variable stellt ein Movie-Objekt dar, das die Daten der Formularübermittlung enthält. Schlägt die Validierung fehl, wird die Klasse ValidationResult mit einer Fehlermeldung zurückgegeben.

IValidatableObject

Im vorherigen Beispiel wurde nur mit Movie-Typen gearbeitet. Eine andere Option zur Validierung auf Klassenebene ist die Implementierung von IValidatableObject in der Modellklasse, wie im folgenden Beispiel dargestellt wird:

public class ValidatableMovie : IValidatableObject
{
    private const int _classicYear = 1960;

    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; }

    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; }

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
        {
            yield return new ValidationResult(
                $"Classic movies must have a release year no later than {_classicYear}.",
                new[] { nameof(ReleaseDate) });
        }
    }
}

Validierung der Knoten auf oberster Ebene

Knoten der obersten Ebene sind unter anderem:

  • Aktionsparameter
  • Controllereigenschaften
  • Seitenhandlerparameter
  • Seitenmodelleigenschaften

An ein Modell gebundene Knoten auf oberster Ebene und Modelleigenschaften werden überprüft. Im folgenden Beispiel aus der Beispiel-App verwendet die VerifyPhone-Methode die RegularExpressionAttribute-Klasse, um die phone-Aktionsparameter zu überprüfen:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
    [RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
    if (!ModelState.IsValid)
    {
        return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
    }

    return Json(true);
}

Knoten auf oberster Ebene können BindRequiredAttribute mit Validierungsattributen verwenden. Im folgenden Beispiel aus der Beispiel-App gibt die CheckAge-Methode an, dass der age-Parameter aus der Abfragezeichenfolge gebunden sein muss, wenn das Formular übermittelt wird:

[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{

Auf der Seite für die Altersprüfung (CheckAge.cshtml) sind zwei Formulare vorhanden. Das erste Formular übermittelt einen Age-Wert von 99 als Abfragezeichenfolgen-Parameter: https://localhost:5001/Users/CheckAge?Age=99.

Wenn ein ordnungsgemäß formatierter age-Parameter aus der Abfragezeichenfolge übermittelt wird, wird das Formular überprüft.

Das zweite Formular auf der Seite für die Altersprüfung übermittelt den Age-Wert im Anforderungstext, worauf die Validierung fehlschlägt. Die Bindung schlägt fehl, da der age-Parameter aus einer Abfragezeichenfolge stammen muss.

Maximale Fehleranzahl

Die Validierung stoppt, wenn die maximale Anzahl an Fehlern erreicht wird. Diese liegt standardmäßig bei 200. Sie können diese Zahl mit dem folgenden Code in Startup.ConfigureServices konfigurieren:

services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.MaxModelValidationErrors = 50;
        options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
            _ => "The field is required.");
    });

services.AddSingleton<IValidationAttributeAdapterProvider,
    CustomValidationAttributeAdapterProvider>();

Maximale Rekursion

ValidationVisitor durchläuft den Objektgraph des Modells, das überprüft wird. Bei Modellen, die umfassend oder unendlich rekursiv sind, führt die Validierung möglicherweise zu einem Stapelüberlauf. MvcOptions.MaxValidationDepth stellt eine Möglichkeit dar, die Validierung frühzeitig zu stoppen, wenn die Besucherrekursion eine konfigurierte Tiefe überschreitet. Der Standardwert von MvcOptions.MaxValidationDepth ist „32“.

Automatischer Kurzschluss

Die Validierung wird automatisch kurzgeschlossen (übersprungen), wenn der Modellgraph keine Validierung erfordert. Objekte, deren Validierung durch die Runtime übersprungen wird, beinhalten Primitivsammlungen (z. B. byte[], string[], Dictionary<string, string>) und komplexe Objektgraphen, die keine Validierungssteuerelemente haben.

Clientseitige Validierung

Die Überprüfung auf Clientseite verhindert die Übermittlung solange, bis das Formular gültig ist. Die Schaltfläche „Übermitteln“ führt JavaScript aus, das entweder das Formular übermittelt oder Fehlermeldungen anzeigt.

Die clientseitige Validierung umgeht einen unnötigen Roundtrip zum Server, wenn es in einem Formular Eingabefehler gibt. Die folgenden Skriptverweise in _Layout.cshtml und _ValidationScriptsPartial.cshtml unterstützen die clientseitige Validierung:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>

Das jQuery Unobtrusive Validation-Skript ist eine benutzerdefinierte Microsoft-Front-End-Bibliothek, die auf dem beliebten jQuery Validation-Plug-In aufbaut. Ohne jQuery Unobtrusive Validation müssten Sie dieselbe Validierungslogik an zwei unterschiedlichen Stellen codieren: einmal in den Attributen der Validierung auf Serverseite für Modelleigenschaften und einmal in den Skripts auf Clientseite. Stattdessen verwenden die Taghilfsprogramme und die HTML-Hilfsprogramme die Validierungsattribute und Typmetadaten aus den Modelleigenschaften, um HTML 5-Attribute des Typs data- in den Formularelementen zu rendern, die validiert werden müssen. jQuery Unobtrusive Validation analysiert die Attribute und übergibt die Logik an jQuery Validation und kopiert die serverseitige Validierungslogik effektiv an data- den Client. Sie können Validierungsfehler im Client anzeigen, indem Sie die relevanten Taghilfsprogramme wie folgt verwenden:

<div class="form-group">
    <label asp-for="Movie.ReleaseDate" class="control-label"></label>
    <input asp-for="Movie.ReleaseDate" class="form-control" />
    <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>

Die vorangegangenen Taghilfsprogramme rendern die folgende HTML:

<div class="form-group">
    <label class="control-label" for="Movie_ReleaseDate">Release Date</label>
    <input class="form-control" type="date" data-val="true"
        data-val-required="The Release Date field is required."
        id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
    <span class="text-danger field-validation-valid"
        data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>

Beachten Sie, dass die data--Attribute in der HTML-Ausgabe mit den Validierungsattributen für die Movie.ReleaseDate-Eigenschaft übereinstimmen. Das data-val-required-Attribut enthält eine Fehlermeldung, die angezeigt wird, wenn der Benutzer das Datumsfeld für die Veröffentlichung nicht ausfüllt. jQuery Unobtrusive Validation übergibt diesen Wert an die jQuery Validation required()-Methode, die diese Meldung dann im zugehörigen Element <span> anzeigt.

Die Datentypvalidierung basiert auf dem .NET-Typ einer Eigenschaft, es sei denn, dieser wird von einem [DataType]-Attribut überschrieben. Browser haben ihre eigenen Standardfehlermeldungen, aber das jQuery Validation Unobtrusive Validation-Paket kann diese Meldungen überschreiben. Mithilfe von [DataType]-Attributen und Subklassen wie [EmailAddress] können Sie die Fehlermeldung angeben.

Unaufdringliche Validierung

Informationen zur unaufdringlichen Validierung finden Sie in diesem GitHub-Issue.

Hinzufügen der Validierung zu dynamischen Formularen

jQuery Unobtrusive Validation übergibt validierungslogik und parameter an jQuery Validation, wenn die Seite zum ersten Mal geladen wird. Deshalb funktioniert die Validierung nicht automatisch bei dynamisch generierten Formularen. Wenn Sie die Validierung aktivieren möchten, müssen Sie jQuery Unobtrusive Validation auffordern, das dynamische Formular direkt nach dem Erstellen zu analysieren. Beispielweise wird im nachfolgenden Code dargestellt, wie Sie die Validierung auf Clientseite für ein Formular einrichten können, das über AJAX hinzugefügt wurde.

$.get({
    url: "https://url/that/returns/a/form",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add form. " + errorThrown);
    },
    success: function(newFormHTML) {
        var container = document.getElementById("form-container");
        container.insertAdjacentHTML("beforeend", newFormHTML);
        var forms = container.getElementsByTagName("form");
        var newForm = forms[forms.length - 1];
        $.validator.unobtrusive.parse(newForm);
    }
})

Die $.validator.unobtrusive.parse()-Methode akzeptiert einen jQuery-Selektor für ihr einziges Argument. Diese Methode fordert „jQuery Unobtrusive Validation“ auf, die data--Attribute von Formularen in diesem Selektor zu analysieren. Die Werte dieser Attribute werden dann an das jQuery Validation-Plug-In übergeben.

Hinzufügen der Validierung zu dynamischen Steuerelementen

Die $.validator.unobtrusive.parse()-Methode funktioniert für ein gesamtes Formular, nicht für individuelle, dynamisch erstellte Steuerelemente wie <input> und <select/>. Wenn das Formular erneut analysiert werden soll, entfernen Sie die Validierungsdaten, die bei der vorherigen Analyse des Formulars hinzugefügt wurden, wie es im folgenden Beispiel gezeigt wird:

$.get({
    url: "https://url/that/returns/a/control",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add control. " + errorThrown);
    },
    success: function(newInputHTML) {
        var form = document.getElementById("my-form");
        form.insertAdjacentHTML("beforeend", newInputHTML);
        $(form).removeData("validator")    // Added by jQuery Validation
               .removeData("unobtrusiveValidation");   // Added by jQuery Unobtrusive Validation
        $.validator.unobtrusive.parse(form);
    }
})

Benutzerdefinierte clientseitige Validierung

Die benutzerdefinierte clientseitige Validierung erfolgt durch Generieren von data- HTML-Attributen, die mit einem benutzerdefinierten jQuery-Validierungsadapter funktionieren. Der folgende Beispieladaptercode wurde für die [ClassicMovie]- und [ClassicMovieWithClientValidator]-Attribute geschrieben, die zuvor in diesem Artikel bereits eingeführt wurden:

$.validator.addMethod('classicmovie', function (value, element, params) {
    var genre = $(params[0]).val(), year = params[1], date = new Date(value);

    // The Classic genre has a value of '0'.
    if (genre && genre.length > 0 && genre[0] === '0') {
        // The release date for a Classic is valid if it's no greater than the given year.
        return date.getUTCFullYear() <= year;
    }

    return true;
});

$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
    var element = $(options.form).find('select#Movie_Genre')[0];

    options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
    options.messages['classicmovie'] = options.message;
});

Informationen zum Schreiben von Adaptern finden Sie in der Dokumentation zur jQuery-Validierung.

Die Verwendung eines Adapters für ein bestimmtes Feld wird von data--Attributen ausgelöst, die Folgendes tun:

  • Das Feld wird so gekennzeichnet, dass es validiert wird (data-val="true").
  • Ein Name für die Validierungsregel und ein Text für die Fehlermeldung werden bestimmt (z. B. data-val-rulename="Error message.").
  • Zusätzliche Parameter werden bereitgestellt, die vom Validierungssteuerelement benötigt werden (z. B. data-val-rulename-param1="value").

Im folgenden Beispiel sehen Sie die data--Attribute des ClassicMovie-Attributs der Beispiel-App:

<input class="form-control" type="date"
    data-val="true"
    data-val-classicmovie="Classic movies must have a release year no later than 1960."
    data-val-classicmovie-year="1960"
    data-val-required="The Release Date field is required."
    id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">

Wie bereits gesagt, verwenden Taghilfsprogramme und HTML-Hilfsprogramme Informationen der Validierungsattribute, um data--Attribute zu rendern. Es gibt zwei Möglichkeiten, Code zu schreiben, der zur Erstellung von benutzerdefinierten HTML-Attributen des Typs data- führt:

  • Erstellen einer Klasse, die von AttributeAdapterBase<TAttribute> ableitet, und einer Klasse, die IValidationAttributeAdapterProvider implementiert, und Registrieren Ihres Attributs und des dazugehörigen Adapters in der Abhängigkeitsinjektion. Diese Methode folgt dem Single-Responsibility-Prinzip, nach dem sich der serverbezogene und der clientbezogene Validierungscode in unterschiedlichen Klassen befinden. Der Adapter bietet außerdem den Vorteil, dass andere Dienste in der Abhängigkeitsinjektion bei Bedarf zur Verfügung stehen, da der Adapter in der Abhängigkeitsinjektion registriert ist.
  • Implementieren von IClientModelValidator in Ihrer ValidationAttribute-Klasse. Diese Methode ist geeignet, wenn das Attribut für keinerlei serverseitige Validierung zuständig ist und keine Dienste aus der Abhängigkeitsinjektion benötigt.

AttributeAdapter für die clientseitige Validierung

Diese Methode, bei der data--Attribute in HTML gerendert werden, wird vom ClassicMovie-Attribut in der Beispiel-App verwendet. So fügen Sie die Clientvalidierung mithilfe dieser Methode hinzu:

  1. Erstellen Sie eine Attributadapterklasse für das benutzerdefinierte Validierungsattribut. Leiten Sie Klasse aus AttributeAdapterBase<TAttribute>ab. Erstellen Sie eine AddValidation-Methode, die der gerenderten Ausgabe data--Attribute hinzufügt, wie es im folgenden Beispiel gezeigt wird:

    public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
    {
        public ClassicMovieAttributeAdapter(ClassicMovieAttribute attribute,
            IStringLocalizer stringLocalizer)
            : base(attribute, stringLocalizer)
        {
    
        }
    
        public override void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
    
            var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public override string GetErrorMessage(ModelValidationContextBase validationContext) =>
            Attribute.GetErrorMessage();
    }
    
  2. Erstellen Sie eine Adapteranbieterklasse, die IValidationAttributeAdapterProvider implementiert. Übergeben Sie in der GetAttributeAdapter-Methode dem Konstruktor des Adapters das benutzerdefinierte Attribut, wie es im folgenden Beispiel gezeigt wird:

    public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
    {
        private readonly IValidationAttributeAdapterProvider baseProvider =
            new ValidationAttributeAdapterProvider();
    
        public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute,
            IStringLocalizer stringLocalizer)
        {
            if (attribute is ClassicMovieAttribute classicMovieAttribute)
            {
                return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
            }
    
            return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
        }
    }
    
  3. Registrieren Sie den Adapteranbieter für die Abhängigkeitsinjektion in Startup.ConfigureServices:

    services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    services.AddSingleton<IValidationAttributeAdapterProvider,
        CustomValidationAttributeAdapterProvider>();
    

IClientModelValidator für die clientseitige Validierung

Diese Methode, bei der data--Attribute in HTML gerendert werden, wird vom ClassicMovieWithClientValidator-Attribut in der Beispiel-App verwendet. So fügen Sie die Clientvalidierung mithilfe dieser Methode hinzu:

  • Implementieren Sie im benutzerdefinierten Validierungsattribut die IClientModelValidator-Schnittstelle, und erstellen Sie eine AddValidation-Methode. Fügen Sie in der AddValidation-Methode die data--Attribute für die Validierung hinzu, wie es im folgenden Beispiel gezeigt wird:

    public class ClassicMovieWithClientValidatorAttribute :
        ValidationAttribute, IClientModelValidator
    {
        public ClassicMovieWithClientValidatorAttribute(int year)
        {
            Year = year;
        }
    
        public int Year { get; }
    
        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
    
            var year = Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public string GetErrorMessage() =>
            $"Classic movies must have a release year no later than {Year}.";
    
        protected override ValidationResult IsValid(object value,
            ValidationContext validationContext)
        {
            var movie = (Movie)validationContext.ObjectInstance;
            var releaseYear = ((DateTime)value).Year;
    
            if (movie.Genre == Genre.Classic && releaseYear > Year)
            {
                return new ValidationResult(GetErrorMessage());
            }
    
            return ValidationResult.Success;
        }
    
        private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
    
            attributes.Add(key, value);
            return true;
        }
    }
    

Deaktivieren der clientseitigen Validierung

Der folgende Code deaktiviert die Clientüberprüfung in Razor Pages:

services.AddRazorPages()
    .AddViewOptions(options =>
    {
        options.HtmlHelperOptions.ClientValidationEnabled = false;
    });

Weitere Optionen zum Deaktivieren der clientseitigen Validierung:

  • Kommentieren Sie den Verweis auf _ValidationScriptsPartial in allen CSHTML-Dateien aus.
  • Entfernen Sie den Inhalt der Pages\Shared_ValidationScriptsPartial.cshtml-Datei.

Der vorherige Ansatz verhindert nicht die clientseitige Validierung der ASP.NET Core Identity Razor Klassenbibliothek. Weitere Informationen finden Sie unter IdentityGerüstbau in ASP.NET Core Projekten.

Zusätzliche Ressourcen

In diesem Artikel wird erläutert, wie Sie Benutzereingaben in einer ASP.NET Core MVC- oder Razor Pages-App überprüfen.

Zeigen Sie Beispielcode an, oder laden Sie diesen herunter (Vorgehensweise zum Herunterladen).

Modellstatus

Der Modellstatus stellt Fehler dar, die aus zwei Subsystemen kommen: Modellbindung und Modellvalidierung. Fehler, die Ihren Ursprung bei der Modellbindung haben, sind normalerweise Fehler bei der Datenkonvertierung, z. B. wenn in ein Feld ein „x“ eingegeben wird, das einen Integer erwartet. Die Modellvalidierung erfolgt nach der Modellbindung und meldet Fehler, wenn die Daten in Konflikt mit den Geschäftsregeln stehen, z. B. wenn in ein Feld eine 0 eingegeben wird, das eine Bewertung zwischen 1 und 5 erwartet.

Sowohl Modellbindung als auch Validierung erfolgen vor der Ausführung einer Controlleraktion oder einer Razor Pages-Handlermethode. Bei Web-Apps liegen die Überprüfung von ModelState.IsValid und entsprechende Maßnahmen im Verantwortungsbereich der App. Web-Apps zeigen die Seite normalerweise mit einer Fehlermeldung erneut an:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Web-API-Controller müssen ModelState.IsValid nicht überprüfen, wenn sie das [ApiController]-Attribut haben. In diesem Fall wird eine automatische HTTP 400-Antwort zurückgegeben, die Fehlerdetails beinhaltet, wenn der Modellstatus ungültig ist. Weitere Informationen finden Sie unter Automatische HTTP 400-Antworten.

Validierung erneut ausführen

Die Validierung erfolgt automatisch, aber ggf. möchten Sie sie manuell wiederholen. Eine Szenario wäre, dass Sie einen Wert für eine Eigenschaft berechnen und möchten, dass die Validierung erneut ausgeführt wird, nachdem der berechnete Wert für die Eigenschaft festgelegt wurde. Wenn Sie die Validierung erneut ausführen möchten, rufen Sie wie im Folgenden dargestellt die TryValidateModel-Methode auf:

var movie = new Movie
{
    Title = title,
    Genre = genre,
    ReleaseDate = modifiedReleaseDate,
    Description = description,
    Price = price,
    Preorder = preorder,
};

TryValidateModel(movie);

if (ModelState.IsValid)
{
    _context.AddMovie(movie);
    _context.SaveChanges();

    return RedirectToAction(actionName: nameof(Index));
}

return View(movie);

Validierungsattribute

Mit Validierungsattributen können Sie Validierungsregeln für Modelleigenschaften angeben. Im folgenden Beispiel aus der Beispiel-App sehen Sie eine Modellklasse, die mit Validierungsattributen versehen wurde. Das [ClassicMovie]-Attribut ist ein benutzerdefiniertes Validierungsattribut; die anderen sind integriert. Nicht gezeigt wird hier das Attribut [ClassicMovie2], das eine Alternative darstellt, ein benutzerdefiniertes Attribut zu implementieren.

public class Movie
{
    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; }

    [ClassicMovie(1960)]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; }

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    [Required]
    public Genre Genre { get; set; }

    public bool Preorder { get; set; }
}

Integrierte Attribute

Zu den integrierten Validierungsattributen gehören:

  • [CreditCard]: Überprüft, ob die Eigenschaft über ein Kreditkartenformat verfügt.
  • [Compare]: Überprüft, ob zwei Eigenschaften in einem Modell übereinstimmen. Beispielsweise verwendet die Register.cshtml.cs-Datei [Compare], um zu prüfen, ob die beiden eingegebenen Kennwörter übereinstimmen. Gerüst Identity , um den Registercode zu sehen.
  • [EmailAddress]: Überprüft, ob die Eigenschaft über ein E-Mail-Format verfügt.
  • [Phone]: Überprüft, ob die Eigenschaft über ein Telefonnummernformat verfügt.
  • [Range]: Überprüft, ob der Eigenschaftswert innerhalb eines angegebenen Bereichs liegt.
  • [RegularExpression]: Überprüft, ob der Eigenschaftswert einem angegebenen regulären Ausdruck entspricht.
  • [Required]: Überprüft, ob das Feld nicht NULL ist. Details [Required] zum Verhalten dieses Attributs finden Sie unter -Attribut.
  • [StringLength]: Überprüft, ob ein Zeichenfolgeneigenschaftswert ein angegebenes Längenlimit nicht überschreitet.
  • [Url]: Überprüft, ob die Eigenschaft über ein URL-Format verfügt.
  • [Remote]: Überprüft die Eingabe auf dem Client durch Aufrufen einer Aktionsmethode auf dem Server. Details [Remote] zum Verhalten dieses Attributs finden Sie unter -Attribut.

Wenn das [RegularExpression]-Attribut mit der clientseitigen Validierung verwendet wird, wird der Regex in JavaScript auf dem Client ausgeführt. Dies bedeutet, dass ein mit ECMAScript übereinstimmendes Verhalten verwendet wird. Weitere Informationen finden Sie in diesem GitHub-Issue.

Im System.ComponentModel.DataAnnotations-Namespace finden Sie eine vollständige Liste der Validierungsattribute.

Fehlermeldungen

Mit Validierungsattributen können Sie die Fehlermeldung angeben, die im Fall einer ungültigen Eingabe angezeigt werden soll. Beispiel:

[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]

Intern rufen die Attribute die Methode String.Format mit einem Platzhalter für den Feldnamen und manchmal zusätzliche Platzhalter auf. Beispiel:

[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]

Bei Anwendung auf eine Name-Eigenschaft wäre die Fehlermeldung, die vom vorangehenden Code erstellt wurde, die Folgende: „Name length must be between 6 and 8“ (Die Länge des Namens muss zwischen 6 und 8 Zeichen betragen).

Wenn Sie herausfinden möchten, welche Parameter an die Methode String.Format für die Fehlermeldung eines bestimmten Attributs übergeben werden, sehen Sie sich den DataAnnotations-Quellcode an.

[Required]-Attribut

Standardmäßig behandelt das Validierungssystem Parameter oder Eigenschaften, die keine NULL-Werte zulassen, wie wenn diese ein [Required(AllowEmptyStrings = true)]-Attribut hätten. Werttypen wie decimal und int lassen keine NULL-Werte zu.

[Required]-Validierung auf dem Server

Auf dem Server wird ein erforderlicher Wert als fehlend betrachtet, wenn für eine Eigenschaft NULL festgelegt wurde. Ein Feld, das keine NULL-Werte zulässt, ist immer gültig, und die Fehlermeldung des [Required]-Attributs wird nie angezeigt.

Die Modellbindung für eine Eigenschaft, die keine NULL-Werte zulässt, schlägt jedoch möglicherweise fehl, und führt zu einer Fehlermeldung wie The value '' is invalid (Der Wert „“ ist ungültig). Wenn Sie eine benutzerdefinierte Fehlermeldung für die serverseitige Validierung von Typen, die nicht NULL zulassen, angeben möchten, gibt es die folgenden Optionen:

  • Lassen Sie für das Feld NULL-Werte zu (z. B. decimal? statt decimal). Nullable <T> Werttypen werden wie Standardtypen behandelt, die NULL-Werte zu verwenden.

  • Geben Sie die Standardfehlermeldung an, die von der Modellbindung verwendet werden soll, wie es im folgenden Beispiel gezeigt wird:

    services.AddMvc(options => 
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                (_) => "The field is required.");
        })
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    services.AddSingleton
        <IValidationAttributeAdapterProvider, 
         CustomValidationAttributeAdapterProvider>();
    

    Weitere Informationen zu Fehlern bei der Modellbindung, für die Sie Standardmeldungen festlegen können, finden Sie unter DefaultModelBindingMessageProvider.

[Required]-Validierung auf dem Client

Typen und Zeichenfolgen, für die keine NULL-Werte festgelegt werden können, werden auf dem Client anders behandelt wie auf dem Server. Auf dem Client:

  • Ein Wert gilt nur als vorhanden, wenn eine Eingabe für ihn erfolgt ist. Deshalb werden bei der clientseitigen Validierung Typen, für die keine NULL-Werte festgelegt werden können, gleich behandelt wie Nullable-Typen.
  • Leerzeichen in einem Zeichenfolgenfeld werden von der jQuery Validation-Required-Methode als gültige Eingabe betrachtet. Die serverseitige Validierung betrachtet ein erforderliches Zeichenfolgenfeld als ungültig, wenn nur Leerzeichen eingegeben werden.

Wie bereits gesagt wurde, werden Typen, die keine NULL-Werte zulassen, so behandelt, als hätten sie ein [Required(AllowEmptyStrings = true)]-Attribut. Das heißt, die clientseitige Validierung erfolgt, auch wenn Sie das [Required(AllowEmptyStrings = true)]-Attribut nicht anwenden. Wenn Sie das Attribut jedoch nicht anwenden, erhalten Sie eine Standardfehlermeldung. Wenn Sie eine benutzerdefinierte Fehlermeldung angeben möchten, verwenden Sie das Attribut.

[Remote]-Attribut

Das [Remote]-Attribut implementiert die clientseitige Validierung, für die eine Methode auf dem Server aufgerufen werden muss, um zu bestimmen, ob die Feldeingabe gültig ist. So muss die App z. B. überprüfen, ob eine Benutzername bereits verwendet wird.

So wird die Remotevalidierung implementiert:

  1. Erstellen Sie eine Aktionsmethode für JavaScript, die aufgerufen werden soll. Die jQuery Validate-Remote-Methode erwartet eine JSON-Antwort:

    • "true" bedeutet, die Eingabedaten sind gültig.
    • "false", undefined oder null bedeutet, dass die Eingabe ungültig ist. Verwenden Sie die Standardfehlermeldung.
    • Jede andere Zeichenfolge bedeutet, dass die Eingabe ungültig ist. Verwenden Sie die Zeichenfolge als benutzerdefinierte Fehlermeldung.

    Hier finden Sie ein Beispiel einer Aktionsmethode, die eine benutzerdefinierte Fehlermeldung zurückgibt:

    [AcceptVerbs("Get", "Post")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userRepository.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. Versehen Sie in der Modellklasse die Eigenschaft mit einem [Remote]-Attribut, das auf die Validierungsaktionsmethode zeigt, wie es im folgenden Beispiel gezeigt wird:

    [Remote(action: "VerifyEmail", controller: "Users")]
    public string Email { get; set; }
    

    Das [Remote]-Attribut ist im Microsoft.AspNetCore.Mvc-Namespace enthalten. Wenn Sie nicht das Metapaket Microsoft.AspNetCore.App oder Microsoft.AspNetCore.All verwenden, installieren Sie das NuGet-Paket Microsoft.AspNetCore.Mvc.ViewFeatures.

Zusätzliche Felder

Die AdditionalFields-Eigenschaft des [Remote]-Attributs ermöglicht Ihnen die Validierung von Kombinationen von Feldern für Daten auf dem Server. Wenn z. B. das User-Modell die Eigenschaften FirstName und LastName hätte, sollten Sie überprüfen, dass kein bereits vorhandener Benutzer diese Namenskombination verwendet. Das folgende Beispiel veranschaulicht die Verwendung von AdditionalFields:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
public string FirstName { get; set; }
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
public string LastName { get; set; }

AdditionalFields könnte explizit auf die Zeichenfolgen "FirstName" und "LastName" festgelegt werden, aber die Verwendung des nameof-Operators vereinfacht das spätere Refactoring. Die Aktionsmethode für diese Validierung muss sowohl Vor- als auch Nachnamensargumente akzeptieren:

[AcceptVerbs("Get", "Post")]
public IActionResult VerifyName(string firstName, string lastName)
{
    if (!_userRepository.VerifyName(firstName, lastName))
    {
        return Json($"A user named {firstName} {lastName} already exists.");
    }

    return Json(true);
}

Wenn der Benutzer einen Vor- oder einen Nachnamen eingibt, führt JavaScript einen Remoteaufruf durch, um zu prüfen, ob dieses Paar Namen bereits verwendet wurde.

Wenn Sie mindestens zwei weitere Felder überprüfen möchten, geben Sie sie als eine mit Kommas getrennte Liste an. Wenn Sie z. B. dem Modell eine MiddleName-Eigenschaft hinzufügen möchten, legen Sie das [Remote]-Attribut wie im folgenden Code veranschaulicht fest:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }

AdditionalFields muss wie alle anderen Attributargumente ein konstanter Ausdruck sein. Aus diesem Grund verwenden Sie keine interpolierte Zeichenfolge oder rufen Join auf, um AdditionalFields zu initialisieren.

Alternativen zu integrierten Attributen

Wenn Sie eine Validierung benötigen, die nicht von integrierten Attributen bereitgestellt wird, haben Sie die folgenden Möglichkeiten:

Benutzerdefinierte Attribute

Für Szenarios, die die integrierten Validierungsattribute nicht verarbeiten können, können Sie benutzerdefinierte Validierungsattribute erstellen. Erstellen Sie eine Klasse, die von ValidationAttribute erbt, und überschreiben Sie die IsValid-Methode.

Die IsValid-Methode akzeptiert ein Objekt namens value. Dies ist die Eingabe, die überprüft werden soll. Ein Überladen akzeptiert auch ein ValidationContext-Objekt, das zusätzliche Informationen bereitstellt, z. B. die Modellinstanz, die von der Modellbindung erstellt wurde.

Im folgenden Beispiel wird überprüft, ob das Veröffentlichungsdatum eines Films aus dem Genre Classic (Klassiker) zeitlich nicht hinter einer angegebenen Jahreszahl liegt. Das [ClassicMovie2]-Attribut prüft zuerst das Genre und fährt nur fort, wenn als Genre Classic bestimmt werden kann. Für Filme, die als Klassisch identifiziert werden, wird das Veröffentlichungsdatum überprüft, um sicherzustellen, dass es nicht höher als der Grenzwert ist, der an den Attributkonstruktor übergeben wurde.

public class ClassicMovieAttribute : ValidationAttribute
{
    private int _year;

    public ClassicMovieAttribute(int year)
    {
        _year = year;
    }

    protected override ValidationResult IsValid(
        object value, ValidationContext validationContext)
    {
        var movie = (Movie)validationContext.ObjectInstance;
        var releaseYear = ((DateTime)value).Year;

        if (movie.Genre == Genre.Classic && releaseYear > _year)
        {
            return new ValidationResult(GetErrorMessage());
        }

        return ValidationResult.Success;
    }

    public int Year => _year;

    public string GetErrorMessage()
    {
        return $"Classic movies must have a release year no later than {_year}.";
    }
}

Die obenstehende movie-Variable stellt ein Movie-Objekt dar, das die Daten der Formularübermittlung enthält. Die IsValid-Methode prüft das Datum und das Genre. Ist die Validierung erfolgreich, gibt IsValid einen ValidationResult.Success-Code zurück. Schlägt die Validierung fehl, wird die Klasse ValidationResult mit einer Fehlermeldung zurückgegeben.

IValidatableObject

Im vorherigen Beispiel wurde nur mit Movie-Typen gearbeitet. Eine andere Option zur Validierung auf Klassenebene ist die Implementierung von IValidatableObject in der Modellklasse, wie im folgenden Beispiel dargestellt wird:

public class MovieIValidatable : IValidatableObject
{
    private const int _classicYear = 1960;

    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; }

    [Required]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; }

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    [Required]
    public Genre Genre { get; set; }

    public bool Preorder { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
        {
            yield return new ValidationResult(
                $"Classic movies must have a release year earlier than {_classicYear}.",
                new[] { "ReleaseDate" });
        }
    }
}

Validierung der Knoten auf oberster Ebene

Knoten der obersten Ebene sind unter anderem:

  • Aktionsparameter
  • Controllereigenschaften
  • Seitenhandlerparameter
  • Seitenmodelleigenschaften

An ein Modell gebundene Knoten auf oberster Ebene und Modelleigenschaften werden überprüft. Im folgenden Beispiel aus der Beispiel-App verwendet die VerifyPhone-Methode die RegularExpressionAttribute-Klasse, um die phone-Aktionsparameter zu überprüfen:

[AcceptVerbs("Get", "Post")]
public IActionResult VerifyPhone(
    [RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
    if (!ModelState.IsValid)
    {
        return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
    }

    return Json(true);
}

Knoten auf oberster Ebene können BindRequiredAttribute mit Validierungsattributen verwenden. Im folgenden Beispiel aus der Beispiel-App gibt die CheckAge-Methode an, dass der age-Parameter aus der Abfragezeichenfolge gebunden sein muss, wenn das Formular übermittelt wird:

[HttpPost]
public IActionResult CheckAge(
    [BindRequired, FromQuery] int age)
{

Auf der Seite für die Altersprüfung (CheckAge.cshtml) sind zwei Formulare vorhanden. Das erste Formular übermittelt einen Age-Wert von 99 als Abfragezeichenfolge: https://localhost:5001/Users/CheckAge?Age=99.

Wenn ein ordnungsgemäß formatierter age-Parameter aus der Abfragezeichenfolge übermittelt wird, wird das Formular überprüft.

Das zweite Formular auf der Seite für die Altersprüfung übermittelt den Age-Wert im Anforderungstext, worauf die Validierung fehlschlägt. Die Bindung schlägt fehl, da der age-Parameter aus einer Abfragezeichenfolge stammen muss.

Bei Ausführung mit der Kompatibilitätsversion CompatibilityVersion.Version_2_1 oder höher, ist die Knotenvalidierung der obersten Eben standardmäßig aktiviert. Andernfalls ist die Knotenvalidierung der obersten Ebene deaktiviert. Die Standardoption kann überschrieben werden, indem die Eigenschaft AllowValidatingTopLevelNodes in (Startup.ConfigureServices) festgelegt wird. Sehen Sie sich dazu das folgende Beispiel an:

services.AddMvc(options => 
    {
        options.MaxModelValidationErrors = 50;
        options.AllowValidatingTopLevelNodes = false;
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

Maximale Fehleranzahl

Die Validierung stoppt, wenn die maximale Anzahl an Fehlern erreicht wird. Diese liegt standardmäßig bei 200. Sie können diese Zahl mit dem folgenden Code in Startup.ConfigureServices konfigurieren:

services.AddMvc(options => 
    {
        options.MaxModelValidationErrors = 50;
        options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
            (_) => "The field is required.");
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSingleton
    <IValidationAttributeAdapterProvider, 
     CustomValidationAttributeAdapterProvider>();

Maximale Rekursion

ValidationVisitor durchläuft den Objektgraph des Modells, das überprüft wird. Bei Modellen, die sehr umfassend oder unendlich rekursiv sind, führt die Validierung möglicherweise zu einem Stapelüberlauf. MvcOptions.MaxValidationDepth stellt eine Möglichkeit dar, die Validierung frühzeitig zu stoppen, wenn die Besucherrekursion eine konfigurierte Tiefe überschreitet. Der Standardwert von MvcOptions.MaxValidationDepth ist bei Ausführung mit CompatibilityVersion.Version_2_2 oder höher „32“. Bei früheren Versionen ist der Wert NULL, d.h. es gibt keine Tiefeneinschränkung.

Automatischer Kurzschluss

Die Validierung wird automatisch kurzgeschlossen (übersprungen), wenn der Modellgraph keine Validierung erfordert. Objekte, deren Validierung durch die Runtime übersprungen wird, beinhalten Primitivsammlungen (z. B. byte[], string[], Dictionary<string, string>) und komplexe Objektgraphen, die keine Validierungssteuerelemente haben.

Validierung deaktivieren

So deaktivieren Sie die Validierung:

  1. Erstellen Sie eine Implementierung von IObjectModelValidator, die keine Felder als ungültig kennzeichnet.

    public class NullObjectModelValidator : IObjectModelValidator
    {
        public void Validate(
            ActionContext actionContext,
            ValidationStateDictionary validationState,
            string prefix,
            object model)
        {
        }
    }
    
  2. Fügen Sie Startup.ConfigureServices den folgenden Code hinzu, sodass die Standardimplementierung von IObjectModelValidator im Abhängigkeitsinjektionscontainer ersetzt wird.

    // There is only one `IObjectModelValidator` object,
    // so AddSingleton replaces the default one.
    services.AddSingleton<IObjectModelValidator>(new NullObjectModelValidator());
    

Möglicherweise werden weiterhin Modellstatusfehler angezeigt, die ihren Ursprung in der Modellbindung haben.

Clientseitige Validierung

Die Überprüfung auf Clientseite verhindert die Übermittlung solange, bis das Formular gültig ist. Die Schaltfläche „Übermitteln“ führt JavaScript aus, das entweder das Formular übermittelt oder Fehlermeldungen anzeigt.

Die clientseitige Validierung umgeht einen unnötigen Roundtrip zum Server, wenn es in einem Formular Eingabefehler gibt. Die folgenden Skriptverweise in _Layout.cshtml und _ValidationScriptsPartial.cshtml unterstützen die clientseitige Validierung:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>

Bei dem jQuery Unobtrusive Validation-Skript handelt es sich um eine benutzerdefinierte Front-End-Bibliothek von Microsoft, die auf dem beliebten jQuery Validate-Plugin basiert. Ohne jQuery Unobtrusive Validation müssten Sie dieselbe Validierungslogik an zwei unterschiedlichen Stellen codieren: einmal in den Attributen der Validierung auf Serverseite für Modelleigenschaften und einmal in den Skripts auf Clientseite. Stattdessen verwenden die Taghilfsprogramme und die HTML-Hilfsprogramme die Validierungsattribute und Typmetadaten aus den Modelleigenschaften, um HTML 5-Attribute des Typs data- in den Formularelementen zu rendern, die validiert werden müssen. jQuery Unobtrusive Validation analysiert dann diese data--Attribute und übergibt die Logik an jQuery Validate, wodurch die Logik der Validierung auf Serverseite in den Client „kopiert“ wird. Sie können Validierungsfehler im Client anzeigen, indem Sie die relevanten Taghilfsprogramme wie folgt verwenden:

<div class="form-group">
    <label asp-for="ReleaseDate" class="col-md-2 control-label"></label>
    <div class="col-md-10">
        <input asp-for="ReleaseDate" class="form-control" />
        <span asp-validation-for="ReleaseDate" class="text-danger"></span>
    </div>
</div>

Die vorangegangenen Taghilfsprogramme rendern die folgende HTML.

<form action="/Movies/Create" method="post">
    <div class="form-horizontal">
        <h4>Movie</h4>
        <div class="text-danger"></div>
        <div class="form-group">
            <label class="col-md-2 control-label" for="ReleaseDate">ReleaseDate</label>
            <div class="col-md-10">
                <input class="form-control" type="datetime"
                data-val="true" data-val-required="The ReleaseDate field is required."
                id="ReleaseDate" name="ReleaseDate" value="">
                <span class="text-danger field-validation-valid"
                data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
            </div>
        </div>
    </div>
</form>

Beachten Sie, dass die data--Attribute in der HTML-Ausgabe mit den Validierungsattributen für die ReleaseDate-Eigenschaft übereinstimmen. Das data-val-required-Attribut enthält eine Fehlermeldung, die angezeigt wird, wenn der Benutzer das Datumsfeld für die Veröffentlichung nicht ausfüllt. jQuery Unobtrusive Validation übergibt diesen Wert an die jQuery Validate required()-Methode, die diese Meldung dann im zugehörigen Element <span> anzeigt.

Die Datentypvalidierung basiert auf dem .NET-Typ einer Eigenschaft, es sei denn, dieser wird von einem [DataType]-Attribut überschrieben. Browser haben ihre eigenen Standardfehlermeldungen, aber das jQuery Validation Unobtrusive Validation-Paket kann diese Meldungen überschreiben. Mithilfe von [DataType]-Attributen und Subklassen wie [EmailAddress] können Sie die Fehlermeldung angeben.

Hinzufügen der Validierung zu dynamischen Formularen

jQuery Unobtrusive Validation übergibt die Validierungslogik und -parameter an jQuery Validate, wenn die Seite das erste Mal geladen wird. Deshalb funktioniert die Validierung nicht automatisch bei dynamisch generierten Formularen. Wenn Sie die Validierung aktivieren möchten, müssen Sie jQuery Unobtrusive Validation auffordern, das dynamische Formular direkt nach dem Erstellen zu analysieren. Beispielweise wird im nachfolgenden Code dargestellt, wie Sie die Validierung auf Clientseite für ein Formular einrichten können, das über AJAX hinzugefügt wurde.

$.get({
    url: "https://url/that/returns/a/form",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add form. " + errorThrown);
    },
    success: function(newFormHTML) {
        var container = document.getElementById("form-container");
        container.insertAdjacentHTML("beforeend", newFormHTML);
        var forms = container.getElementsByTagName("form");
        var newForm = forms[forms.length - 1];
        $.validator.unobtrusive.parse(newForm);
    }
})

Die $.validator.unobtrusive.parse()-Methode akzeptiert einen jQuery-Selektor für ihr einziges Argument. Diese Methode fordert „jQuery Unobtrusive Validation“ auf, die data--Attribute von Formularen in diesem Selektor zu analysieren. Die Werte diese Attribute werden dann an das jQuery Validate-Plugin übergeben.

Hinzufügen der Validierung zu dynamischen Steuerelementen

Die $.validator.unobtrusive.parse()-Methode funktioniert für ein gesamtes Formular, nicht für individuelle, dynamisch erstellte Steuerelemente wie <input> und <select/>. Wenn das Formular erneut analysiert werden soll, entfernen Sie die Validierungsdaten, die bei der vorherigen Analyse des Formulars hinzugefügt wurden, wie es im folgenden Beispiel gezeigt wird:

$.get({
    url: "https://url/that/returns/a/control",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add control. " + errorThrown);
    },
    success: function(newInputHTML) {
        var form = document.getElementById("my-form");
        form.insertAdjacentHTML("beforeend", newInputHTML);
        $(form).removeData("validator")    // Added by jQuery Validate
               .removeData("unobtrusiveValidation");   // Added by jQuery Unobtrusive Validation
        $.validator.unobtrusive.parse(form);
    }
})

Benutzerdefinierte clientseitige Validierung

Die benutzerdefinierte clientseitige Validierung erfolgt über die Generierung von HTML-Attributen des Typs data-, die mit einem benutzerdefinierten jQuery Validate-Adapter funktionieren. Der folgende Beispieladaptercode wurde für die ClassicMovie- und ClassicMovie2-Attribute geschrieben, die zuvor in diesem Artikel bereits eingeführt wurden:

$.validator.addMethod('classicmovie',
    function (value, element, params) {
        // Get element value. Classic genre has value '0'.
        var genre = $(params[0]).val(),
            year = params[1],
            date = new Date(value);
        if (genre && genre.length > 0 && genre[0] === '0') {
            // Since this is a classic movie, invalid if release date is after given year.
            return date.getUTCFullYear() <= year;
        }

        return true;
    });

$.validator.unobtrusive.adapters.add('classicmovie',
    ['year'],
    function (options) {
        var element = $(options.form).find('select#Genre')[0];
        options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
        options.messages['classicmovie'] = options.message;
    });

Informationen zum Schreiben dieser Adapter finden Sie in der jQuery Validate-Dokumentation.

Die Verwendung eines Adapters für ein bestimmtes Feld wird von data--Attributen ausgelöst, die Folgendes tun:

  • Das Feld wird so gekennzeichnet, dass es validiert wird (data-val="true").
  • Ein Name für die Validierungsregel und ein Text für die Fehlermeldung werden bestimmt (z. B. data-val-rulename="Error message.").
  • Zusätzliche Parameter werden bereitgestellt, die vom Validierungssteuerelement benötigt werden (z. B. data-val-rulename-parm1="value").

Im folgenden Beispiel sehen Sie die data--Attribute des ClassicMovie-Attributs der Beispiel-App:

<input class="form-control" type="datetime"
    data-val="true"
    data-val-classicmovie1="Classic movies must have a release year earlier than 1960."
    data-val-classicmovie1-year="1960"
    data-val-required="The ReleaseDate field is required."
    id="ReleaseDate" name="ReleaseDate" value="">

Wie bereits gesagt, verwenden Taghilfsprogramme und HTML-Hilfsprogramme Informationen der Validierungsattribute, um data--Attribute zu rendern. Es gibt zwei Möglichkeiten, Code zu schreiben, der zur Erstellung von benutzerdefinierten HTML-Attributen des Typs data- führt:

  • Erstellen einer Klasse, die von AttributeAdapterBase<TAttribute> ableitet, und einer Klasse, die IValidationAttributeAdapterProvider implementiert, und Registrieren Ihres Attributs und des dazugehörigen Adapters in der Abhängigkeitsinjektion. Diese Methode folgt dem Single-Responsibility-Prinzip, nach dem sich der serverbezogene und der clientbezogene Validierungscode in unterschiedlichen Klassen befinden. Der Adapter bietet außerdem den Vorteil, dass andere Dienste in der Abhängigkeitsinjektion bei Bedarf zur Verfügung stehen, da der Adapter in der Abhängigkeitsinjektion registriert ist.
  • Implementieren von IClientModelValidator in Ihrer ValidationAttribute-Klasse. Diese Methode ist geeignet, wenn das Attribut für keinerlei serverseitige Validierung zuständig ist und keine Dienste aus der Abhängigkeitsinjektion benötigt.

AttributeAdapter für die clientseitige Validierung

Diese Methode, bei der data--Attribute in HTML gerendert werden, wird vom ClassicMovie-Attribut in der Beispiel-App verwendet. So fügen Sie die Clientvalidierung mithilfe dieser Methode hinzu:

  1. Erstellen Sie eine Attributadapterklasse für das benutzerdefinierte Validierungsattribut. Leiten Sie Klasse aus AttributeAdapterBase<TAttribute>ab. Erstellen Sie eine AddValidation-Methode, die der gerenderten Ausgabe data--Attribute hinzufügt, wie es im folgenden Beispiel gezeigt wird:

    public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
    {
        private int _year;
    
        public ClassicMovieAttributeAdapter(ClassicMovieAttribute attribute, IStringLocalizer stringLocalizer) : base (attribute, stringLocalizer)
        {
            _year = attribute.Year;
        }
        public override void AddValidation(ClientModelValidationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
    
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
    
            var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
        public override string GetErrorMessage(ModelValidationContextBase validationContext)
        {
            return Attribute.GetErrorMessage();
        }
    }
    
  2. Erstellen Sie eine Adapteranbieterklasse, die IValidationAttributeAdapterProvider implementiert. Übergeben Sie in der GetAttributeAdapter-Methode dem Konstruktor des Adapters das benutzerdefinierte Attribut, wie es im folgenden Beispiel gezeigt wird:

    public class CustomValidationAttributeAdapterProvider :
        IValidationAttributeAdapterProvider
    {
        IValidationAttributeAdapterProvider baseProvider =
            new ValidationAttributeAdapterProvider();
        public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute,
            IStringLocalizer stringLocalizer)
        {
            if (attribute is ClassicMovieAttribute classicMovieAttribute)
            {
                return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
            }
            else
            {
                return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
            }
        }
    }
    
  3. Registrieren Sie den Adapteranbieter für die Abhängigkeitsinjektion in Startup.ConfigureServices:

    services.AddMvc(options => 
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                (_) => "The field is required.");
        })
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    services.AddSingleton
        <IValidationAttributeAdapterProvider, 
         CustomValidationAttributeAdapterProvider>();
    

IClientModelValidator für die clientseitige Validierung

Diese Methode, bei der data--Attribute in HTML gerendert werden, wird vom ClassicMovie2-Attribut in der Beispiel-App verwendet. So fügen Sie die Clientvalidierung mithilfe dieser Methode hinzu:

  • Implementieren Sie im benutzerdefinierten Validierungsattribut die IClientModelValidator-Schnittstelle, und erstellen Sie eine AddValidation-Methode. Fügen Sie in der AddValidation-Methode die data--Attribute für die Validierung hinzu, wie es im folgenden Beispiel gezeigt wird:

    
    public class ClassicMovie2Attribute : ValidationAttribute, IClientModelValidator
    {
        private int _year;
    
        public ClassicMovie2Attribute(int year)
        {
            _year = year;
        }
    
        protected override ValidationResult IsValid(
            object value, ValidationContext validationContext)
        {
            var movie = (Movie)validationContext.ObjectInstance;
            var releaseYear = ((DateTime)value).Year;
    
            if (movie.Genre == Genre.Classic && releaseYear > _year)
            {
                return new ValidationResult(GetErrorMessage());
            }
    
            return ValidationResult.Success;
        }
    
        public void AddValidation(ClientModelValidationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
    
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
    
            var year = _year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
        private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
    
            attributes.Add(key, value);
            return true;
        }
        protected string GetErrorMessage()
        {
            return $"Classic movies must have a release year no later than {_year} [from attribute 2].";
        }
    }
    

Deaktivieren der clientseitigen Validierung

Im folgenden Code wird die Clientvalidierung in MVC-Ansichten deaktiviert:

services.AddMvc().AddViewOptions(options =>
{
    if (_env.IsDevelopment())
    {
        options.HtmlHelperOptions.ClientValidationEnabled = false;
    }
});

Und in Razor Pages:

services.Configure<HtmlHelperOptions>(o => o.ClientValidationEnabled = false);

Eine andere Möglichkeit, um die Clientvalidierung zu deaktivieren, ist es, den Verweis auf _ValidationScriptsPartial in Ihrer .cshtml-Datei auszukommentieren.

Zusätzliche Ressourcen