Überprüfung von ASP.NET Core Blazor-Formularen
Hinweis
Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Warnung
Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der .NET- und .NET Core-Supportrichtlinie. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Wichtig
Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.
Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
In diesem Artikel wird erläutert, wie Sie die Überprüfung in Blazor-Formularen verwenden.
Formularprüfung
In grundlegenden Formularvalidierungsszenarien kann eine EditForm-Instanz deklarierte EditContext- und ValidationMessageStore-Instanzen verwenden, um Formularfelder zu überprüfen. Ein Handler für das OnValidationRequested-Ereignis von EditContext führt eine benutzerdefinierte Validierungslogik aus. Das Ergebnis des Handlers aktualisiert die ValidationMessageStore-Instanz.
Die grundlegende Formularvalidierung ist in Fällen nützlich, in denen das Modell des Formulars in der Komponente definiert ist, die das Formular hostet, entweder als Member direkt in der Komponente oder in einer Unterklasse. Die Verwendung einer Validierungskomponente wird empfohlen, wenn eine unabhängige Modellklasse für mehrere Komponenten verwendet wird.
In Blazor Web Apps erfordert die clientseitige Überprüfung eine aktive BlazorSignalR-Leitung. Die clientseitige Überprüfung ist für Formulare in Komponenten, die statisches serverseitiges Rendering (statisches SSR) übernommen haben, nicht verfügbar. Formulare, die statisches SSR übernehmen, werden auf dem Server überprüft, nachdem das Formular gesendet wurde.
In der folgenden Komponente löscht die HandleValidationRequested
-Handlermethode alle vorhandenen Validierungsnachrichten, indem sie vor dem Überprüfen des Formulars ValidationMessageStore.Clear aufruft.
Starship8.razor
:
@page "/starship-8"
@implements IDisposable
@inject ILogger<Starship8> Logger
<h2>Holodeck Configuration</h2>
<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship8">
<div>
<label>
<InputCheckbox @bind-Value="Model!.Subsystem1" />
Safety Subsystem
</label>
</div>
<div>
<label>
<InputCheckbox @bind-Value="Model!.Subsystem2" />
Emergency Shutdown Subsystem
</label>
</div>
<div>
<ValidationMessage For="() => Model!.Options" />
</div>
<div>
<button type="submit">Update</button>
</div>
</EditForm>
@code {
private EditContext? editContext;
[SupplyParameterFromForm]
private Holodeck? Model { get; set; }
private ValidationMessageStore? messageStore;
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
editContext.OnValidationRequested += HandleValidationRequested;
messageStore = new(editContext);
}
private void HandleValidationRequested(object? sender,
ValidationRequestedEventArgs args)
{
messageStore?.Clear();
// Custom validation logic
if (!Model!.Options)
{
messageStore?.Add(() => Model.Options, "Select at least one.");
}
}
private void Submit() => Logger.LogInformation("Submit: Processing form");
public class Holodeck
{
public bool Subsystem1 { get; set; }
public bool Subsystem2 { get; set; }
public bool Options => Subsystem1 || Subsystem2;
}
public void Dispose()
{
if (editContext is not null)
{
editContext.OnValidationRequested -= HandleValidationRequested;
}
}
}
@page "/starship-8"
@implements IDisposable
@inject ILogger<Starship8> Logger
<h2>Holodeck Configuration</h2>
<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship8">
<div>
<label>
<InputCheckbox @bind-Value="Model!.Subsystem1" />
Safety Subsystem
</label>
</div>
<div>
<label>
<InputCheckbox @bind-Value="Model!.Subsystem2" />
Emergency Shutdown Subsystem
</label>
</div>
<div>
<ValidationMessage For="() => Model!.Options" />
</div>
<div>
<button type="submit">Update</button>
</div>
</EditForm>
@code {
private EditContext? editContext;
[SupplyParameterFromForm]
private Holodeck? Model { get; set; }
private ValidationMessageStore? messageStore;
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
editContext.OnValidationRequested += HandleValidationRequested;
messageStore = new(editContext);
}
private void HandleValidationRequested(object? sender,
ValidationRequestedEventArgs args)
{
messageStore?.Clear();
// Custom validation logic
if (!Model!.Options)
{
messageStore?.Add(() => Model.Options, "Select at least one.");
}
}
private void Submit() => Logger.LogInformation("Submit: Processing form");
public class Holodeck
{
public bool Subsystem1 { get; set; }
public bool Subsystem2 { get; set; }
public bool Options => Subsystem1 || Subsystem2;
}
public void Dispose()
{
if (editContext is not null)
{
editContext.OnValidationRequested -= HandleValidationRequested;
}
}
}
@page "/starship-8"
@implements IDisposable
@inject ILogger<Starship8> Logger
<h2>Holodeck Configuration</h2>
<EditForm EditContext="editContext" OnValidSubmit="Submit">
<div>
<label>
<InputCheckbox @bind-Value="Model!.Subsystem1" />
Safety Subsystem
</label>
</div>
<div>
<label>
<InputCheckbox @bind-Value="Model!.Subsystem2" />
Emergency Shutdown Subsystem
</label>
</div>
<div>
<ValidationMessage For="() => Model!.Options" />
</div>
<div>
<button type="submit">Update</button>
</div>
</EditForm>
@code {
private EditContext? editContext;
public Holodeck? Model { get; set; }
private ValidationMessageStore? messageStore;
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
editContext.OnValidationRequested += HandleValidationRequested;
messageStore = new(editContext);
}
private void HandleValidationRequested(object? sender,
ValidationRequestedEventArgs args)
{
messageStore?.Clear();
// Custom validation logic
if (!Model!.Options)
{
messageStore?.Add(() => Model.Options, "Select at least one.");
}
}
private void Submit()
{
Logger.LogInformation("Submit called: Processing the form");
}
public class Holodeck
{
public bool Subsystem1 { get; set; }
public bool Subsystem2 { get; set; }
public bool Options => Subsystem1 || Subsystem2;
}
public void Dispose()
{
if (editContext is not null)
{
editContext.OnValidationRequested -= HandleValidationRequested;
}
}
}
Validierungssteuerelementkomponente für Datenanmerkungen und benutzerdefinierte Validierung
Die DataAnnotationsValidator-Komponente fügt einem kaskadierten EditContext Validierung durch Datenanmerkungen hinzu. Zum Aktivieren der Validierung durch Datenanmerkungen ist die DataAnnotationsValidator-Komponente erforderlich. Wenn Sie ein anderes Validierungssystem als Datenanmerkungen verwenden möchten, verwenden Sie eine benutzerdefinierte Implementierung anstelle der DataAnnotationsValidator-Komponente. Die Frameworkimplementierungen für DataAnnotationsValidator können Sie sich in der Verweisquelle ansehen:
Hinweis
Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).
Blazor führt zwei Validierungstypen aus:
- Die Feldvalidierung wird ausgeführt, wenn der Benutzer auf eine Stelle außerhalb des Felds tippt. Während der Feldvalidierung ordnet die DataAnnotationsValidator-Komponente alle gemeldeten Validierungsergebnisse dem Feld zu.
- Modellvalidierung wird ausgeführt, wenn der Benutzer ein Formular sendet. Während der Modellvalidierung versucht die DataAnnotationsValidator-Komponente, das Feld basierend auf dem Namen des Members zu ermitteln, das vom Validierungsergebnis gemeldet wird. Validierungsergebnisse, die keinem einzelnen Member zugeordnet werden, werden dem Modell und keinem Feld zugeordnet.
Validierungssteuerelementkomponente
Validierungssteuerelementkomponenten unterstützen Formularvalidierung durch die Verwaltung eines ValidationMessageStore für den EditContext eines Formulars.
Das Blazor-Framework stellt die DataAnnotationsValidator-Komponente zum Anfügen von Validierungsunterstützung zu Formularen auf der Grundlage von Validierungsattributen (Datenanmerkungen) zur Verfügung. Sie können Komponenten für benutzerdefinierte Validierungssteuerelemente erstellen, um Validierungsmeldungen für verschiedene Formulare auf derselben Seite oder im selben Formular in verschiedenen Schritten der Formularverarbeitung zu verarbeiten, z. B. Clientvalidierung gefolgt von Servervalidierung. Das in diesem Abschnitt gezeigte Beispiel für eine Validierungssteuerelementkomponente (CustomValidation
) wird in den folgenden Abschnitten dieses Artikels verwendet:
- Geschäftslogikvaliderung mit einer Validierungskomponente
- Servervaliderung mit einer Validierungskomponente
Von den in Datenanmerkungen integrierten Validatoren wird nur das [Remote]
-Überprüfungsattribut in Blazor nicht unterstützt.
Hinweis
In vielen Fällen können benutzerdefinierte Datenanmerkungs-Validierungsattribute anstelle von benutzerdefinierten Validierungssteuerelementkomponenten verwendet werden. Benutzerdefinierte Attribute, die auf das Modell des Formulars angewendet werden, werden mit der Verwendung der DataAnnotationsValidator-Komponente aktiviert. Bei Verwendung mit Servervalidierung müssen alle benutzerdefinierten Attribute, die auf das Modell angewendet werden, auf dem Server ausführbar sein. Weitere Informationen finden Sie im Abschnitt Benutzerdefinierte Validierungsattribute.
Erstellen Sie eine Validierungssteuerelementkomponente aus ComponentBase:
- Der EditContext des Formulars ist ein kaskadierenden Parameter der Komponente.
- Wenn die Validierungssteuerelementkomponente initialisiert wird, wird ein neuer ValidationMessageStore erstellt, um eine aktuelle Liste von Formularfehlern zu verwalten.
- Der Nachrichtenspeicher empfängt Fehler, wenn Entwicklercode in der Komponente des Formulars die
DisplayErrors
-Methode aufruft. Die Fehler werden in einemDictionary<string, List<string>>
-Element an dieDisplayErrors
-Methode übergeben. Im Wörterbuch ist der Schlüssel der Name des Formularfelds, das mindestens einen Fehler aufweist. Der Wert ist die Fehlerliste. - Meldungen werden gelöscht, wenn eines der folgenden Ereignisse aufgetreten ist:
- Validierung wird für den EditContext angefordert, wenn das OnValidationRequested-Ereignis ausgelöst wird. Alle Fehler werden gelöscht.
- Ein Feld ändert sich im Formular, wenn das OnFieldChanged-Ereignis ausgelöst wird. Nur die Fehler für das Feld werden gelöscht.
- Die
ClearErrors
-Methode wird vom Entwicklercode aufgerufen. Alle Fehler werden gelöscht.
Aktualisieren Sie den Namespace in der folgenden Klasse so, dass er dem Namespace Ihrer App entspricht.
CustomValidation.cs
:
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
namespace BlazorSample;
public class CustomValidation : ComponentBase
{
private ValidationMessageStore? messageStore;
[CascadingParameter]
private EditContext? CurrentEditContext { get; set; }
protected override void OnInitialized()
{
if (CurrentEditContext is null)
{
throw new InvalidOperationException(
$"{nameof(CustomValidation)} requires a cascading " +
$"parameter of type {nameof(EditContext)}. " +
$"For example, you can use {nameof(CustomValidation)} " +
$"inside an {nameof(EditForm)}.");
}
messageStore = new(CurrentEditContext);
CurrentEditContext.OnValidationRequested += (s, e) =>
messageStore?.Clear();
CurrentEditContext.OnFieldChanged += (s, e) =>
messageStore?.Clear(e.FieldIdentifier);
}
public void DisplayErrors(Dictionary<string, List<string>> errors)
{
if (CurrentEditContext is not null)
{
foreach (var err in errors)
{
messageStore?.Add(CurrentEditContext.Field(err.Key), err.Value);
}
CurrentEditContext.NotifyValidationStateChanged();
}
}
public void ClearErrors()
{
messageStore?.Clear();
CurrentEditContext?.NotifyValidationStateChanged();
}
}
Wichtig
Beim Ableiten von ComponentBase ist die Angabe eines Namespace erforderlich. Wenn kein Namespace angegeben wird, führt dies zu einem Buildfehler:
Tag helpers cannot target tag name '<global namespace>.{CLASS NAME}' because it contains a ' ' character.
Der Platzhalter {CLASS NAME}
ist der Name der Komponentenklasse. Im Beispiel für ein benutzerdefiniertes Validierungssteuerelement in diesem Abschnitt wird der Beispielnamespace BlazorSample
angegeben.
Hinweis
Im obigen Beispiel sind anonyme Lambdaausdrücke registrierte Ereignishandler für OnValidationRequested und OnFieldChanged. In diesem Szenario muss IDisposable nicht implementiert und das Abonnement der Ereignisdelegate nicht beendet werden. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
Geschäftslogikvaliderung mit einer Validierungskomponente
Verwenden Sie zur allgemeinen Validierung der Geschäftslogik eine Validierungssteuerelementkomponente, die Formularfehler in einem Wörterbuch empfängt.
Die grundlegende Validierung ist in Fällen nützlich, in denen das Modell des Formulars in der Komponente definiert ist, die das Formular hostet, entweder als Member direkt in der Komponente oder in einer Unterklasse. Die Verwendung einer Validierungskomponente wird empfohlen, wenn eine unabhängige Modellklasse für mehrere Komponenten verwendet wird.
Im folgenden Beispiel:
- Es wird eine verkürzte Version des
Starfleet Starship Database
-Formulars (Starship3
-Komponente) aus dem Abschnitt Beispielformular im Artikel Eingabekomponenten verwendet, die nur die Klassifizierung und Beschreibung des Raumfahrzeugs akzeptiert. Die Validierung von Datenkommentaren wird bei der Übermittlung des Formulars nicht ausgelöst, da die Komponente DataAnnotationsValidator nicht im Formular enthalten ist. - Die
CustomValidation
-Komponente aus dem Abschnitt Validierungssteuerelementkomponenten dieses Artikels wird verwendet. - Die Validierung erfordert einen Wert für die Beschreibung des Schiffs (
Description
), wenn der Benutzer die Schiffsklassifizierung (Classification
) „Defense
“ auswählt.
Wenn Validierungsmeldungen in der Komponente festgelegt werden, werden sie dem ValidationMessageStore des Validierungssteuerelements hinzugefügt und in der Validierungszusammenfassung von EditForm angezeigt.
Starship9.razor
:
@page "/starship-9"
@inject ILogger<Starship9> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship9">
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<div>
<label>
Primary Classification:
<InputSelect @bind-Value="Model!.Classification">
<option value="">
Select classification ...
</option>
<option checked="@(Model!.Classification == "Exploration")"
value="Exploration">
Exploration
</option>
<option checked="@(Model!.Classification == "Diplomacy")"
value="Diplomacy">
Diplomacy
</option>
<option checked="@(Model!.Classification == "Defense")"
value="Defense">
Defense
</option>
</InputSelect>
</label>
</div>
<div>
<label>
Description (optional):
<InputTextArea @bind-Value="Model!.Description" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
private CustomValidation? customValidation;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() =>
Model ??= new() { ProductionDate = DateTime.UtcNow };
private void Submit()
{
customValidation?.ClearErrors();
var errors = new Dictionary<string, List<string>>();
if (Model!.Classification == "Defense" &&
string.IsNullOrEmpty(Model.Description))
{
errors.Add(nameof(Model.Description),
new() { "For a 'Defense' ship classification, " +
"'Description' is required." });
}
if (errors.Any())
{
customValidation?.DisplayErrors(errors);
}
else
{
Logger.LogInformation("Submit called: Processing the form");
}
}
}
@page "/starship-9"
@inject ILogger<Starship9> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship9">
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<div>
<label>
Primary Classification:
<InputSelect @bind-Value="Model!.Classification">
<option value="">
Select classification ...
</option>
<option checked="@(Model!.Classification == "Exploration")"
value="Exploration">
Exploration
</option>
<option checked="@(Model!.Classification == "Diplomacy")"
value="Diplomacy">
Diplomacy
</option>
<option checked="@(Model!.Classification == "Defense")"
value="Defense">
Defense
</option>
</InputSelect>
</label>
</div>
<div>
<label>
Description (optional):
<InputTextArea @bind-Value="Model!.Description" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
private CustomValidation? customValidation;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() =>
Model ??= new() { ProductionDate = DateTime.UtcNow };
private void Submit()
{
customValidation?.ClearErrors();
var errors = new Dictionary<string, List<string>>();
if (Model!.Classification == "Defense" &&
string.IsNullOrEmpty(Model.Description))
{
errors.Add(nameof(Model.Description),
new() { "For a 'Defense' ship classification, " +
"'Description' is required." });
}
if (errors.Any())
{
customValidation?.DisplayErrors(errors);
}
else
{
Logger.LogInformation("Submit called: Processing the form");
}
}
}
@page "/starship-9"
@inject ILogger<Starship9> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm Model="Model" OnValidSubmit="Submit">
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<div>
<label>
Primary Classification:
<InputSelect @bind-Value="Model!.Classification">
<option value="">Select classification ...</option>
<option value="Exploration">Exploration</option>
<option value="Diplomacy">Diplomacy</option>
<option value="Defense">Defense</option>
</InputSelect>
</label>
</div>
<div>
<label>
Description (optional):
<InputTextArea @bind-Value="Model!.Description" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
private CustomValidation? customValidation;
public Starship? Model { get; set; }
protected override void OnInitialized() =>
Model ??= new() { ProductionDate = DateTime.UtcNow };
private void Submit()
{
customValidation?.ClearErrors();
var errors = new Dictionary<string, List<string>>();
if (Model!.Classification == "Defense" &&
string.IsNullOrEmpty(Model.Description))
{
errors.Add(nameof(Model.Description),
new() { "For a 'Defense' ship classification, " +
"'Description' is required." });
}
if (errors.Any())
{
customValidation?.DisplayErrors(errors);
}
else
{
Logger.LogInformation("Submit called: Processing the form");
}
}
}
Hinweis
Als Alternative zur Verwendung von Validierungkomponenten können Datenanmerkungs-Validierungsattribute verwendet werden. Benutzerdefinierte Attribute, die auf das Modell des Formulars angewendet werden, werden mit der Verwendung der DataAnnotationsValidator-Komponente aktiviert. Bei Verwendung mit Servervalidierung müssen die Attribute auf dem Server ausführbar sein. Weitere Informationen finden Sie im Abschnitt Benutzerdefinierte Validierungsattribute.
Servervaliderung mit einer Validierungskomponente
In diesem Abschnitt geht es primär um Blazor Web App-Szenarien, aber der gleiche allgemeine Ansatz wird für jede Art von App verwendet, die eine Servervalidierung mit der Web-API nutzt.
In diesem Abschnitt geht es primär um gehostete Blazor WebAssembly-Szenarien, aber der gleiche allgemeine Ansatz wird für jede Art von App verwendet, die eine Servervalidierung mit der Web-API nutzt.
Die Servervalidierung wird zusätzlich zur Clientvalidierung unterstützt:
- Verarbeiten Sie die Clientvalidierung im Formular mit der DataAnnotationsValidator-Komponente.
- Wenn das Formular die Clientvalidierung besteht (OnValidSubmit wird aufgerufen), senden Sie EditContext.Model zur Formularverarbeitung an eine Back-End-Server-API.
- Verarbeiten Sie die Modellvalidierung auf dem Server.
- Die Server-API umfasst sowohl die integrierte Framework-Datenanmerkungsvalidierung als auch benutzerdefinierte Validierungslogik, die vom Entwickler bereitgestellt wird. Wenn die Validierung auf dem Server bestanden wird, wird das Formular verarbeitet und ein Erfolgsstatuscode zurückgesendet (
200 - OK
). Wenn die Validierung fehlschlägt, werden ein Fehlerstatuscode (400 - Bad Request
) und die Feldvalidierungsfehler zurückgegeben. - Deaktivieren Sie entweder das Formular bei Erfolg, oder zeigen Sie die Fehler an.
Die grundlegende Validierung ist in Fällen nützlich, in denen das Modell des Formulars in der Komponente definiert ist, die das Formular hostet, entweder als Member direkt in der Komponente oder in einer Unterklasse. Die Verwendung einer Validierungskomponente wird empfohlen, wenn eine unabhängige Modellklasse für mehrere Komponenten verwendet wird.
Das folgende Beispiel beruht auf Folgendem:
- Einer Blazor Web App mit interaktiven WebAssembly-Komponenten, die aus der Blazor Web App-Projektvorlage erstellt wurden.
- Das
Starship
-Modell (Starship.cs
) aus dem Abschnitt Beispielformular des Artikels Eingabekomponenten - Der
CustomValidation
-Komponente, die im Abschnitt Validierungssteuerelementkomponenten gezeigt wird.
Platzieren Sie das Starship
-Modell (Starship.cs
) in einem freigegebenen Klassenbibliotheksprojekt, damit sowohl Client- als auch Serverprojekte das Modell verwenden können. Fügen Sie den Namespace hinzu, oder aktualisieren Sie ihn so, dass er mit dem Namespace der freigegebenen App (z. B. namespace BlazorSample.Shared
) übereinstimmt. Da für das Modell Datenanmerkungen erforderlich sind, vergewissern Sie sich, dass die freigegebene Klassenbibliothek das freigegebene Framework verwendet, oder fügen Sie das System.ComponentModel.Annotations
-Paket dem freigegebenen Projekt hinzu.
Hinweis
Eine Anleitung zum Hinzufügen von Paketen zu .NET-Anwendungen finden Sie in den Artikeln unter Pakete installieren und verwalten unter Workflow für die Paketnutzung (NuGet-Dokumentation). Überprüfen Sie unter NuGet.org, ob die richtige Paketversion verwendet wird.
Fügen Sie im Hauptprojekt der Blazor Web App einen Controller hinzu, um die Anforderungen für die Überprüfung des Raumfahrzeugs zu verarbeiten und bei Überprüfungsfehlern Meldungen zurückzugeben. Aktualisieren Sie die Namespaces in der letzten using
-Anweisung für das freigegebene Klassenbibliotheksprojekt sowie den namespace
für die Controllerklasse. Zusätzlich zur Client- und Servervalidierung durch Datenanmerkungen überprüft der Controller, ob ein Wert für die Beschreibung des Schiffs (Description
) angegeben ist, wenn der oder die Benutzer*in die Schiffsklassifizierung Defense
(Classification
) auswählt.
- Einer gehosteten Blazor WebAssemblyLösung, die auf Grundlage der Blazor WebAssembly-Projektvorlage erstellt wurde. Der Ansatz wird für alle sicheren gehosteten Blazor-Lösungen unterstützt, die in der Dokumentation zur gehosteten Blazor WebAssembly-Sicherheit beschrieben sind.
- Das
Starship
-Modell (Starship.cs
) aus dem Abschnitt Beispielformular des Artikels Eingabekomponenten - Der
CustomValidation
-Komponente, die im Abschnitt Validierungssteuerelementkomponenten gezeigt wird.
Platzieren Sie das Starship
-Modell (Starship.cs
) im Shared
-Projekt der Lösung, sodass sowohl die Client- als auch die Server-App das Modell verwenden kann. Fügen Sie den Namespace hinzu, oder aktualisieren Sie ihn so, dass er mit dem Namespace der freigegebenen App (z. B. namespace BlazorSample.Shared
) übereinstimmt. Fügen Sie das Paket System.ComponentModel.Annotations
dem Projekt Shared
hinzu, da das Modell Datenanmerkungen erfordert.
Hinweis
Eine Anleitung zum Hinzufügen von Paketen zu .NET-Anwendungen finden Sie in den Artikeln unter Pakete installieren und verwalten unter Workflow für die Paketnutzung (NuGet-Dokumentation). Überprüfen Sie unter NuGet.org, ob die richtige Paketversion verwendet wird.
Fügen Sie im Server -Projekt einen Controller hinzu, um die Anforderungen für die Validierung von „Starship“ zu verarbeiten und bei Validierungsfehlern Meldungen zurückzugeben. Aktualisieren Sie die Namespaces in der letzten using
-Anweisung für das Shared
-Projekt sowie den namespace
für die Controllerklasse. Zusätzlich zur Client- und Servervalidierung durch Datenanmerkungen überprüft der Controller, ob ein Wert für die Beschreibung des Schiffs (Description
) angegeben ist, wenn der oder die Benutzer*in die Schiffsklassifizierung Defense
(Classification
) auswählt.
Die Validierung für die Schiffsklassifizierung Defense
erfolgt nur serverseitig im Controller, da das anstehende Formular clientseitig nicht dieselbe Validierung ausführt, wenn das Formular an den Server übermittelt wird. Die Serverüberprüfung ohne Clientüberprüfung ist in Apps üblich, die eine private Überprüfung der Geschäftslogik von Benutzereingaben auf dem Server erfordern. Beispielsweise können private Informationen aus Daten, die für einen Benutzer gespeichert sind, erforderlich sein, um Benutzereingaben zu überprüfen. Private Daten können offensichtlich nicht zur Clientvalidierung an den Client gesendet werden.
Hinweis
Der StarshipValidation
-Controller in diesem Abschnitt verwendet Microsoft Identity 2.0. Die Web-API akzeptiert nur Token für Benutzer, die den Bereich API.Access
für diese API besitzen. Eine zusätzliche Anpassung ist erforderlich, wenn sich der Bereichsname der API von API.Access
unterscheidet.
Weitere Informationen zur Sicherheit finden Sie unter:
- Blazor-Authentifizierung und Autorisierung in ASP.NET Core (und den anderen Artikeln im Knoten BlazorSicherheit und Identity)
- Dokumentation zur Microsoft identity-Plattform
Controllers/StarshipValidation.cs
:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using BlazorSample.Shared;
namespace BlazorSample.Server.Controllers;
[Authorize]
[ApiController]
[Route("[controller]")]
public class StarshipValidationController(
ILogger<StarshipValidationController> logger)
: ControllerBase
{
static readonly string[] scopeRequiredByApi = new[] { "API.Access" };
[HttpPost]
public async Task<IActionResult> Post(Starship model)
{
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
try
{
if (model.Classification == "Defense" &&
string.IsNullOrEmpty(model.Description))
{
ModelState.AddModelError(nameof(model.Description),
"For a 'Defense' ship " +
"classification, 'Description' is required.");
}
else
{
logger.LogInformation("Processing the form asynchronously");
// async ...
return Ok(ModelState);
}
}
catch (Exception ex)
{
logger.LogError("Validation Error: {Message}", ex.Message);
}
return BadRequest(ModelState);
}
}
Überprüfen Sie, ob der Namespace des vorherigen Controllers (BlazorSample.Server.Controllers
) mit dem Namespace des Controllers der App übereinstimmt, oder aktualisieren Sie ihn.
Wenn ein Validierungsfehler bei der Modellbindung auf dem Server auftritt, gibt ein ApiController
(ApiControllerAttribute) normalerweise eine Standardantwort „Ungültige Anforderung“ mit ValidationProblemDetails zurück. Die Antwort enthält mehr Daten als nur die Validierungsfehler (wie im folgenden Beispiel gezeigt), wenn alle Felder des Starfleet Starship Database
-Formulars nicht übermittelt wurden und die Validierung des Formulars fehlschlägt:
{
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Id": ["The Id field is required."],
"Classification": ["The Classification field is required."],
"IsValidatedDesign": ["This form disallows unapproved ships."],
"MaximumAccommodation": ["Accommodation invalid (1-100000)."]
}
}
Hinweis
Um die vorangehende JSON-Antwort zu veranschaulichen, müssen Sie entweder die Clientvalidierung des Formulars deaktivieren, um die Übermittlung leerer Feldformulare zu ermöglichen, oder ein Tool verwenden, um eine Anforderung direkt an die Server-API zu senden, z. B. Firefox Browser Developer.
Wenn die Server-API die vorherige JSON-Standardantwort zurückgibt, ist es möglich, dass der Client die Antwort im Entwicklercode analysiert, um die untergeordneten Elemente des errors
-Knotens für die Verarbeitung von Formularvalidierungsfehlern abzurufen. Es ist unpraktisch, Entwicklercode zu schreiben, um die Datei zu analysieren. Die manuelle Analyse des JSON-Codes erfordert, dass nach dem Aufruf von ReadFromJsonAsync ein Dictionary<string, List<string>>
der Fehler erzeugt wird. Im Idealfall sollte die Server-API nur die Überprüfungsfehler zurückgeben, wie im folgenden Beispiel gezeigt:
{
"Id": ["The Id field is required."],
"Classification": ["The Classification field is required."],
"IsValidatedDesign": ["This form disallows unapproved ships."],
"MaximumAccommodation": ["Accommodation invalid (1-100000)."]
}
Um die Antwort der Server-API so zu ändern, dass sie nur die Validierungsfehler zurückgibt, ändern Sie den Delegaten, der bei Aktionen aufgerufen wird, die mit ApiControllerAttribute in der Datei Program
annotiert sind. Geben Sie für den API-Endpunkt (/StarshipValidation
) ein BadRequestObjectResult mit ModelStateDictionary zurück. Behalten Sie für alle anderen API-Endpunkte das Standardverhalten bei, indem Sie das Objektergebnis mit neuen ValidationProblemDetails zurückgeben.
Fügen Sie den Microsoft.AspNetCore.Mvc-Namespace am Anfang der Datei Program
im Hauptprojekt der Blazor Web App hinzu:
using Microsoft.AspNetCore.Mvc;
Fügen Sie in der Datei Program
die folgende AddControllersWithViews-Erweiterungsmethode hinzu, oder aktualisieren Sie sie, und fügen Sie den folgenden Aufruf von ConfigureApiBehaviorOptions ein:
builder.Services.AddControllersWithViews()
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
if (context.HttpContext.Request.Path == "/StarshipValidation")
{
return new BadRequestObjectResult(context.ModelState);
}
else
{
return new BadRequestObjectResult(
new ValidationProblemDetails(context.ModelState));
}
};
});
Wenn Sie dem Hauptprojekt der Blazor Web App zum ersten Mal Controller hinzufügen, ordnen Sie Controllerendpunkte zu, wenn Sie den vorherigen Code einfügen, der Dienste für Controller registriert. Im folgenden Beispiel werden Standardcontrollerrouten verwendet:
app.MapDefaultControllerRoute();
Hinweis
Im obigen Beispiel werden Controllerdienste explizit registriert, indem AddControllersWithViews aufgerufen wird, um automatisch XSRF/CSRF-Angriffe (Cross-Site Request Forgery, websiteübergreifende Anforderungsfälschung) abzumildern. Wenn Sie nur AddControllers verwenden, wird der Fälschungsschutz nicht automatisch aktiviert.
Weitere Informationen zur Reaktion auf Fehler beim Controllerrouting und bei Überprüfungen finden Sie in den folgenden Ressourcen:
- Routing zu Controlleraktionen in ASP.NET Core
- Behandeln von Fehlern in ASP.NET Core-controllerbasierten Web-APIs
Fügen Sie im .Client
-Projekt die im Abschnitt Validierungssteuerelementkomponenten gezeigte CustomValidation
-Komponenten hinzu. Aktualisieren Sie den Namespace so, dass er mit dem der App (z. B.namespace BlazorSample.Client
) übereinstimmt.
Im .Client
-Projekt wird das Starfleet Starship Database
-Formular aktualisiert, um Servervalidierungsfehler mithilfe der CustomValidation
-Komponente anzuzeigen. Wenn die Server-API Validierungsmeldungen zurückgibt, werden diese dem ValidationMessageStore der CustomValidation
-Komponente hinzugefügt. Die Fehler sind im EditContext des Formulars zur Anzeige durch die Validierungszusammenfassung des Formulars verfügbar.
Ändern Sie in der folgenden Komponente den Namespace des freigegebenen Projekts (@using BlazorSample.Shared
) in den Namespace des freigegebenen Projekts. Beachten Sie, dass das Formular eine Autorisierung erfordert, weshalb der Benutzer bei der App angemeldet sein muss, um zum Formular navigieren zu können.
Fügen Sie den Microsoft.AspNetCore.Mvc-Namespace am Anfang der Datei Program
in der Server -App hinzu:
using Microsoft.AspNetCore.Mvc;
Suchen Sie in der Datei Program
nach der Erweiterungsmethode AddControllersWithViews und fügen Sie den folgenden Aufruf von ConfigureApiBehaviorOptions hinzu:
builder.Services.AddControllersWithViews()
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
if (context.HttpContext.Request.Path == "/StarshipValidation")
{
return new BadRequestObjectResult(context.ModelState);
}
else
{
return new BadRequestObjectResult(
new ValidationProblemDetails(context.ModelState));
}
};
});
Hinweis
Im obigen Beispiel werden Controllerdienste explizit registriert, indem AddControllersWithViews aufgerufen wird, um automatisch XSRF/CSRF-Angriffe (Cross-Site Request Forgery, websiteübergreifende Anforderungsfälschung) abzumildern. Wenn Sie nur AddControllers verwenden, wird der Fälschungsschutz nicht automatisch aktiviert.
Fügen Sie im Client-Projekt die im Abschnitt Validierungssteuerelementkomponenten gezeigte CustomValidation
-Komponenten hinzu. Aktualisieren Sie den Namespace so, dass er mit dem der App (z. B.namespace BlazorSample.Client
) übereinstimmt.
Im Client -Projekt wird das Starfleet Starship Database
-Formular aktualisiert, um Servervalidierungsfehler mithilfe der CustomValidation
-Komponente anzuzeigen. Wenn die Server-API Validierungsmeldungen zurückgibt, werden diese dem ValidationMessageStore der CustomValidation
-Komponente hinzugefügt. Die Fehler sind im EditContext des Formulars zur Anzeige durch die Validierungszusammenfassung des Formulars verfügbar.
Aktualisieren Sie in der folgenden Komponente den Namespace des Shared
-Projekts (@using BlazorSample.Shared
) auf den Namespace des gemeinsam genutzten Projekts. Beachten Sie, dass das Formular eine Autorisierung erfordert, weshalb der Benutzer bei der App angemeldet sein muss, um zum Formular navigieren zu können.
Starship10.razor
:
Hinweis
Formulare, die auf EditForm basieren, aktivieren automatisch den Schutz vor Fälschung. Der Controller sollte AddControllersWithViews zum Registrieren von Controllerdiensten verwenden und die Unterstützung für den Schutz vor Fälschung für die Web-API automatisch aktivieren.
@page "/starship-10"
@rendermode InteractiveWebAssembly
@using System.Net
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using BlazorSample.Shared
@attribute [Authorize]
@inject HttpClient Http
@inject ILogger<Starship10> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm FormName="Starship10" Model="Model" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" disabled="@disabled" />
</label>
</div>
<div>
<label>
Description (optional):
<InputTextArea @bind-Value="Model!.Description"
disabled="@disabled" />
</label>
</div>
<div>
<label>
Primary Classification:
<InputSelect @bind-Value="Model!.Classification" disabled="@disabled">
<option value="">Select classification ...</option>
<option value="Exploration">Exploration</option>
<option value="Diplomacy">Diplomacy</option>
<option value="Defense">Defense</option>
</InputSelect>
</label>
</div>
<div>
<label>
Maximum Accommodation:
<InputNumber @bind-Value="Model!.MaximumAccommodation"
disabled="@disabled" />
</label>
</div>
<div>
<label>
Engineering Approval:
<InputCheckbox @bind-Value="Model!.IsValidatedDesign"
disabled="@disabled" />
</label>
</div>
<div>
<label>
Production Date:
<InputDate @bind-Value="Model!.ProductionDate" disabled="@disabled" />
</label>
</div>
<div>
<button type="submit" disabled="@disabled">Submit</button>
</div>
<div style="@messageStyles">
@message
</div>
</EditForm>
@code {
private CustomValidation? customValidation;
private bool disabled;
private string? message;
private string messageStyles = "visibility:hidden";
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() =>
Model ??= new() { ProductionDate = DateTime.UtcNow };
private async Task Submit(EditContext editContext)
{
customValidation?.ClearErrors();
try
{
var response = await Http.PostAsJsonAsync<Starship>(
"StarshipValidation", (Starship)editContext.Model);
var errors = await response.Content
.ReadFromJsonAsync<Dictionary<string, List<string>>>() ??
new Dictionary<string, List<string>>();
if (response.StatusCode == HttpStatusCode.BadRequest &&
errors.Any())
{
customValidation?.DisplayErrors(errors);
}
else if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(
$"Validation failed. Status Code: {response.StatusCode}");
}
else
{
disabled = true;
messageStyles = "color:green";
message = "The form has been processed.";
}
}
catch (AccessTokenNotAvailableException ex)
{
ex.Redirect();
}
catch (Exception ex)
{
Logger.LogError("Form processing error: {Message}", ex.Message);
disabled = true;
messageStyles = "color:red";
message = "There was an error processing the form.";
}
}
}
Das .Client
-Projekt einer Blazor Web App muss auch einen HttpClient für HTTP POST-Anforderungen an einen Back-End-Web-API-Controller registrieren. Überprüfen Sie Folgendes in der Datei Program
des .Client
-Projekts, oder fügen Sie es hinzu:
builder.Services.AddScoped(sp =>
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
Im vorherigen Beispiel wird die Basisadresse mit builder.HostEnvironment.BaseAddress
(IWebAssemblyHostEnvironment.BaseAddress) festgelegt, wodurch die Basisadresse für die App abgerufen und in der Regel vom href
-Wert des <base>
-Tags auf der Hostseite abgeleitet wird.
@page "/starship-10"
@using System.Net
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using BlazorSample.Shared
@attribute [Authorize]
@inject HttpClient Http
@inject ILogger<Starship10> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm Model="Model" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" disabled="@disabled" />
</label>
</div>
<div>
<label>
Description (optional):
<InputTextArea @bind-Value="Model!.Description"
disabled="@disabled" />
</label>
</div>
<div>
<label>
Primary Classification:
<InputSelect @bind-Value="Model!.Classification" disabled="@disabled">
<option value="">Select classification ...</option>
<option value="Exploration">Exploration</option>
<option value="Diplomacy">Diplomacy</option>
<option value="Defense">Defense</option>
</InputSelect>
</label>
</div>
<div>
<label>
Maximum Accommodation:
<InputNumber @bind-Value="Model!.MaximumAccommodation"
disabled="@disabled" />
</label>
</div>
<div>
<label>
Engineering Approval:
<InputCheckbox @bind-Value="Model!.IsValidatedDesign"
disabled="@disabled" />
</label>
</div>
<div>
<label>
Production Date:
<InputDate @bind-Value="Model!.ProductionDate" disabled="@disabled" />
</label>
</div>
<div>
<button type="submit" disabled="@disabled">Submit</button>
</div>
<div style="@messageStyles">
@message
</div>
</EditForm>
@code {
private CustomValidation? customValidation;
private bool disabled;
private string? message;
private string messageStyles = "visibility:hidden";
public Starship? Model { get; set; }
protected override void OnInitialized() =>
Model ??= new() { ProductionDate = DateTime.UtcNow };
private async Task Submit(EditContext editContext)
{
customValidation?.ClearErrors();
try
{
var response = await Http.PostAsJsonAsync<Starship>(
"StarshipValidation", (Starship)editContext.Model);
var errors = await response.Content
.ReadFromJsonAsync<Dictionary<string, List<string>>>() ??
new Dictionary<string, List<string>>();
if (response.StatusCode == HttpStatusCode.BadRequest &&
errors.Any())
{
customValidation?.DisplayErrors(errors);
}
else if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(
$"Validation failed. Status Code: {response.StatusCode}");
}
else
{
disabled = true;
messageStyles = "color:green";
message = "The form has been processed.";
}
}
catch (AccessTokenNotAvailableException ex)
{
ex.Redirect();
}
catch (Exception ex)
{
Logger.LogError("Form processing error: {Message}", ex.Message);
disabled = true;
messageStyles = "color:red";
message = "There was an error processing the form.";
}
}
}
Hinweis
Als Alternative zur Verwendung einer Validierungssteuerelementkomponente können Datenanmerkungs-Validierungsattribute verwendet werden. Benutzerdefinierte Attribute, die auf das Modell des Formulars angewendet werden, werden mit der Verwendung der DataAnnotationsValidator-Komponente aktiviert. Bei Verwendung mit Servervalidierung müssen die Attribute auf dem Server ausführbar sein. Weitere Informationen finden Sie im Abschnitt Benutzerdefinierte Validierungsattribute.
Hinweis
Der Servervalidierungsansatz in diesem Abschnitt eignet sich für jedes der Beispiele für gehostete Blazor WebAssembly-Lösungen in dieser Dokumentation:
InputText
basierend auf dem Eingabeereignis
Verwenden Sie die InputText-Komponente, um eine benutzerdefinierte Komponente zu erstellen, die das oninput
-Ereignis (input
) anstelle des onchange
-Ereignisses (change
) nutzt. Die Verwendung des input
-Ereignisses löst die Feldvalidierung bei jeder Tastatureingabe aus.
Die folgende CustomInputText
-Komponente erbt die InputText
-Komponente des Frameworks und legt die Ereignisbindung auf das oninput
-Ereignis (input
) fest.
CustomInputText.razor
:
@inherits InputText
<input @attributes="AdditionalAttributes"
class="@CssClass"
@bind="CurrentValueAsString"
@bind:event="oninput" />
Die CustomInputText
-Komponente kann überall dort verwendet werden, wo InputText verwendet wird. Die folgende Komponente verwendet die gemeinsam genutzte CustomInputText
-Komponente.
Starship11.razor
:
@page "/starship-11"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship11> Logger
<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship11">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<CustomInputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
<div>
CurrentValue: @Model?.Id
</div>
@code {
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() => Logger.LogInformation("Submit: Processing form");
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
@page "/starship-11"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship11> Logger
<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship11">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<CustomInputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
<div>
CurrentValue: @Model?.Id
</div>
@code {
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() => Logger.LogInformation("Submit: Processing form");
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
@page "/starship-11"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship11> Logger
<EditForm Model="Model" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<ValidationSummary />
<CustomInputText @bind-Value="Model!.Id" />
<button type="submit">Submit</button>
</EditForm>
<div>
CurrentValue: @Model?.Id
</div>
@code {
public Starship? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit()
{
Logger.LogInformation("Submit called: Processing the form");
}
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
Komponenten der Validierungszusammenfassung und der Validierungsnachricht
Die ValidationSummary-Komponente fasst alle Validierungsnachrichten zusammen. Das ähnelt dem Taghilfsprogramm für Validierungszusammenfassungen:
<ValidationSummary />
Validierungsmeldungen für ein bestimmtes Modell werden mit dem Model
-Parameter ausgegeben:
<ValidationSummary Model="Model" />
Die ValidationMessage<TValue>-Komponente zeigt Validierungsnachrichten für ein bestimmtes Feld an. Das ähnelt dem Taghilfsprogramm für Validierungsmeldungen. Das Validierungsfeld wird mit dem For-Attribut und einem Lambdaausdruck angegeben, der die Modelleigenschaft benennt:
<ValidationMessage For="@(() => Model!.MaximumAccommodation)" />
Die ValidationMessage<TValue>- und ValidationSummary-Komponenten unterstützen arbiträre Attribute. Attribute, die nicht mit einem Komponentenparameter übereinstimmen, werden dem gerenderten <div>
- oder <ul>
-Element hinzugefügt.
Steuern Sie den Stil von Validierungsmeldungen im Stylesheet (wwwroot/css/app.css
oder wwwroot/css/site.css
) der App. Die validation-message
-Standardklasse legt die Textfarbe von Validierungsmeldungen auf Rot fest:
.validation-message {
color: red;
}
Ermitteln der Gültigkeit eines Formularfelds
Verwenden Sie EditContext.IsValid, um zu ermitteln, ob ein Feld gültig ist, ohne Validierungsmeldungen abzurufen.
wird unterstützt, aber nicht empfohlen:
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
empfohlen:
var isValid = editContext.IsValid(fieldIdentifier);
Benutzerdefinierte Validierungsattribute
Übergeben Sie die MemberName-Eigenschaft des Validierungskontexts bei der Erstellung der ValidationResult-Klasse, um sicherzustellen, dass ein Validierungsergebnis korrekt einem Feld zugeordnet wird, wenn ein benutzerdefiniertes Validierungsattribut verwendet wird.
CustomValidator.cs
:
using System;
using System.ComponentModel.DataAnnotations;
public class CustomValidator : ValidationAttribute
{
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
...
return new ValidationResult("Validation message to user.",
new[] { validationContext.MemberName });
}
}
Fügen Sie über ValidationContext Dienste in benutzerdefinierte Validierungsattribute ein. Das folgende Beispiel demonstriert ein SaladChef-Formular, das Benutzereingaben mit Abhängigkeitsinjektion (Dependency Injection, DI) validiert.
Die SaladChef
-Klasse gibt die genehmigte Starship-Zutatenliste für einen Ten Forward-Salat an.
SaladChef.cs
:
namespace BlazorSample;
public class SaladChef
{
public string[] SaladToppers = { "Horva", "Kanda Root", "Krintar", "Plomeek",
"Syto Bean" };
}
Registrieren Sie SaladChef
im DI-Container der App in der Datei Program
:
builder.Services.AddTransient<SaladChef>();
Die IsValid
-Methode der folgenden SaladChefValidatorAttribute
-Klasse ruft den Dienst SaladChef
aus DI ab, um die Eingaben des Benutzers zu prüfen.
SaladChefValidatorAttribute.cs
:
using System.ComponentModel.DataAnnotations;
namespace BlazorSample;
public class SaladChefValidatorAttribute : ValidationAttribute
{
protected override ValidationResult? IsValid(object? value,
ValidationContext validationContext)
{
var saladChef = validationContext.GetRequiredService<SaladChef>();
if (saladChef.SaladToppers.Contains(value?.ToString()))
{
return ValidationResult.Success;
}
return new ValidationResult("Is that a Vulcan salad topper?! " +
"The following toppers are available for a Ten Forward salad: " +
string.Join(", ", saladChef.SaladToppers));
}
}
Die folgende Komponente validiert Benutzereingaben, indem sie das SaladChefValidatorAttribute
([SaladChefValidator]
) auf die Zeichenfolge für Salatzutaten (SaladIngredient
) anwendet.
Starship12.razor
:
@page "/starship-12"
@inject SaladChef SaladChef
<EditForm Model="this" autocomplete="off" FormName="Starship12">
<DataAnnotationsValidator />
<div>
<label>
Salad topper (@saladToppers):
<input @bind="SaladIngredient" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
<ul>
@foreach (var message in context.GetValidationMessages())
{
<li class="validation-message">@message</li>
}
</ul>
</EditForm>
@code {
private string? saladToppers;
[SaladChefValidator]
public string? SaladIngredient { get; set; }
protected override void OnInitialized() =>
saladToppers ??= string.Join(", ", SaladChef.SaladToppers);
}
@page "/starship-12"
@inject SaladChef SaladChef
<EditForm Model="this" autocomplete="off" FormName="Starship12">
<DataAnnotationsValidator />
<div>
<label>
Salad topper (@saladToppers):
<input @bind="SaladIngredient" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
<ul>
@foreach (var message in context.GetValidationMessages())
{
<li class="validation-message">@message</li>
}
</ul>
</EditForm>
@code {
private string? saladToppers;
[SaladChefValidator]
public string? SaladIngredient { get; set; }
protected override void OnInitialized() =>
saladToppers ??= string.Join(", ", SaladChef.SaladToppers);
}
@page "/starship-12"
@inject SaladChef SaladChef
<EditForm Model="this" autocomplete="off">
<DataAnnotationsValidator />
<p>
<label>
Salad topper (@saladToppers):
<input @bind="SaladIngredient" />
</label>
</p>
<button type="submit">Submit</button>
<ul>
@foreach (var message in context.GetValidationMessages())
{
<li class="validation-message">@message</li>
}
</ul>
</EditForm>
@code {
private string? saladToppers;
[SaladChefValidator]
public string? SaladIngredient { get; set; }
protected override void OnInitialized() =>
saladToppers ??= string.Join(", ", SaladChef.SaladToppers);
}
CSS-Klassenattribute für die benutzerdefinierte Validierung
CSS-Klassenattribute für die benutzerdefinierte Validierung sind nützlich, wenn eine Integration in CSS-Frameworks wie Bootstrap erfolgt.
Um CSS-Klassenattribute für die benutzerdefinierte Validierung anzugeben, geben Sie zunächst CSS-Stile für die benutzerdefinierte Validierung an. Im folgenden Beispiel werden Stile für „gültig“ (validField
) und für „ungültig“ (invalidField
) angegeben.
Fügen Sie die folgenden CSS-Klassen zur Formatvorlage der App hinzu:
.validField {
border-color: lawngreen;
}
.invalidField {
background-color: tomato;
}
Erstellen Sie eine von FieldCssClassProvider abgeleitete Klasse, die auf Feldvalidierungsmeldungen prüft und den entsprechenden Stil für „gültig“ oder „ungültig“ anwendet.
CustomFieldClassProvider.cs
:
using Microsoft.AspNetCore.Components.Forms;
public class CustomFieldClassProvider : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext,
in FieldIdentifier fieldIdentifier)
{
var isValid = editContext.IsValid(fieldIdentifier);
return isValid ? "validField" : "invalidField";
}
}
using Microsoft.AspNetCore.Components.Forms;
public class CustomFieldClassProvider : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext,
in FieldIdentifier fieldIdentifier)
{
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
return isValid ? "validField" : "invalidField";
}
}
Legen Sie die CustomFieldClassProvider
-Klasse als CSS-Klassenanbieter für Felder auf der EditContext-Instanz des Formulars mit SetFieldCssClassProvider fest.
Starship13.razor
:
@page "/starship-13"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship13> Logger
<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship13">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
private EditContext? editContext;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
editContext.SetFieldCssClassProvider(new CustomFieldClassProvider());
}
private void Submit() => Logger.LogInformation("Submit: Processing form");
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
@page "/starship-13"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship13> Logger
<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship13">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
private EditContext? editContext;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
editContext.SetFieldCssClassProvider(new CustomFieldClassProvider());
}
private void Submit() => Logger.LogInformation("Submit: Processing form");
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
@page "/starship-13"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship13> Logger
<EditForm EditContext="editContext" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<ValidationSummary />
<InputText @bind-Value="Model!.Id" />
<button type="submit">Submit</button>
</EditForm>
@code {
private EditContext? editContext;
public Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??= new();
editContext = new(Model);
editContext.SetFieldCssClassProvider(new CustomFieldClassProvider());
}
private void Submit()
{
Logger.LogInformation("Submit called: Processing the form");
}
public class Starship
{
[Required]
[StringLength(10, ErrorMessage = "Id is too long.")]
public string? Id { get; set; }
}
}
Im vorherigen Beispiel wird die Gültigkeit aller Formularfelder überprüft und auf jedes Feld ein Stil angewendet. Wenn das Formular benutzerdefinierte Stile nur auf eine Teilmenge der Felder anwenden soll, lassen Sie CustomFieldClassProvider
Stile bedingt anwenden. Im folgenden CustomFieldClassProvider2
-Beispiel wird ein Stil nur auf das Feld Name
angewendet. Für Felder, deren Namen nicht Name
entsprechen, wird string.Empty
zurückgegeben, und es wird kein Stil angewendet. Mithilfe der Reflexion wird das Feld mit der Eigenschaft oder dem Feldnamen des Modellelements abgeglichen, und nicht mit einer der HTML-Entität zugewiesenen id
.
CustomFieldClassProvider2.cs
:
using Microsoft.AspNetCore.Components.Forms;
public class CustomFieldClassProvider2 : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext,
in FieldIdentifier fieldIdentifier)
{
if (fieldIdentifier.FieldName == "Name")
{
var isValid = editContext.IsValid(fieldIdentifier);
return isValid ? "validField" : "invalidField";
}
return string.Empty;
}
}
using Microsoft.AspNetCore.Components.Forms;
public class CustomFieldClassProvider2 : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext,
in FieldIdentifier fieldIdentifier)
{
if (fieldIdentifier.FieldName == "Name")
{
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
return isValid ? "validField" : "invalidField";
}
return string.Empty;
}
}
Hinweis
Beim Abgleich des Feldnamens im vorherigen Beispiel wird die Groß-/Kleinschreibung beachtet, sodass ein Modelleigenschaftenmember, der als „Name
“ festgelegt ist, einer bedingten Überprüfung von „Name
“ entsprechen muss:
- Stimmt überein:
fieldId.FieldName == "Name"
- Stimmt nicht überein:
fieldId.FieldName == "name"
- Stimmt nicht überein:
fieldId.FieldName == "NAME"
- Stimmt nicht überein:
fieldId.FieldName == "nAmE"
Fügen Sie Model
eine zusätzliche Eigenschaft hinzu, z. B.:
[StringLength(10, ErrorMessage = "Description is too long.")]
public string? Description { get; set; }
Fügen Sie dem Formular der CustomValidationForm
-Komponente die Description
hinzu:
<InputText @bind-Value="Model!.Description" />
Aktualisieren Sie die EditContext-Instanz in der OnInitialized
-Methode der Komponente, um den neuen CSS-Klassenanbieter für Felder zu verwenden:
editContext?.SetFieldCssClassProvider(new CustomFieldClassProvider2());
Da eine CSS-Validierungsklasse nicht auf das Feld Description
angewendet wird, wird sie nicht formatiert. Die Feldvalidierung wird jedoch normal ausgeführt. Wenn mehr als 10 Zeichen angegeben werden, gibt die Validierungszusammenfassung den Fehler an:
Die Beschreibung ist zu lang.
Im folgenden Beispiel:
Der benutzerdefinierte CSS-Stil wird auf das
Name
-Feld angewendet.Alle anderen Felder wenden Logik ähnlich der Standardlogik von Blazor an und verwenden die CSS-Validierungsstile
modified
mitvalid
oderinvalid
des Standardfelds von Blazor. Beachten Sie, dass Sie für die Standardstile diese nicht dem Stylesheet der App hinzufügen müssen, wenn die App auf einer Blazor-Projektvorlage basiert. Für Apps, die nicht auf einer Blazor-Projektvorlage basieren, können die Standardstile dem Stylesheet der App hinzugefügt werden:.valid.modified:not([type=checkbox]) { outline: 1px solid #26b050; } .invalid { outline: 1px solid red; }
CustomFieldClassProvider3.cs
:
using Microsoft.AspNetCore.Components.Forms;
public class CustomFieldClassProvider3 : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext,
in FieldIdentifier fieldIdentifier)
{
var isValid = editContext.IsValid(fieldIdentifier);
if (fieldIdentifier.FieldName == "Name")
{
return isValid ? "validField" : "invalidField";
}
else
{
if (editContext.IsModified(fieldIdentifier))
{
return isValid ? "modified valid" : "modified invalid";
}
else
{
return isValid ? "valid" : "invalid";
}
}
}
}
using Microsoft.AspNetCore.Components.Forms;
public class CustomFieldClassProvider3 : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext,
in FieldIdentifier fieldIdentifier)
{
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
if (fieldIdentifier.FieldName == "Name")
{
return isValid ? "validField" : "invalidField";
}
else
{
if (editContext.IsModified(fieldIdentifier))
{
return isValid ? "modified valid" : "modified invalid";
}
else
{
return isValid ? "valid" : "invalid";
}
}
}
}
Aktualisieren Sie die EditContext-Instanz in der OnInitialized
-Methode der Komponente, um den vorherigen CSS-Klassenanbieter für Felder zu verwenden:
editContext.SetFieldCssClassProvider(new CustomFieldClassProvider3());
Verwenden von CustomFieldClassProvider3
:
- Das
Name
-Feld verwendet die benutzerdefinierten CSS-Validierungsstile der App. - Das
Description
-Feld verwendet Logik ähnlich der Logik von Blazor und die CSS-Validierungsstile des Standardfelds von Blazor.
Überprüfung auf Klassenebene mit IValidatableObject
Die Überprüfung auf Klassenebene mit IValidatableObject
(API-Dokumentation) wird für Blazor-Formularmodelle unterstützt. Die IValidatableObject-Überprüfung wird nur ausgeführt, wenn das Formular übermittelt wird und nur dann, wenn alle anderen Überprüfungen erfolgreich sind.
Validierungspakete für Datenanmerkungen in Blazor
Microsoft.AspNetCore.Components.DataAnnotations.Validation
ist ein Paket, das Lücken bei der Validierung mithilfe der DataAnnotationsValidator-Komponente schließt. Das Paket ist aktuell experimentell.
Warnung
Das Paket Microsoft.AspNetCore.Components.DataAnnotations.Validation
enthält die neueste Version des Release Candidate auf NuGet.org. Verwenden Sie bis auf Weiteres das experimentelle Release Candidate-Paket. Experimentelle Features werden für die Untersuchung der Funktionsfähigkeit von Features bereitgestellt und dürfen nicht in einer stabilen Version enthalten sein. Weitere Aktualisierungen finden Sie im GitHub-Repository „Ankündigungen“, im dotnet/aspnetcore
Repository von GitHub oder in diesem Themenabschnitt.
[CompareProperty]
-Attribut
Das CompareAttribute funktioniert nicht gut mit der DataAnnotationsValidator-Komponente, weil das DataAnnotationsValidator das Validierungsergebnis nicht mit einem bestimmten Mitglied verknüpft. Das führt zu einem nicht konsistenten Verhalten zwischen Validierung auf Feldebene und der Validierung des gesamten Modells bei Sendevorgängen. Mit dem experimentellen Paket Microsoft.AspNetCore.Components.DataAnnotations.Validation
wird das zusätzliche Validierungsattribut ComparePropertyAttribute
eingeführt, das diese Einschränkungen umgeht. In einer Blazor-App ist [CompareProperty]
ein direkter Ersatz für das Attribut [Compare]
.
Verschachtelte Modelle, Sammlungstypen und komplexe Typen
Blazor bietet Unterstützung für die Validierung von Formulareingaben mithilfe von Datenanmerkungen mit dem integrierten DataAnnotationsValidator. DataAnnotationsValidator validiert jedoch nur Eigenschaften des Modells auf oberster Ebene, die an das Formular gebunden sind, bei denen es sich aber um keine Sammlungs- oder komplexen Eigenschaften handelt.
Verwenden Sie den ObjectGraphDataAnnotationsValidator
, der im experimentellen Paket Microsoft.AspNetCore.Components.DataAnnotations.Validation
bereitgestellt wird, um den gesamten Objektgraph des gebundenen Modells zu validieren, einschließlich Sammlungseigenschaften und komplexer Eigenschaften:
<EditForm ...>
<ObjectGraphDataAnnotationsValidator />
...
</EditForm>
Fügen Sie Modelleigenschaften Anmerkungen mit [ValidateComplexType]
hinzu. In den folgenden Modellklassen enthält die ShipDescription
-Klasse zusätzliche Datenanmerkungen für die Validierung, wenn das Modell an das Formular gebunden ist:
Starship.cs
:
using System;
using System.ComponentModel.DataAnnotations;
public class Starship
{
...
[ValidateComplexType]
public ShipDescription ShipDescription { get; set; } = new();
...
}
ShipDescription.cs
:
using System;
using System.ComponentModel.DataAnnotations;
public class ShipDescription
{
[Required]
[StringLength(40, ErrorMessage = "Description too long (40 char).")]
public string? ShortDescription { get; set; }
[Required]
[StringLength(240, ErrorMessage = "Description too long (240 char).")]
public string? LongDescription { get; set; }
}
Aktivieren der Schaltfläche zum Senden basierend auf der Formularvalidierung
Um die Schaltfläche zum Senden basierend auf der Formularvalidierung zu aktivieren oder deaktivieren, geht das folgende Beispiel so vor:
- Verwendet eine gekürzte Version des obigen
Starfleet Starship Database
-Formulars (Starship3
-Komponente) aus dem Abschnitt Beispielformular im Artikel Eingabekomponenten, der nur einen Wert für die ID des Raumfahrzeugs akzeptiert. Die anderenStarship
-Eigenschaften erhalten gültige Standardwerte, wenn eine Instanz desStarship
-Typs erstellt wird. - Es verwendet den EditContext des Formulars, um das Modell zuzuweisen, wenn die Komponente initialisiert wird.
- Es validiert das Formular im OnFieldChanged-Rückruf des Kontexts, um die Schaltfläche zum Senden zu aktivieren oder zu deaktivieren.
- Es implementiert IDisposable, und beenden das Abonnement des Ereignishandlers in der
Dispose
-Methode. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
Hinweis
Weisen Sie bei der Zuweisung zu EditForm.EditContext nicht auch EditForm.Model dem EditForm zu.
Starship14.razor
:
@page "/starship-14"
@implements IDisposable
@inject ILogger<Starship14> Logger
<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship14">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit" disabled="@formInvalid">Submit</button>
</div>
</EditForm>
@code {
private bool formInvalid = false;
private EditContext? editContext;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??=
new()
{
Id = "NCC-1701",
Classification = "Exploration",
MaximumAccommodation = 150,
IsValidatedDesign = true,
ProductionDate = new DateTime(2245, 4, 11)
};
editContext = new(Model);
editContext.OnFieldChanged += HandleFieldChanged;
}
private void HandleFieldChanged(object? sender, FieldChangedEventArgs e)
{
if (editContext is not null)
{
formInvalid = !editContext.Validate();
StateHasChanged();
}
}
private void Submit() => Logger.LogInformation("Submit: Processing form");
public void Dispose()
{
if (editContext is not null)
{
editContext.OnFieldChanged -= HandleFieldChanged;
}
}
}
@page "/starship-14"
@implements IDisposable
@inject ILogger<Starship14> Logger
<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship14">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit" disabled="@formInvalid">Submit</button>
</div>
</EditForm>
@code {
private bool formInvalid = false;
private EditContext? editContext;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??=
new()
{
Id = "NCC-1701",
Classification = "Exploration",
MaximumAccommodation = 150,
IsValidatedDesign = true,
ProductionDate = new DateTime(2245, 4, 11)
};
editContext = new(Model);
editContext.OnFieldChanged += HandleFieldChanged;
}
private void HandleFieldChanged(object? sender, FieldChangedEventArgs e)
{
if (editContext is not null)
{
formInvalid = !editContext.Validate();
StateHasChanged();
}
}
private void Submit() => Logger.LogInformation("Submit: Processing form");
public void Dispose()
{
if (editContext is not null)
{
editContext.OnFieldChanged -= HandleFieldChanged;
}
}
}
@page "/starship-14"
@implements IDisposable
@inject ILogger<Starship14> Logger
<EditForm EditContext="editContext" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label>
Identifier:
<InputText @bind-Value="Model!.Id" />
</label>
</div>
<div>
<button type="submit" disabled="@formInvalid">Submit</button>
</div>
</EditForm>
@code {
private bool formInvalid = false;
private EditContext? editContext;
private Starship? Model { get; set; }
protected override void OnInitialized()
{
Model ??=
new()
{
Id = "NCC-1701",
Classification = "Exploration",
MaximumAccommodation = 150,
IsValidatedDesign = true,
ProductionDate = new DateTime(2245, 4, 11)
};
editContext = new(Model);
editContext.OnFieldChanged += HandleFieldChanged;
}
private void HandleFieldChanged(object? sender, FieldChangedEventArgs e)
{
if (editContext is not null)
{
formInvalid = !editContext.Validate();
StateHasChanged();
}
}
private void Submit()
{
Logger.LogInformation("Submit called: Processing the form");
}
public void Dispose()
{
if (editContext is not null)
{
editContext.OnFieldChanged -= HandleFieldChanged;
}
}
}
Wenn ein Formular nicht mit gültigen Werten vorab geladen wird und Sie die Schaltfläche Submit
beim Laden des Formulars deaktivieren möchten, legen Sie formInvalid
auf true
fest.
Eine Nebenwirkung des vorangehenden Ansatzes ist, dass eine Validierungszusammenfassung (ValidationSummary-Komponente) mit ungültigen Feldern aufgefüllt wird, nachdem der Benutzer mit einem beliebigen Feld interagiert hat. Behandeln Sie dieses Szenario auf eine der folgenden Arten:
- Verwenden Sie keine ValidationSummary-Komponente des Formulars.
- Lassen Sie die ValidationSummary-Komponente einblenden, wenn auf die Schaltfläche zum Senden geklickt wird, z. B. in einer
Submit
-Methode.
<EditForm ... EditContext="editContext" OnValidSubmit="Submit" ...>
<DataAnnotationsValidator />
<ValidationSummary style="@displaySummary" />
...
<button type="submit" disabled="@formInvalid">Submit</button>
</EditForm>
@code {
private string displaySummary = "display:none";
...
private void Submit()
{
displaySummary = "display:block";
}
}