Condividi tramite


convalida dei moduli di base Blazor di ASP.NET

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 10 di questo articolo.

Avviso

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Questo articolo illustra come usare la convalida nei Blazor moduli.

Convalida del modulo

Negli scenari di convalida dei moduli di base, un'istanza EditForm può usare istanze dichiarate EditContext e ValidationMessageStore per convalidare i campi modulo. Un gestore per l'evento OnValidationRequested dell'oggetto esegue la EditContext logica di convalida personalizzata. Il risultato del gestore aggiorna l'istanza ValidationMessageStore .

La convalida dei moduli di base è utile nei casi in cui il modello del modulo è definito all'interno del componente che ospita il modulo, come membri direttamente nel componente o in una sottoclasse. L'uso di un componente validator è consigliato in cui viene usata una classe di modello indipendente in diversi componenti.

In Blazor Web Apps la convalida lato client richiede un circuito attivo BlazorSignalR . La convalida lato client non è disponibile per i moduli nei componenti che hanno adottato il rendering statico lato server (SSR statico). I moduli che adottano ssr statici vengono convalidati nel server dopo l'invio del modulo.

Nel componente seguente il HandleValidationRequested metodo del gestore cancella tutti i messaggi di convalida esistenti chiamando ValidationMessageStore.Clear prima di convalidare il modulo.

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

Componente Validator annotazioni dati e convalida personalizzata

Il DataAnnotationsValidator componente associa la convalida delle annotazioni dei dati a un oggetto a catena EditContext. Per abilitare la convalida delle annotazioni dei dati è necessario il DataAnnotationsValidator componente . Per usare un sistema di convalida diverso rispetto alle annotazioni dei dati, usare un'implementazione personalizzata anziché il DataAnnotationsValidator componente. Le implementazioni del framework per DataAnnotationsValidator sono disponibili per l'ispezione nell'origine di riferimento:

Per informazioni dettagliate sul comportamento di convalida, vedere la sezione relativa al DataAnnotationsValidator comportamento di convalida .

Se è necessario abilitare il supporto per la convalida delle annotazioni dei dati per un EditContext nel codice, chiamare EnableDataAnnotationsValidation con un IServiceProvider iniettato (@inject IServiceProvider ServiceProvider) nel EditContext. Per un esempio avanzato, vedere il NotifyPropertyChangedValidationComponent componente nel framework Blazor ASP.NET Core BasicTestApp (dotnet/aspnetcore repository GitHub). In una versione di produzione dell'esempio, sostituire l'argomento new TestServiceProvider() del fornitore di servizi con un IServiceProvider iniettato.

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Blazor esegue due tipi di convalida:

  • La convalida dei campi viene eseguita quando l'utente esce da un campo. Durante la convalida del campo, il DataAnnotationsValidator componente associa tutti i risultati di convalida segnalati al campo.
  • La convalida del modello viene eseguita quando l'utente invia il modulo. Durante la convalida del modello, il DataAnnotationsValidator componente tenta di determinare il campo in base al nome del membro segnalato dal risultato della convalida. I risultati della convalida non associati a un singolo membro sono associati al modello anziché a un campo.

Negli scenari di convalida personalizzati:

Esistono due approcci generali per ottenere la convalida personalizzata, descritti nelle due sezioni successive di questo articolo:

  • Convalida manuale tramite l'eventoOnValidationRequested: convalidare manualmente i campi di un modulo con la convalida delle annotazioni dei dati e il codice personalizzato per i controlli dei campi quando viene richiesta la convalida tramite un gestore eventi assegnato all'eventoOnValidationRequested.
  • Componenti del validator: uno o più componenti di validator personalizzati possono essere usati per elaborare la convalida per moduli diversi nella stessa pagina o nello stesso modulo in passaggi diversi dell'elaborazione dei moduli, ad esempio la convalida client seguita dalla convalida del server.

Convalida manuale con l'evento OnValidationRequested

È possibile convalidare manualmente un modulo con un gestore eventi personalizzato assegnato all'evento EditContext.OnValidationRequested per gestire un oggetto ValidationMessageStore.

Il framework Blazor fornisce il componente DataAnnotationsValidator per aggiungere il supporto di convalida aggiuntivo ai moduli basato su attributi di convalida (data annotations).

Richiamando l'esempio di componente precedente Starship8 , il HandleValidationRequested metodo viene assegnato a OnValidationRequested, in cui è possibile eseguire la convalida manuale nel codice C#. Alcune modifiche illustrano la combinazione della convalida manuale esistente con le annotazioni dei dati tramite un DataAnnotationsValidator e un attributo di convalida applicato al Holodeck modello.

Fare riferimento al System.ComponentModel.DataAnnotations namespace nelle direttive del Razor componente nella parte superiore della definizione del componente:

@using System.ComponentModel.DataAnnotations

Aggiungere una Id proprietà al Holodeck modello con un attributo di convalida per limitare la lunghezza della stringa a sei caratteri:

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

Aggiungere un DataAnnotationsValidator componente (<DataAnnotationsValidator />) al modulo. In genere, il componente viene inserito immediatamente sotto il <EditForm> tag, ma è possibile inserirlo in qualsiasi punto del formato:

<DataAnnotationsValidator />

Modificare il comportamento di invio del modulo nel <EditForm> tag da OnSubmit a OnValidSubmit, in modo da garantire che il modulo sia valido prima di eseguire il metodo del gestore eventi assegnato:

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

Nel <EditForm>, aggiungere un campo per la proprietà Id.

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

Dopo aver apportato le modifiche precedenti, il comportamento del modulo corrisponde alla specifica seguente:

  • La convalida delle annotazioni dei dati nella proprietà Id non provoca un errore di convalida quando il campo Id perde semplicemente il focus. La convalida viene eseguita quando l'utente seleziona il Update pulsante.
  • Qualsiasi convalida manuale che si desidera eseguire nel HandleValidationRequested metodo assegnato all'evento del OnValidationRequested modulo viene eseguita quando l'utente seleziona il pulsante del Update modulo. Nel codice esistente dell'esempio Starship8 di componente, l'utente deve selezionare una o entrambe le caselle di controllo per convalidare il modulo.
  • Il modulo non elabora il Submit metodo finché non vengono superate le annotazioni dei dati e la convalida manuale.

Componenti del validator

I componenti di validator supportano la convalida dei moduli gestendo un ValidationMessageStore oggetto per l'oggetto di EditContextun modulo.

Il framework fornisce il componente per allegare il supporto di convalida ai moduli in base agli attributi di convalida (annotazioni dei dati).The Blazor framework provides the DataAnnotationsValidator component to attach validation support to forms based on validation attributes (data annotations). È possibile creare componenti di validator personalizzati per elaborare i messaggi di convalida per moduli diversi nella stessa pagina o nello stesso modulo in passaggi diversi di elaborazione dei moduli, ad esempio la convalida client seguita dalla convalida del server. L'esempio di componente validator illustrato in questa sezione, CustomValidation, viene usato nelle sezioni seguenti di questo articolo:

Dei validator predefiniti per l'annotazione

Nota

Gli attributi di convalida dell'annotazione dei dati personalizzati possono essere usati anziché componenti di validator personalizzati in molti casi. Gli attributi personalizzati applicati al modello del modulo vengono attivati con l'uso del DataAnnotationsValidator componente. Se usato con la convalida del server, tutti gli attributi personalizzati applicati al modello devono essere eseguibili nel server. Per altre informazioni, vedere la sezione Attributi di convalida personalizzati.

Creare un componente validator da ComponentBase:

  • Il form EditContext è un parametro a catena del componente.
  • Quando il componente di convalida viene inizializzato, viene creato un nuovo ValidationMessageStore oggetto per mantenere un elenco corrente di errori di modulo.
  • L'archivio messaggi riceve errori quando il codice dello sviluppatore nel componente del modulo chiama il DisplayErrors metodo . Gli errori vengono passati al DisplayErrors metodo in un oggetto Dictionary<string, List<string>>. Nel dizionario la chiave è il nome del campo modulo con uno o più errori. Il valore è l'elenco degli errori.
  • I messaggi vengono cancellati quando si è verificato uno dei seguenti:
    • La convalida viene richiesta in quando EditContext viene generato l'evento OnValidationRequested . Tutti gli errori vengono cancellati.
    • Un campo cambia nel modulo quando viene generato l'evento OnFieldChanged . Vengono cancellati solo gli errori per il campo.
    • Il ClearErrors metodo viene chiamato dal codice dello sviluppatore. Tutti gli errori vengono cancellati.

Aggiornare lo spazio dei nomi nella classe seguente in modo che corrisponda allo spazio dei nomi dell'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();
    }
}

Importante

Quando si deriva da , è ComponentBase specificare uno spazio dei nomi . Se non si specifica uno spazio dei nomi, viene generato un errore di compilazione:

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

Il {CLASS NAME} segnaposto è il nome della classe del componente. L'esempio di validator personalizzato in questa sezione specifica lo spazio dei nomi BlazorSampledi esempio .

Nota

Le espressioni lambda anonime sono gestori eventi registrati per OnValidationRequested e OnFieldChanged nell'esempio precedente. Non è necessario implementare e annullare IDisposable la sottoscrizione dei delegati dell'evento in questo scenario. Per ulteriori informazioni, vedere eliminazione dei componenti di ASP.NET Core Razor.

Convalida della logica di business con un componente validator

Per la convalida generale della logica di business, usare un componente validator che riceve errori di modulo in un dizionario.

La convalida di base è utile nei casi in cui il modello del modulo è definito all'interno del componente che ospita il modulo, come membri direttamente nel componente o in una sottoclasse. L'uso di un componente validator è consigliato in cui viene usata una classe di modello indipendente in diversi componenti.

Nell'esempio seguente :

  • Viene usata una versione abbreviata del Starfleet Starship Database modulo (Starship3 componente) della sezione Modulo di esempio dell'articolo Componenti di input che accetta solo la classificazione e la descrizione della astronave. La convalida dell'annotazione dei dati non viene attivata durante l'invio di moduli perché il DataAnnotationsValidator componente non è incluso nel modulo.
  • Viene CustomValidation usato il componente della sezione Componenti validator di questo articolo.
  • La convalida richiede un valore per la descrizione della nave (Description) se l'utente seleziona la classificazione della nave "DefenseClassification".

Quando i messaggi di convalida vengono impostati nel componente, vengono aggiunti al validator ValidationMessageStore e visualizzati nel EditFormriepilogo della convalida.

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

Nota

In alternativa all'uso dei componenti di convalida, è possibile usare gli attributi di convalida delle annotazioni dei dati. Gli attributi personalizzati applicati al modello del modulo vengono attivati con l'uso del DataAnnotationsValidator componente. Se usato con la convalida del server, gli attributi devono essere eseguibili nel server. Per altre informazioni, vedere la sezione Attributi di convalida personalizzati.

Convalida del server con un componente validator

Questa sezione è incentrata sugli Blazor Web App scenari, ma l'approccio per qualsiasi tipo di app che usa la convalida del server con l'API Web adotta lo stesso approccio generale.

Questa sezione è incentrata sugli scenari ospitati Blazor WebAssembly , ma l'approccio per qualsiasi tipo di app che usa la convalida del server con l'API Web adotta lo stesso approccio generale.

La convalida del server è supportata oltre alla convalida client:

  • Elaborare la convalida client nel formato con il DataAnnotationsValidator componente .
  • Quando il modulo supera la convalida client (OnValidSubmit viene chiamato), inviare a un'API del server back-end per l'elaborazione EditContext.Model dei moduli.
  • Elaborare la convalida del modello nel server.
  • L'API server include sia la convalida delle annotazioni dei dati del framework predefinite che la logica di convalida personalizzata fornita dallo sviluppatore. Se la convalida viene superata nel server, elaborare il modulo e restituire un codice di stato di esito positivo (200 - OK). Se la convalida non riesce, restituisce un codice di stato dell'errore (400 - Bad Request) e gli errori di convalida del campo.
  • Disabilitare il modulo in caso di esito positivo o visualizzare gli errori.

La convalida di base è utile nei casi in cui il modello del modulo è definito all'interno del componente che ospita il modulo, come membri direttamente nel componente o in una sottoclasse. L'uso di un componente validator è consigliato in cui viene usata una classe di modello indipendente in diversi componenti.

L'esempio seguente si basa su:

  • Oggetto Blazor Web App con componenti Interactive WebAssembly creati dal modelloBlazor Web App progetto.
  • Modello Starship () della Starship.cs dell'articolo Componenti di input.
  • Componente CustomValidation illustrato nella sezione Componenti validator.

Posizionare il Starship modello (Starship.cs) in un progetto di libreria di classi condiviso in modo che i progetti client e server possano usare il modello. Aggiungere o aggiornare lo spazio dei nomi in modo che corrisponda allo spazio dei nomi dell'app condivisa , ad esempio namespace BlazorSample.Shared. Poiché il modello richiede annotazioni di dati, verificare che la libreria di classi condivisa usi il framework condiviso o aggiungere il System.ComponentModel.Annotations pacchetto al progetto condiviso.

Nota

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli sotto Installare e gestire pacchetti in Flusso di lavoro dell'utilizzo di pacchetti (documentazione di NuGet). Confermare le versioni corrette del pacchetto all'indirizzo NuGet.org.

Nel progetto principale di Blazor Web Appaggiungere un controller per elaborare le richieste di convalida starship e restituire messaggi di convalida non riusciti. Aggiornare gli spazi dei nomi nell'ultima using istruzione per il progetto della libreria di classi condivisa e per namespace la classe controller. Oltre alla convalida delle annotazioni dei dati client e server, il controller verifica che venga fornito un valore per la descrizione della nave (Description) se l'utente seleziona la Defense classificazione della nave (Classification).

  • Soluzione ospitataBlazor WebAssemblycreata dal modelloBlazor WebAssembly progetto. L'approccio è supportato per qualsiasi soluzione ospitata sicura Blazor descritta nella Blazor WebAssembly sulla sicurezza ospitata.
  • Modello Starship () della Starship.cs dell'articolo Componenti di input.
  • Componente CustomValidation illustrato nella sezione Componenti validator.

Inserire il Starship modello (Starship.cs) nel progetto della Shared soluzione in modo che le app client e server possano usare il modello. Aggiungere o aggiornare lo spazio dei nomi in modo che corrisponda allo spazio dei nomi dell'app condivisa , ad esempio namespace BlazorSample.Shared. Poiché il modello richiede annotazioni di dati, aggiungere il System.ComponentModel.Annotations pacchetto al Shared progetto.

Nota

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli sotto Installare e gestire pacchetti in Flusso di lavoro dell'utilizzo di pacchetti (documentazione di NuGet). Confermare le versioni corrette del pacchetto all'indirizzo NuGet.org.

Server Nel progetto aggiungere un controller per elaborare le richieste di convalida starship e restituire messaggi di convalida non riusciti. Aggiornare gli spazi dei nomi nell'ultima using istruzione per il Shared progetto e per namespace la classe controller. Oltre alla convalida delle annotazioni dei dati client e server, il controller verifica che venga fornito un valore per la descrizione della nave (Description) se l'utente seleziona la Defense classificazione della nave (Classification).

La convalida per la Defense classificazione della spedizione si verifica solo sul server nel controller perché il modulo successivo non esegue la stessa convalida lato client quando il modulo viene inviato al server. La convalida del server senza convalida client è comune nelle app che richiedono la convalida della logica di business privata dell'input utente nel server. Ad esempio, le informazioni private dei dati archiviati per un utente potrebbero essere necessarie per convalidare l'input dell'utente. I dati privati ovviamente non possono essere inviati al client per la convalida del client.

Nota

Il StarshipValidation controller in questa sezione usa Microsoft Identity 2.0. L'API Web accetta solo token per gli utenti che hanno l'ambito "API.Access" per questa API. È necessaria una personalizzazione aggiuntiva se il nome dell'ambito dell'API è diverso da API.Access.

Per altre informazioni sulla sicurezza, vedere:

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

Confermare o aggiornare lo spazio dei nomi del controller precedente (BlazorSample.Server.Controllers) in modo che corrisponda allo spazio dei nomi dei controller dell'app.

Quando si verifica un errore di convalida dell'associazione di modelli nel server, una ApiController (ApiControllerAttribute) restituisce in genere una risposta di richiesta non valida predefinita con .ValidationProblemDetails La risposta contiene più dati degli errori di convalida, come illustrato nell'esempio seguente quando tutti i campi del Starfleet Starship Database modulo non vengono inviati e la convalida del modulo non riesce:

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

Nota

Per illustrare la risposta JSON precedente, è necessario disabilitare la convalida client del modulo per consentire l'invio di campi vuoti o usare uno strumento per inviare una richiesta direttamente all'API server, ad esempio Firefox Browser Developer.

Se l'API server restituisce la risposta JSON predefinita precedente, è possibile che il client analizzi la risposta nel codice sviluppatore per ottenere gli elementi figlio del nodo per l'elaborazione degli errori di errors convalida dei moduli. È scomodo scrivere codice per sviluppatori per analizzare il file. L'analisi manuale di JSON richiede la generazione di errori Dictionary<string, List<string>> dopo la chiamata ReadFromJsonAsynca . Idealmente, l'API server deve restituire solo gli errori di convalida, come illustrato nell'esempio seguente:

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

Per modificare la risposta dell'API del server in modo che restituisca solo gli errori di convalida, modificare il delegato richiamato sulle azioni con annotate ApiControllerAttribute nel Program file. Per l'endpoint DELL'API (/StarshipValidation), restituire un oggetto BadRequestObjectResult con .ModelStateDictionary Per qualsiasi altro endpoint API, mantenere il comportamento predefinito restituendo il risultato dell'oggetto con un nuovo ValidationProblemDetailsoggetto .

Aggiungere lo Microsoft.AspNetCore.Mvc spazio dei nomi all'inizio del Program file nel progetto principale di Blazor Web App:

using Microsoft.AspNetCore.Mvc;

Program Nel file aggiungere o aggiornare il metodo di estensione seguente AddControllersWithViews e aggiungere la chiamata seguente a 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));
            }
        };
    });

Se si aggiungono controller al progetto principale di Blazor Web App per la prima volta, gli endpoint del controller di mapping vengono posizionati quando si inserisce il codice precedente che registra i servizi per i controller. Nell'esempio seguente vengono usate le route predefinite del controller:

app.MapDefaultControllerRoute();

Nota

L'esempio precedente registra in modo esplicito i servizi controller chiamando AddControllersWithViews per attenuare automaticamente gli attacchi XSRF/CSRF (Cross-Site Request Forgery). Se si usa AddControllerssemplicemente , l'antiforgeria non viene abilitata automaticamente.

Per altre informazioni sul routing del controller e sulle risposte agli errori di convalida, vedere le risorse seguenti:

.Client Nel progetto aggiungere il CustomValidation componente illustrato nella sezione Componenti validator. Aggiornare lo spazio dei nomi in modo che corrisponda all'app , ad esempio namespace BlazorSample.Client.

.Client Nel progetto il Starfleet Starship Database modulo viene aggiornato per visualizzare gli errori di convalida del server con l'aiuto del CustomValidation componente. Quando l'API server restituisce messaggi di convalida, vengono aggiunti al CustomValidation componente ValidationMessageStore. Gli errori sono disponibili nel modulo EditContext per la visualizzazione dal riepilogo della convalida del modulo.

Nel componente seguente aggiornare lo spazio dei nomi del progetto condiviso (@using BlazorSample.Shared) allo spazio dei nomi del progetto condiviso. Si noti che il modulo richiede l'autorizzazione, quindi l'utente deve essere connesso all'app per passare al modulo.

Aggiungere lo Microsoft.AspNetCore.Mvc spazio dei nomi all'inizio del Program file nell'app Server :

using Microsoft.AspNetCore.Mvc;

Program Nel file individuare il AddControllersWithViews metodo di estensione e aggiungere la chiamata seguente a 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));
            }
        };
    });

Nota

L'esempio precedente registra in modo esplicito i servizi controller chiamando AddControllersWithViews per attenuare automaticamente gli attacchi XSRF/CSRF (Cross-Site Request Forgery). Se si usa AddControllerssemplicemente , l'antiforgeria non viene abilitata automaticamente.

Client Nel progetto aggiungere il CustomValidation componente illustrato nella sezione Componenti validator. Aggiornare lo spazio dei nomi in modo che corrisponda all'app , ad esempio namespace BlazorSample.Client.

Client Nel progetto il Starfleet Starship Database modulo viene aggiornato per visualizzare gli errori di convalida del server con l'aiuto del CustomValidation componente. Quando l'API server restituisce messaggi di convalida, vengono aggiunti al CustomValidation componente ValidationMessageStore. Gli errori sono disponibili nel modulo EditContext per la visualizzazione dal riepilogo della convalida del modulo.

Nel componente seguente aggiornare lo spazio dei nomi del Shared progetto (@using BlazorSample.Shared) allo spazio dei nomi del progetto condiviso. Si noti che il modulo richiede l'autorizzazione, quindi l'utente deve essere connesso all'app per passare al modulo.

Starship10.razor:

Nota

I moduli basati su EditForm abilitano automaticamente il supporto antiforgery. Il controller deve usare AddControllersWithViews per registrare i servizi controller e abilitare automaticamente il supporto antiforgery per l'API Web.

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

Il .Client progetto di un Blazor Web App deve anche registrare un HttpClient per le richieste HTTP POST a un controller API Web back-end. Confermare o aggiungere quanto segue al .Client file del Program progetto:

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

L'esempio precedente imposta l'indirizzo di base con builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress), che ottiene l'indirizzo di base per l'app ed è in genere derivato dal <base> valore del href tag nella pagina host.

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

Nota

In alternativa all'uso di un componente di convalida, è possibile usare gli attributi di convalida delle annotazioni dati. Gli attributi personalizzati applicati al modello del modulo vengono attivati con l'uso del DataAnnotationsValidator componente. Se usato con la convalida del server, gli attributi devono essere eseguibili nel server. Per altre informazioni, vedere la sezione Attributi di convalida personalizzati.

Nota

L'approccio di convalida del server in questa sezione è adatto per uno degli esempi di soluzioni ospitate Blazor WebAssembly in questo set di documentazione:

InputText in base all'evento di input

Usare il InputText componente per creare un componente personalizzato che usa l'evento oninput (input) anziché l'evento onchange (change). Uso della convalida dei input campi trigger di evento in ogni sequenza di tasti.

Il componente seguente CustomInputText eredita il componente del InputText framework e imposta l'associazione di eventi all'evento oninput (input).

CustomInputText.razor:

@inherits InputText

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

Il CustomInputText componente può essere usato ovunque InputText venga usato. Il componente seguente usa il componente condiviso 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; }
    }
}

Componenti del riepilogo della convalida e del messaggio di convalida

Il ValidationSummary componente riepiloga tutti i messaggi di convalida, simili all'helper tag di riepilogo della convalida:

<ValidationSummary />

Inviare messaggi di convalida per un modello specifico con il Model parametro :

<ValidationSummary Model="Model" />

Il ValidationMessage<TValue> componente visualizza i messaggi di convalida per un campo specifico, simile all'helper tag del messaggio di convalida. Specificare il campo per la convalida con l'attributo e un'espressione For lambda che denomina la proprietà del modello:

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

I ValidationMessage<TValue> componenti e ValidationSummary supportano attributi arbitrari. Qualsiasi attributo che non corrisponde a un parametro del componente viene aggiunto all'elemento o <div> generato<ul>.

Controllare lo stile dei messaggi di convalida nel foglio di stile dell'app (wwwroot/css/app.css o wwwroot/css/site.css). La classe predefinita validation-message imposta il colore del testo dei messaggi di convalida su rosso:

.validation-message {
    color: red;
}

Determinare se un campo modulo è valido

Utilizzare EditContext.IsValid per determinare se un campo è valido senza ottenere messaggi di convalida.

Supportato, ma non consigliato:

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

Raccomandato:

var isValid = editContext.IsValid(fieldIdentifier);

Attributi di convalida personalizzati

Per assicurarsi che un risultato di convalida sia associato correttamente a un campo quando si usa un

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

Inserire i servizi negli attributi di convalida personalizzati tramite .ValidationContext Nell'esempio seguente viene illustrato un modulo di insalata chef che convalida l'input dell'utente con inserimento delle dipendenze (DI).

La SaladChef classe indica l'elenco di ingredienti di astronave approvato per un'insalata Di dieci avanti.

SaladChef.cs:

namespace BlazorSample;

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

Eseguire la registrazione SaladChef nel contenitore di inserimento delle dipendenze dell'app nel Program file:

builder.Services.AddTransient<SaladChef>();

Il IsValid metodo della classe seguente SaladChefValidatorAttribute ottiene il SaladChef servizio dall'inserimento delle dipendenze per controllare l'input dell'utente.

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

Il componente seguente convalida l'input dell'utente applicando (SaladChefValidatorAttribute[SaladChefValidator]) alla stringa di ingrediente insalata (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);
}

Attributi della classe CSS di convalida personalizzata

Gli attributi della classe CSS di convalida personalizzati sono utili quando si esegue l'integrazione con framework CSS, ad esempio Bootstrap.

Per specificare attributi di classe CSS di convalida personalizzati, iniziare fornendo stili CSS per la convalida personalizzata. Nell'esempio seguente vengono specificati stili validi (validField) e non validi (invalidField).

Aggiungere le classi CSS seguenti al foglio di stile dell'app:

.validField {
    border-color: lawngreen;
}

.invalidField {
    background-color: tomato;
}

Creare una classe derivata da FieldCssClassProvider che controlla i messaggi di convalida dei campi e applica lo stile valido o non valido appropriato.

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

Impostare la CustomFieldClassProvider classe come Provider di classi CSS field nell'istanza del EditContext modulo con 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; }
    }
}

Nell'esempio precedente viene verificata la validità di tutti i campi modulo e viene applicato uno stile a ogni campo. Se il modulo deve applicare stili personalizzati solo a un subset dei campi, applicare CustomFieldClassProvider gli stili in modo condizionale. Nell'esempio seguente CustomFieldClassProvider2 viene applicato solo uno stile al Name campo . Per tutti i campi con nomi che non corrispondono Namea , string.Empty viene restituito e non viene applicato alcuno stile. Usando la reflection, il campo viene confrontato con la proprietà o il nome del campo del membro del modello, non un oggetto id assegnato all'entità HTML.

CustomFieldClassProvider2.cs:

using Microsoft.AspNetCore.Components.Forms;

public class CustomFieldClassProvider2 : FieldCssClassProvider
{
    public override string GetFieldCssClass(EditContext editContext,
        in FieldIdentifier fieldIdentifier)
    {
        if (fieldIdentifier.FieldName == "Name")
        {
            var isValid = editContext.IsValid(fieldIdentifier);

            return isValid ? "validField" : "invalidField";
        }

        return string.Empty;
    }
}
using Microsoft.AspNetCore.Components.Forms;

public class CustomFieldClassProvider2 : FieldCssClassProvider
{
    public override string GetFieldCssClass(EditContext editContext,
        in FieldIdentifier fieldIdentifier)
    {
        if (fieldIdentifier.FieldName == "Name")
        {
            var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();

            return isValid ? "validField" : "invalidField";
        }

        return string.Empty;
    }
}

Nota

La corrispondenza del nome del campo nell'esempio precedente fa distinzione tra maiuscole e minuscole, pertanto un membro della proprietà del modello designato "Name" deve corrispondere a un controllo condizionale su "Name":

  • Corrisponde correttamente:fieldId.FieldName == "Name"
  • Non riesce a trovare la corrispondenza:fieldId.FieldName == "name"
  • Non riesce a trovare la corrispondenza:fieldId.FieldName == "NAME"
  • Non riesce a trovare la corrispondenza:fieldId.FieldName == "nAmE"

Aggiungere una proprietà aggiuntiva a Model, ad esempio:

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

Aggiungere l'oggetto Description al CustomValidationForm modulo del componente:

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

Aggiornare l'istanza EditContext nel metodo del OnInitialized componente per usare il nuovo provider di classi CSS field:

editContext?.SetFieldCssClassProvider(new CustomFieldClassProvider2());

Poiché al campo non viene applicata Description una classe di convalida CSS, non viene applicato lo stile. Tuttavia, la convalida dei campi viene eseguita normalmente. Se vengono forniti più di 10 caratteri, il riepilogo della convalida indica l'errore:

La descrizione è troppo lunga.

Nell'esempio seguente :

  • Lo stile CSS personalizzato viene applicato al Name campo.

  • Qualsiasi altro campo applica logica simile alla Blazorlogica predefinita e usa gli Blazorstili di convalida CSS del campo predefinito, modified con valid o invalid. Tieni presente che per gli stili predefiniti non devi aggiungerli al foglio di stile dell'app se l'app è basata su un Blazor modello di progetto. Per le app non basate su un Blazor modello di progetto, gli stili predefiniti possono essere aggiunti al foglio di stile dell'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";
            }
        }
    }
}

Aggiornare l'istanza EditContext nel metodo del componente per usare il provider di OnInitialized classi CSS field precedente:

editContext.SetFieldCssClassProvider(new CustomFieldClassProvider3());

Utilizzo di CustomFieldClassProvider3:

  • Il Name campo usa gli stili CSS di convalida personalizzati dell'app.
  • Il Description campo usa logica simile alla logica Blazore agli Blazorstili di convalida CSS dei campi predefiniti.

Convalida a livello di classe con IValidatableObject

La convalida a livello di classe con IValidatableObject (documentazione dell'API) è supportata per i Blazor modelli di modulo. IValidatableObject la convalida viene eseguita solo quando il modulo viene inviato e solo se tutte le altre convalide hanno esito positivo.

Blazor pacchetto di convalida delle annotazioni dei dati

Nota

Il Microsoft.AspNetCore.Components.DataAnnotations.Validation pacchetto non è più consigliato per le app destinate a .NET 10 o versione successiva. Per altre informazioni, vedere la sezione Oggetti annidati, tipi di raccolta e tipi complessi .

Il Microsoft.AspNetCore.Components.DataAnnotations.Validation pacchetto riempie le lacune dell'esperienza di convalida usando il DataAnnotationsValidator componente. Il pacchetto è attualmente sperimentale.

Avviso

Il Microsoft.AspNetCore.Components.DataAnnotations.Validation pacchetto ha una versione più recente di versione candidata in NuGet.org. Continuare a usare il pacchetto finale candidato sperimentale in questo momento. Le funzionalità sperimentali vengono fornite allo scopo di valutarne la validità e potrebbero non essere presenti in una versione stabile. Per altri aggiornamenti, guardare il repository GitHub Annunci, il dotnet/aspnetcore repository GitHub o questa sezione dell'argomento.

Attributo [CompareProperty]

Non CompareAttribute funziona bene con il DataAnnotationsValidator componente perché DataAnnotationsValidator non associa il risultato della convalida a un membro specifico. Ciò può comportare un comportamento incoerente tra la convalida a livello di campo e quando l'intero modello viene convalidato in un invio. Il Microsoft.AspNetCore.Components.DataAnnotations.Validation pacchetto sperimentale introduce un attributo di convalida aggiuntivo, ComparePropertyAttribute, che si adatta a queste limitazioni. In un'app Blazor è una sostituzione diretta per l'attributo[CompareProperty][Compare] .

Oggetti annidati e tipi di raccolta

Blazor La convalida dei moduli include il supporto per la convalida delle proprietà degli oggetti annidati e degli elementi della raccolta con l'oggetto predefinito DataAnnotationsValidator.

Per creare un modulo convalidato, usare un DataAnnotationsValidator componente all'interno di un EditForm componente, esattamente come in precedenza.

Per acconsentire esplicitamente alla funzionalità di convalida degli oggetti annidati e dei tipi di raccolta:

  1. Chiamare il metodo di estensione AddValidation nel file Program in cui vengono registrati i servizi.
  2. Dichiarare i tipi di modello di modulo in un file di classe C#, non in un Razor componente (.razor).
  3. Annotare il tipo di modello radice con l'attributo [ValidatableType].

Senza seguire i passaggi precedenti, il comportamento di convalida del modulo non include la convalida del modello annidato e del tipo di raccolta.

L'esempio seguente illustra gli ordini dei clienti con la convalida migliorata del modulo (dettagli omessi per brevità):

In Program.cs, invocare AddValidation sulla raccolta di servizi:

builder.Services.AddValidation();

Nella classe seguente Order , l'attributo [ValidatableType] è obbligatorio nel tipo di modello di primo livello. Gli altri tipi vengono individuati automaticamente. OrderItem e ShippingAddress non vengono visualizzati per brevità, ma la convalida nidificata e delle raccolte funziona allo stesso modo in tali tipi, se fossero stati mostrati.

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

Nel seguente componente OrderPage, il componente DataAnnotationsValidator è presente nel componente 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();
}

Il requisito di dichiarare i tipi di modello al di fuori dei Razor componenti (.razor file) è dovuto al fatto che sia la nuova funzionalità di convalida che il Razor compilatore stesso usano un generatore di origine. Attualmente, l'output di un generatore di origine non può essere usato come input per un altro generatore di origine.

Per indicazioni sull'uso di modelli di convalida da un assembly diverso, vedere la sezione Usare modelli di convalida da un assembly diverso .

Oggetti annidati, tipi di raccolta e tipi complessi

Nota

Per le app destinate a .NET 10 o versioni successive, non è più consigliabile usare il pacchetto Microsoft.AspNetCore.Components.DataAnnotations.Validation e l'approccio descritto in questa sezione. È consigliabile usare le funzionalità di convalida predefinite del DataAnnotationsValidator componente.

Blazor fornisce il supporto per convalidare l'input del modulo usando annotazioni di dati con l'oggetto predefinito DataAnnotationsValidator. Tuttavia, DataAnnotationsValidator in .NET 9 o versioni precedenti convalida solo le proprietà di primo livello del modello associato al form che non sono proprietà di tipo complesso o di raccolta.

Per convalidare l'intero oggetto grafico dell'intero modello associato, incluse le proprietà di tipo complesso e raccolta, usare l'oggetto ObjectGraphDataAnnotationsValidator fornito dal pacchetto sperimentaleMicrosoft.AspNetCore.Components.DataAnnotations.Validation in .NET 9 o versioni precedenti:

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

Annotare le proprietà del modello con [ValidateComplexType]. Nelle classi di modello seguenti la ShipDescription classe contiene annotazioni di dati aggiuntive da convalidare quando il modello è associato al modulo:

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

Usare modelli di convalida da un assembly diverso

Per la convalida del modello definita in un assembly diverso, ad esempio una libreria o il .Client progetto di un Blazor Web App:

  • Se la libreria è una libreria di classi normale (non basata sugli Microsoft.NET.Sdk.Web o Microsoft.NET.Sdk.Razor SDK), aggiungere alla libreria un riferimento al pacchetto per il Microsoft.Extensions.Validation pacchetto NuGet. Sono necessari passaggi aggiuntivi per le librerie di classi semplici, descritte più avanti in questa sezione.
  • Creare un metodo nella libreria o nel progetto .Client che riceve un'istanza IServiceCollection come argomento e chiama AddValidation su di essa.
  • Nell'app chiamare sia il metodo che AddValidation.

L'approccio precedente comporta la convalida dei tipi da entrambi gli assembly.

Nell'esempio seguente, il metodo AddValidationForTypesInClient è creato per il progetto .Client di un oggetto per convalida utilizzando i tipi definiti nel progetto Blazor Web App.

ServiceCollectionExtensions.cs (nel .Client progetto):

namespace BlazorSample.Client.Extensions;

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

Nel file del progetto server Program, aggiungere lo spazio dei nomi e chiamare il metodo di estensione della raccolta di servizi del progetto .Client (AddValidationForTypesInClient) e AddValidation:

using BlazorSample.Client.Extensions;

...

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

I nuovi attributi del Microsoft.Extensions.Validation pacchetto (ValidatableTypeAttribute e SkipValidationAttribute) vengono pubblicati come sperimentali in .NET 10. Il pacchetto è progettato per fornire una nuova infrastruttura condivisa per le funzionalità di convalida tra framework e la pubblicazione di tipi sperimentali offre maggiore flessibilità per la progettazione finale dell'API pubblica per un supporto migliore nei framework di utilizzo.

Nelle Blazor app i tipi vengono resi disponibili tramite un attributo incorporato generato. Se un progetto di app Web che usa l'SDK Microsoft.NET.Sdk.Web () o un RCL che usa l'SDK <Project Sdk="Microsoft.NET.Sdk.Web"> (Microsoft.NET.Sdk.Razor) contiene <Project Sdk="Microsoft.NET.Sdk.Razor"> componenti (Razor), il framework genera automaticamente un attributo interno all'interno del progetto (.razor, Microsoft.Extensions.Validation.Embedded.ValidatableTypeMicrosoft.Extensions.Validation.Embedded.SkipValidation). Questi tipi sono intercambiabili con gli attributi effettivi e non contrassegnati come sperimentali. Nella maggior parte dei casi, gli sviluppatori usano gli [ValidatableType]/[SkipValidation] attributi nelle classi senza preoccuparsi dell'origine.

Tuttavia, l'approccio precedente non è fattibile nelle librerie di classi semplici che usano l'SDK Microsoft.NET.Sdk (<Project Sdk="Microsoft.NET.Sdk">). L'uso dei tipi in una libreria di classi normale genera un avviso di analisi del codice:

ASP0029: 'Microsoft.Extensions.Validation.ValidatableTypeAttribute' è solo a scopo di valutazione ed è soggetto a modifiche o rimozione negli aggiornamenti futuri. Eliminare questa diagnostica per continuare.

L'avviso può essere eliminato usando uno degli approcci seguenti:

  • Proprietà <NoWarn> nel file di progetto:

    <PropertyGroup>
      <NoWarn>$(NoWarn);ASP0029</NoWarn>
    </PropertyGroup>
    
  • Una pragma direttiva in cui l'attributo viene usato:

    #pragma warning disable ASP0029
    [Microsoft.Extensions.Validation.ValidatableType]
    #pragma warning restore ASP0029
    
  • Regola di un file EditorConfig (.editorconfig):

    dotnet_diagnostic.ASP0029.severity = none
    

Se la soppressione dell'avviso non è accettabile, creare manualmente l'attributo incorporato nella libreria che viene generato automaticamente dal Web e dagli SDK Razor.

ValidatableTypeAttribute.cs:

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

Usare lo spazio dei nomi esatto (Microsoft.Extensions.Validation.Embedded) e il nome della classe (ValidatableTypeAttribute) affinché il generatore di codice di convalida possa rilevare e utilizzare il tipo. È possibile dichiarare un'istruzione globale using per lo spazio dei nomi, con un'istruzione global using Microsoft.Extensions.Validation.Embedded; o con un <Using Include="Microsoft.Extensions.Validation.Embedded" /> elemento nel file di progetto della libreria.

Indipendentemente dall'approccio adottato, indicare la presenza della soluzione alternativa per un aggiornamento futuro del codice. Gli aggiornamenti del framework per semplificare l'adozione dei tipi di convalida nelle librerie di classi semplici sono pianificati per .NET 11 (novembre 2026).

Abilitare il pulsante di invio in base alla convalida del modulo

Per abilitare e disabilitare il pulsante submit in base alla convalida del modulo, l'esempio seguente:

  • Usa una versione abbreviata del modulo precedente Starfleet Starship Database (Starship3 componente) della sezione Modulo di esempio dell'articolo Componenti di input che accetta solo un valore per l'ID della spedizione. Le altre Starship proprietà ricevono valori predefiniti validi quando viene creata un'istanza del Starship tipo.
  • Usa il modulo EditContext per assegnare il modello quando il componente viene inizializzato.
  • Convalida il modulo nel callback del OnFieldChanged contesto per abilitare e disabilitare il pulsante di invio.
  • Implementa IDisposable e annulla la sottoscrizione del gestore eventi nel Dispose metodo . Per ulteriori informazioni, vedere eliminazione dei componenti di ASP.NET Core Razor.

Nota

Quando si assegna a EditForm.EditContext, non assegnare anche un oggetto EditForm.Model a EditForm.

Starship14.razor:

@page "/starship-14"
@implements IDisposable
@inject ILogger<Starship14> Logger

<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship14">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <div>
        <label>
            Identifier:
            <InputText @bind-Value="Model!.Id" />
        </label>
    </div>
    <div>
        <button type="submit" disabled="@formInvalid">Submit</button>
    </div>
</EditForm>

@code {
    private bool formInvalid = false;
    private EditContext? editContext;

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

    protected override void OnInitialized()
    {
        Model ??=
            new()
                {
                    Id = "NCC-1701",
                    Classification = "Exploration",
                    MaximumAccommodation = 150,
                    IsValidatedDesign = true,
                    ProductionDate = new DateTime(2245, 4, 11)
                };
        editContext = new(Model);
        editContext.OnFieldChanged += HandleFieldChanged;
    }

    private void HandleFieldChanged(object? sender, FieldChangedEventArgs e)
    {
        if (editContext is not null)
        {
            formInvalid = !editContext.Validate();
            StateHasChanged();
        }
    }

    private void Submit() => Logger.LogInformation("Submit: Processing form");

    public void Dispose()
    {
        if (editContext is not null)
        {
            editContext.OnFieldChanged -= HandleFieldChanged;
        }
    }
}
@page "/starship-14"
@implements IDisposable
@inject ILogger<Starship14> Logger

<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship14">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <div>
        <label>
            Identifier:
            <InputText @bind-Value="Model!.Id" />
        </label>
    </div>
    <div>
        <button type="submit" disabled="@formInvalid">Submit</button>
    </div>
</EditForm>

@code {
    private bool formInvalid = false;
    private EditContext? editContext;

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

    protected override void OnInitialized()
    {
        Model ??=
            new()
                {
                    Id = "NCC-1701",
                    Classification = "Exploration",
                    MaximumAccommodation = 150,
                    IsValidatedDesign = true,
                    ProductionDate = new DateTime(2245, 4, 11)
                };
        editContext = new(Model);
        editContext.OnFieldChanged += HandleFieldChanged;
    }

    private void HandleFieldChanged(object? sender, FieldChangedEventArgs e)
    {
        if (editContext is not null)
        {
            formInvalid = !editContext.Validate();
            StateHasChanged();
        }
    }

    private void Submit() => Logger.LogInformation("Submit: Processing form");

    public void Dispose()
    {
        if (editContext is not null)
        {
            editContext.OnFieldChanged -= HandleFieldChanged;
        }
    }
}
@page "/starship-14"
@implements IDisposable
@inject ILogger<Starship14> Logger

<EditForm EditContext="editContext" OnValidSubmit="Submit">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <div>
        <label>
            Identifier: 
            <InputText @bind-Value="Model!.Id" />
        </label>
    </div>
    <div>
        <button type="submit" disabled="@formInvalid">Submit</button>
    </div>
</EditForm>

@code {
    private bool formInvalid = false;
    private EditContext? editContext;

    private Starship? Model { get; set; }

    protected override void OnInitialized()
    {
        Model ??=
            new()
            {
                Id = "NCC-1701",
                Classification = "Exploration",
                MaximumAccommodation = 150,
                IsValidatedDesign = true,
                ProductionDate = new DateTime(2245, 4, 11)
            };
        editContext = new(Model);
        editContext.OnFieldChanged += HandleFieldChanged;
    }

    private void HandleFieldChanged(object? sender, FieldChangedEventArgs e)
    {
        if (editContext is not null)
        {
            formInvalid = !editContext.Validate();
            StateHasChanged();
        }
    }

    private void Submit()
    {
        Logger.LogInformation("Submit called: Processing the form");
    }

    public void Dispose()
    {
        if (editContext is not null)
        {
            editContext.OnFieldChanged -= HandleFieldChanged;
        }
    }
}

Se un modulo non viene precaricato con valori validi e si vuole disabilitare il Submit pulsante al caricamento del modulo, impostare su formInvalidtrue.

Un effetto collaterale dell'approccio precedente è che un riepilogo di convalida (ValidationSummary componente) viene popolato con campi non validi dopo che l'utente interagisce con un campo qualsiasi. Risolvere questo scenario in uno dei modi seguenti:

  • Non usare un ValidationSummary componente nel form.
  • Rendere visibile il ValidationSummary componente quando viene selezionato il pulsante submit (ad esempio, in un Submit metodo).
<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 comportamento di convalida

Il DataAnnotationsValidator componente ha lo stesso ordine di convalida e lo stesso comportamento di corto circuito di System.ComponentModel.DataAnnotations.Validator. Quando si convalida un'istanza di tipo Tvengono applicate le regole seguenti:

  1. Le proprietà membro di T vengono convalidate, inclusa la convalida ricorsiva degli oggetti annidati.
  2. Gli attributi a livello di tipo di T vengono convalidati.
  3. Il IValidatableObject.Validate metodo viene eseguito, se T lo implementa.

Se uno dei passaggi precedenti genera un errore di convalida, i passaggi rimanenti vengono ignorati.