Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Annotazioni
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Avvertimento
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere .NET e .NET Core Support Policy. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Questo articolo illustra i componenti del validator riutilizzabili e la convalida remota. Per le API di convalida dei moduli comuni, incluse le annotazioni dei dati, la convalida direttaEditContext, la visualizzazione dei messaggi, lo stile, lo stato e il comportamento di invio, vedere ASP.NET Core Blazor convalida dei moduli.
Per le regole di convalida basate su modello condivise da Blazor e API minime, vedere Convalida in ASP.NET Core.
Per la convalida nel browser nel rendering lato server statico (SSR statico), vedi convalida dei moduli lato client in ASP.NET Core con SSR staticoBlazor.
Crea un componente validatore
Un componente di validazione incapsula la logica di convalida che usa gli elementi EditContext e ValidationMessageStore di un modulo. Ciò è utile quando lo stesso comportamento di convalida viene usato da diversi moduli o quando arrivano errori da un servizio anziché da attributi di convalida nel modello.
Componente:
- Riceve il form
EditContextcome parametro a catena. - Crea un archivio dei messaggi per i propri errori.
- Cancella gli errori di modulo non aggiornati quando viene richiesta la convalida.
- Cancella gli errori non aggiornati di un campo quando il campo cambia.
- Espone i metodi per la visualizzazione e la cancellazione degli errori.
- Annulla la sottoscrizione dei gestori eventi in caso di eliminazione.
CustomValidation.razor:
@implements IDisposable
@code {
[CascadingParameter]
private EditContext? CurrentEditContext { get; set; }
private ValidationMessageStore messages = default!;
protected override void OnInitialized()
{
if (CurrentEditContext is null)
{
throw new InvalidOperationException(
"CustomValidation requires a cascading EditContext.");
}
messages = new ValidationMessageStore(CurrentEditContext);
CurrentEditContext.OnValidationRequested +=
HandleValidationRequested;
CurrentEditContext.OnFieldChanged += HandleFieldChanged;
}
public void DisplayErrors(IDictionary<string, string[]> errors)
{
foreach (var error in errors)
{
messages.Add(
CurrentEditContext!.Field(error.Key),
error.Value);
}
CurrentEditContext!.NotifyValidationStateChanged();
}
public void ClearErrors()
{
messages.Clear();
CurrentEditContext!.NotifyValidationStateChanged();
}
private void HandleValidationRequested(
object? sender, ValidationRequestedEventArgs e) =>
ClearErrors();
private void HandleFieldChanged(
object? sender, FieldChangedEventArgs e)
{
messages.Clear(e.FieldIdentifier);
CurrentEditContext!.NotifyValidationStateChanged();
}
public void Dispose()
{
if (CurrentEditContext is not null)
{
CurrentEditContext.OnValidationRequested -=
HandleValidationRequested;
CurrentEditContext.OnFieldChanged -= HandleFieldChanged;
}
}
}
Posizionare il componente all'interno di un EditForm elemento e acquisire un riferimento a un componente quando il modulo o un servizio deve visualizzare errori:
<EditForm Model="Model" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<CustomValidation @ref="customValidation" />
<ValidationSummary />
...
</EditForm>
@code {
private CustomValidation? customValidation;
}
Il componente può essere usato insieme a DataAnnotationsValidator. Ogni validator ha un proprio archivio messaggi associato allo stesso EditContexte ValidationMessage visualizza ValidationSummary i messaggi di entrambi i validator.
Per implementare una regola business all'interno del componente validator anziché accettare errori esterni, eseguire la regola da HandleValidationRequested o HandleFieldChanged e aggiungerne i messaggi a messages. Per un esempio più piccolo che esegue questa operazione direttamente in un componente del modulo, vedere ASP.NET Core Blazor convalida dei moduli.
Aggiungere la convalida asincrona
Lo stesso modello di componente supporta il lavoro asincrono:
- In un gestore
OnValidationRequested, chiamaree.AddAsyncValidatorper registrare l'elaborazione a livello di modulo.EditFormattende prima di richiamareOnValidSubmitoOnInvalidSubmit. - In un gestore
OnFieldChanged, chiamaEditContext.RegisterAsyncFieldValidatorper avviare la validazione per quel campo. L'avvio di un'altra convalida per lo stesso campo sostituisce e annulla l'operazione precedente.
Per la convalida asincrona a livello di modulo:
private void HandleValidationRequested(
object? sender, ValidationRequestedEventArgs e) =>
e.AddAsyncValidator(ValidateAsync);
private async Task ValidateAsync(CancellationToken cancellationToken)
{
var field = CurrentEditContext!.Field(nameof(Model.Username));
messages.Clear(field);
var available = await Http.GetFromJsonAsync<bool>(
$"api/usernames/available?value={Uri.EscapeDataString(Model.Username)}",
cancellationToken);
if (!available)
{
messages.Add(field, "The username is already taken.");
}
CurrentEditContext.NotifyValidationStateChanged();
}
Per la convalida asincrona a livello di campo:
private void HandleFieldChanged(
object? sender, FieldChangedEventArgs e)
{
CurrentEditContext!.RegisterAsyncFieldValidator(
e.FieldIdentifier,
token => ValidateFieldAsync(e.FieldIdentifier, token));
}
Passare il token di annullamento fornito all'I/O. Cancellare i messaggi precedenti prima di avviare l'operazione, evitare di pubblicare risultati parziali dopo un'eccezione e chiamare NotifyValidationStateChanged dopo l'aggiornamento dei messaggi.
Un'operazione annullata perché è stata superata o perché la fase di convalida è stata annullata viene scartata. Altre eccezioni portano il campo o il modulo nello stato di errore. Per visualizzare lo stato in attesa e di errore, vedere convalida dei moduli in ASP.NET Core Blazor.
Per un componente completo che combina la convalida asincrona a livello di modulo e per campo, vedere l'esempio seguente:
@implements IDisposable
@inject HttpClient Http
@* A validator component that runs asynchronous validation, both when the whole form is
validated on submit and per field as the user edits the Username field. *@
@code {
[CascadingParameter]
private EditContext? CurrentEditContext { get; set; }
[Parameter, EditorRequired]
public RegistrationModel Model { get; set; } = default!;
private ValidationMessageStore? messages;
protected override void OnInitialized()
{
ArgumentNullException.ThrowIfNull(CurrentEditContext);
messages = new ValidationMessageStore(CurrentEditContext);
CurrentEditContext.OnValidationRequested += OnValidationRequested;
CurrentEditContext.OnFieldChanged += OnFieldChanged;
}
// Registers asynchronous work for the whole form. EditForm awaits it before invoking
// OnValidSubmit.
private void OnValidationRequested(
object? sender, ValidationRequestedEventArgs e) =>
e.AddAsyncValidator(ValidateUsernameAsync);
// Registers asynchronous work for a single field. A new registration supersedes and
// cancels any validation already in flight for the field.
private void OnFieldChanged(object? sender, FieldChangedEventArgs e)
{
if (e.FieldIdentifier.FieldName != nameof(RegistrationModel.Username))
{
return;
}
CurrentEditContext!.RegisterAsyncFieldValidator(
e.FieldIdentifier,
token => CheckAsync(e.FieldIdentifier, token));
}
private Task ValidateUsernameAsync(CancellationToken token) =>
CheckAsync(CurrentEditContext!.Field(nameof(Model.Username)), token);
private async Task CheckAsync(FieldIdentifier field, CancellationToken token)
{
messages!.Clear(field);
var available = await Http.GetFromJsonAsync<bool>(
$"api/usernames/available?value={Uri.EscapeDataString(Model.Username)}",
token);
if (!available)
{
messages.Add(field, "The username is already taken.");
}
CurrentEditContext!.NotifyValidationStateChanged();
}
public void Dispose()
{
if (CurrentEditContext is not null)
{
CurrentEditContext.OnValidationRequested -= OnValidationRequested;
CurrentEditContext.OnFieldChanged -= OnFieldChanged;
}
}
}
Per gli attributi di convalida asincrona nel modello, vedere Convalida in ASP.NET Core.
Il codice del componente di convalida viene eseguito nello stesso ambiente in cui viene eseguito il componente. In Interactive WebAssembly, l'esecuzione avviene nel browser, mentre in Interactive Server avviene sul server tramite il circuito.
Nell'SSR statico, il codice del componente di convalida viene eseguito sul server durante l'invio del modulo e non fornisce la convalida in tempo reale dei campi .NET tra una richiesta e l'altra.
Validazione remota da Interactive WebAssembly
La convalida remota invia i dati del modulo da un componente Interactive WebAssembly a un endpoint del server e aggiunge all'elemento EditContext del modulo gli errori restituiti relativi ai campi. È utile quando una regola richiede dati del server privato, un servizio esterno o un'altra logica che non deve essere eseguita nel browser.
Il modulo:
- Esegue la convalida delle annotazioni dei dati in locale.
- Invia all'endpoint dati di input validi in locale da
OnValidSubmit. - Riceve dal server errori di validazione associati ai campi.
- Aggiunge errori remoti al modulo tramite il componente validator.
OnValidSubmit significa solo che la convalida locale ha avuto esito positivo. Elaborare o salvare il modello solo dopo che la convalida remota ha esito positivo.
Importante
Non inviare dati di convalida privati o regole business al browser. Il server deve convalidare ogni richiesta in modo indipendente perché è possibile ignorare la convalida lato client.
In questo esempio viene convalidata in remoto quando viene inviato il modulo. Per i controlli remoti in tempo reale per campo, usare il modello asincrono di convalida dei campi da Aggiungi convalida asincrona.
Se il modulo WebAssembly è prerenderizzato, anche i servizi lato client devono essere disponibili durante il prerendering. Per gli approcci disponibili, vedere Prerender ASP.NET Core Razor componenti.
Convalidare con un'API minima
Chiamare AddValidation nel progetto server per convalidare i parametri dell'endpoint supportati prima dell'esecuzione del gestore.
Le Microsoft.Extensions.Validation API usate per i metadati di convalida generati sono sperimentali in .NET 10. Per informazioni dettagliate, vedere Convalida in ASP.NET Core.
Se il modello viene dichiarato nel .Client progetto, registrare i metadati di convalida generati in entrambi i progetti come descritto in Convalida in ASP.NET Core.
L'endpoint aggiunge una regola di business privata e restituisce errori indicizzati in base al nome del membro del modello:
app.MapPost("/api/starships/validate", (StarshipModel model) =>
{
Dictionary<string, string[]> errors = [];
if (model.Classification == "Defense" &&
string.IsNullOrWhiteSpace(model.Description))
{
errors[nameof(model.Description)] =
["A defense ship requires a description."];
}
if (errors.Count > 0)
{
return Results.ValidationProblem(errors);
}
return Results.NoContent();
});
app.MapPost("/api/starships/validate", (StarshipModel model) =>
{
Dictionary<string, string[]> errors = [];
if (model.Classification == "Defense" &&
string.IsNullOrWhiteSpace(model.Description))
{
errors[nameof(model.Description)] =
["A defense ship requires a description."];
}
if (errors.Count > 0)
{
return Results.ValidationProblem(errors);
}
return Results.NoContent();
});
La convalida automatica rifiuta annotazioni di dati non valide prima dell'esecuzione del gestore.
ValidationProblem restituisce 400 Bad Request con una errors proprietà contenente messaggi con chiave di campo. Una convalida riuscita restituisce 204 No Content.
Convalidare con un controller API
In una soluzione ospitata Blazor WebAssembly, posizionare il modello condiviso nel progetto Shared e convalidarlo con un controller API nel progetto Server. L'attributo [ApiController] rifiuta automaticamente annotazioni di dati non valide prima dell'esecuzione dell'azione.
[ApiController]
[Route("api/starships/validate")]
public class StarshipValidationController : ControllerBase
{
[HttpPost]
public IActionResult Validate(StarshipModel model)
{
if (model.Classification == "Defense" &&
string.IsNullOrWhiteSpace(model.Description))
{
ModelState.AddModelError(
nameof(model.Description),
"A defense ship requires a description.");
}
if (!ModelState.IsValid)
{
return ValidationProblem(ModelState);
}
return NoContent();
}
}
Registrare e mappare i controller nel progetto server. Il controller restituisce 400 Bad Request una ValidationProblemDetails risposta quando la convalida ha esito negativo e 204 No Content quando ha esito positivo.
Chiamare l'endpoint e visualizzare gli errori
Registrare un oggetto HttpClient nel progetto WebAssembly con l'indirizzo di base dell'app:
builder.Services.AddScoped(sp =>
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped(sp =>
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
Posiziona il componente CustomValidation di Creare un componente di convalida nel modulo:
@page "/"
@using System.Net
@using System.Net.Http.Json
@using BlazorWebAppRemoteValidation.Client.Models
@inject HttpClient Http
<PageTitle>Remote validation</PageTitle>
<h1>Remote validation</h1>
<p>Local validation runs before the form calls the remote endpoint. The endpoint validates the model again and applies a private business rule that requires a description for defense ships.</p>
<EditForm Model="Model" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<CustomValidation @ref="remoteErrors" />
<ValidationSummary />
<p>
<label>
Identifier:
<InputText id="identifier" @bind-Value="Model.Identifier" />
</label>
<ValidationMessage For="() => Model.Identifier" />
</p>
<p>
<label>
Classification:
<InputSelect id="classification" @bind-Value="Model.Classification">
<option value="">Select...</option>
<option value="Exploration">Exploration</option>
<option value="Defense">Defense</option>
</InputSelect>
</label>
<ValidationMessage For="() => Model.Classification" />
</p>
<p>
<label>
Description:
<InputText id="description" @bind-Value="Model.Description" />
</label>
<ValidationMessage For="() => Model.Description" />
</p>
<button id="submit" type="submit">Submit</button>
</EditForm>
@if (accepted)
{
<p id="accepted" role="status">The server accepted the form.</p>
}
@code {
private StarshipModel Model { get; } = new();
private CustomValidation? remoteErrors;
private bool accepted;
private async Task Submit()
{
accepted = false;
using var response = await Http.PostAsJsonAsync(
"api/starships/validate", Model);
if (response.IsSuccessStatusCode)
{
accepted = true;
return;
}
if (response.StatusCode == HttpStatusCode.BadRequest)
{
var problem = await response.Content
.ReadFromJsonAsync<ValidationProblemResponse>()
?? throw new InvalidOperationException(
"The validation response didn't contain a response body.");
remoteErrors!.DisplayErrors(problem.Errors);
return;
}
response.EnsureSuccessStatusCode();
}
private sealed record ValidationProblemResponse(
Dictionary<string, string[]> Errors);
}
@using System.Net
@using System.Net.Http.Json
@inject HttpClient Http
<EditForm Model="Model" OnValidSubmit="Submit">
<DataAnnotationsValidator />
<CustomValidation @ref="remoteErrors" />
<ValidationSummary />
...
</EditForm>
@code {
private StarshipModel Model { get; } = new StarshipModel();
private CustomValidation? remoteErrors;
private async Task Submit()
{
using var response = await Http.PostAsJsonAsync(
"api/starships/validate", Model);
if (response.IsSuccessStatusCode)
{
// Process or save the model.
return;
}
if (response.StatusCode == HttpStatusCode.BadRequest)
{
var problem = await response.Content
.ReadFromJsonAsync<ValidationProblemResponse>();
if (problem is not null)
{
remoteErrors!.DisplayErrors(problem.Errors);
}
return;
}
response.EnsureSuccessStatusCode();
}
private sealed class ValidationProblemResponse
{
public Dictionary<string, string[]> Errors { get; set; } =
new Dictionary<string, string[]>();
}
}
Il componente validator cancella un errore di campo remoto quando il campo cambia, in modo che l'utente possa correggere il valore e inviarlo di nuovo. Proteggere l'endpoint in base ai requisiti di sicurezza dell'applicazione; autenticazione e autorizzazione non rientrano nell'ambito di questo esempio di convalida.
L'esempio completo di validazione remota include l'endpoint dell'host, il modello condiviso, la registrazione della validazione tra assembly, il componente di validazione e il modulo WebAssembly interattivo.
L'esempio di convalida remota .NET 10 illustra lo stesso flusso di convalida in un'app Interattiva automatica con autenticazione e un proxy lato server.