Modellüberprüfung in ASP.NET Core MVC und Razor Seiten

In diesem Artikel wird erläutert, wie Benutzereingaben in einer ASP.NET Core MVC- oder Razor Seiten-App überprüft werden.

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 Modellüberprüfung treten vor der Ausführung einer Controlleraktion oder einer Razor Pages-Handlermethode auf. Bei Web-Apps liegen die Überprüfung von ModelState.IsValid und entsprechende Maßnahmen im Verantwortungsbereich der App. Web-Apps zeigen die Seite in der Regel erneut mit einer Fehlermeldung an, wie im folgenden Razor Beispiel für Seiten gezeigt:

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

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

    return RedirectToPage("./Index");
}

Für ASP.NET Core MVC mit Controllern und Ansichten zeigt das folgende Beispiel, wie Sie innerhalb einer Controlleraktion überprüfenModelState.IsValid:

public async Task<IActionResult> Create(Movie movie)
{
    if (!ModelState.IsValid)
    {
        return View(movie);
    }

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

    return RedirectToAction(nameof(Index));
}

Web-API-Controller müssen nicht überprüfen ModelState.IsValid , ob sie über das [ApiController] -Attribut verfügen. 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. Um die Überprüfung erneut ausführen zu können, rufen Sie ModelStateDictionary.ClearValidationState die überprüfungsspezifisch für das Modell auf, 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. Das folgende Beispiel aus der Beispiel-App zeigt eine Modellklasse, die mit Überprüfungsattributen versehen ist. Das [ClassicMovie] Attribut ist ein benutzerdefiniertes Validierungsattribute und die anderen sind integriert. Nicht gezeigt wird hier das Attribut [ClassicMovieWithClientValidator], 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; } = null!;

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

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

    [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 an, dass eine Eigenschaft oder ein Parameter von der Überprüfung ausgeschlossen werden soll.
  • [CreditCard]: Überprüft, ob die Eigenschaft über ein Kreditkartenformat verfügt. Erfordert zusätzliche Methoden für die jQuery-Überprüfung.
  • [Compare]: Überprüft, ob zwei Eigenschaften in einem Modell übereinstimmen.
  • [EmailAddress]: Überprüft, ob die Eigenschaft über ein E-Mail-Format verfügt.
  • [Telefon]: Ü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.
  • [Erforderlich]: Überprüft, ob das Feld nicht null ist. Details zum Verhalten dieses Attributs finden Sie unter [Required] Attribut .
  • [StringLength]: Überprüft, ob ein Zeichenfolgeneigenschaftswert keinen angegebenen Längengrenzwert überschreitet.
  • [URL]: Überprüft, ob die Eigenschaft über ein URL-Format verfügt.
  • [Remote]: Überprüft eingaben auf dem Client, indem eine Aktionsmethode auf dem Server aufgerufen wird. Details zum Verhalten dieses Attributs finden Sie unter [Remote] Attribut .

Eine vollständige Liste der Überprüfungsattribute finden Sie im System.ComponentModel.DataAnnotations Namespace.

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.

Nicht nullfähige Referenztypen und [Required] -Attribut

Das Validierungssystem behandelt nicht nullable Parameter oder gebundene Eigenschaften, als ob sie ein [Required(AllowEmptyStrings = true)] Attribut hatten. Durch aktivieren von Nullable Kontexten beginnt MVC implizit, nicht nullable Eigenschaften oder Parameter zu validieren, als ob sie dem [Required(AllowEmptyStrings = true)] Attribut zugeordnet wurden. Betrachten Sie folgenden Code:

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

Wenn die App mit <Nullable>enable</Nullable>einem fehlenden Wert für Name einen JSON- oder Formularbeitrag erstellt wurde, wird ein Überprüfungsfehler angezeigt. Verwenden Sie einen nullfähigen Verweistyp, um null- oder fehlende Werte für die Name Eigenschaft anzugeben:

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

Dieses Verhalten kann deaktiviert werden, indem SuppressImplicitRequiredAttributeForNonNullableReferenceTypes in Program.cs konfiguriert wird:

builder.Services.AddControllers(
    options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

Nicht nullfähige Referenztypen und das [Required]-Attribut

Das Validierungssystem behandelt nicht nullable Parameter oder gebundene Eigenschaften, als ob sie ein [Required(AllowEmptyStrings = true)] Attribut hatten. Durch Aktivieren von Nullable Kontexten beginnt MVC implizit mit der Überprüfung nicht nullbarer Eigenschaften für nicht generische Typen oder Parameter, als ob sie dem [Required(AllowEmptyStrings = true)] Attribut zugeordnet wurden. Betrachten Sie folgenden Code:

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

Wenn die App mit <Nullable>enable</Nullable>einem fehlenden Wert für Name einen JSON- oder Formularbeitrag erstellt wurde, wird ein Überprüfungsfehler angezeigt. Verwenden Sie einen nullfähigen Verweistyp, um null- oder fehlende Werte für die Name Eigenschaft anzugeben:

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

Dieses Verhalten kann deaktiviert werden, indem SuppressImplicitRequiredAttributeForNonNullableReferenceTypes in Program.cs konfiguriert wird:

builder.Services.AddControllers(
    options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

Nicht nullfähige Eigenschaften für generische Typen und [Required] -Attribut

Nicht nullfähige Eigenschaften für generische Typen müssen das [Required] Attribut enthalten, wenn der Typ erforderlich ist. Im folgenden Code TestRequired ist nicht erforderlich:

public class WeatherForecast<T>
{
    public string TestRequired { get; set; } = null!;
    public T? Inner { get; set; }
}

Im folgenden Code TestRequired wird explizit als erforderlich gekennzeichnet:

using System.ComponentModel.DataAnnotations;

public class WeatherForecast<T>
{
    [Required]
    public string TestRequired { get; set; } = null!;
    public T? Inner { get; set; }
}

[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 standardmäßige nullable Typen behandelt.

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

    builder.Services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    builder.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 Attribut [Remote] implementiert clientseitige Überprüfung, die das Aufrufen einer Methode auf dem Server erfordert, um zu ermitteln, 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; } = null!;
    

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; } = null!;

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

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; } = null!;

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

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

    [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 Überprüfung des phone Aktionsparameters:

[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 Parameter aus der Abfragezeichenfolge gebunden werden muss, wenn das age Formular übermittelt wird:

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

Auf der Seite "Alter überprüfen" (CheckAge.cshtml) gibt es zwei Formulare. 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 Program.cs konfigurieren:

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

builder.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 bietet eine Möglichkeit, die Überprüfung frühzeitig zu beenden, wenn die Besucher rekursion 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 Skriptbezüge in _Layout.cshtml und _ValidationScriptsPartial.cshtml unterstützen die clientseitige Überprüfung:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.min.js"></script>

Das Skript "jQuery Unobtrusive Validation " ist eine benutzerdefinierte Microsoft-Front-End-Bibliothek, die auf dem beliebten jQuery Validation-Plug-In 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 Unbtrusive Validation analysiert die data- Attribute und übergibt die Logik an jQuery Validation, effektiv das Kopieren der serverseitigen Validierungslogik in 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 dann diese Nachricht im begleitenden <Span-Element> anzeigt.

Die Datentypüberprüfung basiert auf dem .NET-Typ einer Eigenschaft, es sei denn, das wird von einem [DataType] -Attribut außer Kraft gesetzt. Browser haben ihre eigenen Standardfehlermeldungen, aber das jQuery Validation Unobtrusive Validation-Paket kann diese Meldungen überschreiben. [DataType] Attribute und Unterklassen wie [EmailAddress] lassen 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 zuerst 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 Überprüfung erfolgt durch Generieren data- von 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 jQuery-Validierungsdokumentation.

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").

Das folgende Beispiel zeigt die Attribute für das data- Attribut der Beispiel-AppClassicMovie :

<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 Prinzip der einzelnen Verantwortung in diesem serverbezogenen und clientbezogenen Validierungscode in separaten Klassen. Der Adapter hat auch den Vorteil, dass es nach Bedarf in DI registriert ist, sind andere Dienste in DI verfügbar.
  • 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 des Renderns data- von Attributen in HTML 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 Program.cs:

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

IClientModelValidator für die clientseitige Validierung

Diese Methode des Renderns data- von Attributen in HTML 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 static 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 Seiten:

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

Weitere Optionen zum Deaktivieren der clientseitigen Validierung:

  • Kommentieren Sie den Verweis _ValidationScriptsPartial in allen .cshtml Dateien.
  • Entfernen Sie den Inhalt der Datei "Pages\Shared_ValidationScriptsPartial.cshtml ".

Der vorherige Ansatz verhindert die clientseitige Überprüfung der ASP.NET Core IdentityRazor Klassenbibliothek. Weitere Informationen finden Sie unter Gerüst Identity in ASP.NET Core-Projekten.

Zusätzliche Ressourcen

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

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 Modellüberprüfung treten vor der Ausführung einer Controlleraktion oder einer Razor Pages-Handlermethode auf. 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 nicht überprüfen ModelState.IsValid , ob sie über das Attribut [ApiController] verfügen. 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. Um die Überprüfung erneut zu ausführen, rufen Sie ModelStateDictionary.ClearValidationState auf, um die überprüfungsspezifisch für das 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 der Beispiel-App wird eine Modellklasse angezeigt, die mit Validierungsattributen versehen ist. Das [ClassicMovie] Attribut ist ein benutzerdefiniertes Validierungsattribute, und die anderen sind integriert. Nicht gezeigt wird hier das Attribut [ClassicMovieWithClientValidator], 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)]
    [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:

  • [ValidNever]: Gibt an, dass eine Eigenschaft oder ein Parameter aus der Überprüfung ausgeschlossen werden sollte.
  • [CreditCard]: Überprüft, dass die Eigenschaft über ein Kreditkartenformat verfügt. Erfordert zusätzliche Methoden zur Überprüfung von jQuery.
  • [Vergleich]: Überprüft, ob zwei Eigenschaften in einem Modell übereinstimmen.
  • [EmailAddress]: Überprüft, ob die Eigenschaft über ein E-Mail-Format verfügt.
  • [Telefon]: Ü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.
  • [Erforderlich]: Überprüft, ob das Feld nicht null ist. Weitere Informationen zum Verhalten dieses Attributs finden Sie unter [Required] Attribut .
  • [StringLength]: Überprüft, ob ein Zeichenfolgeneigenschaftswert keinen angegebenen Längenwert überschreitet.
  • [URL]: Überprüft, ob die Eigenschaft über ein URL-Format verfügt.
  • [Remote]: Überprüft die Eingabe auf dem Client, indem eine Aktionsmethode auf dem Server aufgerufen wird. Weitere Informationen zum Verhalten dieses Attributs finden Sie unter [Remote] Attribut .

Eine vollständige Liste der Überprüfungsattribute finden Sie im System.ComponentModel.DataAnnotations Namespace.

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.

Nicht nullable Referenztypen und [Erforderlich] -Attribut

Das Validierungssystem behandelt nicht nullable Parameter oder gebundene Eigenschaften wie ein [Required(AllowEmptyStrings = true)] Attribut. Durch Aktivieren Nullable von Kontexten beginnt MVC implizit das Validieren von nicht nullablen Eigenschaften oder Parametern, wie sie dem [Required(AllowEmptyStrings = true)] Attribut zugeordnet wurden. Betrachten Sie folgenden Code:

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

Wenn die App mit <Nullable>enable</Nullable>erstellt wurde, führt ein fehlender Wert für Name einen JSON- oder Formularbeitrag zu einem Überprüfungsfehler. Verwenden Sie einen nullablen Verweistyp, um null- oder fehlende Werte für die Name Eigenschaft anzugeben:

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 standardmäßige NULL-Typen behandelt.

  • 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 Attribut [Remote] implementiert die clientseitige Überprüfung, die das Aufrufen einer Methode auf dem Server erfordert, um zu ermitteln, 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; }
    

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 Überprüfung des phone Aktionsparameters:

[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 Parameter aus der Abfragezeichenfolge gebunden werden muss, wenn das age Formular übermittelt wird:

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

Auf der Seite "Alter überprüfen" (CheckAge.cshtml) gibt es zwei Formulare. 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 bietet eine Möglichkeit, die Überprüfung frühzeitig zu beenden, wenn die Besucher rekursion 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 Skriptbezüge in _Layout.cshtml und _ValidationScriptsPartial.cshtml unterstützen die clientseitige Überprüfung:

<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 Skript "jQuery Unobtrusive Validation " ist eine benutzerdefinierte Microsoft-Front-End-Bibliothek, die auf dem beliebten jQuery Validation-Plug-In 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 Unbtrusive Validation analysiert die data- Attribute und übergibt die Logik an jQuery Validation, effektiv das Kopieren der serverseitigen Validierungslogik in 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 dann diese Nachricht im begleitenden <Span-Element> anzeigt.

Die Datentypüberprüfung basiert auf dem .NET-Typ einer Eigenschaft, es sei denn, das wird von einem [DataType] -Attribut außer Kraft gesetzt. Browser haben ihre eigenen Standardfehlermeldungen, aber das jQuery Validation Unobtrusive Validation-Paket kann diese Meldungen überschreiben. [DataType] Attribute und Unterklassen wie [EmailAddress] lassen 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 zuerst 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 Überprüfung erfolgt durch Generieren data- von 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 jQuery-Validierungsdokumentation.

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").

Das folgende Beispiel zeigt die Attribute für das data- Attribut der Beispiel-AppClassicMovie :

<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 Prinzip der einzelnen Verantwortung in diesem serverbezogenen und clientbezogenen Validierungscode in separaten Klassen. Der Adapter hat auch den Vorteil, dass es nach Bedarf in DI registriert ist, sind andere Dienste in DI verfügbar.
  • 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 des Renderns data- von Attributen in HTML 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 des Renderns data- von Attributen in HTML 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 Seiten:

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

Weitere Optionen zum Deaktivieren der clientseitigen Validierung:

  • Kommentieren Sie den Verweis _ValidationScriptsPartial in allen .cshtml Dateien.
  • Entfernen Sie den Inhalt der Datei "Pages\Shared_ValidationScriptsPartial.cshtml ".

Der vorherige Ansatz verhindert die clientseitige Überprüfung der ASP.NET Core IdentityRazor Klassenbibliothek. Weitere Informationen finden Sie unter Gerüst Identity in ASP.NET Core-Projekten.

Zusätzliche Ressourcen