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 wersję tego artykułu platformy .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 platformy .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 wersję tego artykułu platformy .NET 9.

W tym artykule wyjaśniono, jak używać walidacji w Blazor formularzach.

Walidacja formularza

W podstawowych scenariuszach weryfikacji formularzy EditForm wystąpienie może używać zadeklarowanych EditContext wystąpień i ValidationMessageStore do sprawdzania poprawności pól formularza. Procedura obsługi zdarzenia OnValidationRequested EditContext wykonuje niestandardową logikę walidacji. Wynik programu obsługi aktualizuje ValidationMessageStore wystąpienie.

Podstawowa weryfikacja formularza jest przydatna w przypadkach, gdy model formularza jest zdefiniowany w składniku hostujący 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.

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 przyjmują statyczne SSR, są weryfikowane na serwerze po przesłaniu formularza.

W poniższym składniku HandleValidationRequested metoda obsługi czyści wszelkie istniejące komunikaty sprawdzania poprawności przez wywołanie ValidationMessageStore.Clear metody sprawdzania poprawności formularza.

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 struktury dla programu DataAnnotationsValidator są dostępne do inspekcji w źródle referencyjnym:

Uwaga

Linki dokumentacji do źródła referencyjnego platformy .NET zwykle ładują domyślną gałąź repozytorium, która odzwierciedla bieżące programowanie dla następnej wersji platformy .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 wyjmuje z pola. 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.

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:

Wbudowane moduły sprawdzania poprawności adnotacji danych nie są obsługiwane tylko [Remote] w atrybucie walidacji.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 aktywowane przy użyciu DataAnnotationsValidator składnika. 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 składnika modułu sprawdzania poprawności zostanie utworzony nowy ValidationMessageStore element w celu zachowania bieżącej listy 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 obiekcie 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 są czyszczone.
    • Pole zmienia się w formularzu po wystąpieniu OnFieldChanged zdarzenia. Tylko błędy dla pola są czyszczone.
    • Metoda jest wywoływana ClearErrors przez kod dewelopera. Wszystkie błędy są czyszczone.

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. Nie można określić przestrzeni nazw powoduje błąd kompilacji:

Tag helpers cannot target tag name '<global namespace>.{CLASS NAME}' because it contains a ' ' character.

Symbol {CLASS NAME} zastępczy to nazwa klasy składnika. 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 jest konieczne zaimplementowanie IDisposable i anulowanie subskrypcji delegatów zdarzeń w tym scenariuszu. Aby uzyskać więcej informacji, zobacz Cykl życia składników platformy ASP.NET Core Razor.

Walidacja logiki biznesowej ze składnikiem modułu sprawdzania poprawności

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ący 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.

W poniższym przykładzie:

  • Użyto skróconej Starfleet Starship Database wersji formularza (Starship3 składnika) sekcji Przykładowy formularz artykułu Składniki wejściowe, które akceptują tylko klasyfikację i opis starship. Walidacja adnotacji danych nie jest wyzwalana podczas przesyłania formularza, ponieważ DataAnnotationsValidator składnik nie jest uwzględniony w formularzu.
  • Używany CustomValidation jest składnik z sekcji Składniki modułu sprawdzania poprawności tego artykułu.
  • Walidacja wymaga wartości opisu statku (Description), jeśli użytkownik wybierze klasyfikację wysyłki "Defense" (Classification).

Po ustawieniu komunikatów sprawdzania poprawności w składniku są one dodawane do modułów sprawdzania poprawności ValidationMessageStore i wyświetlane w podsumowaniu EditFormweryfikacji.

Starship9.razor:

@page "/starship-9"
@inject ILogger<Starship9> Logger

<h1>Starfleet Starship Database</h1>

<h2>New Ship Entry Form</h2>

<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship9">
    <CustomValidation @ref="customValidation" />
    <ValidationSummary />
    <div>
        <label>
            Primary Classification:
            <InputSelect @bind-Value="Model!.Classification">
                <option value="">
                    Select classification ...
                </option>
                <option checked="@(Model!.Classification == "Exploration")" 
                    value="Exploration">
                    Exploration
                </option>
                <option checked="@(Model!.Classification == "Diplomacy")" 
                    value="Diplomacy">
                    Diplomacy
                </option>
                <option checked="@(Model!.Classification == "Defense")" 
                    value="Defense">
                    Defense
                </option>
            </InputSelect>
        </label>
    </div>
    <div>
        <label>
            Description (optional):
            <InputTextArea @bind-Value="Model!.Description" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@code {
    private CustomValidation? customValidation;

    [SupplyParameterFromForm]
    private Starship? Model { get; set; }

    protected override void OnInitialized() =>
        Model ??= new() { ProductionDate = DateTime.UtcNow };

    private void Submit()
    {
        customValidation?.ClearErrors();

        var errors = new Dictionary<string, List<string>>();

        if (Model!.Classification == "Defense" &&
                string.IsNullOrEmpty(Model.Description))
        {
            errors.Add(nameof(Model.Description),
                new() { "For a 'Defense' ship classification, " +
                "'Description' is required." });
        }

        if (errors.Any())
        {
            customValidation?.DisplayErrors(errors);
        }
        else
        {
            Logger.LogInformation("Submit called: Processing the form");
        }
    }
}
@page "/starship-9"
@inject ILogger<Starship9> Logger

<h1>Starfleet Starship Database</h1>

<h2>New Ship Entry Form</h2>

<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship9">
    <CustomValidation @ref="customValidation" />
    <ValidationSummary />
    <div>
        <label>
            Primary Classification:
            <InputSelect @bind-Value="Model!.Classification">
                <option value="">
                    Select classification ...
                </option>
                <option checked="@(Model!.Classification == "Exploration")" 
                    value="Exploration">
                    Exploration
                </option>
                <option checked="@(Model!.Classification == "Diplomacy")" 
                    value="Diplomacy">
                    Diplomacy
                </option>
                <option checked="@(Model!.Classification == "Defense")" 
                    value="Defense">
                    Defense
                </option>
            </InputSelect>
        </label>
    </div>
    <div>
        <label>
            Description (optional):
            <InputTextArea @bind-Value="Model!.Description" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@code {
    private CustomValidation? customValidation;

    [SupplyParameterFromForm]
    private Starship? Model { get; set; }

    protected override void OnInitialized() =>
        Model ??= new() { ProductionDate = DateTime.UtcNow };

    private void Submit()
    {
        customValidation?.ClearErrors();

        var errors = new Dictionary<string, List<string>>();

        if (Model!.Classification == "Defense" &&
                string.IsNullOrEmpty(Model.Description))
        {
            errors.Add(nameof(Model.Description),
                new() { "For a 'Defense' ship classification, " +
                "'Description' is required." });
        }

        if (errors.Any())
        {
            customValidation?.DisplayErrors(errors);
        }
        else
        {
            Logger.LogInformation("Submit called: Processing the form");
        }
    }
}
@page "/starship-9"
@inject ILogger<Starship9> Logger

<h1>Starfleet Starship Database</h1>

<h2>New Ship Entry Form</h2>

<EditForm Model="Model" OnValidSubmit="Submit">
    <CustomValidation @ref="customValidation" />
    <ValidationSummary />
    <div>
        <label>
            Primary Classification:
            <InputSelect @bind-Value="Model!.Classification">
                <option value="">Select classification ...</option>
                <option value="Exploration">Exploration</option>
                <option value="Diplomacy">Diplomacy</option>
                <option value="Defense">Defense</option>
            </InputSelect>
        </label>
    </div>
    <div>
        <label>
            Description (optional):
            <InputTextArea @bind-Value="Model!.Description" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@code {
    private CustomValidation? customValidation;

    public Starship? Model { get; set; }

    protected override void OnInitialized() =>
        Model ??= new() { ProductionDate = DateTime.UtcNow };

    private void Submit()
    {
        customValidation?.ClearErrors();

        var errors = new Dictionary<string, List<string>>();

        if (Model!.Classification == "Defense" &&
                string.IsNullOrEmpty(Model.Description))
        {
            errors.Add(nameof(Model.Description),
                new() { "For a 'Defense' ship classification, " +
                "'Description' is required." });
        }

        if (errors.Any())
        {
            customValidation?.DisplayErrors(errors);
        }
        else
        {
            Logger.LogInformation("Submit called: Processing the form");
        }
    }
}

Uwaga

Zamiast używania składników weryfikacji można użyć atrybutów weryfikacji adnotacji danych. Atrybuty niestandardowe zastosowane do modelu formularza aktywowane przy użyciu DataAnnotationsValidator składnika. 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 ze składnikiem modułu sprawdzania poprawności

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 weryfikacji klienta:

  • Przetwarzanie weryfikacji klienta w formularzu za DataAnnotationsValidator pomocą składnika.
  • Gdy formularz przejdzie weryfikację klienta (OnValidSubmit jest wywoływany), wyślij element do interfejsu EditContext.Model API serwera zaplecza na potrzeby przetwarzania formularzy.
  • Walidacja modelu przetwarzania na serwerze.
  • Interfejs API serwera zawiera zarówno wbudowane adnotacje danych platformy, 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ący 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:

  • Element Blazor Web App z interaktywnymi składnikami zestawu WebAssembly utworzonymi na podstawie szablonu Blazor Web Appprojektu.
  • Starship Model (Starship.cs) sekcji Przykładowy formularz artykułu Składniki wejściowe.
  • Składnik CustomValidation pokazany w sekcji Składniki modułu sprawdzania poprawności.

Umieść model (Starship.cs) w Starship projekcie biblioteki klas udostępnionych, 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 programu Blazor Web Appdodaj kontroler w celu przetwarzania żądań weryfikacji starship i zwracania komunikatów weryfikacji, które zakończyły się niepowodzeniem. 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 wartość jest podana dla opisu statku (Description), jeśli użytkownik wybierze klasyfikację Defense wysyłki (Classification).

  • Blazor WebAssemblyHostowane 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 Blazor Blazor WebAssembly zabezpieczeń.
  • Starship Model (Starship.cs) sekcji Przykładowy formularz artykułu Składniki wejściowe.
  • Składnik CustomValidation pokazany w sekcji Składniki modułu sprawdzania poprawności.

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 starship i zwracania komunikatów weryfikacji, które zakończyły się niepowodzeniem. Zaktualizuj przestrzenie nazw w ostatniej using instrukcji dla Shared projektu i namespace klasy kontrolera. Oprócz weryfikacji adnotacji danych klienta i serwera kontroler sprawdza, czy wartość jest podana dla opisu statku (Description), jeśli użytkownik wybierze klasyfikację Defense wysyłki (Classification).

Walidacja Defense klasyfikacji wysyłki odbywa się tylko na serwerze w kontrolerze, ponieważ zbliżający się formularz nie wykonuje tej samej weryfikacji 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 = 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ć weryfikację klienta formularza, aby zezwolić na przesyłanie pustego formularza pola 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. Jest to niewygodne, aby napisać kod dewelopera, aby przeanalizować plik. 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 na akcje, które są oznaczone ApiControllerAttribute w Program pliku. 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 ValidationProblemDetailselementu .

Microsoft.AspNetCore.Mvc Dodaj przestrzeń nazw na początku Program pliku w głównym projekcie pliku :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 punkty końcowe kontrolera mapy podczas umieszczania poprzedniego kodu, 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 ograniczenia ataków między witrynami (XSRF/CSRF). Jeśli używasz tylko programu AddControllers, antyforgery nie jest włączana automatycznie.

Aby uzyskać więcej informacji na temat odpowiedzi błędów błędu routingu i walidacji kontrolera, zobacz następujące zasoby:

W projekcie .Client dodaj CustomValidation składnik pokazany w sekcji Składniki modułu sprawdzania poprawności. Zaktualizuj przestrzeń nazw tak, namespace BlazorSample.Clientaby odpowiadała aplikacji (na przykład ).

W projekcie .Client formularz jest aktualizowany w Starfleet Starship Database celu wyświetlania błędów walidacji serwera z pomocą CustomValidation składnika. Gdy interfejs API serwera zwraca komunikaty sprawdzania poprawności, 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.

Microsoft.AspNetCore.Mvc Dodaj przestrzeń nazw na początku Program pliku w Server aplikacji:

using Microsoft.AspNetCore.Mvc;

Program W pliku znajdź metodę AddControllersWithViews rozszerzenia i dodaj następujące wywołanie do ConfigureApiBehaviorOptionspliku :

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 ograniczenia ataków między witrynami (XSRF/CSRF). Jeśli używasz tylko programu AddControllers, antyforgery nie jest włączana automatycznie.

W projekcie Client dodaj CustomValidation składnik pokazany w sekcji Składniki modułu sprawdzania poprawności. Zaktualizuj przestrzeń nazw tak, namespace BlazorSample.Clientaby odpowiadała aplikacji (na przykład ).

W projekcie Client formularz jest aktualizowany w Starfleet Starship Database celu wyświetlania błędów walidacji serwera z pomocą CustomValidation składnika. Gdy interfejs API serwera zwraca komunikaty sprawdzania poprawności, 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 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 automatycznym EditForm włączaniu obsługi ochrony przed fałszerzami. Kontroler powinien używać AddControllersWithViews do rejestrowania usług kontrolera i automatycznego włączania obsługi ochrony przed fałszerstwa 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 żądania HTTP POST na kontrolerze internetowego interfejsu API zaplecza. 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 (IWebAssemblyHostEnvironment.BaseAddress), który pobiera adres builder.HostEnvironment.BaseAddress podstawowy dla aplikacji i jest zwykle pobierany z <base> wartości tagu href 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 aktywowane przy użyciu DataAnnotationsValidator składnika. 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 sprawdzania poprawności serwera w tej sekcji jest odpowiednia dla dowolnego z przykładów hostowanych Blazor WebAssembly rozwiązań w tym zestawie dokumentacji:

InputText na podstawie zdarzenia wejściowego

InputText Użyj składnika , aby utworzyć składnik niestandardowy, który używa oninput zdarzenia (input) zamiast onchange zdarzenia (change). Użycie walidacji pola wyzwalaczy zdarzeń na każdym naciśnięciu input .

Poniższy CustomInputText składnik dziedziczy składnik platformy InputText i ustawia powiązanie zdarzeń na oninput zdarzenie (input).

CustomInputText.razor:

@inherits InputText

<input @attributes="AdditionalAttributes" 
       class="@CssClass" 
       @bind="CurrentValueAsString" 
       @bind:event="oninput" />

Składnik CustomInputText może być używany w dowolnym miejscu 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 zawiera podsumowanie wszystkich komunikatów sprawdzania poprawności, które są podobne do pomocnika tagu podsumowania weryfikacji:

<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 sprawdzania poprawności dla określonego pola, które jest podobne do Pomocnika tagu komunikatu weryfikacji. Określ pole do weryfikacji za pomocą atrybutu For i wyrażenia lambda nazewnictwa właściwości 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 <div> do wygenerowanego lub <ul> elementu.

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 MemberName weryfikacji podczas tworzenia .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.",
            new[] { validationContext.MemberName });
    }
}

Wstrzykiwanie usług do niestandardowych atrybutów walidacji za pomocą elementu ValidationContext. W poniższym przykładzie pokazano formularz szefa kuchni sałatki, który weryfikuje dane wejściowe użytkownika za pomocą wstrzykiwania zależności (DI).

Klasa SaladChef wskazuje zatwierdzoną listę składników starship dla sałatki Ten Forward.

SaladChef.cs:

namespace BlazorSample;

public class SaladChef
{
    public string[] SaladToppers = { "Horva", "Kanda Root", "Krintar", "Plomeek",
        "Syto Bean" };
}

Zarejestruj się SaladChef w kontenerze DI aplikacji w Program pliku:

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 składnik weryfikuje dane wejściowe użytkownika, stosując SaladChefValidatorAttribute element ([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 klasy CSS walidacji są przydatne podczas integrowania z platformami CSS, takimi jak Bootstrap.

Aby określić niestandardowe atrybuty klasy CSS walidacji, zacznij od udostępnienia stylów CSS na potrzeby walidacji niestandardowej. 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ą, FieldCssClassProvider która sprawdza komunikaty sprawdzania poprawności pól i stosuje odpowiedni prawidłowy lub nieprawidłowy styl.

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ę jako dostawcę CustomFieldClassProvider klas CSS pól w wystąpieniu formularza EditContext za pomocą polecenia 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, zastosuj CustomFieldClassProvider style warunkowo. CustomFieldClassProvider2 Poniższy przykład stosuje tylko styl do Name pola. W przypadku wszystkich pól, które nie pasują do Namenazwy , string.Empty jest zwracany i nie zastosowano żadnego stylu. Przy użyciu odbicia pole jest dopasowywane do właściwości lub pola elementu członkowskiego modelu, a nie id do jednostki HTML.

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 uwzględniane wielkości liter, więc element członkowski właściwości modelu oznaczony jako "Name" musi być zgodny z sprawdzaniem warunkowym w elemencie "Name":

  • Poprawne dopasowanie: fieldId.FieldName == "Name"
  • Nie można dopasować: fieldId.FieldName == "name"
  • Nie można dopasować: fieldId.FieldName == "NAME"
  • Nie można dopasować: fieldId.FieldName == "nAmE"

Dodaj dodatkową właściwość do Modelelementu , na przykład:

[StringLength(10, ErrorMessage = "Description is too long.")]
public string? Description { get; set; } 

Dodaj element Description do CustomValidationForm formularza składnika:

<InputText @bind-Value="Model!.Description" />

EditContext Zaktualizuj wystąpienie w metodzie składnikaOnInitialized, aby użyć nowego dostawcy klas CSS pól:

editContext?.SetFieldCssClassProvider(new CustomFieldClassProvider2());

Ponieważ klasa sprawdzania poprawności CSS nie jest stosowana do Description pola, nie jest stylizowany. 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 Blazordomyślnej logiki i używają Blazordomyślnych stylów walidacji CSS pól 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";
            }
        }
    }
}

EditContext Zaktualizuj wystąpienie w metodzie składnikaOnInitialized, aby użyć poprzedniego dostawcy klas CSS pól:

editContext.SetFieldCssClassProvider(new CustomFieldClassProvider3());

Za pomocą polecenia CustomFieldClassProvider3:

  • Pole Name używa niestandardowych stylów CSS walidacji aplikacji.
  • Pole Description używa logiki podobnej do Blazorlogiki i Blazordomyślnych stylów walidacji CSS pól.

Walidacja na poziomie klasy za pomocą polecenia IValidatableObject

Walidacja IValidatableObject na poziomie klasy (dokumentacja interfejsu 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

Jest Microsoft.AspNetCore.Components.DataAnnotations.Validation to pakiet, który wypełnia luki w sprawdzaniu DataAnnotationsValidator poprawności przy użyciu składnika. Pakiet jest obecnie eksperymentalny.

Ostrzeżenie

Pakiet Microsoft.AspNetCore.Components.DataAnnotations.Validation ma najnowszą wersję kandydata do wydania w NuGet.org. W tej chwili kontynuuj korzystanie z eksperymentalnego pakietu release candidate. Funkcje eksperymentalne są udostępniane w celu eksplorowania możliwości funkcji i mogą nie być dostarczane w stabilnej wersji. Zapoznaj się z repozytorium GitHub Anonss,dotnet/aspnetcore repozytorium GitHub lub sekcją tego tematu, aby uzyskać dalsze aktualizacje.

Atrybut [CompareProperty]

Element CompareAttribute nie działa dobrze z składnikiem DataAnnotationsValidator , ponieważ DataAnnotationsValidator element nie kojarzy wyniku weryfikacji z określonym elementem członkowskim. Może to spowodować niespójne zachowanie między weryfikacją na poziomie pola a weryfikacją całego modelu w przesłaniu. Pakiet Microsoft.AspNetCore.Components.DataAnnotations.Validation eksperymentalny wprowadza dodatkowy atrybut weryfikacji , ComparePropertyAttributektóry działa wokół tych ograniczeń. W aplikacji [CompareProperty] jest bezpośrednim zamiennikiem atrybutu[Compare].Blazor

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 wbudowanym elementem 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 sprawdzić poprawność całego grafu obiektów powiązanego modelu, w tym właściwości kolekcji i typu złożonego, użyj ObjectGraphDataAnnotationsValidator elementu dostarczonego przez pakiet eksperymentalnyMicrosoft.AspNetCore.Components.DataAnnotations.Validation:

<EditForm ...>
    <ObjectGraphDataAnnotationsValidator />
    ...
</EditForm>

Dodawanie adnotacji do właściwości modelu za pomocą polecenia [ValidateComplexType]. W następujących klasach ShipDescription modelu klasa zawiera dodatkowe adnotacje danych w celu zweryfikowania, kiedy 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

Aby włączyć i wyłączyć przycisk przesyłania na podstawie weryfikacji formularza, w poniższym przykładzie:

  • Używa skróconej wersji wcześniejszego Starfleet Starship Database formularza (Starship3składnika) sekcji Przykładowy formularz artykułu Składniki wejściowe, które akceptują tylko wartość identyfikatora wysyłki. Inne Starship właściwości otrzymują prawidłowe wartości domyślne po utworzeniu Starship wystąpienia typu.
  • 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 Cykl życia składników platformy ASP.NET Core Razor.

Uwaga

Podczas przypisywania do elementu EditForm.EditContextnie 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 jest wstępnie załadowany z prawidłowymi wartościami i chcesz wyłączyć Submit przycisk podczas ładowania formularza, ustaw wartość formInvalid 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.
  • Ustaw składnik jako widoczny po wybraniu ValidationSummary przycisku przesyłania (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";
    }
}