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

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

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:

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.

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 dei dati, solo l'attributo [Remote] di convalida non è supportato in Blazor.

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, è necessario 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 altre informazioni, vedere Ciclo di vita 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),
                new() { "For a 'Defense' ship classification, " +
                "'Description' is required." });
        }

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

<h1>Starfleet Starship Database</h1>

<h2>New Ship Entry Form</h2>

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

@code {
    private CustomValidation? customValidation;

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

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

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

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

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

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

<h1>Starfleet Starship Database</h1>

<h2>New Ship Entry Form</h2>

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

@code {
    private CustomValidation? customValidation;

    public Starship? Model { get; set; }

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

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

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

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

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

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 modello di Blazor Web App progetto.
  • Modello Starship () della sezione Modulo di esempio dell'articolo Componenti di input.Starship.cs
  • 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 ospitata Blazor WebAssemblycreata dal modello di Blazor WebAssembly progetto. L'approccio è supportato per qualsiasi soluzione ospitata sicura Blazor descritta nella documentazione sulla sicurezza ospitataBlazor WebAssembly.
  • Modello Starship () della sezione Modulo di esempio dell'articolo Componenti di input.Starship.cs
  • 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 = 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
        {
            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
        {
            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 <ul> generato<div>.

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 attributo di convalida personalizzato, passare il contesto di convalida durante la creazione di MemberName ValidationResult.

CustomValidator.cs:

using System;
using System.ComponentModel.DataAnnotations;

public class CustomValidator : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, 
        ValidationContext validationContext)
    {
        ...

        return new ValidationResult("Validation message to user.",
            new[] { validationContext.MemberName });
    }
}

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

Microsoft.AspNetCore.Components.DataAnnotations.Validation è un pacchetto che riempie le lacune nell'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[Compare] . [CompareProperty]

Modelli annidati, tipi di raccolta e tipi complessi

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

Per convalidare l'intero grafico a oggetti del modello associato, incluse le proprietà di tipo complesso e raccolta, usare l'oggetto ObjectGraphDataAnnotationsValidator fornito dal pacchetto sperimentaleMicrosoft.AspNetCore.Components.DataAnnotations.Validation:

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

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 altre informazioni, vedere Ciclo di vita 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 formInvalid true.

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