Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão do .NET 10 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a política de suporte do .NET e do .NET Core. Para informações sobre a versão vigente, confira a versão .NET 9 deste artigo.
Este artigo explica como usar a validação em formulários Blazor.
Validação de formulário
Em cenários básicos de validação de formulário, uma instância EditForm pode usar instâncias EditContext e ValidationMessageStore declaradas para validar campos de formulário. Um manipulador para o evento OnValidationRequested do EditContext executa lógica de validação personalizada. O resultado do manipulador atualiza a instância ValidationMessageStore.
A validação básica de formulário é útil nos casos em que o modelo do formulário é definido dentro do componente que hospeda o formulário, seja como membros diretamente no componente, seja em uma subclasse. O uso de um componente validador é recomendável quando uma classe de modelo independente é usada em vários componentes.
Em Blazor Web Apps, a validação do lado do cliente exige um circuito BlazorSignalR ativo. A validação do lado do cliente não está disponível para formulários em componentes que adotaram a renderização estática do lado do servidor (SSR estática). Os formulários que adotam SSR estático são validados no servidor depois que o formulário é enviado.
No componente a seguir, o método de manipulador HandleValidationRequested limpa todas as mensagens de validação existentes chamando ValidationMessageStore.Clear antes de validar o formulário.
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 Validador de Anotações de Dados e validação personalizada
O componente DataAnnotationsValidator anexa a validação de anotações de dados a um EditContext em cascata. A habilitação da validação de anotações de dados requer o componente DataAnnotationsValidator. Para usar um sistema de validação diferente das anotações de dados, use uma implementação personalizada em vez do componente DataAnnotationsValidator. As implementações de estrutura para DataAnnotationsValidator estão disponíveis para inspeção na fonte de referência:
Para obter detalhes sobre o comportamento de validação, consulte a DataAnnotationsValidator seção de comportamento de validação .
Se você precisar habilitar o suporte à validação de anotações de dados para um EditContext no código, chame EnableDataAnnotationsValidation com um IServiceProvider injetado (@inject IServiceProvider ServiceProvider) no EditContext. Para obter um exemplo avançado, consulte o NotifyPropertyChangedValidationComponent componente na estrutura Blazor do ASP.NET Core BasicTestApp (dotnet/aspnetcore repositório GitHub). Em uma versão de produção do exemplo, substitua o argumento new TestServiceProvider() do provedor de serviços por um argumento IServiceProvider injetado.
Observação
Os links de documentação para a fonte de referência do .NET geralmente carregam o branch padrão do repositório, que representa o desenvolvimento atual da próxima versão do .NET. Para selecionar uma marca para uma versão específica, use a lista suspensa para Alternar branches ou marcas. Para saber mais, confira Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).
O Blazor executa dois tipos de validação:
- A Validação de campo ocorre quando o usuário pressiona Tab para sair do campo. Durante a validação de campo, o componente DataAnnotationsValidator associa todos os resultados de validação relatados com o campo.
- A validação do modelo ocorre quando o usuário envia o formulário. Durante a validação do modelo, o componente DataAnnotationsValidator tenta determinar o campo com base no nome do membro que o resultado da validação relata. Os resultados de validação que não estão associados a um membro individual são associados ao modelo em vez de a um campo.
Em cenários de validação personalizados:
- A validação gerencia um ValidationMessageStore para o EditContext de um formulário.
- O DataAnnotationsValidator componente é usado para anexar o suporte de validação a formulários com base em atributos de validação (anotações de dados).
Há duas abordagens gerais para obter a validação personalizada, que são descritas nas próximas duas seções deste artigo:
-
Validação manual usando o
OnValidationRequestedevento: valide manualmente os campos de um formulário com validação de anotações de dados e código personalizado para verificações de campo quando a validação é solicitada por meio de um manipulador de eventos atribuído ao OnValidationRequested evento. - Componentes do validador: um ou mais componentes de validador personalizados podem ser usados para processar a validação de formulários diferentes na mesma página ou no mesmo formulário em diferentes etapas de processamento de formulário (por exemplo, validação de cliente seguida pela validação do servidor).
Validação manual usando o OnValidationRequested evento
Você pode validar manualmente um formulário com um manipulador de eventos personalizado atribuído ao EditContext.OnValidationRequested evento para gerenciar um ValidationMessageStore.
A Blazor estrutura fornece o DataAnnotationsValidator componente para anexar suporte de validação adicional a formulários com base em atributos de validação (anotações de dados).
Ao recordar o exemplo de componente anterior Starship8, o método HandleValidationRequested é atribuído a OnValidationRequested, onde você pode executar a validação manual no código C#. Algumas alterações demonstram a combinação da validação manual existente com a validação por anotações de dados, que usa um DataAnnotationsValidator e um atributo de validação aplicado ao modelo Holodeck.
Faça referência ao namespace System.ComponentModel.DataAnnotations nas diretivas do componente Razor no início do arquivo de definição de componente.
@using System.ComponentModel.DataAnnotations
Adicione uma Id propriedade ao Holodeck modelo com um atributo de validação para limitar o comprimento da cadeia de caracteres a seis caracteres:
[StringLength(6)]
public string? Id { get; set; }
Adicione um DataAnnotationsValidator componente (<DataAnnotationsValidator />) ao formulário. Normalmente, o componente é colocado imediatamente sob a <EditForm> marca, mas você pode colocá-lo em qualquer lugar no formulário:
<DataAnnotationsValidator />
Altere o comportamento de envio do formulário na marca <EditForm> de OnSubmit para OnValidSubmit, o que garante que o formulário seja válido antes de executar o método do manipulador de eventos atribuído.
- OnSubmit="Submit"
+ OnValidSubmit="Submit"
No campo <EditForm>, adicione uma propriedade Id.
<div>
<label>
<InputText @bind-Value="Model!.Id" />
ID (6 characters max)
</label>
<ValidationMessage For="() => Model!.Id" />
</div>
Depois de fazer as alterações anteriores, o comportamento do formulário corresponde à seguinte especificação:
- A validação das anotações de dados na propriedade
Idnão resulta em uma falha de validação quando o campoIdapenas perde o foco. A validação é executada quando o usuário seleciona oUpdatebotão. - Qualquer validação manual que você deseja executar no
HandleValidationRequestedmétodo atribuído ao evento do OnValidationRequested formulário é executada quando o usuário seleciona o botão doUpdateformulário. No código existente do exemplo doStarship8componente, o usuário deve selecionar uma ou ambas as caixas de seleção para validar o formulário. - O formulário não processa o
Submitmétodo até que as anotações de dados e a validação manual passem.
Componentes validador
Os componentes validadores dão suporte à validação de formulário com o gerenciamento de um ValidationMessageStore para um EditContext de um formulário.
A estruturaBlazor fornece o componente DataAnnotationsValidator para anexar o suporte de validação a formulários com base em atributos de validação (anotações de dados). Você pode criar componentes validadores personalizados para processar mensagens de validação para diferentes formulários na mesma página ou no mesmo formulário em diferentes etapas de processamento de formulário (por exemplo, validação de cliente seguida de validação de servidor). O exemplo de componente do validador mostrado nesta seção, CustomValidation, é usado nas seções abaixo deste artigo:
- Validação de lógica de negócios com um componente validador
- Validação de servidor com um componente validador
Entre os validadores de anotação de dados internos, somente o [Remote] atributo de validação não é compatível com o Blazor.
Observação
Os atributos de validação de anotação de dados personalizados podem ser usados no lugar de componentes de validador personalizados em muitos casos. Os atributos personalizados aplicados ao modelo do formulário são ativados com o uso do componente DataAnnotationsValidator. Quando usado com a validação do lado do servidor, é necessário que todos os atributos personalizados aplicados ao modelo possam ser executados no servidor. Para mais informações, consulte a seção Atributos de validação personalizados.
Crie um componente de validador de ComponentBase:
- O formulário EditContext é um parâmetro em cascata do componente.
- Quando o componente validador é inicializado, um novo ValidationMessageStore é criado para manter uma lista atual de erros de formulário.
- O repositório de mensagens recebe erros quando o código do desenvolvedor no componente do formulário chama o método
DisplayErrors. Os erros são transmitidos ao métodoDisplayErrorsem umDictionary<string, List<string>>. No dicionário, a chave é o nome do campo de formulário que tem um ou mais erros. O valor é a lista de erros. - As mensagens são desmarcadas quando uma das seguintes mensagens ocorre:
- A validação é solicitada no EditContext quando o evento OnValidationRequested é gerado. Todos os erros são limpos.
- Um campo é alterado no formulário quando o evento OnFieldChanged é acionado. Somente os erros do campo são limpos.
- O método
ClearErrorsé chamado pelo código do desenvolvedor. Todos os erros são limpos.
Atualize o namespace na classe a seguir para corresponder ao namespace do aplicativo.
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
A especificação de um namespace é obrigatória quando derivado de ComponentBase. A não especificação do namespace resultará em um erro de build:
Tag helpers cannot target tag name '<global namespace>.{CLASS NAME}' because it contains a ' ' character.
O espaço reservado {CLASS NAME} é o nome da classe do componente. O exemplo de validador personalizado nesta seção especifica o namespace de exemplo BlazorSample.
Observação
Expressões lambda anônimas são manipuladores de eventos registrados para OnValidationRequested e OnFieldChanged no exemplo anterior. Não é necessário implementar IDisposable e cancelar a assinatura dos delegados do evento nesse cenário. Para obter mais informações, consulte descarte de componente do ASP.NET CoreRazor.
Validação de lógica de negócios com um componente validador
Para validação geral da lógica de negócios, use um componente validador que recebe erros de formulário em um dicionário.
A validação básica é útil nos casos em que o modelo do formulário é definido dentro do componente que hospeda o formulário, seja como membros diretamente no componente, seja em uma subclasse. O uso de um componente validador é recomendável quando uma classe de modelo independente é usada em vários componentes.
No exemplo a seguir:
- Uma versão reduzida do formulário
Starfleet Starship Database(componenteStarship3) da seção Formulário de exemplo do artigo Componentes de entrada é usada com aceitação apenas da classificação e da descrição da nave estelar. A validação de anotação de dados não é disparada no envio do formulário porque o componente DataAnnotationsValidator não está incluído no formulário. - O componente
CustomValidationda seção componentes validadores deste artigo é usado. - A validação exigirá um valor para a descrição do navio (
Description) se o usuário selecionar a classificação de navio "Defense" (Classification).
Quando as mensagens de validação são definidas no componente, elas são adicionadas ao ValidationMessageStore do validador e mostradas no resumo de validação do EditForm.
Starship9.razor:
@page "/starship-9"
@inject ILogger<Starship9> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship9">
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<div>
<label>
Primary Classification:
<InputSelect @bind-Value="Model!.Classification">
<option value="">
Select classification ...
</option>
<option checked="@(Model!.Classification == "Exploration")"
value="Exploration">
Exploration
</option>
<option checked="@(Model!.Classification == "Diplomacy")"
value="Diplomacy">
Diplomacy
</option>
<option checked="@(Model!.Classification == "Defense")"
value="Defense">
Defense
</option>
</InputSelect>
</label>
</div>
<div>
<label>
Description (optional):
<InputTextArea @bind-Value="Model!.Description" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
private CustomValidation? customValidation;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() =>
Model ??= new() { ProductionDate = DateTime.UtcNow };
private void Submit()
{
customValidation?.ClearErrors();
var errors = new Dictionary<string, List<string>>();
if (Model!.Classification == "Defense" &&
string.IsNullOrEmpty(Model.Description))
{
errors.Add(nameof(Model.Description),
[ "For a 'Defense' ship classification, " +
"'Description' is required." ]);
}
if (errors.Any())
{
customValidation?.DisplayErrors(errors);
}
else
{
Logger.LogInformation("Submit called: Processing the form");
}
}
}
@page "/starship-9"
@inject ILogger<Starship9> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship9">
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<div>
<label>
Primary Classification:
<InputSelect @bind-Value="Model!.Classification">
<option value="">
Select classification ...
</option>
<option checked="@(Model!.Classification == "Exploration")"
value="Exploration">
Exploration
</option>
<option checked="@(Model!.Classification == "Diplomacy")"
value="Diplomacy">
Diplomacy
</option>
<option checked="@(Model!.Classification == "Defense")"
value="Defense">
Defense
</option>
</InputSelect>
</label>
</div>
<div>
<label>
Description (optional):
<InputTextArea @bind-Value="Model!.Description" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
private CustomValidation? customValidation;
[SupplyParameterFromForm]
private Starship? Model { get; set; }
protected override void OnInitialized() =>
Model ??= new() { ProductionDate = DateTime.UtcNow };
private void Submit()
{
customValidation?.ClearErrors();
var errors = new Dictionary<string, List<string>>();
if (Model!.Classification == "Defense" &&
string.IsNullOrEmpty(Model.Description))
{
errors.Add(nameof(Model.Description),
[ "For a 'Defense' ship classification, " +
"'Description' is required." ]);
}
if (errors.Any())
{
customValidation?.DisplayErrors(errors);
}
else
{
Logger.LogInformation("Submit called: Processing the form");
}
}
}
@page "/starship-9"
@inject ILogger<Starship9> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm Model="Model" OnValidSubmit="Submit">
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<div>
<label>
Primary Classification:
<InputSelect @bind-Value="Model!.Classification">
<option value="">Select classification ...</option>
<option value="Exploration">Exploration</option>
<option value="Diplomacy">Diplomacy</option>
<option value="Defense">Defense</option>
</InputSelect>
</label>
</div>
<div>
<label>
Description (optional):
<InputTextArea @bind-Value="Model!.Description" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
private CustomValidation? customValidation;
public Starship? Model { get; set; }
protected override void OnInitialized() =>
Model ??= new() { ProductionDate = DateTime.UtcNow };
private void Submit()
{
customValidation?.ClearErrors();
var errors = new Dictionary<string, List<string>>();
if (Model!.Classification == "Defense" &&
string.IsNullOrEmpty(Model.Description))
{
errors.Add(nameof(Model.Description),
new() { "For a 'Defense' ship classification, " +
"'Description' is required." });
}
if (errors.Any())
{
customValidation?.DisplayErrors(errors);
}
else
{
Logger.LogInformation("Submit called: Processing the form");
}
}
}
Observação
Como alternativa ao uso de componentes validadores, os atributos de validação de anotação de dados podem ser usados. Os atributos personalizados aplicados ao modelo do formulário são ativados com o uso do componente DataAnnotationsValidator. Quando usados com a validação do lado do servidor, é necessário que os atributos possam ser executados no servidor. Para mais informações, consulte a seção Atributos de validação personalizados.
Validação de servidor com um componente validador
Esta seção aborda cenários Blazor Web App, porém a abordagem geral é a mesma para qualquer tipo de aplicativo que utiliza a validação no servidor com a API da Web.
Esta seção se concentra em cenários Blazor WebAssembly hospedados, mas a abordagem para qualquer tipo de aplicativo que usa a validação de servidor com a API Web adota a mesma abordagem geral.
Há suporte à validação de servidor, além da validação do lado do cliente:
- Processe a validação do cliente no formulário com o componente DataAnnotationsValidator.
- Quando o formulário passar pela validação do cliente (OnValidSubmit é chamado), envie o EditContext.Model a uma API de servidor de back-end para processamento de formulário.
- Processe a validação do modelo no servidor.
- A API do servidor inclui a validação de anotações de dados da estrutura interna e a lógica de validação personalizada fornecida pelo desenvolvedor. Se a validação for aprovada no servidor, processe o formulário e envie de volta um código de status de êxito (
200 - OK). Se a validação falhar, retorne um código de status de falha (400 - Bad Request) e os erros de validação de campo. - Desabilite o formulário com êxito ou exiba os erros.
A validação básica é útil nos casos em que o modelo do formulário é definido dentro do componente que hospeda o formulário, seja como membros diretamente no componente, seja em uma subclasse. O uso de um componente validador é recomendável quando uma classe de modelo independente é usada em vários componentes.
O exemplo abaixo baseia-se em:
- Um Blazor Web App com componentes WebAssembly interativos criados com base no modelo Blazor Web App do projeto.
- O modelo
Starship(Starship.cs) da seção Formulário de exemplo do artigo Componentes de entrada. - O componente
CustomValidationmostrado na seção Componentes validadores.
Coloque o modelo Starship (Starship.cs) em um projeto de biblioteca de classes compartilhada para que os projetos de cliente e servidor possam usar o modelo. Adicione ou atualize o namespace para corresponder ao namespace do aplicativo compartilhado (por exemplo, namespace BlazorSample.Shared). Como o modelo requer anotações de dados, confirme se a biblioteca de classes compartilhada usa a estrutura compartilhada ou adicione o pacote System.ComponentModel.Annotations ao projeto compartilhado.
Observação
Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.
No projeto principal do Blazor Web App, adicione um controlador para processar solicitações de validação de starship e retornar mensagens de validação com falha. Atualize os namespaces na última instrução using do projeto da biblioteca de classes compartilhada e o namespace para a classe do controlador. Além da validação das anotações de dados do cliente e do servidor, o controlador valida se um valor é fornecido para a descrição de envio (Description ) caso o usuário selecione a classificação de envio Defense (Classification ).
- Uma solução Blazor WebAssembly hospedada criada com base no modelo de projeto Blazor WebAssembly. A abordagem é compatível com qualquer uma das soluções Blazor hospedadas seguras descritas na documentação de segurança hospedada Blazor WebAssembly.
- O modelo
Starship(Starship.cs) da seção Formulário de exemplo do artigo Componentes de entrada. - O componente
CustomValidationmostrado na seção Componentes validadores.
Coloque o modelo Starship (Starship.cs) no projeto Shared da solução para que os aplicativos cliente e servidor possam usar o modelo. Adicione ou atualize o namespace para corresponder ao namespace do aplicativo compartilhado (por exemplo, namespace BlazorSample.Shared). Como o modelo requer anotações de dados, adicione o pacote System.ComponentModel.Annotations ao projeto Shared.
Observação
Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.
No projeto Server, adicione um controlador para processar solicitações de validação de starship e retornar mensagens de validação com falha. Atualize os namespaces na última instrução using do projeto Shared e o namespace da classe do controlador. Além da validação das anotações de dados do cliente e do servidor, o controlador valida se um valor é fornecido para a descrição de envio (Description ) caso o usuário selecione a classificação de envio Defense (Classification ).
A validação para a classificação de envio Defense ocorre apenas no servidor no controlador porque o formulário seguinte não executa o mesmo cliente de validação quando o formulário é enviado para o servidor. A validação do servidor sem validação do cliente é comum em aplicativos que exigem a validação da lógica de negócios privada da entrada do usuário no servidor. Por exemplo, informações privadas de dados armazenados para um usuário podem ser exigidas para validar a entrada do usuário. Dados privados, obviamente, não podem ser enviados ao cliente para validação do cliente.
Observação
O controlador StarshipValidation nesta seção usa o Microsoft Identity 2.0. A API Web aceita apenas tokens para usuários que têm o escopo "API.Access" para essa API. Será necessário fazer uma personalização adicional se o nome do escopo da API for diferente de API.Access.
Para obter mais informações sobre segurança, confira:
- Autenticação e autorização do ASP.NET Core Blazor (e os outros artigos no nó BlazorSegurança e Identity)
- Documentação da plataforma de identidade da Microsoft
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);
}
}
Confirme ou atualize o namespace do controlador anterior (BlazorSample.Server.Controllers) para corresponder ao namespace dos controladores do aplicativo.
Quando ocorre um erro de validação de associação de modelo no servidor, um ApiController (ApiControllerAttribute) normalmente retorna uma resposta de solicitação inválida padrão com um ValidationProblemDetails. A resposta contém mais dados do que apenas os erros de validação, conforme mostrado no exemplo a seguir, quando todos os campos do formulário Starfleet Starship Database não são enviados e o formulário falha na validação:
{
"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)." ]
}
}
Observação
Para demonstrar a resposta JSON anterior, é necessário desabilitar a validação do formulário do cliente para permitir o envio de formulário de campo vazio ou usar uma ferramenta para enviar uma solicitação diretamente à API do servidor, como Desenvolvedor do Navegador Firefox.
Se a API do servidor retornar a resposta JSON padrão anterior, é possível que o cliente analise a resposta no código do desenvolvedor para obter os filhos do nó errors para o processamento de erros de validação de formulários. É inconveniente escrever código do desenvolvedor para analisar o arquivo. Analisar o JSON manualmente requer a produção de um Dictionary<string, List<string>> de erros após chamar ReadFromJsonAsync. Idealmente, a API do servidor só deve retornar os erros de validação, como mostra o seguinte exemplo:
{
"Id": [ "The Id field is required." ],
"Classification": [ "The Classification field is required." ],
"IsValidatedDesign": [ "This form disallows unapproved ships." ],
"MaximumAccommodation": [ "Accommodation invalid (1-100000)." ]
}
Para modificar a resposta da API do servidor e fazer com que ela retorna apenas os erros de validação, altere o delegado invocado em ações anotadas com ApiControllerAttribute no arquivo Program. Para o ponto de extremidade da API (/StarshipValidation), retorne um BadRequestObjectResult com o ModelStateDictionary. Para outros pontos de extremidade de API, preserve o comportamento padrão retornando o resultado do objeto com um novo ValidationProblemDetails.
Adicione o namespace Microsoft.AspNetCore.Mvc à parte superior do arquivo Program no projeto principal do Blazor Web App:
using Microsoft.AspNetCore.Mvc;
No arquivo Program, adicione ou atualize o seguinte método de extensão AddControllersWithViews e adicione a seguinte chamada 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 você estiver adicionando controladores ao projeto principal do Blazor Web App pela primeira vez, mapeie os pontos de extremidade do controlador quando colocar o código anterior que registra serviços de controladores. O exemplo a seguir usa rotas de controlador padrão:
app.MapDefaultControllerRoute();
Observação
O exemplo anterior registra explicitamente os serviços do controlador chamando AddControllersWithViews para atenuar automaticamente os ataques de XSRF/CSRF (solicitação intersite forjada). Se você usar somente AddControllers, a antifalsificação não será habilitada automaticamente.
Para obter mais informações sobre o roteamento do controlador e as respostas de erro de falha de validação, consulte os seguintes recursos:
No projeto .Client, adicione o componente CustomValidation mostrado na seção Componentes validadores. Atualize o namespace para corresponder ao aplicativo (por exemplo, namespace BlazorSample.Client).
No projeto .Client, o formulário Starfleet Starship Database é atualizado para mostrar erros de validação do servidor com a ajuda do componente CustomValidation. Quando a API do servidor retorna mensagens de validação, elas são adicionadas ao CustomValidation do componente ValidationMessageStore. Os erros estão disponíveis no EditContext do formulário para exibição pelo resumo de validação do formulário.
No componente a seguir, atualize o namespace do projeto compartilhado (@using BlazorSample.Shared) para o namespace do projeto compartilhado. Observe que o formulário requer autorização, ou seja, o usuário precisa ser conectado ao aplicativo para navegar até o formulário.
Adicione o namespace Microsoft.AspNetCore.Mvc à parte superior do arquivo Program no aplicativo Server:
using Microsoft.AspNetCore.Mvc;
No arquivo Program, localize o método de extensão AddControllersWithViews e adicione a seguinte chamada 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));
}
};
});
Observação
O exemplo anterior registra explicitamente os serviços do controlador chamando AddControllersWithViews para atenuar automaticamente os ataques de XSRF/CSRF (solicitação intersite forjada). Se você usar somente AddControllers, a antifalsificação não será habilitada automaticamente.
No projeto Client, adicione o componente CustomValidation mostrado na seção Componentes validadores. Atualize o namespace para corresponder ao aplicativo (por exemplo, namespace BlazorSample.Client).
No projeto Client, o formulário Starfleet Starship Database é atualizado para mostrar erros de validação do servidor com a ajuda do componente CustomValidation. Quando a API do servidor retorna mensagens de validação, elas são adicionadas ao CustomValidation do componente ValidationMessageStore. Os erros estão disponíveis no EditContext do formulário para exibição pelo resumo de validação do formulário.
No componente a seguir, atualize o namespace do projeto Shared (@using BlazorSample.Shared) para o namespace do projeto compartilhado. Observe que o formulário requer autorização, ou seja, o usuário precisa ser conectado ao aplicativo para navegar até o formulário.
Starship10.razor:
Observação
Os formulários criados com EditForm ativam o suporte antifalsificação de forma automática. É necessário que o controlador utilize AddControllersWithViews para registrar serviços de controlador e ativar o suporte antifalsificação de forma automática para a API da 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.";
}
}
}
O projeto .Client de um Blazor Web App também deve registrar um HttpClient para solicitações HTTP POST para um controlador de API Web de back-end. Confirme ou adicione o seguinte ao arquivo .Client projeto Program:
builder.Services.AddScoped(sp =>
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
O exemplo anterior define o endereço base com builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress), que obtém o endereço base do aplicativo e é normalmente derivado do valor <base> da tag href na página do 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.";
}
}
}
Observação
Como alternativa ao uso de componentes validadores, os atributos de validação de anotação de dados podem ser usados. Os atributos personalizados aplicados ao modelo do formulário são ativados com o uso do componente DataAnnotationsValidator. Quando usados com a validação do lado do servidor, é necessário que os atributos possam ser executados no servidor. Para mais informações, consulte a seção Atributos de validação personalizados.
Observação
A abordagem de validação do servidor nessa seção é adequada para qualquer um dos exemplos de solução hospedados do Blazor WebAssembly nesse conjunto de documentação:
InputText com base no evento de entrada
Use o componente InputText para criar um componente personalizado que usa o evento oninput (input) em vez do evento onchange (change). O uso do evento input dispara validação de campo em cada pressionamento de tecla.
O componente CustomInputText a seguir herda o componente InputText estrutura e define a associação de eventos ao evento oninput (input).
CustomInputText.razor:
@inherits InputText
<input @attributes="AdditionalAttributes"
class="@CssClass"
@bind="CurrentValueAsString"
@bind:event="oninput" />
O componente CustomInputText pode ser usado em qualquer lugar em que InputText é usado. O componente a seguir usa o componente CustomInputText compartilhado.
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; }
}
}
Componentes de Mensagem de Validação e Resumo de Validação
O componente ValidationSummary resume todas as mensagens de validação, que são semelhantes ao Auxiliar de marca de resumo de validação:
<ValidationSummary />
Mensagens de validação de saída para um modelo específico com o parâmetro Model:
<ValidationSummary Model="Model" />
O componente ValidationMessage<TValue> exibe mensagens de validação para um campo específico, que é semelhante ao Auxiliar de Marca de Mensagem de Validação. Especifique o campo para validação com o atributo For e uma expressão lambda que nomeia a propriedade do modelo:
<ValidationMessage For="@(() => Model!.MaximumAccommodation)" />
Os componentes ValidationMessage<TValue> e ValidationSummary dão suporte a atributos arbitrários. Todo atributo que não corresponda a um parâmetro de componente é adicionado ao elemento <div> ou <ul> gerado.
Controle o estilo de mensagens de validação na folha de estilos do aplicativo (wwwroot/css/app.css ou wwwroot/css/site.css). A classe padrão validation-message define a cor do texto das mensagens de validação como vermelho:
.validation-message {
color: red;
}
Determinar se um campo de formulário é válido
Use EditContext.IsValid para determinar se um campo é válido sem obter mensagens de validação.
Com suporte, mas não recomendado:
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
Recomendado:
var isValid = editContext.IsValid(fieldIdentifier);
Atributos de validação personalizados
Para garantir que um resultado de validação esteja corretamente associado a um campo ao usar um atributo de validação personalizado, transmita o MemberName do contexto de validação ao criar o ValidationResult.
CustomValidator.cs:
using System;
using System.ComponentModel.DataAnnotations;
public class CustomValidator : ValidationAttribute
{
protected override ValidationResult IsValid(object? value,
ValidationContext validationContext)
{
...
return new ValidationResult("Validation message to user.",
[ validationContext.MemberName! ]);
}
}
using System;
using System.ComponentModel.DataAnnotations;
public class CustomValidator : ValidationAttribute
{
protected override ValidationResult IsValid(object? value,
ValidationContext validationContext)
{
...
return new ValidationResult("Validation message to user.",
new[] { validationContext.MemberName! });
}
}
using System;
using System.ComponentModel.DataAnnotations;
public class CustomValidator : ValidationAttribute
{
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
...
return new ValidationResult("Validation message to user.",
new[] { validationContext.MemberName });
}
}
Injete serviços em atributos de validação personalizados por meio do ValidationContext. O exemplo a seguir demonstra um formulário de chef de salada que valida a entrada do usuário com DI (injeção de dependência).
A classe SaladChef indica a lista de ingredientes da nave estelar aprovados para uma salada do Ten Forward.
SaladChef.cs:
namespace BlazorSample;
public class SaladChef
{
public string[] SaladToppers = { "Horva", "Kanda Root", "Krintar", "Plomeek",
"Syto Bean" };
}
Registre SaladChef no contêiner de DI do aplicativo no arquivo Program:
builder.Services.AddTransient<SaladChef>();
O método IsValid da classe SaladChefValidatorAttribute a seguir obtém o serviço SaladChef da DI para verificar a entrada do usuário.
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));
}
}
O componente a seguir valida a entrada do usuário aplicando o SaladChefValidatorAttribute ([SaladChefValidator]) à cadeia de caracteres de ingrediente de salada (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);
}
Atributos de classe CSS de validação personalizada
Os atributos de classe CSS de validação personalizada são úteis na integração com estruturas CSS, por exemplo, o Bootstrap.
Para especificar atributos de classe CSS de validação personalizada, comece fornecendo estilos CSS para validação personalizada. No exemplo a seguir, estilos válidos (validField) e inválidos (invalidField) são especificados.
Adicione as seguintes classes CSS à folha de estilos do aplicativo:
.validField {
border-color: lawngreen;
}
.invalidField {
background-color: tomato;
}
Crie uma classe derivada dessa FieldCssClassProvider que verifica a validação de mensagens de campo e aplica o estilo válido ou inválido apropriado.
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";
}
}
Defina a classe CustomFieldClassProvider como o Provedor de Classe CSS de Campo na instância EditContext do formulário com 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; }
}
}
O exemplo anterior verifica a validade de todos os campos de formulário e aplica um estilo a cada campo. Se o formulário só deve aplicar estilos personalizados a um subconjunto dos campos, faça com que CustomFieldClassProvider aplique os estilos condicionalmente. O exemplo de CustomFieldClassProvider2 a seguir aplica apenas um estilo ao campo Name. Para todos os campos com nomes que não correspondem a Name, string.Empty é retornado e nenhum estilo é aplicado. Usando reflexão, o campo corresponderá à propriedade ou ao nome do campo do membro do modelo, não a um id atribuído à entidade 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;
}
}
Observação
A correspondência do nome do campo no exemplo anterior diferencia maiúsculas de minúsculas, portanto, um membro de propriedade de modelo designado "Name" deve corresponder a uma verificação condicional em "Name":
-
Corresponde corretamente:
fieldId.FieldName == "Name" -
Falha ao corresponder:
fieldId.FieldName == "name" -
Falha ao corresponder:
fieldId.FieldName == "NAME" -
Falha ao corresponder:
fieldId.FieldName == "nAmE"
Adicione uma propriedade adicional a Model, por exemplo:
[StringLength(10, ErrorMessage = "Description is too long.")]
public string? Description { get; set; }
Adicione a Description ao formulário do componente CustomValidationForm:
<InputText @bind-Value="Model!.Description" />
Atualize a instância EditContext no método OnInitialized do componente para usar o novo Provedor de Classe CSS de campo:
editContext?.SetFieldCssClassProvider(new CustomFieldClassProvider2());
Como uma classe de validação CSS não é aplicada ao campo Description, ela não é estilizada. No entanto, a validação de campo é executada normalmente. Se mais de 10 caracteres forem fornecidos, o resumo da validação indicará o erro:
A descrição é muito longa.
No exemplo a seguir:
O estilo CSS personalizado é aplicado ao campo
Name.Todos os outros campos aplicam lógica semelhante à lógica padrão do Blazor e usam os estilos de validação de CSS de campo padrão do Blazor,
modifiedcomvalidouinvalid. Observe que, para os estilos padrão, você não precisará adicioná-los à folha de estilos do aplicativo se o aplicativo for baseado em um modelo de projeto do Blazor. Para aplicativos que não se baseiam em um modelo de projeto do Blazor, os estilos padrão podem ser adicionados à folha de estilos do aplicativo:.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";
}
}
}
}
Atualize a instância EditContext no método OnInitialized do componente para usar o Provedor de Classe CSS de campo anterior:
editContext.SetFieldCssClassProvider(new CustomFieldClassProvider3());
Usando CustomFieldClassProvider3:
- O campo
Nameusa os estilos CSS de validação personalizados do aplicativo. - O campo
Descriptionusa uma lógica semelhante à lógica do Blazor e aos estilos de validação CSS de campo padrão do Blazor.
Validação em nível de classe com IValidatableObject
A validação de nível de classe com a IValidatableObject (documentação da API) é compatível com modelos de formulário Blazor.
IValidatableObject A validação ocorre somente quando o formulário é enviado e apenas se todas as outras validações forem bem-sucedidas.
Pacote Blazor de validação de anotações de dados
Observação
O Microsoft.AspNetCore.Components.DataAnnotations.Validation pacote não é mais recomendado para aplicativos direcionados ao .NET 10 ou posterior. Para obter mais informações, consulte a seção Objetos aninhados, tipos de coleção e tipos complexos .
O Microsoft.AspNetCore.Components.DataAnnotations.Validation pacote preenche lacunas de experiência de validação usando o DataAnnotationsValidator componente. No momento, o pacote é experimental.
Aviso
O Microsoft.AspNetCore.Components.DataAnnotations.Validation pacote tem uma versão mais recente do release candidate em NuGet.org. Continue a usar o pacote de candidatos à versão experimental no momento. Os recursos experimentais são fornecidos com a finalidade de explorar a viabilidade do recurso e podem não ser fornecidos em uma versão estável. Fique de olho no Repositório de comunicados do GitHub, o Repositório dotnet/aspnetcore no GitHub ou na seção deste tópico para obter mais atualizações.
Atributo [CompareProperty]
O CompareAttribute não funciona bem com o componente DataAnnotationsValidator porque o DataAnnotationsValidator não associa o resultado da validação a um membro específico. Isso pode resultar em um comportamento inconsistente entre a validação no nível do campo e quando todo o modelo é validado em um envio. O Microsoft.AspNetCore.Components.DataAnnotations.Validation pacote experimental apresenta um atributo de validação adicional, ComparePropertyAttributeque funciona em torno dessas limitações. Em um aplicativo Blazor, [CompareProperty] é uma substituição direta do atributo [Compare].
Objetos aninhados e tipos de coleção
A validação de formulário Blazor inclui suporte para validar propriedades de objetos aninhados e itens de coleção com recursos DataAnnotationsValidator internos.
Para criar um formulário validado, use um DataAnnotationsValidator componente dentro de um EditForm componente, exatamente como antes.
Para ativar o recurso de validação para objetos aninhados e tipos de coleção:
- Chame o método de extensão AddValidation no arquivo
Programonde os serviços são registrados. - Declare os tipos de modelo de formulário em um arquivo de classe C#, não em um Razor componente (
.razor). - Anotar o tipo de modelo de formulário raiz com o
[ValidatableType]atributo.
Caso as etapas anteriores não sejam seguidas, a validação de formulários não abrangerá validação de modelos aninhados e tipos de coleção.
O exemplo a seguir demonstra os pedidos do cliente com a validação de formulário aprimorada (detalhes omitidos para brevidade):
No Program.cs, chame AddValidation na coleção de serviços.
builder.Services.AddValidation();
Na classe a seguir Order , o [ValidatableType] atributo é necessário no tipo de modelo de nível superior. Os outros tipos são descobertos automaticamente.
OrderItem e ShippingAddress não são exibidos por motivos de simplificação, mas a validação de tipos aninhados e coleções funcionaria da mesma maneira nesses casos, se estivessem incluídos.
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();
}
No componente OrderPage a seguir, o componente DataAnnotationsValidator está presente no 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();
}
O requisito para declarar os tipos de modelo fora dos Razor componentes (.razor arquivos) deve-se ao fato de que o novo recurso de validação e o Razor próprio compilador estão usando um gerador de origem. Atualmente, a saída de um gerador de origem não pode ser usada como entrada para outro gerador de origem.
Para obter diretrizes sobre como usar modelos de validação de um assembly diferente, consulte a seção Usar modelos de validação de um assembly diferente.
Objetos aninhados, tipos de coleção e tipos complexos
Observação
Para aplicativos direcionados ao .NET 10 ou posterior, não recomendamos mais usar o pacote Microsoft.AspNetCore.Components.DataAnnotations.Validation e a abordagem descritos nesta seção. É recomendável usar os recursos de validação internos do DataAnnotationsValidator componente.
O Blazor dá suporte à validação da entrada do formulário usando anotações de dados com o DataAnnotationsValidator interno. No entanto, o DataAnnotationsValidator .NET 9 ou anterior valida apenas as propriedades de nível superior do modelo associadas ao formulário que não são propriedades de tipo complexo ou coleção.
Para validar todo o grafo de objeto do modelo associado, incluindo propriedades de tipo complexo e de coleção, use o ObjectGraphDataAnnotationsValidator fornecido pelo pacote experimentalMicrosoft.AspNetCore.Components.DataAnnotations.Validation no .NET 9 ou anterior:
<EditForm ...>
<ObjectGraphDataAnnotationsValidator />
...
</EditForm>
Anote propriedades do modelo com [ValidateComplexType]. Nas classes de modelo abaixo, a classe ShipDescription contém anotações de dados adicionais para validar quando o modelo está associado ao formulário:
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; }
}
Usar modelos de validação de um assembly diferente
Para validação de modelo definida em um assembly diferente, como uma biblioteca ou o .Client projeto de um Blazor Web App:
- Se a biblioteca for uma biblioteca de classes simples (não se baseia nos SDKs
ou ), adicione uma referência de pacote à biblioteca para o pacote NuGet . Etapas adicionais são necessárias para bibliotecas de classes simples, que são descritas posteriormente nesta seção. - Crie um método na biblioteca ou
.Clientprojeto que receba uma instância de IServiceCollection como argumento e chame AddValidation. - No aplicativo, chame tanto o método quanto AddValidation.
A abordagem anterior resulta na validação dos tipos de ambos os assemblies.
No exemplo a seguir, o AddValidationForTypesInClient método é criado para o .Client projeto de uma Blazor Web App para validação usando tipos definidos no projeto .Client.
ServiceCollectionExtensions.cs (no .Client projeto):
namespace BlazorSample.Client.Extensions;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddValidationForTypesInClient(
this IServiceCollection collection)
{
return collection.AddValidation();
}
}
No arquivo Program do projeto do servidor, adicione o namespace e chame o método de extensão da coleção de serviços do projeto .Client (AddValidationForTypesInClient) e AddValidation.
using BlazorSample.Client.Extensions;
...
builder.Services.AddValidationForTypesInClient();
builder.Services.AddValidation();
Os novos atributos do Microsoft.Extensions.Validation pacote (ValidatableTypeAttribute e SkipValidationAttribute) são publicados como experimentais no .NET 10. O pacote destina-se a fornecer uma nova infraestrutura compartilhada para recursos de validação entre estruturas e a publicação de tipos experimentais fornece maior flexibilidade para o design final da API pública para obter melhor suporte em estruturas de consumo.
Em Blazor aplicativos, os tipos são disponibilizados por meio de um atributo gerado incorporado. Se um projeto de aplicativo Web que usa o Microsoft.NET.Sdk.Web SDK (<Project Sdk="Microsoft.NET.Sdk.Web">) ou uma RCL que usa o Microsoft.NET.Sdk.Razor SDK (<Project Sdk="Microsoft.NET.Sdk.Razor">) contiver Razor componentes (.razor), a estrutura gerará automaticamente um atributo interno dentro do projeto (Microsoft.Extensions.Validation.Embedded.ValidatableType, Microsoft.Extensions.Validation.Embedded.SkipValidation). Esses tipos são intercambiáveis com os atributos reais e não são marcados como experimentais. Na maioria dos casos, os desenvolvedores usam os [ValidatableType]/[SkipValidation] atributos em suas classes sem preocupação com sua origem.
No entanto, a abordagem anterior não é viável em bibliotecas de classes simples que usam o Microsoft.NET.Sdk SDK (<Project Sdk="Microsoft.NET.Sdk">). O uso dos tipos em uma biblioteca de classes simples resulta em um aviso de análise de código:
ASP0029: 'Microsoft.Extensions.Validation.ValidatableTypeAttribute' é apenas para fins de avaliação e está sujeito a alterações ou remoção em atualizações futuras. Suprima este diagnóstico para continuar.
O aviso pode ser suprimido usando qualquer uma das seguintes abordagens:
Uma
<NoWarn>propriedade no arquivo de projeto:<PropertyGroup> <NoWarn>$(NoWarn);ASP0029</NoWarn> </PropertyGroup>Uma
pragmadiretiva em que o atributo é usado:#pragma warning disable ASP0029 [Microsoft.Extensions.Validation.ValidatableType] #pragma warning restore ASP0029Uma regra de arquivo EditorConfig (
.editorconfig):dotnet_diagnostic.ASP0029.severity = none
Se suprimir o aviso não for aceitável, crie manualmente o atributo incorporado na biblioteca que os SDKs da Web e Razor geram automaticamente.
ValidatableTypeAttribute.cs:
namespace Microsoft.Extensions.Validation.Embedded
{
[AttributeUsage(AttributeTargets.Class)]
internal sealed class ValidatableTypeAttribute : Attribute
{
}
}
Use o namespace exato (Microsoft.Extensions.Validation.Embedded) e o nome da classe (ValidatableTypeAttribute) para que o gerador de origem de validação detecte e use o tipo. Você pode declarar uma instrução global using para o namespace, com a instrução global using Microsoft.Extensions.Validation.Embedded; ou com o item <Using Include="Microsoft.Extensions.Validation.Embedded" /> no arquivo de projeto da biblioteca.
Seja qual for a abordagem adotada, indique a presença da solução alternativa para uma atualização futura em seu código. As atualizações de estrutura para facilitar a adoção de tipos de validação em bibliotecas de classes simples estão previstas para o .NET 11 (novembro de 2026).
Habilitar o botão de envio com base na validação do formulário
Para habilitar e desabilitar o botão de envio com base na validação de formulário, o exemplo abaixo:
- Usa uma versão reduzida do formulário
Starfleet Starship Databaseanterior (componenteStarship3) da seção Formulário de exemplo do artigo Componentes de entrada que aceita apenas um valor para a ID da nave. As outras propriedadesStarshiprecebem valores padrão válidos quando uma instância do tipoStarshipé criada. - Usa o formulário EditContext para atribuir o modelo quando o componente é inicializado.
- Valida o formulário no retorno de chamada OnFieldChanged do contexto para habilitar e desabilitar o botão de envio.
- Implementa IDisposable e cancela a assinatura do manipulador de eventos no método
Dispose. Para obter mais informações, consulte descarte de componente do ASP.NET CoreRazor.
Observação
Ao atribuir ao EditForm.EditContext, não atribua também um EditForm.Model ao 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 um formulário não for pré-carregado com valores válidos e você desejar desabilitar o botão Submit no carregamento do formulário, defina formInvalid como true.
Um efeito colateral da abordagem anterior é que um resumo de validação (componente ValidationSummary) é preenchido com campos inválidos depois que o usuário interage com algum campo. Resolva esse cenário de qualquer uma das seguintes maneiras:
- Não use um componente ValidationSummary no formulário.
- Torne o componente ValidationSummary visível quando o botão de envio estiver selecionado (por exemplo, em um método
Submit).
<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 de validação
O DataAnnotationsValidator componente tem a mesma ordem de validação e o comportamento de curto-circuito que System.ComponentModel.DataAnnotations.Validator. As regras a seguir são aplicadas ao validar uma instância do tipo T:
- As propriedades de
Tmembro são validadas, incluindo a validação recursiva de objetos aninhados. - Os atributos de nível de
Ttipo são validados. - O IValidatableObject.Validate método será executado, se
To implementar.
Se uma das etapas anteriores produzir um erro de validação, as etapas restantes serão ignoradas.