Dela via


validering av ASP.NET Core Blazor-formulär

Observera

Det här är inte den senaste versionen av den här artikeln. Den aktuella versionen finns i .NET 9-versionen av denna artikel.

Varning

Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i .NET och .NET Core Support Policy. Den aktuella versionen finns i .NET 9-versionen av denna artikel.

Viktig

Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.

Den aktuella versionen finns i .NET 9-versionen av denna artikel.

Den här artikeln beskriver hur du använder validering i Blazor formulär.

Formulärverifiering

I grundläggande formulärvalideringsscenarier kan en EditForm-instans använda deklarerade EditContext och ValidationMessageStore instanser för att verifiera formulärfält. En hanterare för händelsen OnValidationRequested för EditContext utför anpassad valideringslogik. Handlerns resultat uppdaterar ValidationMessageStore-instansen.

Grundläggande formulärverifiering är användbart i fall där formulärets modell definieras i komponenten som är värd för formuläret, antingen som medlemmar direkt på komponenten eller i en underklass. Användning av en validatorkomponent rekommenderas där en oberoende modellklass används för flera komponenter.

I Blazor Web Appkräver validering på klientsidan en aktiv BlazorSignalR krets. Validering på klientsidan är inte tillgängligt för formulär i komponenter som har antagit statisk återgivning på serversidan (statisk SSR). Formulär som antar statisk SSR verifieras på servern när formuläret har skickats.

I följande komponent rensar HandleValidationRequested-hanteringsmetoden alla befintliga valideringsmeddelanden genom att anropa ValidationMessageStore.Clear innan formuläret verifieras.

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

Valideringskomponent för dataanteckningar och anpassad validering

Komponenten DataAnnotationsValidator kopplar validering av datamärkningar till en kaskaderande EditContext. För att aktivera validering av dataanteckningar krävs DataAnnotationsValidator komponenten. Om du vill använda ett annat valideringssystem än dataanteckningar använder du en anpassad implementering i stället för komponenten DataAnnotationsValidator. Ramverksimplementeringarna för DataAnnotationsValidator är tillgängliga för inspektion i referenskällan:

Mer information om valideringsbeteende finns i avsnittet om verifieringsbeteendeDataAnnotationsValidator.

Om du behöver aktivera valideringsstöd för dataanteckningar för en EditContext i kod anropar du EnableDataAnnotationsValidation med en inmatad IServiceProvider (@inject IServiceProvider ServiceProvider) på EditContext. Ett avancerat exempel finns i komponenten NotifyPropertyChangedValidationComponent i ASP.NET Core Blazor Frameworks BasicTestApp (dotnet/aspnetcore GitHub-lagringsplats). I en produktionsversion av exemplet ersätter du argumentet new TestServiceProvider() för tjänstleverantören med en inmatad IServiceProvider.

Observera

Dokumentationslänkar till .NET-referenskällan läser vanligtvis in lagringsplatsens standardgren, vilket representerar den aktuella utvecklingen för nästa version av .NET. Om du vill välja en tagg för en specifik utgåva använder du listrutan Växla grenar eller taggar. Mer information finns i Så här väljer du en versionstagg för ASP.NET Core-källkod (dotnet/AspNetCore.Docs #26205).

Blazor utför två typer av validering:

  • Fältverifiering utförs när användaren flikar ut ur ett fält. Under fältverifiering associerar DataAnnotationsValidator-komponenten alla rapporterade valideringsresultat med fältet.
  • Modellverifiering utförs när användaren skickar formuläret. Under modellverifieringen försöker DataAnnotationsValidator-komponenten fastställa fältet baserat på det medlemsnamn som verifieringsresultatet rapporterar. Valideringsresultat som inte är associerade med en enskild medlem är associerade med modellen i stället för ett fält.

I anpassade valideringsscenarier:

Det finns två allmänna metoder för att uppnå anpassad validering, som beskrivs i följande två avsnitt i den här artikeln:

  • Manuell validering med hjälp av OnValidationRequested händelse: Verifiera ett formulärs fält manuellt med validering av dataanteckningar och anpassad kod för fältkontroller när validering begärs via en händelsehanterare som tilldelats OnValidationRequested händelsen.
  • Validatorkomponenter: En eller flera anpassade valideringskomponenter kan användas för att bearbeta validering för olika formulär på samma sida eller samma formulär vid olika steg i formulärbearbetningen (till exempel klientverifiering följt av serververifiering).

Manuell validering med hjälp av händelsen OnValidationRequested

Du kan verifiera ett formulär manuellt med en anpassad händelsehanterare tilldelad till händelsen EditContext.OnValidationRequested för att hantera en ValidationMessageStore.

Det Blazor-ramverket tillhandahåller DataAnnotationsValidator-komponenten för att bifoga ytterligare valideringsstöd till formulär baserat på valideringsattribut (dataanoteringar).

Med det tidigare Starship8 komponentexemplet tilldelas HandleValidationRequested-metoden till OnValidationRequested, där du kan utföra manuell validering i C#-kod. Några ändringar visar hur du kombinerar den befintliga manuella valideringen med validering av dataanteckningar via en DataAnnotationsValidator och ett valideringsattribut som tillämpas på Holodeck modellen.

Referera till System.ComponentModel.DataAnnotations namnområdet i komponentens Razor-direktiv överst i komponentdefinitionsfilen:

@using System.ComponentModel.DataAnnotations

Lägg till en Id-egenskap i Holodeck-modellen med ett valideringsattribut för att begränsa strängens längd till sex tecken:

[StringLength(6)]
public string? Id { get; set; }

Lägg till en DataAnnotationsValidator komponent (<DataAnnotationsValidator />) i formuläret. Vanligtvis placeras komponenten direkt under taggen <EditForm>, men du kan placera den var som helst i formuläret:

<DataAnnotationsValidator />

Ändra formulärets sändningsbeteende i taggen <EditForm> från OnSubmit till OnValidSubmit, vilket säkerställer att formuläret är giltigt innan du kör den tilldelade händelsehanterarmetoden:

- OnSubmit="Submit"
+ OnValidSubmit="Submit"

I <EditForm>lägger du till ett fält för egenskapen Id:

<div>
    <label>
        <InputText @bind-Value="Model!.Id" />
        ID (6 characters max)
    </label>
    <ValidationMessage For="() => Model!.Id" />
</div>

När du har gjort föregående ändringar matchar formulärets beteende följande specifikation:

  • Valideringen av dataanteckningar på egenskapen Id utlöser inte ett valideringsfel när fältet Id bara förlorar fokus. Verifieringen körs när användaren väljer knappen Update.
  • Manuell validering som du vill utföra i den HandleValidationRequested metod som tilldelats formulärets OnValidationRequested händelse körs när användaren väljer formulärets Update-knapp. I den befintliga koden i Starship8 komponentexemplet måste användaren markera antingen eller båda kryssrutorna för att verifiera formuläret.
  • Formuläret bearbetar inte metoden Submit förrän både datavalideringarna och den manuella valideringen är godkända.

Validatorkomponenter

Valideringskomponenter stöder formulärverifiering genom att hantera en ValidationMessageStore för ett formulärs EditContext.

Det Blazor-ramverket tillhandahåller DataAnnotationsValidator-komponenten för att lägga till valideringsstöd till formulär baserat på valideringsattribut (datakommentarer). Du kan skapa anpassade valideringskomponenter för att bearbeta valideringsmeddelanden för olika formulär på samma sida eller samma formulär i olika steg i formulärbearbetningen (till exempel klientverifiering följt av servervalidering). Validatorkomponentexemplet som visas i det här avsnittet, CustomValidation, används i följande avsnitt i den här artikeln:

Av de inbyggda datakommentarernastöds endast [Remote] valideringsattributet inte i Blazor.

Observera

Valideringsattribut för anpassad dataanteckning kan användas i stället för anpassade valideringskomponenter i många fall. Anpassade attribut som tillämpas på formulärets modell aktiveras med hjälp av komponenten DataAnnotationsValidator. När de används med serververifiering måste alla anpassade attribut som tillämpas på modellen vara körbara på servern. Mer information finns i avsnittet Anpassade valideringsattribut.

Skapa en valideringskomponent från ComponentBase:

  • Formulärets EditContext är en kaskadparameter till komponenten.
  • När valideringskomponenten initieras skapas en ny ValidationMessageStore för att upprätthålla en aktuell lista över formulärfel.
  • Meddelandearkivet får fel när utvecklarkod i formulärets komponent anropar metoden DisplayErrors. Felen skickas till metoden DisplayErrors i en Dictionary<string, List<string>>. I ordlistan är nyckeln namnet på det formulärfält som har ett eller flera fel. Värdet är listan med fel.
  • Meddelanden rensas när något av följande har inträffat:
    • Validering begärs på EditContext när händelsen OnValidationRequested aktiveras. Alla fel rensas.
    • Ett fält ändras i formuläret när händelsen OnFieldChanged utlöses. Endast felen för fältet rensas.
    • Metoden ClearErrors anropas av utvecklarkoden. Alla fel rensas.

Uppdatera namnområdet i följande klass så att det matchar appens namnområde.

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

Viktig

Att ange ett namnområde krävs när du härleder från ComponentBase. Om du inte anger ett namnområde resulterar det i ett build-fel:

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

Platshållaren {CLASS NAME} är namnet på komponentklassen. Det anpassade valideringsexemplet i det här avsnittet anger exempelnamnområdet BlazorSample.

Observera

Anonyma lambda-uttryck är registrerade händelsehanterare för OnValidationRequested och OnFieldChanged i föregående exempel. Det är inte nödvändigt att implementera IDisposable och avregistrera händelsedelegaterna i det här scenariot. Mer information finns i ASP.NET Core Razor komponenthantering.

Validering av affärslogik med en valideringskomponent

För allmän validering av affärslogik använder du en valideringskomponent som tar emot formulärfel i en ordlista.

Grundläggande validering är användbart i fall där formulärets modell definieras i komponenten som är värd för formuläret, antingen som medlemmar direkt på komponenten eller i en underklass. Användning av en valideringskomponent rekommenderas där en oberoende modellklass används för flera komponenter.

I följande exempel:

  • En förkortad version av Starfleet Starship Database-formuläret (Starship3-komponent) av Exempelformulärs-avsnitt av Input-komponenters-artikel används, som endast accepterar rymdskeppets klassificering och beskrivning. Verifiering av dataanteckning utlöses inte vid formuläröverföring eftersom den DataAnnotationsValidator komponenten inte ingår i formuläret.
  • Den CustomValidation komponenten från Validator-komponenterna avsnittet i den här artikeln används.
  • Verifieringen kräver ett värde för fartygets beskrivning (Description) om användaren väljer "Defense" ship classification (Classification).

När valideringsmeddelanden anges i komponenten läggs de till i validerarens ValidationMessageStore och visas i EditFormvalideringssammanfattning.

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

Observera

Som ett alternativ till att använda valideringskomponenterkan valideringsattribut för dataanteckning användas. Anpassade attribut som tillämpas på formulärets modell aktiveras med hjälp av komponenten DataAnnotationsValidator. När de används med serververifiering måste attributen vara körbara på servern. Mer information finns i avsnittet Anpassade valideringsattribut.

Serververifiering med en valideringskomponent

Det här avsnittet fokuserar på Blazor Web App scenarier, men metoden för alla typer av appar som använder serververifiering med webb-API använder samma allmänna metod.

Det här avsnittet fokuserar på värdbaserade Blazor WebAssembly scenarier, men metoden för alla typer av appar som använder serververifiering med webb-API använder samma allmänna metod.

Serververifiering stöds utöver klientverifiering:

  • Bearbeta klientvalidering i formuläret med komponenten DataAnnotationsValidator.
  • När formuläret godkänns av klientverifieringen (OnValidSubmit anropas) skickar du EditContext.Model till ett server-API för formulärbearbetning.
  • Processmodellvalidering på servern.
  • Server-API:et innehåller både den inbyggda ramverksdataanteckningsverifieringen och den anpassade valideringslogik som tillhandahålls av utvecklaren. Om valideringen skickas på servern bearbetar du formuläret och skickar tillbaka en statuskod för lyckad status (200 - OK). Om verifieringen misslyckas returnerar du en felstatuskod (400 - Bad Request) och fältverifieringsfelen.
  • Inaktivera formuläret vid lyckat resultat eller visa felen.

Grundläggande validering är användbart i fall där formulärets modell definieras i komponenten som är värd för formuläret, antingen som medlemmar direkt på komponenten eller i en underklass. Användning av en valideringskomponent rekommenderas där en oberoende modellklass används för flera komponenter.

Följande exempel baseras på:

  • En Blazor Web App med interaktiva WebAssembly-komponenter som skapats från Blazor Web App-projektmallen.
  • Starship-modellen (Starship.cs) av -exempelformulärets-sektion i artikeln om Indatakomponenter .
  • Komponenten CustomValidation visas i avsnittet i Validator-komponenter.

Placera Starship-modellen (Starship.cs) i ett delat klassbiblioteksprojekt så att både klient- och serverprojekten kan använda modellen. Lägg till eller uppdatera namnområdet så att det matchar namnområdet för den delade appen (till exempel namespace BlazorSample.Shared). Eftersom modellen kräver dataanteckningar kontrollerar du att det delade klassbiblioteket använder det delade ramverket eller lägger till System.ComponentModel.Annotations-paketet i det delade projektet.

Observera

Mer information om hur du lägger till paket i .NET-appar finns i artiklarna under Installera och hantera paketArbetsflöde för paketförbrukning (NuGet-dokumentation). Bekräfta rätt paketversioner på NuGet.org.

I huvudprojektet för Blazor Web Applägger du till en kontrollant för att bearbeta valideringsbegäranden för rymdskepp och returnera misslyckade valideringsmeddelanden. Uppdatera namnrymderna i den senaste using-instruktionen för det delade klassbiblioteksprojektet och namespace för kontrollantklassen. Förutom validering av klient- och serverdatakommentarer kontrollerar kontrollanten att ett värde anges för fartygets beskrivning (Description) om användaren väljer Defense-fartygsklassificering (Classification).

Placera Starship-modellen (Starship.cs) i lösningens Shared projekt så att både klient- och serverapparna kan använda modellen. Lägg till eller uppdatera namnområdet så att det matchar namnområdet för den delade appen (till exempel namespace BlazorSample.Shared). Eftersom modellen kräver dataanteckningar lägger du till System.ComponentModel.Annotations-paketet i Shared-projektet.

Observera

Mer information om hur du lägger till paket i .NET-appar finns i artiklarna under Installera och hantera paketArbetsflöde för paketförbrukning (NuGet-dokumentation). Bekräfta rätt paketversioner på NuGet.org.

I Server-projektet lägger du till en kontrollant för att bearbeta valideringsbegäranden för rymdskepp och returnera misslyckade valideringsmeddelanden. Uppdatera namnrymderna i den senaste using-instruktionen för Shared-projektet och namespace för kontrollantklassen. Förutom validering av klient- och serverdatakommentarer kontrollerar kontrollanten att ett värde anges för fartygets beskrivning (Description) om användaren väljer Defense-fartygsklassificering (Classification).

Verifieringen för Defense-skeppklassificering sker endast på servern i kontrollern eftersom det kommande formuläret inte utför samma validering på klientsidan när formuläret skickas till servern. Servervalidering utan klientverifiering är vanligt i appar som kräver privat affärslogikvalidering av användarindata på servern. Privat information från data som lagras för en användare kan till exempel krävas för att verifiera användarindata. Privata data kan uppenbarligen inte skickas till klienten för klientverifiering.

Observera

Den StarshipValidation kontrollanten i det här avsnittet använder Microsoft Identity 2.0. Webb-API:et accepterar endast token för användare som har omfånget "API.Access" för det här API:et. Ytterligare anpassning krävs om API:ets omfångsnamn skiljer sig från API.Access.

Mer information om säkerhet finns i:

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

Bekräfta eller uppdatera namnområdet för den föregående kontrollanten (BlazorSample.Server.Controllers) för att matcha appens namnområde för kontrollanter.

När ett valideringsfel för modellbindning inträffar på servern returnerar en ApiController (ApiControllerAttribute) normalt ett standardsvar för felaktig begäran med en ValidationProblemDetails. Svaret innehåller mer data än bara valideringsfelen, vilket visas i följande exempel när alla fält i Starfleet Starship Database-formuläret inte skickas och formuläret misslyckas med valideringen:

{
  "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)." ]
  }
}

Observera

För att demonstrera föregående JSON-svar måste du antingen inaktivera formulärets klientvalidering för att tillåta att tomma fältformulär skickas eller använda ett verktyg för att skicka en begäran direkt till server-API:et, till exempel Firefox Browser Developer.

Om server-API:et returnerar det föregående JSON-standardsvaret är det möjligt för klienten att parsa svaret i kod skriven av utvecklare för att hämta underordnade till errors-noden för behandling av valideringsfel i formulär. Det är obekvämt att skriva utvecklarkod för att parsa filen. För att parsa JSON manuellt krävs det att du skapar en Dictionary<string, List<string>> av fel när du har anropat ReadFromJsonAsync. Helst bör server-API:et endast returnera valideringsfelen, vilket visas i följande exempel:

{
  "Id": [ "The Id field is required." ],
  "Classification": [ "The Classification field is required." ],
  "IsValidatedDesign": [ "This form disallows unapproved ships." ],
  "MaximumAccommodation": [ "Accommodation invalid (1-100000)." ]
}

Om du vill ändra server-API:ets svar så att det bara returnerar valideringsfelen ändrar du det ombud som anropas för åtgärder som kommenteras med ApiControllerAttribute i Program-filen. För API-slutpunkten (/StarshipValidation) returnerar du en BadRequestObjectResult med ModelStateDictionary. För andra API-slutpunkter bevarar du standardbeteendet genom att returnera objektresultatet med en ny ValidationProblemDetails.

Lägg till namnområdet Microsoft.AspNetCore.Mvc överst i filen Program i huvudprojektet för Blazor Web App:

using Microsoft.AspNetCore.Mvc;

I filen Program lägger du till eller uppdaterar följande AddControllersWithViews tilläggsmetod och lägger till följande anrop i 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));
            }
        };
    });

Om du lägger till kontrollörer i huvudprojektet för Blazor Web App för första gången, mappa kontrollörernas slutpunkter när du placerar den föregående koden som registrerar tjänster för kontrollörer. I följande exempel används standardstyrenhetsvägar:

app.MapDefaultControllerRoute();

Observera

I föregående exempel registreras explicit kontrollanttjänster genom att anropa AddControllersWithViews för att automatiskt minimera XSRF-/CSRF-attacker (Cross-Site Request Forgery). Om du bara använder AddControllersaktiveras inte anti-förfalskning automatiskt.

Mer information om kontrollantroutning och svar vid verifieringsfel finns i följande resurser:

I .Client-projektet lägger du till komponenten CustomValidation som visas i avsnittet Validator-komponenter. Uppdatera namnområdet så att det matchar appen (till exempel namespace BlazorSample.Client).

I .Client-projektet uppdateras formuläret Starfleet Starship Database för att visa serververifieringsfel med hjälp av komponenten CustomValidation. När server-API:et returnerar valideringsmeddelanden läggs de till i CustomValidation komponentens ValidationMessageStore. Felen är tillgängliga i formulärets EditContext för visning av formulärets valideringssammanfattning.

I följande komponent uppdaterar du namnområdet för det delade projektet (@using BlazorSample.Shared) till det delade projektets namnområde. Observera att formuläret kräver auktorisering, så användaren måste vara inloggad i appen för att navigera till formuläret.

Lägg till Microsoft.AspNetCore.Mvc-namnområdet överst i Program-filen i Server-appen:

using Microsoft.AspNetCore.Mvc;

Leta upp Program-tilläggsmetoden i filen AddControllersWithViews och lägg till följande anrop i 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));
            }
        };
    });

Observera

I föregående exempel registreras explicit kontrollanttjänster genom att anropa AddControllersWithViews för att automatiskt minimera XSRF-/CSRF-attacker (Cross-Site Request Forgery). Om du bara använder AddControllersaktiveras inte anti-förfalskning automatiskt.

I Client-projektet lägger du till komponenten CustomValidation som visas i avsnittet Validator-komponenter. Uppdatera namnområdet så att det matchar appen (till exempel namespace BlazorSample.Client).

I Client-projektet uppdateras formuläret Starfleet Starship Database för att visa serververifieringsfel med hjälp av komponenten CustomValidation. När server-API:et returnerar valideringsmeddelanden läggs de till i CustomValidation komponentens ValidationMessageStore. Felen är tillgängliga i formulärets EditContext för visning av formulärets valideringssammanfattning.

I följande komponent uppdaterar du namnområdet för Shared-projektet (@using BlazorSample.Shared) till det delade projektets namnområde. Observera att formuläret kräver auktorisering, så användaren måste vara inloggad i appen för att navigera till formuläret.

Starship10.razor:

Observera

Formulär baserade på EditForm aktiverar automatiskt antiforgery-stöd. Kontrollanten bör använda AddControllersWithViews för att registrera kontrollanttjänster och automatiskt aktivera stöd mot förfalskning för webb-API:et.

@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
        {
            using 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.";
        }
    }
}

Det .Client projektet för en Blazor Web App måste också registrera en HttpClient för HTTP POST-begäranden till en backend-webb-API-kontroller. Bekräfta eller lägg till följande i .Client-projektets Program-fil:

builder.Services.AddScoped(sp => 
    new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

I föregående exempel anges basadressen med builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress), som hämtar basadressen för appen och vanligtvis härleds från <base> taggens href värde på värdsidan.

@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
        {
            using 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.";
        }
    }
}

Observera

Som ett alternativ till användningen av en valideringskomponentkan valideringsattribut för dataanteckning användas. Anpassade attribut som tillämpas på formulärets modell aktiveras med hjälp av komponenten DataAnnotationsValidator. När de används med serververifiering måste attributen vara körbara på servern. Mer information finns i avsnittet Anpassade valideringsattribut.

Observera

Metoden för serververifiering i det här avsnittet är lämplig för någon av de värdbaserade Blazor WebAssembly lösningsexempel i den här dokumentationsuppsättningen:

InputText baserat på inmatningshändelsen

Använd komponenten InputText för att skapa en anpassad komponent som använder händelsen oninput (input) i stället för händelsen onchange (change). Användning av input händelsen utlöser fältvalidering på varje tangenttryckning.

Följande CustomInputText komponent ärver ramverkets InputText komponent och anger händelsebindning till händelsen oninput (input).

CustomInputText.razor:

@inherits InputText

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

Komponenten CustomInputText kan användas var som helst InputText används. Följande komponent använder den delade komponenten 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; }
    }
}

Komponenter för valideringssammanfattning och valideringsmeddelande

Komponenten ValidationSummary sammanfattar alla valideringsmeddelanden, vilket liknar Valideringssammanfattningens Tag Helper:

<ValidationSummary />

Skicka valideringsmeddelanden för en specifik modell med parametern Model:

<ValidationSummary Model="Model" />

Komponenten ValidationMessage<TValue> visar valideringsmeddelanden för ett visst fält, vilket liknar hjälpverktyget för verifieringsmeddelande. Ange fältet för validering med attributet For och ett lambda-uttryck som namnger modellegenskapen:

<ValidationMessage For="@(() => Model!.MaximumAccommodation)" />

Komponenterna ValidationMessage<TValue> och ValidationSummary stöder godtyckliga attribut. Alla attribut som inte matchar en komponentparameter läggs till i det genererade <div>- eller <ul>-elementet.

Kontrollera formatet för valideringsmeddelanden i appens formatmall (wwwroot/css/app.css eller wwwroot/css/site.css). Standardklassen validation-message anger textfärgen för valideringsmeddelanden till röd:

.validation-message {
    color: red;
}

Kontrollera om ett formulärfält är giltigt

Använd EditContext.IsValid för att avgöra om ett fält är giltigt utan att hämta valideringsmeddelanden.

stöds, men rekommenderas inte:

var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();

✔️ rekommenderas:

var isValid = editContext.IsValid(fieldIdentifier);

Anpassade valideringsattribut

För att säkerställa att ett valideringsresultat är korrekt associerat med ett fält när du använder ett anpassat valideringsattributskickar du valideringskontextens MemberName när du skapar 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 });
    }
}

Injicera tjänster i anpassade valideringsattribut via ValidationContext. I följande exempel visas ett salladskockformulär som validerar användarindata med beroendeinmatning (DI).

Klassen SaladChef anger den godkända starship-ingredienslistan för en Ten Forward-sallad.

SaladChef.cs:

namespace BlazorSample;

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

Registrera SaladChef i appens DI-container i filen Program:

builder.Services.AddTransient<SaladChef>();

Metoden IsValid i följande SaladChefValidatorAttribute-klass hämtar SaladChef-tjänsten från DI för att kontrollera användarens indata.

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

Följande komponent validerar användarindata genom att tillämpa SaladChefValidatorAttribute ([SaladChefValidator]) på salladsingredienssträngen (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);
}

CSS-klassattribut för anpassad validering

CsS-klassattribut för anpassad validering är användbara när du integrerar med CSS-ramverk, till exempel Bootstrap-.

Om du vill ange css-klassattribut för anpassad validering börjar du med att ange CSS-format för anpassad validering. I följande exempel anges giltiga (validField) och ogiltiga (invalidField) stilar.

Lägg till följande CSS-klasser i appens formatmall:

.validField {
    border-color: lawngreen;
}

.invalidField {
    background-color: tomato;
}

Skapa en klass som härleds från FieldCssClassProvider som söker efter fältverifieringsmeddelanden och tillämpar lämpligt giltigt eller ogiltigt format.

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

Ange klassen CustomFieldClassProvider som CSS-klassprovider i formulärets EditContext-instans med 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; }
    }
}

Föregående exempel kontrollerar giltigheten för alla formulärfält och tillämpar ett format på varje fält. Om formuläret bara ska använda anpassade formatmallar för en delmängd av fälten gör du CustomFieldClassProvider tillämpa formatmallar villkorsstyrt. Följande CustomFieldClassProvider2 exempel använder bara ett format för fältet Name. För fält med namn som inte matchar Namereturneras string.Empty och inget format används. Med reflektionmatchas fältet med modellmedlemmens egenskap eller fältnamn, inte en id som tilldelats HTML-entiteten.

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

Observera

Att matcha fältnamnet i föregående exempel är skiftlägeskänsligt, så en fältmedlem med namnet "Name" måste matcha en villkorskontroll på "Name":

  • stämmer överens med:fieldId.FieldName == "Name"
  • kan inte matcha:fieldId.FieldName == "name"
  • kan inte matcha:fieldId.FieldName == "NAME"
  • kan inte matcha:fieldId.FieldName == "nAmE"

Lägg till ytterligare en egenskap i Model, till exempel:

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

Lägg till Description i CustomValidationForm-komponentens formulär:

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

Uppdatera EditContext-instansen i komponentens OnInitialized-metod för att använda den nya CSS-klassprovidern för fält:

editContext?.SetFieldCssClassProvider(new CustomFieldClassProvider2());

Eftersom en CSS-valideringsklass inte tillämpas på fältet Description är den inte formaterad. Fältvalidering körs dock normalt. Om fler än 10 tecken anges anger valideringssammanfattningen felet:

Beskrivningen är för lång.

I följande exempel:

  • Det anpassade CSS-formatet tillämpas på fältet Name.

  • Andra fält använder logik som liknar standardlogik för Blazoroch använder Blazorcss-valideringsformat för standardfält, modified med valid eller invalid. Observera att för standardformaten behöver du inte lägga till dem i appens formatmall om appen baseras på en Blazor projektmall. För appar som inte baseras på en Blazor projektmall kan standardformaten läggas till i appens formatmall:

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

Uppdatera EditContext-instansen i komponentens OnInitialized-metod för att använda den föregående CSS-klassprovidern för fält:

editContext.SetFieldCssClassProvider(new CustomFieldClassProvider3());

Använda CustomFieldClassProvider3:

  • Fältet Name använder appens anpassade CSS-format för validering.
  • Fältet Description använder logik som liknar Blazorlogik och Blazorcss-valideringsformat för standardfält.

Validering på klassnivå med IValidatableObject

validering på klassnivå med IValidatableObject (API-dokumentation) stöds för Blazor formulärmodeller. IValidatableObject verifieringen körs bara när formuläret skickas och endast om alla andra verifieringar lyckas.

valideringspaket för Blazor-dataanteckningar

Observera

PaketetMicrosoft.AspNetCore.Components.DataAnnotations.Validation rekommenderas inte längre för appar som är avsedda för .NET 10 eller senare. Mer information finns i avsnittet Kapslade objekt, samlingstyper och komplexa typer .

PaketetMicrosoft.AspNetCore.Components.DataAnnotations.Validation fyller valideringsupplevelsens luckor med hjälp av komponentenDataAnnotationsValidator. Paketet är för närvarande experimentell.

Varning

PaketetMicrosoft.AspNetCore.Components.DataAnnotations.Validation har en senaste version av versionskandidatenpå NuGet.org. Fortsätt att använda det experimentella versionskandidatpaketet just nu. Experimentella funktioner tillhandahålls för att utforska funktionskraft och kanske inte levereras i en stabil version. Titta på GitHub-lagringsplatsen för Meddelanden , GitHub-lagringsplatsen dotnet/aspnetcoreeller det här avsnittet för ytterligare uppdateringar.

[CompareProperty] attribut

CompareAttribute fungerar inte bra med komponenten DataAnnotationsValidator eftersom DataAnnotationsValidator inte associerar valideringsresultatet med en viss medlem. Detta kan resultera i inkonsekvent beteende mellan validering på fältnivå och när hela modellen verifieras vid en sändning. Det Microsoft.AspNetCore.Components.DataAnnotations.Validationexperimentella paketet introducerar ytterligare ett valideringsattribut, ComparePropertyAttribute, som fungerar kring dessa begränsningar. I en Blazor app ersätter [CompareProperty] direkt attributet [Compare].

Använda valideringsmodeller från en annan sammansättning

För modellverifiering som definierats i en annan sammansättning, till exempel ett bibliotek eller .Client projektet för en Blazor Web App:

  • Om biblioteket är ett vanligt klassbibliotek (det är inte baserat på Microsoft.NET.Sdk.Web eller Microsoft.NET.Sdk.Razor SDK:er) lägger du till en paketreferens till biblioteket för Microsoft.Extensions.Validation NuGet-paketet.
  • Skapa en metod i biblioteket eller .Client projektet som tar emot en IServiceCollection instans som ett argument och anropar AddValidation den.
  • I appen anropar du både -metoden och AddValidation.

Föregående metod resulterar i validering av typerna från båda sammansättningarna.

I följande exempel skapas AddValidationForTypesInClient-metoden för .Client-projektet i en Blazor Web App för validering med hjälp av typer som definierats i .Client-projektet.

ServiceCollectionExtensions.cs (i .Client projektet):

namespace BlazorSample.Client.Extensions;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddValidationForTypesInClient(
        this IServiceCollection collection)
    {
        return collection.AddValidation();
    }
}

I serverprojektets Program fil lägger du till namnområdet och anropar .Client projektets tjänstsamlingstilläggsmetod (AddValidationForTypesInClient) och AddValidation:

using BlazorSample.Client.Extensions;

...

builder.Services.AddValidationForTypesInClient();
builder.Services.AddValidation();

Kapslade objekt och samlingstyper

Blazor formulärverifiering innehåller stöd för validering av egenskaper för kapslade objekt och samlingsobjekt med den inbyggda DataAnnotationsValidator.

Om du vill skapa ett verifierat formulär använder du en DataAnnotationsValidator komponent i en EditForm komponent, precis som tidigare.

Så här aktiverar du valideringsfunktionen för kapslade objekt och samlingstyper:

  1. Anropa AddValidation-tilläggsmetoden i Program-filen där tjänster är registrerade.
  2. Deklarera formulärmodelltyperna i en C#-klassfil, inte i en Razor komponent (.razor).
  3. Kommentera typ av rotformulärmodell med attributet [ValidatableType] .

Utan att följa föregående steg omfattar formulärvalideringsbeteendet inte kapslad modell och verifiering av samlingstyp.

I följande exempel visas kundorder med förbättrad formulärverifiering (information utelämnas för korthet):

I Program.cs anropar du AddValidation på tjänstsamlingen:

builder.Services.AddValidation();

I följande Order-klass krävs [ValidatableType] attributet på modelltypen på högsta nivå. De andra typerna identifieras automatiskt. OrderItem och ShippingAddress visas inte av hänsyn till korthet, men inkapslad och samlingsverifiering fungerar på samma sätt i dessa typer om de skulle visas.

Order.cs:

[ValidatableType]
public class Order
{
    public Customer Customer { get; set; } = new();
    public List<OrderItem> OrderItems { get; set; } = [];
}

public class Customer
{
    [Required(ErrorMessage = "Name is required.")]
    public string? FullName { get; set; }

    [Required(ErrorMessage = "Email is required.")]
    public string? Email { get; set; }

    public ShippingAddress ShippingAddress { get; set; } = new();
}

I följande OrderPage komponent finns komponenten DataAnnotationsValidator i komponenten EditForm .

OrderPage.razor:

<EditForm Model="Model">
    <DataAnnotationsValidator />

    <h3>Customer Details</h3>
    <div class="mb-3">
        <label>
            Full Name
            <InputText @bind-Value="Model!.Customer.FullName" />
        </label>
        <ValidationMessage For="@(() => Model!.Customer.FullName)" />
    </div>

    // ... form continues ...
</EditForm>

@code {
    public Order? Model { get; set; }

    protected override void OnInitialized() => Model ??= new();
}

Kravet på att deklarera modelltyper utanför Razor komponenter (.razor filer) beror på att både den nya valideringsfunktionen och Razor själva kompilatorn använder en källgenerator. För närvarande kan utdata från en källgenerator inte användas som indata för en annan källgenerator.

Kapslade objekt, samlingstyper och komplexa typer

Observera

För appar som riktar sig till .NET 10 eller senare rekommenderar vi inte längre att du använder det Microsoft.AspNetCore.Components.DataAnnotations.Validationexperimentella paketet och metoden som beskrivs i det här avsnittet. Vi rekommenderar att du använder de inbyggda valideringsfunktionerna för komponenten DataAnnotationsValidator .

Blazor har stöd för validering av formulärindata med hjälp av datanoteringar med den inbyggda DataAnnotationsValidator. I .NET 9 eller tidigare verifieras dock DataAnnotationsValidator endast egenskaper på den översta nivån för modellen som är bunden till formuläret som inte är egenskaper av typen samling eller komplex.

Om du vill verifiera den bundna modellens hela objektdiagram, inklusive egenskaper av typen samling och komplex, använder du det ObjectGraphDataAnnotationsValidator som tillhandahålls av experimentpaketetMicrosoft.AspNetCore.Components.DataAnnotations.Validation i .NET 9 eller tidigare:

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

Kommentera modellegenskaper med [ValidateComplexType]. I följande modellklasser innehåller klassen ShipDescription ytterligare dataanteckningar för att verifiera när modellen är bunden till formuläret:

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

Aktivera knappen Skicka baserat på formulärverifiering

Om du vill aktivera och inaktivera knappen Skicka baserat på formulärverifiering, följande exempel:

  • Använder en förkortad version av det tidigare Starfleet Starship Database-formuläret (Starship3-komponenten) av -exempelformulärets-avsnitt i -indatakomponenternas-artikel, som endast accepterar ett värde för fartygets ID. De andra Starship-egenskaperna får giltiga standardvärden när en instans av Starship-typen skapas.
  • Använder formulärets EditContext för att tilldela modellen när komponenten initieras.
  • Validerar formuläret i kontextens OnFieldChanged-callback för att aktivera eller inaktivera knappen skicka.
  • Implementerar IDisposable och avregistrerar händelsehanteraren i metoden Dispose. Mer information finns i ASP.NET Core Razor komponenthantering.

Observera

När du tilldelar EditForm.EditContextska du inte tilldela en EditForm.Model till EditFormheller.

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

Om ett formulär inte är förladdat med giltiga värden och du vill inaktivera knappen Submit när formuläret laddas, anger du formInvalid till true.

En bieffekt av föregående metod är att en valideringssammanfattning (ValidationSummary komponent) fylls med ogiltiga fält när användaren har interagerat med ett fält. Åtgärda det här scenariot på något av följande sätt:

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

DataAnnotationsValidator verifieringsbeteende

Komponenten DataAnnotationsValidator har samma verifieringsordning och kortslutningsbeteende som System.ComponentModel.DataAnnotations.Validator. Följande regler tillämpas vid validering av en instans av typen T:

  1. Medlemsegenskaper för T verifieras, inklusive rekursiv validering av kapslade objekt.
  2. Typnivåattribut T för verifieras.
  3. Metoden IValidatableObject.Validate körs, om T den implementeras.

Om något av föregående steg genererar ett verifieringsfel hoppas de återstående stegen över.