Delen via


validatie van ASP.NET Core Blazor-formulieren

Notitie

Dit is niet de nieuwste versie van dit artikel. Zie de .NET 10-versie van dit artikel voor de huidige release.

Waarschuwing

Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.

In dit artikel wordt uitgelegd hoe u validatie gebruikt in Blazor formulieren.

Formuliervalidatie

In eenvoudige scenario's voor formuliervalidatie kan een EditForm exemplaar gedeclareerde EditContext en ValidationMessageStore instanties gebruiken om formuliervelden te valideren. Een handler voor de OnValidationRequested gebeurtenis van de EditContext voert aangepaste validatielogica uit. Het resultaat van de handler werkt de ValidationMessageStore instantie bij.

Basisformuliervalidatie is handig in gevallen waarin het model van het formulier wordt gedefinieerd in het onderdeel dat als host fungeert voor het formulier, hetzij als leden rechtstreeks in het onderdeel of in een subklasse. Het gebruik van een validatoronderdeel wordt aanbevolen wanneer een onafhankelijke modelklasse wordt gebruikt in verschillende onderdelen.

In Blazor Web Appis voor validatie aan de clientzijde een actief BlazorSignalR circuit vereist. Validatie aan de clientzijde is niet beschikbaar voor formulieren in onderdelen die statische rendering op de server hebben aangenomen (statische SSR). Formulieren die statische SSR aannemen, worden gevalideerd op de server nadat het formulier is verzonden.

In het volgende component wist de HandleValidationRequested-handlermethode eventuele bestaande validatieberichten door ValidationMessageStore.Clear aan te roepen, alvorens het formulier te valideren.

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

Validatiecomponent voor Data Annotations en aangepaste validatie

Het DataAnnotationsValidator-onderdeel koppelt gegevensannotatie-validatie aan een cascade van EditContext. Voor het inschakelen van data-annotaties is het DataAnnotationsValidator component vereist. Als u een ander validatiesysteem wilt gebruiken dan gegevensaantekeningen, gebruikt u een aangepaste implementatie in plaats van het DataAnnotationsValidator-onderdeel. De framework-implementaties voor DataAnnotationsValidator zijn beschikbaar voor inspectie in de referentiebron:

Zie de DataAnnotationsValidator sectie validatiegedrag voor meer informatie over het validatiegedrag.

Als u ondersteuning voor validatie van gegevensaantekeningen voor een EditContext in code wilt inschakelen, roept u EnableDataAnnotationsValidation aan met een geïnjecteerde IServiceProvider (@inject IServiceProvider ServiceProvider) op de EditContext. Zie het NotifyPropertyChangedValidationComponent-onderdeel in de ASP.NET Core Blazor Framework-BasicTestApp (dotnet/aspnetcore GitHub-opslagplaats) voor een geavanceerd voorbeeld. Vervang in een productieversie van het voorbeeld het argument new TestServiceProvider() voor de serviceprovider door een geïnjecteerde IServiceProvider.

Notitie

Documentatiekoppelingen naar .NET-referentiebron laden meestal de standaardbranch van de opslagplaats, die de huidige ontwikkeling vertegenwoordigt voor de volgende release van .NET. Als u een tag voor een specifieke release wilt selecteren, gebruikt u de Switch-vertakkingen of tags vervolgkeuzelijst. Zie Een versietag selecteren van ASP.NET Core-broncode (dotnet/AspNetCore.Docs #26205)voor meer informatie.

Blazor voert twee typen validatie uit:

  • Veldvalidatie wordt uitgevoerd zodra de gebruiker uit een veld tabt. Tijdens veldvalidatie koppelt het DataAnnotationsValidator onderdeel alle gerapporteerde validatieresultaten aan het veld.
  • modelvalidatie wordt uitgevoerd wanneer de gebruiker het formulier indient. Tijdens modelvalidatie probeert het DataAnnotationsValidator onderdeel het veld te bepalen op basis van de lidnaam die door het validatieresultaat wordt gerapporteerd. Validatieresultaten die niet aan een afzonderlijk lid zijn gekoppeld, zijn gekoppeld aan het model in plaats van een veld.

In aangepaste validatiescenario's:

Er zijn twee algemene benaderingen voor het bereiken van aangepaste validatie, die worden beschreven in de volgende twee secties van dit artikel:

  • Handmatige validatie met behulp van de OnValidationRequested gebeurtenis: de velden van een formulier handmatig valideren met validatie van gegevensaantekeningen en aangepaste code voor veldcontroles wanneer validatie wordt aangevraagd via een gebeurtenishandler die is toegewezen aan de OnValidationRequested gebeurtenis.
  • Validator-onderdelen: een of meer aangepaste validatieonderdelen kunnen worden gebruikt voor het verwerken van validatie voor verschillende formulieren op dezelfde pagina of hetzelfde formulier in verschillende stappen van formulierverwerking (bijvoorbeeld clientvalidatie gevolgd door servervalidatie).

Handmatige validatie met behulp van de OnValidationRequested-gebeurtenis

U kunt een formulier handmatig valideren met een aangepaste gebeurtenishandler die is toegewezen aan de EditContext.OnValidationRequested gebeurtenis om een ValidationMessageStorete beheren.

Het Blazor framework biedt het DataAnnotationsValidator onderdeel om aanvullende validatieondersteuning toe te voegen aan formulieren op basis van validatiekenmerken (gegevensaantekeningen).

Zoals eerder Starship8 onderdeelvoorbeeld, wordt de HandleValidationRequested methode toegewezen aan OnValidationRequested, waar u handmatige validatie kunt uitvoeren in C#-code. Enkele wijzigingen laten zien hoe u de bestaande handmatige validatie combineert met validatie van gegevensaantekeningen via een DataAnnotationsValidator en een validatiekenmerk dat is toegepast op het Holodeck model.

Verwijs naar de System.ComponentModel.DataAnnotations namespace in de Razor richtlijnen van de component bovenaan het definitiebestand van de component.

@using System.ComponentModel.DataAnnotations

Voeg een eigenschap Id toe aan het Holodeck-model met een validatiekenmerk om de lengte van de tekenreeks te beperken tot zes tekens:

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

Voeg een DataAnnotationsValidator-onderdeel (<DataAnnotationsValidator />) toe aan het formulier. Normaal gesproken wordt het onderdeel direct onder de <EditForm> tag geplaatst, maar u kunt het overal in de vorm plaatsen:

<DataAnnotationsValidator />

Wijzig het verzendgedrag van het formulier in de <EditForm>-tag van OnSubmit in OnValidSubmit. Dit zorgt ervoor dat het formulier geldig is voordat de toegewezen gebeurtenishandlermethode wordt uitgevoerd:

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

Voeg in de <EditForm>een veld toe voor de eigenschap Id:

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

Nadat u de voorgaande wijzigingen hebt aangebracht, komt het gedrag van het formulier overeen met de volgende specificatie:

  • De validatie van gegevensaantekeningen op de eigenschap Id activeert geen validatiefout wanneer het Id veld slechts de focus verliest. De validatie wordt uitgevoerd wanneer de gebruiker de knop Update selecteert.
  • Handmatige validatie die u wilt uitvoeren in de HandleValidationRequested methode die is toegewezen aan de OnValidationRequested gebeurtenis van het formulier wordt uitgevoerd wanneer de gebruiker de knop Update van het formulier selecteert. In de bestaande code van het voorbeeld van het Starship8-onderdeel moet de gebruiker een of beide selectievakjes inschakelen om het formulier te valideren.
  • Het formulier verwerkt de Submit methode pas nadat de gegevensaantekeningen en de handmatige validatie zijn geslaagd.

Validatieonderdelen

Validator-onderdelen ondersteunen formuliervalidatie door een ValidationMessageStore te beheren voor de EditContextvan een formulier.

Het Blazor framework biedt het DataAnnotationsValidator onderdeel voor het koppelen van validatieondersteuning aan formulieren op basis van validatiekenmerken (gegevensaantekeningen). U kunt aangepaste validatieonderdelen maken voor het verwerken van validatieberichten voor verschillende formulieren op dezelfde pagina of hetzelfde formulier in verschillende stappen voor formulierverwerking (bijvoorbeeld clientvalidatie gevolgd door servervalidatie). Het voorbeeld van het validatieonderdeel dat wordt weergegeven in deze sectie, CustomValidation, wordt gebruikt in de volgende secties van dit artikel:

Van de ingebouwde data-annotatieis alleen het [Remote] validatiekenmerk niet ondersteund in Blazor.

Notitie

Validatiekenmerken voor aangepaste gegevensaantekeningen kunnen in veel gevallen worden gebruikt in plaats van aangepaste validatieonderdelen. Aangepaste kenmerken die zijn toegepast op het model van het formulier worden geactiveerd met behulp van het DataAnnotationsValidator-onderdeel. Bij gebruik met servervalidatie moeten alle aangepaste kenmerken die op het model worden toegepast, uitvoerbaar zijn op de server. Zie de sectie Aangepaste validatiekenmerken voor meer informatie.

Een validatieonderdeel maken op basis van ComponentBase:

  • De EditContext van het formulier is een trapsgewijze parameter van het onderdeel.
  • Wanneer het validatieonderdeel wordt geïnitialiseerd, wordt er een nieuwe ValidationMessageStore gemaakt om een huidige lijst met formulierfouten te onderhouden.
  • Het berichtenarchief ontvangt fouten wanneer ontwikkelaarscode in het onderdeel van het formulier de DisplayErrors methode aanroept. De fouten worden doorgegeven aan de DisplayErrors methode in een Dictionary<string, List<string>>. In de woordenlijst is de sleutel de naam van het formulierveld met een of meer fouten. De waarde is de lijst met fouten.
  • Berichten worden gewist wanneer zich een van de volgende situaties voordoet:
    • Validatie wordt aangevraagd op de EditContext wanneer de OnValidationRequested gebeurtenis wordt gegenereerd. Alle fouten worden gewist.
    • Er wordt een veld in het formulier gewijzigd wanneer de gebeurtenis OnFieldChanged wordt gegenereerd. Alleen de fouten voor het veld worden gewist.
    • De methode ClearErrors wordt aangeroepen door ontwikkelaarscode. Alle fouten worden gewist.

Werk de naamruimte in de volgende klasse bij zodat deze overeenkomt met de naamruimte van uw app.

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

Belangrijk

Het specificeren van een naamruimte is vereist wanneer er wordt afgeleid van ComponentBase. Als er geen naamruimte wordt opgegeven, leidt dat tot een buildfout:

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

De tijdelijke aanduiding {CLASS NAME} is de naam van de onderdeelklasse. In het aangepaste validatievoorbeeld in deze sectie wordt de voorbeeldnaamruimte BlazorSampleopgegeven.

Notitie

Anonieme lambda-expressies zijn geregistreerde gebeurtenis-handlers voor OnValidationRequested en OnFieldChanged in het vorige voorbeeld. Het is niet nodig om IDisposable te implementeren en de gedelegeerden voor gebeurtenissen in dit scenario af te melden. Zie ASP.NET Core Razor component verwijderingvoor meer informatie.

Validatie van bedrijfslogica met een validatieonderdeel

Voor algemene validatie van bedrijfslogica gebruikt u een validatoronderdeel dat formulierfouten in een woordenlijst ontvangt.

Basisvalidatie is handig in gevallen waarin het model van het formulier wordt gedefinieerd in het onderdeel dat als host fungeert voor het formulier, hetzij als leden rechtstreeks in het onderdeel of in een subklasse. Het gebruik van een validatieonderdeel wordt aanbevolen wanneer een onafhankelijke modelklasse wordt gebruikt voor verschillende onderdelen.

In het volgende voorbeeld:

  • Een verkorte versie van het Starfleet Starship Database formulier (Starship3 onderdeel) van het voorbeeldformulier sectie van de invoeronderdelen artikel wordt gebruikt dat alleen de classificatie en beschrijving van het sterschip accepteert. Validatie van gegevensaantekening wordt niet geactiveerd bij het indienen van formulieren omdat het DataAnnotationsValidator onderdeel niet is opgenomen in het formulier.
  • Het CustomValidation onderdeel van de Validator-onderdelen sectie van dit artikel wordt gebruikt.
  • Voor de validatie is een waarde vereist voor de beschrijving van het schip (Description) als de gebruiker de "Defense" verzendclassificatie (Classification) selecteert.

Wanneer validatieberichten zijn ingesteld in het onderdeel, worden ze toegevoegd aan de ValidationMessageStore van de validator en weergegeven in het validatieoverzicht van de EditForm.

Starship9.razor:

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

<h1>Starfleet Starship Database</h1>

<h2>New Ship Entry Form</h2>

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

@code {
    private CustomValidation? customValidation;

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

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

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

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

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

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

<h1>Starfleet Starship Database</h1>

<h2>New Ship Entry Form</h2>

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

@code {
    private CustomValidation? customValidation;

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

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

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

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

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

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

<h1>Starfleet Starship Database</h1>

<h2>New Ship Entry Form</h2>

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

@code {
    private CustomValidation? customValidation;

    public Starship? Model { get; set; }

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

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

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

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

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

Notitie

Als alternatief voor het gebruik van validatieonderdelenkunnen validatiekenmerken voor gegevensaantekeningen worden gebruikt. Aangepaste kenmerken die zijn toegepast op het model van het formulier worden geactiveerd met behulp van het DataAnnotationsValidator-onderdeel. Bij gebruik met servervalidatie moeten de kenmerken uitvoerbaar zijn op de server. Zie de sectie Aangepaste validatiekenmerken voor meer informatie.

Servervalidatie met een validatieonderdeel

Deze sectie is gericht op Blazor Web App scenario's, maar de benadering voor elk type app dat gebruikmaakt van servervalidatie met web-API, heeft dezelfde algemene benadering.

Deze sectie is gericht op gehoste Blazor WebAssembly scenario's, maar de benadering voor elk type app dat gebruikmaakt van servervalidatie met web-API, heeft dezelfde algemene benadering.

Servervalidatie wordt ondersteund naast clientvalidatie:

  • Voer de clientvalidatie uit in het formulier met de DataAnnotationsValidator-component.
  • Wanneer het formulier clientvalidatie doorgeeft (OnValidSubmit wordt aangeroepen), verzendt u de EditContext.Model naar een API van de back-endserver voor formulierverwerking.
  • Procesmodelvalidatie op de server.
  • De server-API bevat zowel de ingebouwde frameworkgegevensvalidatie als aangepaste validatielogica die door de ontwikkelaar wordt geleverd. Als de validatie wordt doorgegeven aan de server, verwerkt u het formulier en stuurt u een geslaagde statuscode (200 - OK). Als de validatie mislukt, retourneert u een foutcode (400 - Bad Request) en de veldvalidatiefouten.
  • Schakel het formulier uit bij succes of geef de fouten weer.

Basisvalidatie is handig in gevallen waarin het model van het formulier wordt gedefinieerd in het onderdeel dat als host fungeert voor het formulier, hetzij als leden rechtstreeks in het onderdeel of in een subklasse. Het gebruik van een validatieonderdeel wordt aanbevolen wanneer een onafhankelijke modelklasse wordt gebruikt voor verschillende onderdelen.

Het volgende voorbeeld is gebaseerd op:

Plaats het Starship model (Starship.cs) in een gedeeld klassebibliotheekproject, zodat zowel de client- als serverprojecten het model kunnen gebruiken. Voeg de naamruimte toe of werk deze bij zodat deze overeenkomt met de naamruimte van de gedeelde app (bijvoorbeeld namespace BlazorSample.Shared). Omdat voor het model gegevensaantekeningen zijn vereist, controleert u of de gedeelde klassebibliotheek gebruikmaakt van het gedeelde framework of het System.ComponentModel.Annotations-pakket toevoegt aan het gedeelde project.

Notitie

Zie de artikelen onder Pakketten installeren en beheren bij Pakketverbruikworkflow (NuGet-documentatie)voor hulp bij het toevoegen van pakketten aan .NET-apps. Bevestig de juiste pakketversies op NuGet.org.

Voeg in het hoofdproject van de Blazor Web Appeen controller toe om sterschipvalidatieaanvragen te verwerken en mislukte validatieberichten te retourneren. Werk de naamruimten in de laatste using-instructie voor het gedeelde klassebibliotheekproject en de namespace voor de controllerklasse bij. Naast validatie van client- en servergegevens valideert de controller dat er een waarde is opgegeven voor de beschrijving van het schip (Description) als de gebruiker de Defense scheepsclassificatie (Classification) selecteert.

Plaats het Starship model (Starship.cs) in het Shared project van de oplossing, zodat zowel de client- als server-apps het model kunnen gebruiken. Voeg de naamruimte toe of werk deze bij zodat deze overeenkomt met de naamruimte van de gedeelde app (bijvoorbeeld namespace BlazorSample.Shared). Omdat voor het model gegevensaantekeningen zijn vereist, voegt u het System.ComponentModel.Annotations-pakket toe aan het Shared-project.

Notitie

Zie de artikelen onder Pakketten installeren en beheren bij Pakketverbruikworkflow (NuGet-documentatie)voor hulp bij het toevoegen van pakketten aan .NET-apps. Bevestig de juiste pakketversies op NuGet.org.

Voeg in het Server project een controller toe om sterschipvalidatieaanvragen te verwerken en mislukte validatieberichten te retourneren. Werk de naamruimten in de laatste using-instructie voor het Shared-project en de namespace voor de controllerklasse bij. Naast validatie van client- en servergegevens valideert de controller dat er een waarde is opgegeven voor de beschrijving van het schip (Description) als de gebruiker de Defense scheepsclassificatie (Classification) selecteert.

De validatie voor de Defense scheepsclassificatie vindt alleen plaats op de server in de controller, omdat het aanstaande formulier niet dezelfde validatie aan de clientzijde uitvoert wanneer het formulier naar de server wordt verzonden. Servervalidatie zonder clientvalidatie is gebruikelijk in apps waarvoor persoonlijke bedrijfslogicavalidatie van gebruikersinvoer op de server is vereist. Privégegevens van gegevens die zijn opgeslagen voor een gebruiker, kunnen bijvoorbeeld vereist zijn om gebruikersinvoer te valideren. Privégegevens kunnen uiteraard niet naar de client worden verzonden voor clientvalidatie.

Notitie

De StarshipValidation controller in deze sectie maakt gebruik van Microsoft Identity 2.0. De web-API accepteert alleen tokens voor gebruikers met het bereik 'API.Access' voor deze API. Aanvullende aanpassing is vereist als de bereiknaam van de API verschilt van API.Access.

Zie voor meer informatie over beveiliging:

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

Bevestig of werk de naamruimte van de voorgaande controller (BlazorSample.Server.Controllers) bij zodat deze overeenkomt met de naamruimte van de controllers van de app.

Wanneer een validatiefout voor modelbinding optreedt op de server, retourneert een ApiController (ApiControllerAttribute) normaal gesproken een standaardantwoord voor ongeldige aanvragen met een ValidationProblemDetails. Het antwoord bevat meer gegevens dan alleen de validatiefouten, zoals wordt weergegeven in het volgende voorbeeld wanneer alle velden van het formulier Starfleet Starship Database niet worden verzonden en de validatie van het formulier mislukt:

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

Notitie

Als u het voorgaande JSON-antwoord wilt demonstreren, moet u de clientvalidatie van het formulier uitschakelen om het indienen van lege velden toe te staan of een hulpprogramma gebruiken om een aanvraag rechtstreeks naar de server-API te verzenden, zoals Firefox Browser Developer-.

Als de server-API het voorgaande standaard-JSON-antwoord retourneert, kan de client het antwoord parseren in ontwikkelaarscode om de onderliggende elementen van het errors-knooppunt te verkrijgen voor het verwerken van formuliervalidatiefouten. Het is onhandig om code voor ontwikkelaars te schrijven om het bestand te parseren. Voor het handmatig parseren van de JSON moeten er Dictionary<string, List<string>> fouten worden gegenereerd nadat ReadFromJsonAsyncis aangeroepen. In het ideale geval moet de server-API alleen de validatiefouten retourneren, zoals in het volgende voorbeeld wordt weergegeven:

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

Als u het antwoord van de server-API wilt wijzigen zodat deze alleen de validatiefouten retourneert, wijzigt u de gemachtigde die wordt aangeroepen voor acties die zijn geannoteerd met ApiControllerAttribute in het Program-bestand. Voor het API-eindpunt (/StarshipValidation) retourneert u een BadRequestObjectResult met de ModelStateDictionary. Voor andere API-eindpunten behoudt u het standaardgedrag door het objectresultaat te retourneren met een nieuwe ValidationProblemDetails.

Voeg de Microsoft.AspNetCore.Mvc-naamruimte toe aan de bovenkant van het Program-bestand in het hoofdproject van de Blazor Web App:

using Microsoft.AspNetCore.Mvc;

Voeg in het bestand Program de volgende AddControllersWithViews extensiemethode toe of werk deze bij en voeg de volgende aanroep toe aan 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));
            }
        };
    });

Als u voor het eerst controllers toevoegt aan het hoofdproject van de Blazor Web App, wijs de eindpunten van controllers toe wanneer u de voorafgaande code plaatst die de services voor controllers registreert. In het volgende voorbeeld worden standaardcontrollerroutes gebruikt:

app.MapDefaultControllerRoute();

Notitie

In het voorgaande voorbeeld worden controllerservices expliciet geregistreerd door AddControllersWithViews aan te roepen om automatisch aanvallen op cross-siteaanvraagvervalsing (XSRF/CSRF) te beperken. Als u slechts AddControllersgebruikt, wordt antivervalsing niet automatisch ingeschakeld.

Zie de volgende bronnen voor meer informatie over foutreacties voor controllerroutering en validatiefouten:

Voeg in het .Client project het CustomValidation onderdeel toe dat wordt weergegeven in de sectie Validator-onderdelen. Werk de naamruimte bij zodat deze overeenkomt met de app (bijvoorbeeld namespace BlazorSample.Client).

In het .Client-project wordt het Starfleet Starship Database formulier bijgewerkt om servervalidatiefouten weer te geven met behulp van het CustomValidation-onderdeel. Wanneer de server-API validatieberichten retourneert, worden deze toegevoegd aan de CustomValidationvan het ValidationMessageStore onderdeel. De fouten zijn beschikbaar in formulier EditContext voor weergave in de validatiesamenvatting van het formulier.

Werk in het volgende onderdeel de naamruimte van het gedeelde project (@using BlazorSample.Shared) bij naar de naamruimte van het gedeelde project. Houd er rekening mee dat voor het formulier autorisatie is vereist, dus de gebruiker moet zijn aangemeld bij de app om naar het formulier te navigeren.

Voeg de Microsoft.AspNetCore.Mvc naamruimte boven aan het Program-bestand toe in de Server-app:

using Microsoft.AspNetCore.Mvc;

Zoek in het bestand Program de AddControllersWithViews-extensiemethode en voeg de volgende aanroep toe aan 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));
            }
        };
    });

Notitie

In het voorgaande voorbeeld worden controllerservices expliciet geregistreerd door AddControllersWithViews aan te roepen om automatisch aanvallen op cross-siteaanvraagvervalsing (XSRF/CSRF) te beperken. Als u slechts AddControllersgebruikt, wordt antivervalsing niet automatisch ingeschakeld.

Voeg in het Client project het CustomValidation onderdeel toe dat wordt weergegeven in de sectie Validator-onderdelen. Werk de naamruimte bij zodat deze overeenkomt met de app (bijvoorbeeld namespace BlazorSample.Client).

In het Client-project wordt het Starfleet Starship Database formulier bijgewerkt om servervalidatiefouten weer te geven met behulp van het CustomValidation-onderdeel. Wanneer de server-API validatieberichten retourneert, worden deze toegevoegd aan de CustomValidationvan het ValidationMessageStore onderdeel. De fouten zijn beschikbaar in formulier EditContext voor weergave in de validatiesamenvatting van het formulier.

Werk in het volgende onderdeel de naamruimte van het Shared project (@using BlazorSample.Shared) bij naar de naamruimte van het gedeelde project. Houd er rekening mee dat voor het formulier autorisatie is vereist, dus de gebruiker moet zijn aangemeld bij de app om naar het formulier te navigeren.

Starship10.razor:

Notitie

Formulieren op basis van EditForm schakelen automatisch ondersteuning voor antivervalsing in. De controller moet AddControllersWithViews gebruiken om controllerservices te registreren en automatisch antiforgery-ondersteuning in te schakelen voor de web-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
        {
            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.";
        }
    }
}

Het .Client project van een Blazor Web App moet ook een HttpClient registreren voor HTTP POST-aanvragen naar een back-endweb-API-controller. Bevestig of voeg het volgende toe aan het .Client-bestand van het Program project:

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

In het voorgaande voorbeeld wordt het basisadres ingesteld met builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress), waarmee het basisadres voor de app wordt opgehaald en doorgaans wordt afgeleid van de <base> waarde van de href tag op de hostpagina.

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

Notitie

Als alternatief voor het gebruik van een validatieonderdeel, kunnen validatiekenmerken voor gegevensaantekening worden gebruikt. Aangepaste kenmerken die zijn toegepast op het model van het formulier worden geactiveerd met behulp van het DataAnnotationsValidator-onderdeel. Bij gebruik met servervalidatie moeten de kenmerken uitvoerbaar zijn op de server. Zie de sectie Aangepaste validatiekenmerken voor meer informatie.

Notitie

De servervalidatiemethode in deze sectie is geschikt voor een van de gehoste Blazor WebAssembly oplossingsvoorbeelden in deze documentatieset:

InputText op basis van de ingevoerde gebeurtenis

Gebruik het InputText-onderdeel om een aangepast onderdeel te maken dat gebruikmaakt van de oninput gebeurtenis (input) in plaats van de onchange gebeurtenis (change). Het gebruik van de input gebeurtenis activeert veldvalidatie op elke toetsaanslag.

Het volgende CustomInputText onderdeel neemt het InputText-onderdeel van het framework over en stelt gebeurtenisbinding in op de oninput gebeurtenis (input).

CustomInputText.razor:

@inherits InputText

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

Het CustomInputText-onderdeel kan overal worden gebruikt waar InputText wordt gebruikt. Het volgende onderdeel maakt gebruik van het gedeelde CustomInputText-onderdeel.

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

Onderdelen van validatiesamenvatting en validatiebericht

Het ValidationSummary-onderdeel bevat een overzicht van alle validatieberichten, die vergelijkbaar zijn met de Helper voor validatieoverzichtstags:

<ValidationSummary />

Uitvoervalidatieberichten voor een specifiek model met de parameter Model:

<ValidationSummary Model="Model" />

Het ValidationMessage<TValue>-onderdeel geeft validatieberichten weer voor een specifiek veld. Dit is vergelijkbaar met de Helper voor validatieberichttags. Geef het veld voor validatie op met het kenmerk For en een lambda-expressie met de naam van de modeleigenschap:

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

De ValidationMessage<TValue>- en ValidationSummary-onderdelen ondersteunen willekeurige kenmerken. Een kenmerk dat niet overeenkomt met een onderdeelparameter, wordt toegevoegd aan het gegenereerde <div>- of <ul>-element.

De stijl van validatieberichten in het opmaakmodel van de app beheren (wwwroot/css/app.css of wwwroot/css/site.css). De standaardklasse validation-message stelt de tekstkleur van validatieberichten in op rood:

.validation-message {
    color: red;
}

Bepalen of een formulierveld geldig is

Gebruik EditContext.IsValid om te bepalen of een veld geldig is zonder validatieberichten te verkrijgen.

ondersteund, maar niet aanbevolen:

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

✔️ aanbevolen:

var isValid = editContext.IsValid(fieldIdentifier);

Aangepaste validatiekenmerken

Om ervoor te zorgen dat het validatieresultaat correct is gekoppeld aan een veld wanneer u een aangepast validatiekenmerk gebruikt, geef dan de MemberName van de validatiecontext door bij het maken van het 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 });
    }
}

Injecteer services in aangepaste validatiekenmerken via de ValidationContext. In het volgende voorbeeld ziet u een salade chef-formulier waarmee gebruikersinvoer wordt gevalideerd met afhankelijkheidsinjectie (DI).

De SaladChef-klasse geeft de goedgekeurde ingrediëntenlijst voor een Ten Forward-salade aan.

SaladChef.cs:

namespace BlazorSample;

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

Registreer SaladChef in de DI-container van de app in het Program-bestand:

builder.Services.AddTransient<SaladChef>();

De IsValid methode van de volgende SaladChefValidatorAttribute klasse verkrijgt de SaladChef service van DI om de invoer van de gebruiker te controleren.

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

Het volgende onderdeel valideert gebruikersinvoer door de SaladChefValidatorAttribute ([SaladChefValidator]) toe te passen op de tekenreeks voor salade-ingrediënten (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);
}

Aangepaste css-klassekenmerken voor validatie

Aangepaste css-klassekenmerken voor validatie zijn handig bij het integreren met CSS-frameworks, zoals Bootstrap-.

Als u aangepaste css-klassekenmerken voor validatie wilt opgeven, geeft u eerst CSS-stijlen voor aangepaste validatie op. In het volgende voorbeeld worden geldige stijlen (validField) en ongeldige stijlen (invalidField) opgegeven.

Voeg de volgende CSS-klassen toe aan het opmaakmodel van de app:

.validField {
    border-color: lawngreen;
}

.invalidField {
    background-color: tomato;
}

Maak een klasse die is afgeleid van FieldCssClassProvider die controleert op veldvalidatieberichten en de juiste geldige of ongeldige stijl toepast.

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

Stel de CustomFieldClassProvider klasse in als veld-CSS-klasseprovider in het EditContext exemplaar van het formulier met 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; }
    }
}

In het voorgaande voorbeeld wordt de geldigheid van alle formuliervelden gecontroleerd en wordt een stijl toegepast op elk veld. Als het formulier alleen aangepaste stijlen mag toepassen op een subset van de velden, moet CustomFieldClassProvider stijlen voorwaardelijk toepassen. In het volgende CustomFieldClassProvider2 voorbeeld wordt alleen een stijl toegepast op het Name veld. Voor velden met namen die niet overeenkomen met Name, wordt string.Empty geretourneerd en wordt er geen stijl toegepast. Met weerspiegelingwordt het veld afgestemd op de eigenschap of veldnaam van het modelonderdeel, niet een veld id toegewezen aan de HTML-entiteit.

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

Notitie

Het overeenkomen met de veldnaam in het vorige voorbeeld is hoofdlettergevoelig, dus moet een modeleigenschapslid dat is aangeduid met 'Name' overeenkomen met een voorwaardelijke controle op 'Name'.

  • ✔️ komt correct overeen:fieldId.FieldName == "Name"
  • komt niet overeen:fieldId.FieldName == "name"
  • komt niet overeen:fieldId.FieldName == "NAME"
  • komt niet overeen:fieldId.FieldName == "nAmE"

Voeg een extra eigenschap toe aan Model, bijvoorbeeld:

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

Voeg de Description toe aan de vorm van de CustomValidationForm-component.

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

Werk het EditContext exemplaar bij in de OnInitialized methode van het onderdeel om de nieuwe CSS-klasseprovider voor velden te gebruiken:

editContext?.SetFieldCssClassProvider(new CustomFieldClassProvider2());

Omdat een CSS-validatieklasse niet wordt toegepast op het Description veld, wordt deze niet gestijld. Veldvalidatie wordt echter normaal uitgevoerd. Als er meer dan 10 tekens worden opgegeven, geeft de validatiesamenvatting de fout aan:

Beschrijving is te lang.

In het volgende voorbeeld:

  • De aangepaste CSS-stijl wordt toegepast op het Name veld.

  • Alle andere velden passen logica toe die vergelijkbaar is met de standaardlogica van Blazoren het gebruik van de standaard css-validatiestijlen voor velden van Blazor, modified met valid of invalid. Houd er rekening mee dat u voor de standaardstijlen deze niet hoeft toe te voegen aan het opmaakmodel van de app als de app is gebaseerd op een Blazor projectsjabloon. Voor apps die niet zijn gebaseerd op een Blazor projectsjabloon, kunnen de standaardstijlen worden toegevoegd aan het opmaakmodel van de app:

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

Werk het EditContext-exemplaar bij in de OnInitialized-methode van het onderdeel om de voorgaande Field CSS Class Provider te gebruiken.

editContext.SetFieldCssClassProvider(new CustomFieldClassProvider3());

Met behulp van CustomFieldClassProvider3:

  • Het veld Name maakt gebruik van de aangepaste CSS-stijlen voor validatie van de app.
  • Het veld Description maakt gebruik van logica die vergelijkbaar is met de logica van Blazoren Blazorstandaard css-validatiestijlen voor velden.

Validatie op klasseniveau met IValidatableObject

validatie op klasseniveau met IValidatableObject (API-documentatie) wordt ondersteund voor Blazor formuliermodellen. IValidatableObject validatie wordt alleen uitgevoerd wanneer het formulier wordt verzonden en alleen als alle andere validaties zijn geslaagd.

validatiepakket voor Blazor gegevensaantekeningen

Notitie

Het Microsoft.AspNetCore.Components.DataAnnotations.Validation pakket wordt niet meer aanbevolen voor apps die gericht zijn op .NET 10 of hoger. Zie de sectie Geneste objecten, verzamelingstypen en complexe typen voor meer informatie.

Het Microsoft.AspNetCore.Components.DataAnnotations.Validation pakket vult de validatie-ervaring hiaten in met behulp van het DataAnnotationsValidator onderdeel. Het pakket is momenteel experimenteel.

Waarschuwing

Het Microsoft.AspNetCore.Components.DataAnnotations.Validation pakket heeft een nieuwste versie van de releasekandidaat op NuGet.org. Blijf op dit moment het experimentele release-kandidaatpakket gebruiken. Experimentele functies worden geboden voor het verkennen van de levensvatbaarheid van functies en worden mogelijk niet in een stabiele versie verzonden. Bekijk de GitHub-opslagplaats aankondigingen, de dotnet/aspnetcore GitHub-opslagplaatsof dit onderwerpsectie voor verdere updates.

kenmerk [CompareProperty]

De CompareAttribute werkt niet goed met het DataAnnotationsValidator onderdeel omdat de DataAnnotationsValidator het validatieresultaat niet koppelt aan een specifiek lid. Dit kan leiden tot inconsistent gedrag tussen validatie op veldniveau en wanneer het hele model op een submit wordt gevalideerd. Het Microsoft.AspNetCore.Components.DataAnnotations.Validationexperimentele pakket introduceert een extra validatiekenmerk, ComparePropertyAttributedat deze beperkingen omzeilt. In een Blazor-app is [CompareProperty] een directe vervanging voor het [Compare] kenmerk.

Geneste objecten en verzamelingstypen

Blazor Formuliervalidatie biedt ondersteuning voor het valideren van eigenschappen van geneste objecten en collectie-items met de ingebouwde DataAnnotationsValidator.

Als u een gevalideerd formulier wilt maken, gebruikt u een DataAnnotationsValidator onderdeel in een EditForm onderdeel, net als voorheen.

U kunt kiezen voor de validatiefunctie voor geneste objecten en verzamelingstypen:

  1. Roep de AddValidation extensiemethode aan in het Program bestand waarin services zijn geregistreerd.
  2. Declareer de formuliermodeltypen in een C#-klassebestand, niet in een Razor onderdeel (.razor).
  3. Annoteer het hoofdformuliermodeltype met de [ValidatableType] eigenschap.

Zonder de voorgaande stappen te volgen, bevat formuliervalidatiegedrag geen geneste model- en verzamelingstypevalidatie.

In het volgende voorbeeld ziet u klantorders met de verbeterde formuliervalidatie (details weggelaten voor beknoptheid):

Roep Program.csin op AddValidation de serviceverzameling:

builder.Services.AddValidation();

In de volgende Order klasse is het [ValidatableType] kenmerk vereist voor het modeltype op het hoogste niveau. De andere typen worden automatisch gedetecteerd. OrderItem en ShippingAddress worden niet weergegeven voor beknoptheid, maar geneste validatie en verzamelingsvalidatie werken op dezelfde manier als ze wel getoond zouden worden.

Order.cs:

using System.ComponentModel.DataAnnotations;

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

In het volgende OrderPage onderdeel is het DataAnnotationsValidator onderdeel aanwezig in het EditForm onderdeel.

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

De vereiste om de modeltypen buiten Razor onderdelen (.razor bestanden) te declareren, is te wijten aan het feit dat zowel de nieuwe validatiefunctie als de Razor compiler zelf een brongenerator gebruiken. Momenteel kan uitvoer van één brongenerator niet worden gebruikt als invoer voor een andere brongenerator.

Zie de sectie Validatiemodellen uit een andere assembly gebruiken voor hulp bij het gebruik van validatiemodellen uit een andere assembly .

Geneste objecten, verzamelingstypen en complexe typen

Notitie

Voor apps die gericht zijn op .NET 10 of hoger, raden we u niet meer aan het Microsoft.AspNetCore.Components.DataAnnotations.Validation pakket en de methode te gebruiken die in deze sectie wordt beschreven. U wordt aangeraden de ingebouwde validatiefuncties van het DataAnnotationsValidator onderdeel te gebruiken.

Blazor biedt ondersteuning voor het valideren van formulierinvoer met behulp van gegevensaantekeningen met de ingebouwde DataAnnotationsValidator. DataAnnotationsValidator In .NET 9 of eerder worden echter alleen eigenschappen op het hoogste niveau van het model gevalideerd die zijn gebonden aan het formulier dat geen verzamelings- of complexe eigenschappen zijn.

Als u de volledige objectgrafiek van het afhankelijke model wilt valideren, inclusief verzamelings- en complexe eigenschappen, gebruikt u het ObjectGraphDataAnnotationsValidatorexperimenteleMicrosoft.AspNetCore.Components.DataAnnotations.Validation pakket in .NET 9 of eerder:

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

Aantekeningen toevoegen aan modeleigenschappen met [ValidateComplexType]. In de volgende modelklassen bevat de ShipDescription-klasse aanvullende gegevensaantekeningen om te valideren wanneer het model is gebonden aan het formulier:

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

Validatiemodellen uit een andere assembly gebruiken

Voor modelvalidatie die is gedefinieerd in een andere assembly, zoals een bibliotheek of het .Client project van een Blazor Web App:

  • Als de bibliotheek een gewone klassebibliotheek is (deze is niet gebaseerd op de Microsoft.NET.Sdk.Web of Microsoft.NET.Sdk.Razor SDK's), voegt u een pakketreferentie toe aan de bibliotheek voor het Microsoft.Extensions.Validation NuGet-pakket. Aanvullende stappen zijn vereist voor gewone klassebibliotheken, die verderop in deze sectie worden beschreven.
  • Maak een methode in de bibliotheek of .Client-project die een IServiceCollection exemplaar als argument ontvangt en roept AddValidation aan.
  • Roep in de app zowel de methode als AddValidation aan.

De voorgaande benadering resulteert in validatie van de typen van beide assembly's.

In het volgende voorbeeld wordt de AddValidationForTypesInClient methode gemaakt voor het .Client project van een Blazor Web App voor validatie met behulp van typen die in het .Client project zijn gedefinieerd.

ServiceCollectionExtensions.cs (in het .Client project):

namespace BlazorSample.Client.Extensions;

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

In het Program bestand van het serverproject, voeg de naamruimte toe en roep de extensiemethode voor serviceverzameling van het .Client project aan (AddValidationForTypesInClient) en AddValidation.

using BlazorSample.Client.Extensions;

...

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

De nieuwe kenmerken van het Microsoft.Extensions.Validation pakket (ValidatableTypeAttribute en SkipValidationAttribute) worden gepubliceerd als experimenteel in .NET 10. Het pakket is bedoeld om een nieuwe gedeelde infrastructuur te bieden voor validatiefuncties in verschillende frameworks en het publiceren van experimentele typen biedt meer flexibiliteit voor het uiteindelijke ontwerp van de openbare API voor betere ondersteuning bij het gebruik van frameworks.

In Blazor apps worden typen beschikbaar gemaakt via een gegenereerd ingesloten kenmerk. Als een web-app-project dat gebruikmaakt van de Microsoft.NET.Sdk.Web SDK (<Project Sdk="Microsoft.NET.Sdk.Web">) of een RCL die gebruikmaakt van de Microsoft.NET.Sdk.Razor SDK (<Project Sdk="Microsoft.NET.Sdk.Razor">) onderdelen bevat Razor (.razor), genereert het framework automatisch een intern kenmerk binnen het project (Microsoft.Extensions.Validation.Embedded.ValidatableType, ). Microsoft.Extensions.Validation.Embedded.SkipValidation Deze typen zijn uitwisselbaar met de werkelijke kenmerken en niet gemarkeerd als experimenteel. In de meeste gevallen gebruiken ontwikkelaars de [ValidatableType]/[SkipValidation] kenmerken van hun klassen zonder zorgen over hun bron.

De voorgaande benadering is echter niet haalbaar in gewone klassebibliotheken die gebruikmaken van de Microsoft.NET.Sdk SDK (<Project Sdk="Microsoft.NET.Sdk">). Het gebruik van de typen in een gewone klassebibliotheek resulteert in een waarschuwing voor codeanalyse:

ASP0029: 'Microsoft.Extensions.Validation.ValidatableTypeAttribute' is alleen bedoeld voor evaluatiedoeleinden en kan in toekomstige updates worden gewijzigd of verwijderd. Deze diagnose onderdrukken om door te gaan.

De waarschuwing kan worden onderdrukt met behulp van een van de volgende methoden:

  • Een <NoWarn> eigenschap in het projectbestand:

    <PropertyGroup>
      <NoWarn>$(NoWarn);ASP0029</NoWarn>
    </PropertyGroup>
    
  • Een pragma instructie waarbij het kenmerk wordt gebruikt:

    #pragma warning disable ASP0029
    [Microsoft.Extensions.Validation.ValidatableType]
    #pragma warning restore ASP0029
    
  • Een EditorConfig-bestand (.editorconfig) regel:

    dotnet_diagnostic.ASP0029.severity = none
    

Als het onderdrukken van de waarschuwing niet acceptabel is, maakt u handmatig het embedded attribuut in de bibliotheek aan dat door de web- en Razor SDK's automatisch wordt gegenereerd.

ValidatableTypeAttribute.cs:

namespace Microsoft.Extensions.Validation.Embedded
{
    [AttributeUsage(AttributeTargets.Class)]
    internal sealed class ValidatableTypeAttribute : Attribute
    {
    }
}

Gebruik de exacte naamruimte (Microsoft.Extensions.Validation.Embedded) en de klassenaam (ValidatableTypeAttribute) om de validatiebrongenerator het type te detecteren en te gebruiken. U kunt een globale using instructie declareren voor de naamruimte, ofwel met een global using Microsoft.Extensions.Validation.Embedded; instructie of met een <Using Include="Microsoft.Extensions.Validation.Embedded" /> item in het projectbestand van de bibliotheek.

Welke methode ook wordt gebruikt, geeft de aanwezigheid aan van de tijdelijke oplossing voor een toekomstige update van uw code. Framework-updates om de acceptatie van validatietypen in gewone klassebibliotheken te vereenvoudigen, zijn gepland voor .NET 11 (november 2026).

De knop Verzenden inschakelen op basis van formuliervalidatie

Als u de verzendknop wilt in- en uitschakelen op basis van formuliervalidatie, volgt u het volgende voorbeeld:

  • Maakt gebruik van een verkorte versie van het eerdere Starfleet Starship Database formulier (Starship3 onderdeel) van het voorbeeldformulier in de sectie van het Invoeronderdelen artikel dat alleen een waarde voor het Id van het schip accepteert. De andere Starship Eigenschappen ontvangen geldige standaardwaarden wanneer een instantie van het Starship type wordt gemaakt.
  • Gebruikt de EditContext van het formulier om het model toe te wijzen wanneer het onderdeel wordt geïnitialiseerd.
  • Valideert het formulier in de context van de OnFieldChanged callback om de verzendknop te activeren en te deactiveren.
  • Implementeert IDisposable en schrijft de event handler uit in de methode Dispose. Zie ASP.NET Core Razor component verwijderingvoor meer informatie.

Notitie

Wanneer u een EditForm.EditContext toewijst aan de EditForm.Model, moet u niet ook een EditForm.EditContext toewijzen aan de .

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

Als een formulier niet vooraf is geladen met geldige waarden en u de knop Submit bij het laden van formulieren wilt uitschakelen, stelt u formInvalid in op true.

Een neveneffect van de voorgaande benadering is dat een validatiesamenvatting (ValidationSummary onderdeel) wordt gevuld met ongeldige velden nadat de gebruiker met één veld heeft gecommuniceerd. Los dit scenario op een van de volgende manieren op:

  • Gebruik geen ValidationSummary onderdeel op het formulier.
  • Het ValidationSummary onderdeel zichtbaar maken wanneer de knop Verzenden is geselecteerd (bijvoorbeeld in een Submit methode).
<EditForm ... EditContext="editContext" OnValidSubmit="Submit" ...>
    <DataAnnotationsValidator />
    <ValidationSummary style="@displaySummary" />

    ...

    <button type="submit" disabled="@formInvalid">Submit</button>
</EditForm>

@code {
    private string displaySummary = "display:none";

    ...

    private void Submit()
    {
        displaySummary = "display:block";
    }
}

DataAnnotationsValidator validatiegedrag

Het DataAnnotationsValidator onderdeel heeft dezelfde validatievolgorde en een kortsluitingsgedrag als System.ComponentModel.DataAnnotations.Validator. De volgende regels worden toegepast bij het valideren van een exemplaar van het type T:

  1. Lideigenschappen T worden gevalideerd, inclusief recursief valideren van geneste objecten.
  2. Kenmerken T op typeniveau worden gevalideerd.
  3. De IValidatableObject.Validate methode wordt uitgevoerd als T deze wordt geïmplementeerd.

Als een van de voorgaande stappen een validatiefout produceert, worden de resterende stappen overgeslagen.