Przeczytaj w języku angielskim

Udostępnij za pośrednictwem


sprawdzanie poprawności formularzy ASP.NET Core Blazor

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz artykuł w wersji .NET 9.

Ostrzeżenie

Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz zasady pomocy technicznej platformy .NET i platformy .NET Core. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu .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.

Walidacja formularza

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 modułu sprawdzania poprawności adnotacji danych i walidacja niestandardowa

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:

  • Walidacja pola jest wykonywana, gdy użytkownik przechodzi do następnego pola za pomocą klawisza Tab. Podczas walidacji DataAnnotationsValidator pola składnik kojarzy wszystkie zgłoszone wyniki walidacji z polem.
  • Walidacja modelu jest wykonywana po przesłaniu formularza przez użytkownika. Podczas walidacji DataAnnotationsValidator modelu składnik próbuje określić pole na podstawie nazwy elementu członkowskiego, którą raportuje wynik weryfikacji. Wyniki weryfikacji, które nie są skojarzone z pojedynczym elementem członkowskim, są skojarzone z modelem, a nie z polem.

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:

  • ręczne sprawdzanie poprawności przy użyciuzdarzeń 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.
  • składniki modułu sprawdzania poprawności: co najmniej jeden niestandardowy składnik modułu sprawdzania poprawności może służyć do przetwarzania walidacji dla różnych formularzy na tej samej stronie lub w tym samym formularzu na różnych etapach przetwarzania formularzy (na przykład weryfikacji klienta po weryfikacji serwera).

Ręczna walidacja przy użyciu 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ą:

  • Walidacja adnotacji danych we właściwości Id nie powoduje niepowodzenia walidacji, gdy pole Id traci fokus. Walidacja jest wykonywana, gdy użytkownik wybierze przycisk Update.
  • Każda ręczna walidacja, którą chcesz wykonać w metodzie 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.
  • Formularz nie przetwarza metody 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

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:

  • EditContext Formularz jest parametrem kaskadowym składnika.
  • Po zainicjowaniu komponentu walidatora zostanie utworzony nowy element ValidationMessageStore, aby zachować bieżącą listę błędów formularza.
  • Magazyn komunikatów odbiera błędy, gdy kod dewelopera w składniku formularza wywołuje metodę 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.
  • Komunikaty są czyszczone po wystąpieniu któregokolwiek z następujących elementów:
    • Żądanie weryfikacji jest wymagane w EditContext momencie zgłoszenia OnValidationRequested zdarzenia. Wszystkie błędy zostały usunięte.
    • Pole zmienia się w formularzu po wywołaniu zdarzenia OnFieldChanged. Tylko błędy z pola są usuwane.
    • Metoda jest wywoływana 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.

Walidacja logiki biznesowej za pomocą komponentu walidatora

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:

  • Użyto skróconej wersji formularza 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.
  • Składnik CustomValidation z sekcji Komponenty walidatora tego artykułu jest używany.
  • Walidacja wymaga wartości opisu statku (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.

Walidacja serwera za pomocą komponentu walidatora

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.

  • Przetwarzaj weryfikację klienta w formularzu przy użyciu składnika DataAnnotationsValidator.
  • Gdy formularz przejdzie weryfikację klienta (OnValidSubmit zostanie wywołany), wyślij go do API serwera backendowego w celu przetwarzania formularza.
  • Walidacja modelu przetwarzania na serwerze.
  • Interfejs API serwera zawiera zarówno wbudowaną walidację adnotacji danych frameworku, jak i niestandardową logikę walidacji dostarczaną przez dewelopera. Jeśli weryfikacja zakończy się pomyślnie na serwerze, przetwórz formularz i wyślij z powrotem kod stanu powodzenia (200 - OK). Jeśli walidacja nie powiedzie się, zwróć kod stanu błędu (400 - Bad Request) i błędy walidacji pola.
  • Wyłącz formularz w przypadku powodzenia lub wyświetl błędy.

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:

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

  • Blazor WebAssembly Hostowane rozwiązanie utworzone na podstawie szablonuBlazor WebAssembly projektu. Takie podejście jest obsługiwane w przypadku dowolnego z bezpiecznych rozwiązań hostowanych opisanych w dokumentacji dotyczącej hostowanych zabezpieczeń BlazorBlazor WebAssembly.
  • Starship Model (Starship.cs) sekcji Przykładowy formularz artykułu Składniki wejściowe.
  • Składnik 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 (DefenseClassification).

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, ApiControllerApiControllerAttributezwykle 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:

InputText na podstawie zdarzenia wejściowego

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ładniki podsumowania weryfikacji i komunikatu weryfikacji

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;
}

Ustal, czy pole formularza jest prawidłowe

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);

Niestandardowe atrybuty walidacji

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 klasy CSS walidacji

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

  • Poprawnie dopasowane:fieldId.FieldName == "Name"
  • Nie udaje się dopasować:fieldId.FieldName == "name"
  • Nie można dopasować:fieldId.FieldName == "NAME"
  • Nie można dopasować: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 modifiedvalid 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:

  • Pole Name używa własnych stylów CSS do walidacji aplikacji.
  • Pole Description używa logiki podobnej do logiki Blazor i domyślnych stylów walidacji CSS pól Blazor.

Walidacja na poziomie klasy za pomocą polecenia IValidatableObject

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.

Blazor pakiet weryfikacji adnotacji danych

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.

Atrybut [CompareProperty]

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.Validationeksperymentalny wprowadza dodatkowy atrybut weryfikacji , ComparePropertyAttributektóry działa wokół tych ograniczeń. W aplikacji Blazor, [CompareProperty] jest bezpośrednim zamiennikiem dla atrybutu [Compare].

Zagnieżdżone modele, typy kolekcji i typy złożone

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 dostarczonego przez pakiet eksperymentalny:

<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; }
}

Włączanie przycisku przesyłania na podstawie weryfikacji formularza

Przykład pokazuje, jak włączyć i wyłączyć przycisk przesyłania w zależności od weryfikacji formularza.

  • Używa skróconej wersji wcześniejszego 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.
  • Używa formularza EditContext do przypisania modelu podczas inicjowania składnika.
  • Weryfikuje formularz w wywołaniu zwrotnym kontekstu OnFieldChanged , aby włączyć i wyłączyć przycisk przesyłania.
  • Implementuje IDisposable i anuluje subskrypcję programu obsługi zdarzeń w metodzie 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:

  • Nie używaj ValidationSummary składnika w formularzu.
  • Spraw, aby składnik ValidationSummary był widoczny po wybraniu przycisku 'Wyślij' (na przykład w metodzie 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";
    }
}