Szkolenie
Moduł
Dowiedz się, jak używać zdarzeń DOM, formularzy i walidacji w aplikacji Blazor
Ta przeglądarka nie jest już obsługiwana.
Przejdź na przeglądarkę Microsoft Edge, aby korzystać z najnowszych funkcji, aktualizacji zabezpieczeń i pomocy technicznej.
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz artykuł w wersji .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz artykuł wersji .NET 9 .
W tym artykule wyjaśniono, jak używać walidacji w Blazor formularzach.
W podstawowych scenariuszach weryfikacji formularzy, wystąpienie EditForm może używać zadeklarowanych wystąpień EditContext i ValidationMessageStore do sprawdzania poprawności pól formularza. Procedura obsługi zdarzenia OnValidationRequested dla EditContext wykonuje niestandardową logikę walidacji. Wynik obsługiwacza aktualizuje wystąpienie ValidationMessageStore.
Podstawowa weryfikacja formularza jest przydatna w przypadkach, gdy model formularza jest zdefiniowany w składniku, który hostuje formularz, jako elementy członkowskie bezpośrednio w składniku lub w podklasie. Zaleca się użycie składnika walidatora, gdzie w kilku składnikach jest używana niezależna klasa modelu.
W Blazor Web Appprogramie s weryfikacja po stronie klienta wymaga aktywnego BlazorSignalR obwodu. Walidacja po stronie klienta nie jest dostępna dla formularzy w składnikach, które przyjęły statyczne renderowanie po stronie serwera (statyczne SSR). Formularze, które wykorzystują statyczne SSR, są walidowane na serwerze po przesłaniu.
W poniższym składniku, metoda obsługi HandleValidationRequested
najpierw czyści wszelkie istniejące komunikaty walidacji, wywołując ValidationMessageStore.Clear, zanim zweryfikuje formularz.
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;
}
}
}
Składnik DataAnnotationsValidator dołącza walidację adnotacji danych do kaskadowego EditContextelementu . Włączenie walidacji adnotacji danych wymaga DataAnnotationsValidator składnika. Aby użyć innego systemu weryfikacji niż adnotacje danych, użyj niestandardowej implementacji zamiast DataAnnotationsValidator składnika. Implementacje platformy dla DataAnnotationsValidator są dostępne do przeglądu w źródle odniesienia:
Jeśli musisz włączyć obsługę walidacji adnotacji danych dla EditContext w kodzie, wywołaj EnableDataAnnotationsValidation z wstrzykniętym IServiceProvider (@inject IServiceProvider ServiceProvider
) na EditContext. Aby zapoznać się z zaawansowanym przykładem, zobacz składnik NotifyPropertyChangedValidationComponent
w platformie ASP.NET Core BlazorBasicTestApp
(repozytoriumdotnet/aspnetcore
GitHub). W wersji produkcyjnej tego przykładu, argument new TestServiceProvider()
dostawcy usług zastąp wstrzykniętym IServiceProvider.
Uwaga
Linki dokumentacji do źródła referencyjnego .NET zwykle ładują domyślną gałąź repozytorium, która odzwierciedla aktualny rozwój następnej wersji .NET. Aby wybrać tag dla określonej wersji, użyj listy rozwijanej Przełącz gałęzie lub tagi. Aby uzyskać więcej informacji, zobacz Jak wybrać tag wersji kodu źródłowego platformy ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Blazor wykonuje dwa typy weryfikacji:
W niestandardowych scenariuszach walidacji:
Istnieją dwa ogólne podejścia do osiągnięcia weryfikacji niestandardowej, które opisano w dwóch następnych sekcjach tego artykułu:
OnValidationRequested
: ręcznie zweryfikuj pola formularza z adnotacjami danych i niestandardowy kod sprawdzania pola w przypadku żądania weryfikacji za pośrednictwem procedury obsługi zdarzeń przypisanej do zdarzenia OnValidationRequested.Możesz ręcznie zweryfikować formularz za pomocą niestandardowego programu obsługi zdarzeń przypisanego do zdarzenia EditContext.OnValidationRequested w celu zarządzania ValidationMessageStore.
Struktura Blazor udostępnia składnik DataAnnotationsValidator w celu dołączenia dodatkowej obsługi walidacji do formularzy na podstawie atrybutów weryfikacji (adnotacji danych).
Przypominając wcześniejszy przykład składnika Starship8
, metoda HandleValidationRequested
jest przypisywana do OnValidationRequested, gdzie można przeprowadzić ręczną walidację w kodzie języka C#. Kilka zmian pokazuje połączenie istniejącej ręcznej weryfikacji z walidacją adnotacji danych za pośrednictwem DataAnnotationsValidator i atrybutu weryfikacji zastosowanego do modelu Holodeck
.
Odwołaj się do przestrzeni nazw System.ComponentModel.DataAnnotations w dyrektywach Razor składnika w górnej części pliku definicji składnika:
@using System.ComponentModel.DataAnnotations
Dodaj właściwość Id
do modelu Holodeck
z atrybutem weryfikacji, aby ograniczyć długość ciągu do sześciu znaków:
[StringLength(6)]
public string? Id { get; set; }
Dodaj składnik DataAnnotationsValidator (<DataAnnotationsValidator />
) do formularza. Zazwyczaj składnik jest umieszczany natychmiast pod tagiem <EditForm>
, ale można umieścić go w dowolnym miejscu w formularzu:
<DataAnnotationsValidator />
Zmień zachowanie przesyłania formularza w tagu <EditForm>
z OnSubmit na OnValidSubmit, co gwarantuje, że formularz jest prawidłowy przed wykonaniem przypisanej metody obsługi zdarzeń:
- OnSubmit="Submit"
+ OnValidSubmit="Submit"
W <EditForm>
dodaj pole dla właściwości Id
:
<div>
<label>
<InputText @bind-Value="Model!.Id" />
ID (6 characters max)
</label>
<ValidationMessage For="() => Model!.Id" />
</div>
Po wprowadzeniu powyższych zmian zachowanie formularza jest zgodne z następującą specyfikacją:
Id
nie powoduje niepowodzenia walidacji, gdy pole Id
traci fokus. Walidacja jest wykonywana, gdy użytkownik wybierze przycisk Update
.HandleValidationRequested
przypisanej do zdarzenia OnValidationRequested formularza, jest wykonywana, gdy użytkownik wybierze przycisk Update
formularza. W istniejącym kodzie składnika Starship8
przykładzie użytkownik musi zaznaczyć oba pola wyboru, aby zweryfikować formularz.Submit
, dopóki zarówno adnotacje danych, jak i ręczna walidacja nie zostaną zakończone pomyślnie.Składniki modułu sprawdzania poprawności obsługują walidację formularza, zarządzając elementem ValidationMessageStore dla formularza EditContext.
Platforma Blazor udostępnia DataAnnotationsValidator składnik do dołączania obsługi walidacji do formularzy na podstawie atrybutów weryfikacji (adnotacji danych). Możesz utworzyć niestandardowe składniki modułu sprawdzania poprawności w celu przetwarzania komunikatów weryfikacji dla różnych formularzy na tej samej stronie lub w tym samym formularzu w różnych krokach przetwarzania formularzy (na przykład weryfikacji klienta, a następnie weryfikacji serwera). Przykładowy składnik modułu sprawdzania poprawności przedstawiony w tej sekcji CustomValidation
, jest używany w następujących sekcjach tego artykułu:
Z wbudowanych modułów sprawdzania poprawności adnotacji danych tylko atrybut [Remote]
walidacji nie jest obsługiwany w Blazor.
Uwaga
Niestandardowe atrybuty weryfikacji adnotacji danych mogą być używane zamiast niestandardowych składników modułu sprawdzania poprawności w wielu przypadkach. Atrybuty niestandardowe zastosowane do modelu formularza są aktywowane przy użyciu komponentu DataAnnotationsValidator. W przypadku użycia z walidacją serwera wszystkie atrybuty niestandardowe zastosowane do modelu muszą być wykonywalne na serwerze. Aby uzyskać więcej informacji, zobacz sekcję Niestandardowe atrybuty walidacji.
Utwórz składnik modułu sprawdzania poprawności na podstawie elementu ComponentBase:
DisplayErrors
. Błędy są przekazywane do DisplayErrors
metody w Dictionary<string, List<string>>
. W słowniku kluczem jest nazwa pola formularza, które zawiera co najmniej jeden błąd. Wartość to lista błędów.ClearErrors
przez kod dewelopera. Wszystkie błędy zostały usunięte.Zaktualizuj przestrzeń nazw w poniższej klasie, aby odpowiadała przestrzeni nazw aplikacji.
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();
}
}
Ważne
Określenie przestrzeni nazw jest wymagane podczas wyprowadzania z ComponentBase. Niepodanie przestrzeni nazw skutkuje błędem kompilacji:
Tag helpers cannot target tag name '<global namespace>.{CLASS NAME}' because it contains a ' ' character.
Symbol zastępczy {CLASS NAME}
jest nazwą klasy komponentu. Przykład niestandardowego modułu sprawdzania poprawności w tej sekcji określa przykładową przestrzeń nazw BlazorSample
.
Uwaga
Anonimowe wyrażenia lambda są zarejestrowanymi procedurami obsługi zdarzeń dla OnValidationRequested i OnFieldChanged w poprzednim przykładzie. Nie ma potrzeby implementowania IDisposable i anulowania subskrypcji delegatów zdarzeń w tym scenariuszu. Aby uzyskać więcej informacji, zobacz usuwanie komponentów ASP.NET Core Razor.
W przypadku ogólnej weryfikacji logiki biznesowej należy użyć składnika modułu sprawdzania poprawności, który odbiera błędy formularzy w słowniku.
Podstawowa walidacja jest przydatna w przypadkach, gdy model formularza jest zdefiniowany w składniku hostującym formularz, bezpośrednio jako elementy składnika lub w podklasie. Zaleca się użycie składnika modułu sprawdzania poprawności, w którym jest używana niezależna klasa modelu w kilku składnikach.
W poniższym przykładzie:
Starfleet Starship Database
(Starship3
składnika) sekcji Przykładowy formularz artykułu Składniki wejściowe, który akceptuje tylko klasyfikację i opis statku kosmicznego. Walidacja adnotacji danych nie jest wyzwalana podczas przesyłania formularza, ponieważ DataAnnotationsValidator składnik nie jest uwzględniony w formularzu.CustomValidation
z sekcji Komponenty walidatora tego artykułu jest używany.Description
), jeśli użytkownik wybierze klasyfikację statku "Defense
" (Classification
).Po ustawieniu komunikatów walidacji w składniku są one dodawane do walidatora ValidationMessageStore i wyświetlane w podsumowaniu walidacji EditForm.
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),
[ "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),
[ "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");
}
}
}
Uwaga
Zamiast używania komponentów weryfikacji, można używać atrybutów weryfikacji adnotacji danych. Atrybuty niestandardowe zastosowane do modelu formularza są aktywowane przy użyciu składnika DataAnnotationsValidator. W przypadku użycia z walidacją serwera atrybuty muszą być wykonywalne na serwerze. Aby uzyskać więcej informacji, zobacz sekcję Niestandardowe atrybuty walidacji.
Ta sekcja koncentruje się na Blazor Web App scenariuszach, ale podejście do dowolnej aplikacji korzystającej z weryfikacji serwera za pomocą internetowego interfejsu API przyjmuje takie samo ogólne podejście.
Ta sekcja koncentruje się na scenariuszach hostowanych Blazor WebAssembly , ale podejście dla dowolnego typu aplikacji korzystającej z weryfikacji serwera za pomocą internetowego interfejsu API przyjmuje takie samo ogólne podejście.
Walidacja serwera jest obsługiwana oprócz walidacji klienta.
200 - OK
). Jeśli walidacja nie powiedzie się, zwróć kod stanu błędu (400 - Bad Request
) i błędy walidacji pola.Podstawowa walidacja jest przydatna w przypadkach, gdy model formularza jest zdefiniowany w składniku hostującym formularz, jako elementy członkowskie bezpośrednio w składniku lub w podklasie. Zaleca się użycie składnika modułu sprawdzania poprawności, w którym jest używana niezależna klasa modelu w kilku składnikach.
Poniższy przykład jest oparty na:
Starship
model (Starship.cs
) sekcji Przykładowy formularz artykułu Komponenty wejściowe.CustomValidation
pokazany w sekcji Składniki modułu sprawdzania poprawności.Umieść model (Starship
) w projekcie biblioteki klas udostępnionych (Starship.cs
), aby zarówno projekty klienta, jak i serwera mogły używać modelu. Dodaj lub zaktualizuj przestrzeń nazw, aby odpowiadała przestrzeni nazw udostępnionej aplikacji (na przykład namespace BlazorSample.Shared
). Ponieważ model wymaga adnotacji danych, upewnij się, że biblioteka klas udostępnionych używa struktury udostępnionej lub dodaj System.ComponentModel.Annotations
pakiet do udostępnionego projektu.
Uwaga
Aby uzyskać instrukcje dodawania pakietów do aplikacji .NET, zobacz artykuły w sekcji Instalowanie pakietów i zarządzanie nimi w temacie Przepływ pracy użycia pakietów (dokumentacja programu NuGet). Sprawdź prawidłowe wersje pakietów pod adresem NuGet.org.
W głównym projekcie Blazor Web App dodaj kontroler, który będzie przetwarzać żądania weryfikacji statków kosmicznych i zwracać komunikaty o błędach weryfikacji. Zaktualizuj przestrzenie nazw w ostatniej using
instrukcji dla projektu biblioteki klas udostępnionych i namespace
dla klasy kontrolera. Oprócz weryfikacji adnotacji danych klienta i serwera, kontroler sprawdza, czy podano wartość dla opisu statku (Description
), jeśli użytkownik wybierze klasyfikację statku (Defense
) (Classification
).
Starship
Model (Starship.cs
) sekcji Przykładowy formularz artykułu Składniki wejściowe.CustomValidation
pokazany w sekcji Składniki walidatora.
Starship
Umieść model (Starship.cs
) w projekcie rozwiązaniaShared
, aby aplikacje klienckie i serwerowe mogły używać modelu. Dodaj lub zaktualizuj przestrzeń nazw, aby odpowiadała przestrzeni nazw udostępnionej aplikacji (na przykład namespace BlazorSample.Shared
). Ponieważ model wymaga adnotacji danych, dodaj System.ComponentModel.Annotations
pakiet do Shared
projektu.
Uwaga
Aby uzyskać instrukcje dodawania pakietów do aplikacji .NET, zobacz artykuły w sekcji Instalowanie pakietów i zarządzanie nimi w temacie Przepływ pracy użycia pakietów (dokumentacja programu NuGet). Sprawdź prawidłowe wersje pakietów pod adresem NuGet.org.
W projekcie Server dodaj kontroler do przetwarzania żądań weryfikacji statków kosmicznych i zwracania komunikatów o nieudanej weryfikacji. Zaktualizuj przestrzenie nazw w ostatniej instrukcji using
dla projektu Shared
i klasy kontrolera namespace
. Oprócz weryfikacji adnotacji danych klienta i serwera, kontroler sprawdza, czy podano wartość dla opisu statku (Description
), jeśli użytkownik wybierze klasyfikację statku (Defense
Classification
).
Walidacja klasyfikacji statku odbywa się tylko na serwerze w kontrolerze, ponieważ przesyłany formularz nie wykonuje tej samej walidacji po stronie klienta po przesłaniu formularza do serwera. Walidacja serwera bez walidacji klienta jest powszechna w aplikacjach, które wymagają prywatnej weryfikacji logiki biznesowej danych wejściowych użytkownika na serwerze. Na przykład informacje prywatne z danych przechowywanych dla użytkownika mogą być wymagane do zweryfikowania danych wejściowych użytkownika. Prywatne dane oczywiście nie mogą być wysyłane do klienta w celu weryfikacji klienta.
Uwaga
Kontroler StarshipValidation
w tej sekcji używa platformy Microsoft Identity 2.0. Internetowy interfejs API akceptuje tylko tokeny dla użytkowników, którzy mają zakres "API.Access
" dla tego interfejsu API. Dodatkowe dostosowanie jest wymagane, jeśli nazwa zakresu interfejsu API różni się od API.Access
.
Aby uzyskać więcej informacji na temat zabezpieczeń, zobacz:
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 = [ "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);
}
}
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);
}
}
Potwierdź lub zaktualizuj przestrzeń nazw poprzedniego kontrolera (BlazorSample.Server.Controllers
), aby odpowiadała przestrzeni nazw kontrolerów aplikacji.
Gdy na serwerze wystąpi błąd weryfikacji powiązania modelu, ApiController
ApiControllerAttributezwykle zwraca domyślną nieprawidłową odpowiedź żądania z wartością ValidationProblemDetails. Odpowiedź zawiera więcej danych niż tylko błędy walidacji, jak pokazano w poniższym przykładzie, gdy wszystkie pola Starfleet Starship Database
formularza nie są przesyłane, a walidacja formularza kończy się niepowodzeniem:
{
"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)." ]
}
}
Uwaga
Aby zademonstrować poprzednią odpowiedź JSON, należy wyłączyć walidację formularza po stronie klienta, aby zezwolić na przesyłanie formularza z pustymi polami, lub użyć narzędzia do wysyłania żądania bezpośrednio do interfejsu API serwera, takiego jak Firefox Browser Developer.
Jeśli interfejs API serwera zwraca poprzednią domyślną odpowiedź JSON, klient może przeanalizować odpowiedź w kodzie dewelopera w celu uzyskania elementów podrzędnych węzła errors
na potrzeby przetwarzania błędów walidacji formularzy. Pisanie kodu do analizy pliku jest niewygodne. Ręczne analizowanie kodu JSON wymaga utworzenia Dictionary<string, List<string>>
błędu po wywołaniu metody ReadFromJsonAsync. Najlepiej, aby interfejs API serwera zwracał tylko błędy weryfikacji, jak pokazano w poniższym przykładzie:
{
"Id": [ "The Id field is required." ],
"Classification": [ "The Classification field is required." ],
"IsValidatedDesign": [ "This form disallows unapproved ships." ],
"MaximumAccommodation": [ "Accommodation invalid (1-100000)." ]
}
Aby zmodyfikować odpowiedź interfejsu API serwera, aby zwracała tylko błędy walidacji, zmień delegata wywoływanego w przypadku akcji, które są oznaczone ApiControllerAttribute w pliku Program
. W przypadku punktu końcowego interfejsu API (/StarshipValidation
) zwróć element BadRequestObjectResult z wartością ModelStateDictionary. W przypadku innych punktów końcowych interfejsu API zachowaj domyślne zachowanie, zwracając wynik obiektu przy użyciu nowego ValidationProblemDetails elementu.
Dodaj przestrzeń nazw Microsoft.AspNetCore.Mvc na początku pliku Program
w głównym projekcie Blazor Web App.
using Microsoft.AspNetCore.Mvc;
Program
W pliku dodaj lub zaktualizuj następującą AddControllersWithViews metodę rozszerzenia i dodaj następujące wywołanie do ConfigureApiBehaviorOptions:
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));
}
};
});
W przypadku dodawania kontrolerów do głównego projektu Blazor Web App po raz pierwszy, zmapuj punkty końcowe kontrolerów, umieszczając wspomniany wcześniej kod, który rejestruje usługi dla kontrolerów. W poniższym przykładzie użyto domyślnych tras kontrolera:
app.MapDefaultControllerRoute();
Uwaga
Powyższy przykład jawnie rejestruje usługi kontrolera przez wywołanie metody AddControllersWithViews w celu automatycznego łagodzenia ataków Cross-Site Request Forgery (XSRF/CSRF). Jeśli używasz tylko AddControllers, antyfałszerstwo nie jest włączone automatycznie.
Aby uzyskać więcej informacji na temat odpowiedzi na błędy routingu kontrolera, oraz błędów walidacji, zobacz następujące zasoby:
W projekcie .Client
dodaj CustomValidation
składnik pokazany w sekcji Składniki sprawdzania poprawności. Zaktualizuj przestrzeń nazw, aby odpowiadała aplikacji (na przykład namespace BlazorSample.Client
).
W projekcie .Client
formularz jest aktualizowany, aby wyświetlać błędy walidacji serwera z pomocą składnika CustomValidation
. Gdy interfejs API serwera zwraca komunikaty walidacyjne, są one dodawane do CustomValidation
składnika ValidationMessageStore. Błędy są dostępne w formularzu EditContext do wyświetlenia w podsumowaniu weryfikacji formularza.
W poniższym składniku zaktualizuj przestrzeń nazw udostępnionego projektu (@using BlazorSample.Shared
) do przestrzeni nazw udostępnionego projektu. Pamiętaj, że formularz wymaga autoryzacji, więc użytkownik musi być zalogowany do aplikacji, aby przejść do formularza.
Dodaj przestrzeń nazw Microsoft.AspNetCore.Mvc na początku pliku Program
w aplikacji Server.
using Microsoft.AspNetCore.Mvc;
W pliku Program
zlokalizuj metodę rozszerzenia AddControllersWithViews i dodaj następujące wywołanie do ConfigureApiBehaviorOptions.
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));
}
};
});
Uwaga
Powyższy przykład jawnie rejestruje usługi kontrolera przez wywołanie metody AddControllersWithViews w celu automatycznego zmniejszenia ryzyka fałszowania żądań między witrynami (XSRF/CSRF). Jeśli używasz tylko AddControllers, antyforgery nie włącza się automatycznie.
W projekcie Client dodaj CustomValidation
komponent pokazany w sekcji Komponenty walidatora. Zaktualizuj przestrzeń nazw, aby odpowiadała aplikacji (na przykład namespace BlazorSample.Client
).
W projekcie Client formularz jest aktualizowany, aby wyświetlać błędy walidacji serwera z pomocą składnika CustomValidation
. Gdy interfejs API serwera zwraca komunikaty sprawdzania poprawności, dodawane są one do CustomValidation
składnika ValidationMessageStore. Błędy są dostępne w formularzu EditContext do wyświetlenia w podsumowaniu weryfikacji formularza.
W poniższym składniku zaktualizuj przestrzeń nazw Shared
projektu (@using BlazorSample.Shared
) do przestrzeni nazw udostępnionego projektu. Pamiętaj, że formularz wymaga autoryzacji, więc użytkownik musi być zalogowany do aplikacji, aby przejść do formularza.
Starship10.razor
:
Uwaga
Formularze oparte na EditForm automatycznie włączają obsługę ochrony przed fałszerstwem. Kontroler powinien używać AddControllersWithViews do rejestrowania usług kontrolera i automatycznego włączania obsługi ochrony przed fałszerstwem dla internetowego interfejsu API.
@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.";
}
}
}
Projekt .Client
obiektu Blazor Web App musi również zarejestrować HttpClient dla żądań HTTP POST do kontrolera API części serwerowej. Potwierdź lub dodaj następujący kod do .Client
pliku projektu Program
:
builder.Services.AddScoped(sp =>
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
W poprzednim przykładzie ustawiono adres podstawowy przy użyciu builder.HostEnvironment.BaseAddress
(IWebAssemblyHostEnvironment.BaseAddress), który pobiera adres podstawowy dla aplikacji i jest zwykle wyprowadzany z wartości tagu href
<base>
na stronie hosta.
@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.";
}
}
}
Uwaga
Zamiast używania składnika sprawdzania poprawności można użyć atrybutów weryfikacji adnotacji danych. Atrybuty niestandardowe zastosowane do modelu formularza są aktywowane przy użyciu składnika DataAnnotationsValidator. W przypadku użycia z walidacją serwera atrybuty muszą być wykonywalne na serwerze. Aby uzyskać więcej informacji, zobacz sekcję Niestandardowe atrybuty walidacji.
Uwaga
Metoda weryfikacji serwera opisana w tej części jest odpowiednia dla każdego z hostowanych przykładów rozwiązań w tym zestawie dokumentacji:
Użyj składnika InputText, aby utworzyć składnik niestandardowy, który używa zdarzenia oninput
(input
) zamiast zdarzenia onchange
(change
). Użycie zdarzenia input
powoduje walidację pola przy każdym naciśnięciu klawisza.
Poniższy CustomInputText
składnik dziedziczy składnik platformy InputText
i wiąże zdarzenie z oninput
zdarzeniem (input
).
CustomInputText.razor
:
@inherits InputText
<input @attributes="AdditionalAttributes"
class="@CssClass"
@bind="CurrentValueAsString"
@bind:event="oninput" />
Składnik CustomInputText
może być także używany w dowolnym miejscu, gdzie używany jest InputText. Poniższy składnik używa składnika udostępnionego CustomInputText
.
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; }
}
}
Składnik ValidationSummary podsumowuje wszystkie komunikaty walidacyjne, co jest podobne do tag helpera Podsumowania Walidacji:
<ValidationSummary />
Komunikaty sprawdzania poprawności danych wyjściowych dla określonego modelu z parametrem Model
:
<ValidationSummary Model="Model" />
Składnik ValidationMessage<TValue> wyświetla komunikaty walidacyjne dla określonego pola, co jest podobne do Pomocnika Tagu Komunikatu Walidacji. Określ pole do weryfikacji za pomocą atrybutu For i wyrażenia lambda nazywającego właściwość modelu.
<ValidationMessage For="@(() => Model!.MaximumAccommodation)" />
Składniki ValidationMessage<TValue> i ValidationSummary obsługują dowolne atrybuty. Każdy atrybut, który nie jest zgodny z parametrem składnika, jest dodawany do wygenerowanego elementu <div>
lub <ul>
.
Kontrolowanie stylu komunikatów walidacji w arkuszu stylów aplikacji (wwwroot/css/app.css
lub wwwroot/css/site.css
). Domyślna validation-message
klasa ustawia kolor tekstu komunikatów walidacji na czerwony:
.validation-message {
color: red;
}
Użyj EditContext.IsValid polecenia , aby określić, czy pole jest prawidłowe bez uzyskiwania komunikatów sprawdzania poprawności.
Obsługiwane, ale niezalecane:
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
Zalecane:
var isValid = editContext.IsValid(fieldIdentifier);
Aby upewnić się, że wynik weryfikacji jest poprawnie skojarzony z polem podczas używania niestandardowego atrybutu weryfikacji, przekaż kontekst weryfikacji MemberName podczas tworzenia obiektu ValidationResult.
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.",
[ validationContext.MemberName! ]);
}
}
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! });
}
}
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 });
}
}
Wstrzykiwanie usług do niestandardowych atrybutów walidacji za pomocą elementu ValidationContext. W poniższym przykładzie pokazano formularz szefa sałat, który weryfikuje dane wejściowe użytkownika za pomocą wstrzykiwania zależności (DI).
Klasa SaladChef
wskazuje zatwierdzoną listę składników statku kosmicznego dla sałatki Ten Forward.
SaladChef.cs
:
namespace BlazorSample;
public class SaladChef
{
public string[] SaladToppers = { "Horva", "Kanda Root", "Krintar", "Plomeek",
"Syto Bean" };
}
Zarejestruj SaladChef
w kontenerze DI aplikacji w pliku Program
.
builder.Services.AddTransient<SaladChef>();
Metoda IsValid
poniższej SaladChefValidatorAttribute
klasy uzyskuje usługę SaladChef
z DI w celu sprawdzenia danych wejściowych użytkownika.
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));
}
}
Poniższy komponent weryfikuje dane wejściowe użytkownika, stosując SaladChefValidatorAttribute
([SaladChefValidator]
) do ciągu składnika sałatki (SaladIngredient
).
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);
}
Niestandardowe atrybuty klas CSS walidacji są przydatne podczas integrowania z frameworkami CSS, takimi jak Bootstrap.
Aby określić niestandardowe atrybuty klasy CSS walidacji, zacznij od udostępnienia stylów CSS dla niestandardowej walidacji. W poniższym przykładzie określono prawidłowe style (validField
) i nieprawidłowe (invalidField
).
Dodaj następujące klasy CSS do arkusza stylów aplikacji:
.validField {
border-color: lawngreen;
}
.invalidField {
background-color: tomato;
}
Utwórz klasę pochodną na podstawie FieldCssClassProvider, która sprawdza komunikaty walidacyjne pól i stosuje odpowiedni styl dla poprawnych lub błędnych danych.
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";
}
}
Ustaw klasę CustomFieldClassProvider
jako dostawcę klasy CSS pola w wystąpieniu formularza EditContext z SetFieldCssClassProvider.
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; }
}
}
Powyższy przykład sprawdza ważność wszystkich pól formularza i stosuje styl do każdego pola. Jeśli formularz powinien stosować style niestandardowe tylko do podzestawu pól, spraw, aby CustomFieldClassProvider
stosował style warunkowo.
CustomFieldClassProvider2
Następujący przykład stosuje styl tylko do pola Name
. W przypadku dowolnych pól, których nazwy nie pasują do Name
, zwracana jest wartość string.Empty
i styl nie jest stosowany. Przy użyciu refleksji pole jest dopasowywane do właściwości lub pola elementu członkowskiego modelu, a nie do jednostki HTML przypisanej do 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;
}
}
Uwaga
Dopasowywanie nazwy pola w poprzednim przykładzie jest rozróżniana ze względu na wielkość liter, więc element właściwości modelu oznaczony jako "Name
" musi pasować do sprawdzania warunkowego na "Name
".
fieldId.FieldName == "Name"
fieldId.FieldName == "name"
fieldId.FieldName == "NAME"
fieldId.FieldName == "nAmE"
Dodaj dodatkową właściwość do elementu Model
, na przykład.
[StringLength(10, ErrorMessage = "Description is too long.")]
public string? Description { get; set; }
Dodaj Description
do formularza dla składnika CustomValidationForm
.
<InputText @bind-Value="Model!.Description" />
Zaktualizuj wystąpienie EditContext w metodzie OnInitialized
składnika, aby użyć nowego dostawcy klas CSS dla pól.
editContext?.SetFieldCssClassProvider(new CustomFieldClassProvider2());
Ponieważ klasa walidacji CSS nie jest zastosowana do pola Description
, nie jest ono stylizowane. Jednak walidacja pola jest uruchamiana normalnie. Jeśli podano więcej niż 10 znaków, podsumowanie weryfikacji wskazuje błąd:
Opis jest za długi.
W poniższym przykładzie:
Niestandardowy styl CSS jest stosowany do Name
pola.
Wszystkie inne pola stosują logikę podobną do domyślnej logiki Blazor i używają domyślnych stylów walidacji CSS pól Blazor, z modified
valid
lub invalid
. Pamiętaj, że w przypadku stylów domyślnych nie musisz dodawać ich do arkusza stylów aplikacji, jeśli aplikacja jest oparta na szablonie Blazor projektu. W przypadku aplikacji, które nie są oparte na szablonie Blazor projektu, do arkusza stylów aplikacji można dodać domyślne style:
.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";
}
}
}
}
Zaktualizuj wystąpienie EditContext w metodzie OnInitialized
składnika, aby użyć poprzedniego dostawcy klas CSS dla pola:
editContext.SetFieldCssClassProvider(new CustomFieldClassProvider3());
Korzystanie z CustomFieldClassProvider3
:
Name
używa własnych stylów CSS do walidacji aplikacji.Description
używa logiki podobnej do logiki Blazor i domyślnych stylów walidacji CSS pól Blazor.
Walidacja IValidatableObject
na poziomie klasowym (dokumentacja API) jest obsługiwana w przypadku Blazor modeli formularzy.
IValidatableObject Walidacja jest wykonywana tylko wtedy, gdy formularz zostanie przesłany i tylko wtedy, gdy wszystkie inne walidacje się powiedzie.
Microsoft.AspNetCore.Components.DataAnnotations.Validation
to pakiet, który przy użyciu składnika DataAnnotationsValidator wypełnia luki w doświadczeniu walidacji. Pakiet jest obecnie eksperymentalny.
Ostrzeżenie
Pakiet Microsoft.AspNetCore.Components.DataAnnotations.Validation
ma najnowszą wersję kandydata do wydania w NuGet.org. Na razie kontynuuj korzystanie z eksperymentalnego pakietu kandydata do wydania. Funkcje eksperymentalne są udostępniane w celu eksplorowania możliwości funkcji i mogą nie być dostarczane w stabilnej wersji. Obserwuj repozytorium GitHub Anonsy, dotnet/aspnetcore
repozytorium GitHub lub tę sekcję tematu, aby uzyskać dalsze aktualizacje.
Element CompareAttribute nie działa dobrze z komponentem DataAnnotationsValidator, ponieważ DataAnnotationsValidator nie kojarzy wyniku weryfikacji z określonym członkiem. Może to spowodować niespójne zachowanie między weryfikacją na poziomie pola a weryfikacją całego modelu przy jego przesłaniu. Pakiet Microsoft.AspNetCore.Components.DataAnnotations.Validation
eksperymentalny wprowadza dodatkowy atrybut weryfikacji , ComparePropertyAttribute
który działa wokół tych ograniczeń. W aplikacji Blazor, [CompareProperty]
jest bezpośrednim zamiennikiem dla atrybutu [Compare]
.
Blazor zapewnia obsługę walidacji danych wejściowych formularza przy użyciu adnotacji danych z wbudowaną funkcją DataAnnotationsValidator. Jednak DataAnnotationsValidator tylko weryfikuje właściwości najwyższego poziomu modelu powiązane z formularzem, które nie są właściwościami kolekcji ani typu złożonego.
Aby zweryfikować poprawność całego grafu obiektów powiązanego modelu, w tym właściwości kolekcji i typu złożonego, użyj
<EditForm ...>
<ObjectGraphDataAnnotationsValidator />
...
</EditForm>
Dodawanie adnotacji do właściwości modelu za pomocą polecenia [ValidateComplexType]
. W następujących klasach modelu klasa ShipDescription
zawiera dodatkowe adnotacje danych do weryfikacji, gdy model jest powiązany z formularzem:
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; }
}
Przykład pokazuje, jak włączyć i wyłączyć przycisk przesyłania w zależności od weryfikacji formularza.
Starfleet Starship Database
(Starship3
składnika) sekcji Przykładowy formularz artykułu Składniki wejściowe, która akceptuje tylko wartość identyfikatora wysyłki. Inne właściwości Starship
otrzymują prawidłowe wartości domyślne, gdy tworzona jest instancja typu Starship
.Dispose
. Aby uzyskać więcej informacji, zobacz usuwanie komponentów ASP.NET Core Razor.Uwaga
Podczas przypisywania do elementu EditForm.EditContext nie należy również przypisywać elementu EditForm.Model do elementu EditForm.
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;
}
}
}
Jeśli formularz nie ma wstępnie załadowanych prawidłowych wartości i chcesz wyłączyć przycisk Submit
podczas ładowania formularza, ustaw formInvalid
na true
.
Efektem ubocznym powyższego podejścia jest to, że podsumowanie weryfikacji (ValidationSummary składnik) jest wypełniane nieprawidłowymi polami po interakcji użytkownika z dowolnym polem. Rozwiąż ten scenariusz na jeden z następujących sposobów:
Submit
).<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";
}
}
Opinia o produkcie ASP.NET Core
ASP.NET Core to projekt typu open source. Wybierz link, aby przekazać opinię:
Szkolenie
Moduł
Dowiedz się, jak używać zdarzeń DOM, formularzy i walidacji w aplikacji Blazor
Dokumentacja
Omówienie formularzy ASP.NET Core Blazor
Dowiedz się, jak używać formularzy w programie Blazor.
powiązanie formularzy ASP.NET Core Blazor
Dowiedz się, jak używać powiązania w Blazor formularzach.
powiązanie danych ASP.NET Core Blazor
Dowiedz się więcej o funkcjach powiązania danych dla Razor składników i elementów DOM w Blazor aplikacjach.