Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este artigo explica como validar a entrada do usuário em um aplicativo ASP.NET Core MVC ou Razor Pages.
Visualize ou baixe o código de exemplo (como fazer o download).
Validação no .NET 10
No .NET 10, as APIs de validação unificadas foram movidas para o Microsoft.Extensions.Validation pacote NuGet. Essa alteração torna as APIs de validação disponíveis fora de ASP.NET cenários HTTP principais.
Para usar as Microsoft.Extensions.Validation APIs:
Adicione a seguinte referência de pacote:
<PackageReference Include="Microsoft.Extensions.Validation" Version="10.0.0" />A funcionalidade permanece a mesma, mas agora requer uma referência explícita ao pacote.
Registrar serviços de validação com injeção de dependência:
builder.Services.AddValidation();
Estado do modelo
O estado do modelo representa erros provenientes de dois subsistemas: vinculação do modelo e validação do modelo. Os erros que se originam da vinculação de modelo geralmente são erros de conversão de dados. Por exemplo, um "x" é inserido em um campo inteiro. A validação do modelo ocorre após a vinculação do modelo e relata erros em que os dados não estão em conformidade com as regras de negócios. Por exemplo, um 0 é inserido em um campo que espera uma classificação entre 1 e 5.
A vinculação do modelo e a validação do modelo ocorrem antes da execução de uma ação do controlador ou de um Razor método manipulador Pages. Para aplicativos Web, é responsabilidade do aplicativo inspecionar ModelState.IsValid e reagir adequadamente. Os aplicativos Web normalmente reexibem a página com uma mensagem de erro, conforme mostrado no exemplo de Páginas a seguir Razor :
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Para ASP.NET Core MVC com controladores e exibições, o exemplo a seguir mostra como verificar ModelState.IsValid dentro de uma ação do controlador:
public async Task<IActionResult> Create(Movie movie)
{
if (!ModelState.IsValid)
{
return View(movie);
}
_context.Movies.Add(movie);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
Os controladores de API da Web não precisam verificar ModelState.IsValid se têm o atributo [ApiController]. Nesse caso, uma resposta HTTP 400 automática contendo detalhes de erro é retornada quando o estado do modelo é inválido. Para obter mais informações, consulte Respostas HTTP 400 automáticas.
Executar novamente a validação
A validação é automática, mas convém repeti-la manualmente. Por exemplo, você pode calcular um valor para uma propriedade e querer executar novamente a validação depois de definir a propriedade para o valor calculado. Para executar novamente a validação, chame ModelStateDictionary.ClearValidationState para limpar a validação específica do modelo que está sendo validado, seguido por TryValidateModel:
public async Task<IActionResult> OnPostTryValidateAsync()
{
var modifiedReleaseDate = DateTime.Now.Date;
Movie.ReleaseDate = modifiedReleaseDate;
ModelState.ClearValidationState(nameof(Movie));
if (!TryValidateModel(Movie, nameof(Movie)))
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Atributos de validação
Os atributos de validação permitem especificar regras de validação para as propriedades do modelo. O exemplo a seguir do aplicativo de exemplo mostra uma classe de modelo que é anotada com atributos de validação. O [ClassicMovie] atributo é um atributo de validação personalizado e os outros são incorporados. Não mostrado é [ClassicMovieWithClientValidator], que mostra uma maneira alternativa de implementar um atributo personalizado.
public class Movie
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; } = null!;
[ClassicMovie(1960)]
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; } = null!;
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
}
Atributos incorporados
Para obter uma lista completa de atributos de validação, consulte o System.ComponentModel.DataAnnotations namespace.
Mensagens de erro
Os atributos de validação permitem especificar a mensagem de erro a ser exibida para entrada inválida. Por exemplo:
[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]
Internamente, os atributos chamam String.Format com um espaço reservado para o nome do campo e, às vezes, espaços reservados adicionais. Por exemplo:
[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]
Quando aplicado a uma Name propriedade, a mensagem de erro criada pelo código anterior seria "O comprimento do nome deve estar entre 6 e 8.".
Para descobrir quais parâmetros são passados para a String.Format mensagem de erro de um atributo específico, consulte o código-fonte DataAnnotations.
Usar nomes de propriedade JSON em erros de validação
Por padrão, quando ocorre um erro de validação, a validação do modelo produz um ModelStateDictionary com o nome da propriedade como chave de erro. Alguns aplicativos, como aplicativos de página única, se beneficiam do uso de nomes de propriedade JSON para erros de validação gerados a partir de APIs da Web. O código a seguir configura a validação para que o SystemTextJsonValidationMetadataProvider use os nomes de propriedade JSON.
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.ModelMetadataDetailsProviders.Add(new SystemTextJsonValidationMetadataProvider());
});
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
O código a seguir configura a validação para que NewtonsoftJsonValidationMetadataProvider use o nome da propriedade JSON ao utilizar Json.NET:
using Microsoft.AspNetCore.Mvc.NewtonsoftJson;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonValidationMetadataProvider());
}).AddNewtonsoftJson();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Para obter um exemplo da política de uso de caixa de camelo, consulte Program.cs no GitHub.
Tipos de referência não anuláveis e atributo [Obrigatório]
O sistema de validação trata parâmetros não anuláveis ou propriedades acopladas como se tivessem um [Required(AllowEmptyStrings = true)] atributo. Ao habilitar Nullable contextos, o MVC começa implicitamente a validar propriedades ou parâmetros não anuláveis como se tivessem sido atribuídos ao [Required(AllowEmptyStrings = true)] atributo. Considere o seguinte código:
public class Person
{
public string Name { get; set; }
}
Se o aplicativo foi criado com <Nullable>enable</Nullable>, um valor ausente para Name em uma postagem de formulário ou JSON resultará em um erro de validação. Isso pode parecer contraditório, já que o [Required(AllowEmptyStrings = true)] atributo está implícito, mas esse é um comportamento esperado porque as cadeias de caracteres vazias são convertidas em nulas por padrão. Use um tipo de referência anulável para permitir que valores nulos ou ausentes sejam especificados para a Name propriedade:
public class Person
{
public string? Name { get; set; }
}
Esse comportamento pode ser desabilitado configurando SuppressImplicitRequiredAttributeForNonNullableReferenceTypes em Program.cs:
builder.Services.AddControllers(
options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);
Validação [necessária] no servidor
No servidor, um valor necessário é considerado ausente se a propriedade for null. Um campo não anulável é sempre válido e a [Required] mensagem de erro do atributo nunca é exibida.
No entanto, a vinculação de modelo para uma propriedade não anulável pode falhar, resultando em uma mensagem de erro como The value '' is invalid. Para especificar uma mensagem de erro personalizada para validação do lado do servidor de tipos não anuláveis, você tem as seguintes opções:
Torne o campo anulável (por exemplo,
decimal?em vez dedecimal). Nulo<Os tipos de valor T> são tratados como tipos anuláveis padrão.Especifique a mensagem de erro padrão a ser usada pela vinculação de modelo, conforme mostrado no exemplo a seguir:
builder.Services.AddRazorPages() .AddMvcOptions(options => { options.MaxModelValidationErrors = 50; options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor( _ => "The field is required."); }); builder.Services.AddSingleton <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();Para obter mais informações sobre erros de vinculação de modelo para os quais você pode definir mensagens padrão, consulte DefaultModelBindingMessageProvider.
Validação [necessária] no cliente
Tipos e cadeias de caracteres não anuláveis são tratados de forma diferente no cliente em comparação com o servidor. Sobre o cliente:
- Um valor é considerado presente somente se a entrada for inserida para ele. Portanto, a validação do lado do cliente lida com tipos não anuláveis da mesma forma que tipos anuláveis.
- O espaço em branco num campo de texto é considerado uma entrada válida pelo método required jQuery Validation. A validação do lado do servidor considera um campo de cadeia de caracteres obrigatório inválido se apenas espaço em branco for inserido.
Como observado anteriormente, os tipos não anuláveis são tratados como se tivessem um [Required(AllowEmptyStrings = true)] atributo. Isso significa que você obtém validação do lado do cliente, mesmo que não aplique o [Required(AllowEmptyStrings = true)] atributo. Mas se você não usar o atributo, receberá uma mensagem de erro padrão. Para especificar uma mensagem de erro personalizada, use o atributo.
Atributo [Remoto]
O atributo [Remote] implementa a validação do lado do cliente que requer a chamada de um método no servidor para determinar se a entrada de campo é válida. Por exemplo, o aplicativo pode precisar verificar se um nome de usuário já está em uso.
Para implementar a validação remota:
Crie um método de ação para o JavaScript chamar. O método remoto jQuery Validation espera uma resposta JSON:
-
truesignifica que os dados de entrada são válidos. -
false,undefined, ounullsignifica que a entrada é inválida. Exiba a mensagem de erro padrão. - Qualquer outra cadeia de caracteres significa que a entrada é inválida. Exiba a cadeia de caracteres como uma mensagem de erro personalizada.
Aqui está um exemplo de um método de ação que retorna uma mensagem de erro personalizada:
[AcceptVerbs("GET", "POST")] public IActionResult VerifyEmail(string email) { if (!_userService.VerifyEmail(email)) { return Json($"Email {email} is already in use."); } return Json(true); }-
Na classe model, anote a propriedade com um
[Remote]atributo que aponta para o método de ação de validação, conforme mostrado no exemplo a seguir:[Remote(action: "VerifyEmail", controller: "Users")] public string Email { get; set; } = null!;
A validação do lado do servidor também precisa ser implementada para clientes que desabilitaram o JavaScript.
Campos adicionais
A AdditionalFields propriedade do [Remote] atributo permite validar combinações de campos em relação aos dados no servidor. Por exemplo, se o User modelo tinha FirstName e LastName propriedades, convém verificar se nenhum usuário existente já tem esse par de nomes. O exemplo a seguir mostra como usar AdditionalFields:
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; } = null!;
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; } = null!;
AdditionalFields pode ser definido explicitamente para as cadeias de caracteres "FirstName" e "LastName", mas usar o operador nameof simplifica a refatoração posterior. O método de ação para essa validação deve aceitar ambos os firstNamelastName argumentos:
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
if (!_userService.VerifyName(firstName, lastName))
{
return Json($"A user named {firstName} {lastName} already exists.");
}
return Json(true);
}
Quando o usuário insere um nome ou sobrenome, o JavaScript faz uma chamada remota para ver se esse par de nomes foi usado.
Para validar dois ou mais campos adicionais, forneça-os como uma lista delimitada por vírgula. Por exemplo, para adicionar uma MiddleName propriedade ao modelo, defina o [Remote] atributo como mostrado no exemplo a seguir:
[Remote(action: "VerifyName", controller: "Users",
AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }
AdditionalFields, como todos os argumentos de atributo, deve ser uma expressão constante. Portanto, não use uma cadeia de caracteres interpolada ou Join para inicializar AdditionalFields.
Alternativas aos atributos internos
Se você precisar de validação não fornecida por atributos internos, poderá:
Atributos personalizados
Para cenários que os atributos de validação internos não manipulam, você pode criar atributos de validação personalizados. Crie uma classe que herda de ValidationAttributee substitua o IsValid método.
O IsValid método aceita um objeto chamado value, que é a entrada a ser validada. Uma sobrecarga também aceita um objeto ValidationContext, que fornece informações adicionais, como a instância do modelo criada pela ligação de modelos.
O exemplo a seguir valida que a data de lançamento de um filme do gênero Clássico não é posterior a um ano especificado. O [ClassicMovie] atributo:
- Só é executado no servidor.
- Para filmes clássicos, valida a data de lançamento:
public class ClassicMovieAttribute : ValidationAttribute
{
public ClassicMovieAttribute(int year)
=> Year = year;
public int Year { get; }
public string GetErrorMessage() =>
$"Classic movies must have a release year no later than {Year}.";
protected override ValidationResult? IsValid(
object? value, ValidationContext validationContext)
{
var movie = (Movie)validationContext.ObjectInstance;
var releaseYear = ((DateTime)value!).Year;
if (movie.Genre == Genre.Classic && releaseYear > Year)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
}
A movie variável no exemplo anterior representa um Movie objeto que contém os dados do envio do formulário. Quando a validação falha, uma mensagem de ValidationResult erro é retornada.
IValidatableObject
O exemplo anterior funciona apenas com Movie tipos. Outra opção para validação de nível de classe é implementar IValidatableObject na classe de modelo, como mostrado no exemplo a seguir:
public class ValidatableMovie : IValidatableObject
{
private const int _classicYear = 1960;
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; } = null!;
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; } = null!;
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
{
yield return new ValidationResult(
$"Classic movies must have a release year no later than {_classicYear}.",
new[] { nameof(ReleaseDate) });
}
}
}
Validação personalizada
O código a seguir mostra como adicionar um erro de modelo depois de examinar o modelo:
if (Contact.Name == Contact.ShortName)
{
ModelState.AddModelError("Contact.ShortName",
"Short name can't be the same as Name.");
}
O código a seguir implementa o teste de validação em um controlador:
if (contact.Name == contact.ShortName)
{
ModelState.AddModelError(nameof(contact.ShortName),
"Short name can't be the same as Name.");
}
O código a seguir verifica se o número de telefone e o e-mail são exclusivos:
public async Task<IActionResult> OnPostAsync()
{
// Attach Validation Error Message to the Model on validation failure.
if (Contact.Name == Contact.ShortName)
{
ModelState.AddModelError("Contact.ShortName",
"Short name can't be the same as Name.");
}
if (_context.Contact.Any(i => i.PhoneNumber == Contact.PhoneNumber))
{
ModelState.AddModelError("Contact.PhoneNumber",
"The Phone number is already in use.");
}
if (_context.Contact.Any(i => i.Email == Contact.Email))
{
ModelState.AddModelError("Contact.Email", "The Email is already in use.");
}
if (!ModelState.IsValid || _context.Contact == null || Contact == null)
{
// if model is invalid, return the page with the model state errors.
return Page();
}
_context.Contact.Add(Contact);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
O código a seguir implementa o teste de validação em um controlador:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,ShortName,Email,PhoneNumber")] Contact contact)
{
// Attach Validation Error Message to the Model on validation failure.
if (contact.Name == contact.ShortName)
{
ModelState.AddModelError(nameof(contact.ShortName),
"Short name can't be the same as Name.");
}
if (_context.Contact.Any(i => i.PhoneNumber == contact.PhoneNumber))
{
ModelState.AddModelError(nameof(contact.PhoneNumber),
"The Phone number is already in use.");
}
if (_context.Contact.Any(i => i.Email == contact.Email))
{
ModelState.AddModelError(nameof(contact.Email), "The Email is already in use.");
}
if (ModelState.IsValid)
{
_context.Add(contact);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(contact);
}
A verificação de um número de telefone ou e-mail exclusivo normalmente também é feita com validação remota.
ValidationResult
Considere o seguinte personalizado:ValidateNameAttribute
public class ValidateNameAttribute : ValidationAttribute
{
public ValidateNameAttribute()
{
const string defaultErrorMessage = "Error with Name";
ErrorMessage ??= defaultErrorMessage;
}
protected override ValidationResult? IsValid(object? value,
ValidationContext validationContext)
{
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
return new ValidationResult("Name is required.");
}
if (value.ToString()!.ToLower().Contains("zz"))
{
return new ValidationResult(
FormatErrorMessage(validationContext.DisplayName));
}
return ValidationResult.Success;
}
}
No código a seguir, o atributo personalizado [ValidateName] é aplicado:
public class Contact
{
public Guid Id { get; set; }
[ValidateName(ErrorMessage = "Name must not contain `zz`")]
public string? Name { get; set; }
public string? Email { get; set; }
public string? PhoneNumber { get; set; }
}
Quando o modelo contém zz, um novo ValidationResult é retornado.
Validação de nó de nível superior
Os nós de nível superior incluem:
- Parâmetro da ação
- Propriedades do controlador
- Parâmetros do manipulador de página
- Propriedades do modelo de página
Os nós de nível superior vinculados ao modelo são validados, além de validar as propriedades do modelo. No exemplo a seguir da sample app, o método VerifyPhone utiliza o RegularExpressionAttribute para validar o parâmetro de ação phone.
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
[RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
if (!ModelState.IsValid)
{
return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
}
return Json(true);
}
Os nós de nível superior podem usar BindRequiredAttribute com atributos de validação. No exemplo a seguir do aplicativo de exemplo, o CheckAge método especifica que o age parâmetro deve ser vinculado a partir da cadeia de caracteres de consulta quando o formulário é enviado:
[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{
Na página Verificar Idade (CheckAge.cshtml), existem dois formulários. O primeiro formulário submete o valor de Age como um parâmetro de cadeia de caracteres de consulta: 99https://localhost:5001/Users/CheckAge?Age=99.
Quando um parâmetro formatado age corretamente da cadeia de caracteres de consulta é enviado, o formulário é validado.
O segundo formulário na página Verificar Idade envia o valor Age no corpo do pedido e a validação falha. A vinculação falha porque o age parâmetro deve vir de uma cadeia de caracteres de consulta.
Erros máximos
A validação para quando o número máximo de erros é atingido (200 por padrão). Você pode configurar esse número com o seguinte código em Program.cs:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
Recursão máxima
ValidationVisitor percorre o gráfico de objetos do modelo que está sendo validado. Para modelos que são profundos ou são infinitamente recursivos, a validação pode resultar em estouro de pilha.
MvcOptions.MaxValidationDepth Fornece uma maneira de interromper a validação antecipadamente se a recursão do visitante exceder uma profundidade configurada. O valor padrão de MvcOptions.MaxValidationDepth é 32.
Curto-circuito automático
A validação é automaticamente anulada (ignorada) se o gráfico do modelo não exigir validação. Os objetos para os quais o tempo de execução ignora a validação incluem coleções de primitivos (como byte[], , string[]Dictionary<string, string>) e gráficos de objetos complexos que não têm validadores.
Validação do lado do cliente
A validação do lado do cliente impede o envio até que o formulário seja válido. O botão Enviar executa JavaScript que envia o formulário ou exibe mensagens de erro.
A validação do lado do cliente evita uma viagem de ida e volta desnecessária ao servidor quando há erros de entrada em um formulário. As seguintes referências de script em _Layout.cshtml e _ValidationScriptsPartial.cshtml suportam a validação no lado do cliente:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.js"></script>
O script de Validação Não Intrusivo do jQuery é uma biblioteca front-end personalizada da Microsoft que se baseia no popular plugin de Validação do jQuery. Sem o jQuery Unobtrusive Validation, você teria que codificar a mesma lógica de validação em dois lugares: uma vez nos atributos de validação do lado do servidor nas propriedades do modelo e, em seguida, novamente nos scripts do lado do cliente. Em vez disso, os Auxiliares de Tag e Auxiliares HTML utilizam os atributos de validação e os metadados de tipo das propriedades do modelo para renderizar atributos HTML 5 data- nos elementos de formulário que precisam de validação. jQuery Unobtrusive Validation analisa os data- atributos e passa a lógica para jQuery Validation, efetivamente "copiando" a lógica de validação do lado do servidor para o cliente. Você pode exibir erros de validação no cliente usando auxiliares de tag, conforme mostrado aqui:
<div class="form-group">
<label asp-for="Movie.ReleaseDate" class="control-label"></label>
<input asp-for="Movie.ReleaseDate" class="form-control" />
<span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>
Os auxiliares de tag anteriores renderizam o seguinte HTML:
<div class="form-group">
<label class="control-label" for="Movie_ReleaseDate">Release Date</label>
<input class="form-control" type="date" data-val="true"
data-val-required="The Release Date field is required."
id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
<span class="text-danger field-validation-valid"
data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>
Observe que os data- atributos na saída HTML correspondem aos atributos de validação para a Movie.ReleaseDate propriedade. O data-val-required atributo contém uma mensagem de erro a ser exibida se o usuário não preencher o campo de data de lançamento. jQuery Unobtrusive Validation passa esse valor para o método jQuery Validation required(), que exibe essa mensagem no elemento span< que o acompanha>.
A validação de tipo de dados é baseada no tipo .NET de uma propriedade, a menos que isso seja substituído por um atributo [DataType]. Os navegadores têm as suas próprias mensagens de erro padrão, mas o pacote de Validação Não-Interferente do jQuery pode substituí-las.
[DataType] atributos e subclasses como [EmailAddress] permitem especificar a mensagem de erro.
Validação discreta
Para obter informações sobre validação discreta, consulte este problema do GitHub.
Adicionar validação a formulários dinâmicos
A Validação Não Intrusiva do jQuery passa a lógica e os parâmetros de validação para a Validação do jQuery quando a página é carregada pela primeira vez. Portanto, a validação não funciona automaticamente em formulários gerados dinamicamente. Para habilitar a validação, diga ao jQuery Unobtrusive Validation para analisar o formulário dinâmico imediatamente após criá-lo. Por exemplo, o código a seguir configura a validação do lado do cliente em um formulário adicionado via AJAX.
$.get({
url: "https://url/that/returns/a/form",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add form. " + errorThrown);
},
success: function(newFormHTML) {
var container = document.getElementById("form-container");
container.insertAdjacentHTML("beforeend", newFormHTML);
var forms = container.getElementsByTagName("form");
var newForm = forms[forms.length - 1];
$.validator.unobtrusive.parse(newForm);
}
})
O $.validator.unobtrusive.parse() método aceita um seletor jQuery para seu único argumento. Esse método instrui o jQuery Unobtrusive Validation a analisar os atributos de formulários data- dentro desse seletor. Os valores desses atributos são então passados para o plug-in de Validação do jQuery.
Adicionar validação a controles dinâmicos
O $.validator.unobtrusive.parse() método funciona em um formulário inteiro, não em controles individuais gerados dinamicamente, como <input> e <select/>. Para reanalisar o formulário, remova os dados de validação que já tinham sido adicionados quando o formulário foi analisado anteriormente, como está demonstrado no exemplo a seguir.
$.get({
url: "https://url/that/returns/a/control",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add control. " + errorThrown);
},
success: function(newInputHTML) {
var form = document.getElementById("my-form");
form.insertAdjacentHTML("beforeend", newInputHTML);
$(form).removeData("validator") // Added by jQuery Validation
.removeData("unobtrusiveValidation"); // Added by jQuery Unobtrusive Validation
$.validator.unobtrusive.parse(form);
}
})
Validação personalizada do lado do cliente
A validação personalizada do lado do cliente é feita ao gerar data- atributos HTML que funcionam com um adaptador personalizado do jQuery Validation. O seguinte código de adaptador de exemplo foi escrito para os [ClassicMovie] atributos e [ClassicMovieWithClientValidator] que foram introduzidos anteriormente neste artigo:
$.validator.addMethod('classicmovie', function (value, element, params) {
var genre = $(params[0]).val(), year = params[1], date = new Date(value);
// The Classic genre has a value of '0'.
if (genre && genre.length > 0 && genre[0] === '0') {
// The release date for a Classic is valid if it's no greater than the given year.
return date.getUTCFullYear() <= year;
}
return true;
});
$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
var element = $(options.form).find('select#Movie_Genre')[0];
options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
options.messages['classicmovie'] = options.message;
});
Para obter informações sobre como escrever adaptadores, consulte a documentação de Validação do jQuery.
O uso de um adaptador para um determinado campo é acionado por data- atributos que:
- Sinalizar o campo como sujeito a validação (
data-val="true"). - Identifique o nome de uma regra de validação e o texto da mensagem de erro (por exemplo,
data-val-rulename="Error message."). - Forneça quaisquer parâmetros adicionais que o validador precise (por exemplo,
data-val-rulename-param1="value").
O exemplo a seguir mostra os data- atributos do atributo do aplicativo de exemploClassicMovie:
<input class="form-control" type="date"
data-val="true"
data-val-classicmovie="Classic movies must have a release year no later than 1960."
data-val-classicmovie-year="1960"
data-val-required="The Release Date field is required."
id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
Como observado anteriormente, os Ajudantes de Tag e os Auxiliares de HTML usam informações de atributos de validação para renderizar data- atributos. Há duas opções para escrever código que resulta na criação de atributos HTML personalizados data- :
- Crie uma classe que deriva de AttributeAdapterBase<TAttribute> e uma classe que implementa IValidationAttributeAdapterProvidere registre seu atributo e seu adaptador em DI. Esse método segue o princípio de responsabilidade única em que o código de validação relacionado ao servidor e ao cliente está em classes separadas. O adaptador também tem a vantagem de que, uma vez que está registrado em DI, outros serviços em DI estão disponíveis para ele, se necessário.
- Implemente IClientModelValidator na sua ValidationAttribute classe. Esse método pode ser apropriado se o atributo não fizer nenhuma validação do lado do servidor e não precisar de nenhum serviço da DI.
AttributeAdapter para validação do lado do cliente
Esse método de renderização de atributos data- em HTML é utilizado pelo atributo ClassicMovie no aplicativo de exemplo. Para adicionar a validação do cliente usando este método:
Crie uma classe de adaptador de atributo para o atributo de validação personalizado. Derive a classe de AttributeAdapterBase<TAttribute>. Crie um
AddValidationmétodo que adicionedata-atributos à saída renderizada, conforme mostrado neste exemplo:public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute> { public ClassicMovieAttributeAdapter( ClassicMovieAttribute attribute, IStringLocalizer? stringLocalizer) : base(attribute, stringLocalizer) { } public override void AddValidation(ClientModelValidationContext context) { MergeAttribute(context.Attributes, "data-val", "true"); MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context)); var year = Attribute.Year.ToString(CultureInfo.InvariantCulture); MergeAttribute(context.Attributes, "data-val-classicmovie-year", year); } public override string GetErrorMessage(ModelValidationContextBase validationContext) => Attribute.GetErrorMessage(); }Crie uma classe de provedor de adaptador que implemente o IValidationAttributeAdapterProvider. GetAttributeAdapter No método, passe o atributo personalizado para o construtor do adaptador, conforme mostrado neste exemplo:
public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider { private readonly IValidationAttributeAdapterProvider baseProvider = new ValidationAttributeAdapterProvider(); public IAttributeAdapter? GetAttributeAdapter( ValidationAttribute attribute, IStringLocalizer? stringLocalizer) { if (attribute is ClassicMovieAttribute classicMovieAttribute) { return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer); } return baseProvider.GetAttributeAdapter(attribute, stringLocalizer); } }Registre o provedor do adaptador para DI em
Program.cs:builder.Services.AddRazorPages() .AddMvcOptions(options => { options.MaxModelValidationErrors = 50; options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor( _ => "The field is required."); }); builder.Services.AddSingleton <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
IClientModelValidator para validação do lado do cliente
Esse método de renderização de atributos data- em HTML é utilizado pelo atributo ClassicMovieWithClientValidator no aplicativo de exemplo. Para adicionar a validação do cliente usando este método:
No atributo de validação personalizado, implemente a IClientModelValidator interface e crie um AddValidation método.
AddValidationNo método, adicionedata-atributos para validação, conforme mostrado no exemplo a seguir:public class ClassicMovieWithClientValidatorAttribute : ValidationAttribute, IClientModelValidator { public ClassicMovieWithClientValidatorAttribute(int year) => Year = year; public int Year { get; } public void AddValidation(ClientModelValidationContext context) { MergeAttribute(context.Attributes, "data-val", "true"); MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage()); var year = Year.ToString(CultureInfo.InvariantCulture); MergeAttribute(context.Attributes, "data-val-classicmovie-year", year); } public string GetErrorMessage() => $"Classic movies must have a release year no later than {Year}."; protected override ValidationResult? IsValid( object? value, ValidationContext validationContext) { var movie = (Movie)validationContext.ObjectInstance; var releaseYear = ((DateTime)value!).Year; if (movie.Genre == Genre.Classic && releaseYear > Year) { return new ValidationResult(GetErrorMessage()); } return ValidationResult.Success; } private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value) { if (attributes.ContainsKey(key)) { return false; } attributes.Add(key, value); return true; } }
Desativar a validação do lado do cliente
O código a seguir desabilita a validação do cliente no Razor Pages:
builder.Services.AddRazorPages()
.AddViewOptions(options =>
{
options.HtmlHelperOptions.ClientValidationEnabled = false;
});
Outras opções para desativar a validação do lado do cliente:
- Comente a referência a
_ValidationScriptsPartialem todos os ficheiros.cshtml. - Remova o conteúdo do arquivo Pages\Shared_ValidationScriptsPartial.cshtml .
A abordagem anterior não impedirá a validação do lado do cliente da biblioteca de classes ASP.NET Core IdentityRazor. Para obter mais informações, consulte Scaffold Identity em projetos ASP.NET Core.
Detalhes do problema
Detalhes do problema não são o único formato de resposta para descrever um erro de API HTTP, no entanto, eles são comumente usados para relatar erros para APIs HTTP.
O serviço de detalhes do problema implementa a interface IProblemDetailsService, que suporta a criação de detalhes do problema no ASP.NET Core. O AddProblemDetails(IServiceCollection) método de extensão em IServiceCollection registra a implementação padrão IProblemDetailsService .
Nos aplicações ASP.NET Core, o middleware seguinte gera respostas HTTP com detalhes de problema quando
- ExceptionHandlerMiddleware: Gera uma resposta de detalhes do problema quando um manipulador personalizado não está definido.
- StatusCodePagesMiddleware: Gera uma resposta de detalhes do problema por padrão.
-
DeveloperExceptionPageMiddleware: Gera uma resposta de detalhes do problema durante o desenvolvimento quando o cabeçalho de solicitação HTTP
Acceptnão incluitext/html.
Recursos adicionais
Este artigo explica como validar a entrada do usuário em um aplicativo ASP.NET Core MVC ou Razor Pages.
Visualize ou baixe o código de exemplo (como fazer o download).
Estado do modelo
O estado do modelo representa erros provenientes de dois subsistemas: vinculação do modelo e validação do modelo. Os erros que se originam da vinculação de modelo geralmente são erros de conversão de dados. Por exemplo, um "x" é inserido em um campo inteiro. A validação do modelo ocorre após a vinculação do modelo e relata erros em que os dados não estão em conformidade com as regras de negócios. Por exemplo, um 0 é inserido em um campo que espera uma classificação entre 1 e 5.
A vinculação do modelo e a validação do modelo ocorrem antes da execução de uma ação do controlador ou de um Razor método manipulador Pages. Para aplicativos Web, é responsabilidade do aplicativo inspecionar ModelState.IsValid e reagir adequadamente. Os aplicativos Web normalmente reexibem a página com uma mensagem de erro, conforme mostrado no exemplo de Páginas a seguir Razor :
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Para ASP.NET Core MVC com controladores e exibições, o exemplo a seguir mostra como verificar ModelState.IsValid dentro de uma ação do controlador:
public async Task<IActionResult> Create(Movie movie)
{
if (!ModelState.IsValid)
{
return View(movie);
}
_context.Movies.Add(movie);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
Os controladores de API da Web não precisam verificar ModelState.IsValid se têm o atributo [ApiController]. Nesse caso, uma resposta HTTP 400 automática contendo detalhes de erro é retornada quando o estado do modelo é inválido. Para obter mais informações, consulte Respostas HTTP 400 automáticas.
Executar novamente a validação
A validação é automática, mas convém repeti-la manualmente. Por exemplo, você pode calcular um valor para uma propriedade e querer executar novamente a validação depois de definir a propriedade para o valor calculado. Para executar novamente a validação, chame ModelStateDictionary.ClearValidationState para limpar a validação específica do modelo que está sendo validado, seguido por TryValidateModel:
public async Task<IActionResult> OnPostTryValidateAsync()
{
var modifiedReleaseDate = DateTime.Now.Date;
Movie.ReleaseDate = modifiedReleaseDate;
ModelState.ClearValidationState(nameof(Movie));
if (!TryValidateModel(Movie, nameof(Movie)))
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Atributos de validação
Os atributos de validação permitem especificar regras de validação para as propriedades do modelo. O exemplo a seguir do aplicativo de exemplo mostra uma classe de modelo que é anotada com atributos de validação. O [ClassicMovie] atributo é um atributo de validação personalizado e os outros são incorporados. Não mostrado é [ClassicMovieWithClientValidator], que mostra uma maneira alternativa de implementar um atributo personalizado.
public class Movie
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; } = null!;
[ClassicMovie(1960)]
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; } = null!;
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
}
Atributos incorporados
Aqui estão alguns dos atributos de validação internos:
- [ValidateNever]: Indica que uma propriedade ou parâmetro deve ser excluído da validação.
- [Cartão de Crédito]: Valida que a propriedade tem um formato de cartão de crédito. Requer métodos adicionais de validação do jQuery.
- [Comparar]: Valida que duas propriedades em um modelo correspondem.
- [EmailAddress]: Valida se a propriedade tem um formato de e-mail.
- [Telefone]: Valida se a propriedade tem um formato de número de telefone.
- [Intervalo]: Valida que o valor da propriedade está dentro de um intervalo especificado.
- [RegularExpression]: Valida se o valor da propriedade corresponde a uma expressão regular especificada.
-
[Obrigatório]: Valida que o campo não é nulo. Consulte
[Required]atributo para obter detalhes sobre o comportamento desse atributo. - [StringLength]: Valida que um valor de propriedade string não excede um limite de comprimento especificado.
- [Url]: Valida que a propriedade tem um formato de URL.
-
[Remoto]: Valida a entrada no cliente chamando um método de ação no servidor. Consulte
[Remote]atributo para obter detalhes sobre o comportamento desse atributo.
Uma lista completa de atributos de validação pode ser encontrada no System.ComponentModel.DataAnnotations namespace.
Mensagens de erro
Os atributos de validação permitem especificar a mensagem de erro a ser exibida para entrada inválida. Por exemplo:
[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]
Internamente, os atributos chamam String.Format com um espaço reservado para o nome do campo e, às vezes, espaços reservados adicionais. Por exemplo:
[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]
Quando aplicado a uma Name propriedade, a mensagem de erro criada pelo código anterior seria "O comprimento do nome deve estar entre 6 e 8.".
Para descobrir quais parâmetros são passados para a String.Format mensagem de erro de um atributo específico, consulte o código-fonte DataAnnotations.
Tipos de referência não anuláveis e o atributo [Required]
O sistema de validação trata parâmetros não anuláveis ou propriedades acopladas como se tivessem um [Required(AllowEmptyStrings = true)] atributo. Ao habilitar Nullable contextos, o MVC implicitamente começa a validar propriedades não anuláveis em tipos ou parâmetros não genéricos como se tivessem sido atribuídos ao [Required(AllowEmptyStrings = true)] atributo. Considere o seguinte código:
public class Person
{
public string Name { get; set; }
}
Se o aplicativo foi criado com <Nullable>enable</Nullable>, um valor ausente para Name em uma postagem de formulário ou JSON resultará em um erro de validação. Use um tipo de referência anulável para permitir que valores nulos ou ausentes sejam especificados para a Name propriedade:
public class Person
{
public string? Name { get; set; }
}
Esse comportamento pode ser desabilitado configurando SuppressImplicitRequiredAttributeForNonNullableReferenceTypes em Program.cs:
builder.Services.AddControllers(
options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);
Propriedades não anuláveis em tipos genéricos e atributo [Required]
As propriedades não anuláveis em tipos genéricos devem incluir o [Required] atributo quando o tipo é necessário. No código a seguir, TestRequired não é necessário:
public class WeatherForecast<T>
{
public string TestRequired { get; set; } = null!;
public T? Inner { get; set; }
}
No código a seguir, TestRequired está explicitamente marcado como necessário:
using System.ComponentModel.DataAnnotations;
public class WeatherForecast<T>
{
[Required]
public string TestRequired { get; set; } = null!;
public T? Inner { get; set; }
}
Validação [necessária] no servidor
No servidor, um valor necessário é considerado ausente se a propriedade for null. Um campo não anulável é sempre válido e a [Required] mensagem de erro do atributo nunca é exibida.
No entanto, a vinculação de modelo para uma propriedade não anulável pode falhar, resultando em uma mensagem de erro como The value '' is invalid. Para especificar uma mensagem de erro personalizada para validação do lado do servidor de tipos não anuláveis, você tem as seguintes opções:
Torne o campo anulável (por exemplo,
decimal?em vez dedecimal). Nulo<Os tipos de valor T> são tratados como tipos anuláveis padrão.Especifique a mensagem de erro padrão a ser usada pela vinculação de modelo, conforme mostrado no exemplo a seguir:
builder.Services.AddRazorPages() .AddMvcOptions(options => { options.MaxModelValidationErrors = 50; options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor( _ => "The field is required."); }); builder.Services.AddSingleton <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();Para obter mais informações sobre erros de vinculação de modelo para os quais você pode definir mensagens padrão, consulte DefaultModelBindingMessageProvider.
Validação [necessária] no cliente
Tipos e cadeias de caracteres não anuláveis são tratados de forma diferente no cliente em comparação com o servidor. Sobre o cliente:
- Um valor é considerado presente somente se a entrada for inserida para ele. Portanto, a validação do lado do cliente lida com tipos não anuláveis da mesma forma que tipos anuláveis.
- O espaço em branco num campo de texto é considerado uma entrada válida pelo método required jQuery Validation. A validação do lado do servidor considera um campo de cadeia de caracteres obrigatório inválido se apenas espaço em branco for inserido.
Como observado anteriormente, os tipos não anuláveis são tratados como se tivessem um [Required(AllowEmptyStrings = true)] atributo. Isso significa que você obtém validação do lado do cliente, mesmo que não aplique o [Required(AllowEmptyStrings = true)] atributo. Mas se você não usar o atributo, receberá uma mensagem de erro padrão. Para especificar uma mensagem de erro personalizada, use o atributo.
Atributo [Remoto]
O atributo [Remote] implementa a validação do lado do cliente que requer a chamada de um método no servidor para determinar se a entrada de campo é válida. Por exemplo, o aplicativo pode precisar verificar se um nome de usuário já está em uso.
Para implementar a validação remota:
Crie um método de ação para o JavaScript chamar. O método remoto jQuery Validation espera uma resposta JSON:
-
truesignifica que os dados de entrada são válidos. -
false,undefined, ounullsignifica que a entrada é inválida. Exiba a mensagem de erro padrão. - Qualquer outra cadeia de caracteres significa que a entrada é inválida. Exiba a cadeia de caracteres como uma mensagem de erro personalizada.
Aqui está um exemplo de um método de ação que retorna uma mensagem de erro personalizada:
[AcceptVerbs("GET", "POST")] public IActionResult VerifyEmail(string email) { if (!_userService.VerifyEmail(email)) { return Json($"Email {email} is already in use."); } return Json(true); }-
Na classe model, anote a propriedade com um
[Remote]atributo que aponta para o método de ação de validação, conforme mostrado no exemplo a seguir:[Remote(action: "VerifyEmail", controller: "Users")] public string Email { get; set; } = null!;
Campos adicionais
A AdditionalFields propriedade do [Remote] atributo permite validar combinações de campos em relação aos dados no servidor. Por exemplo, se o User modelo tinha FirstName e LastName propriedades, convém verificar se nenhum usuário existente já tem esse par de nomes. O exemplo a seguir mostra como usar AdditionalFields:
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; } = null!;
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; } = null!;
AdditionalFields pode ser definido explicitamente para as cadeias de caracteres "FirstName" e "LastName", mas usar o operador nameof simplifica a refatoração posterior. O método de ação para essa validação deve aceitar ambos os firstNamelastName argumentos:
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
if (!_userService.VerifyName(firstName, lastName))
{
return Json($"A user named {firstName} {lastName} already exists.");
}
return Json(true);
}
Quando o usuário insere um nome ou sobrenome, o JavaScript faz uma chamada remota para ver se esse par de nomes foi usado.
Para validar dois ou mais campos adicionais, forneça-os como uma lista delimitada por vírgula. Por exemplo, para adicionar uma MiddleName propriedade ao modelo, defina o [Remote] atributo como mostrado no exemplo a seguir:
[Remote(action: "VerifyName", controller: "Users",
AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }
AdditionalFields, como todos os argumentos de atributo, deve ser uma expressão constante. Portanto, não use uma cadeia de caracteres interpolada ou Join para inicializar AdditionalFields.
Alternativas aos atributos internos
Se você precisar de validação não fornecida por atributos internos, poderá:
Atributos personalizados
Para cenários que os atributos de validação internos não manipulam, você pode criar atributos de validação personalizados. Crie uma classe que herda de ValidationAttributee substitua o IsValid método.
O IsValid método aceita um objeto chamado value, que é a entrada a ser validada. Uma sobrecarga também aceita um objeto ValidationContext, que fornece informações adicionais, como a instância do modelo criada pela ligação de modelos.
O exemplo a seguir valida que a data de lançamento de um filme do gênero Clássico não é posterior a um ano especificado. O [ClassicMovie] atributo:
- Só é executado no servidor.
- Para filmes clássicos, valida a data de lançamento:
public class ClassicMovieAttribute : ValidationAttribute
{
public ClassicMovieAttribute(int year)
=> Year = year;
public int Year { get; }
public string GetErrorMessage() =>
$"Classic movies must have a release year no later than {Year}.";
protected override ValidationResult? IsValid(
object? value, ValidationContext validationContext)
{
var movie = (Movie)validationContext.ObjectInstance;
var releaseYear = ((DateTime)value!).Year;
if (movie.Genre == Genre.Classic && releaseYear > Year)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
}
A movie variável no exemplo anterior representa um Movie objeto que contém os dados do envio do formulário. Quando a validação falha, uma mensagem de ValidationResult erro é retornada.
IValidatableObject
O exemplo anterior funciona apenas com Movie tipos. Outra opção para validação de nível de classe é implementar IValidatableObject na classe de modelo, como mostrado no exemplo a seguir:
public class ValidatableMovie : IValidatableObject
{
private const int _classicYear = 1960;
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; } = null!;
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; } = null!;
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
{
yield return new ValidationResult(
$"Classic movies must have a release year no later than {_classicYear}.",
new[] { nameof(ReleaseDate) });
}
}
}
Validação de nó de nível superior
Os nós de nível superior incluem:
- Parâmetro da ação
- Propriedades do controlador
- Parâmetros do manipulador de página
- Propriedades do modelo de página
Os nós de nível superior vinculados ao modelo são validados, além de validar as propriedades do modelo. No exemplo a seguir da sample app, o método VerifyPhone utiliza o RegularExpressionAttribute para validar o parâmetro de ação phone.
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
[RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
if (!ModelState.IsValid)
{
return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
}
return Json(true);
}
Os nós de nível superior podem usar BindRequiredAttribute com atributos de validação. No exemplo a seguir do aplicativo de exemplo, o CheckAge método especifica que o age parâmetro deve ser vinculado a partir da cadeia de caracteres de consulta quando o formulário é enviado:
[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{
Na página Verificar Idade (CheckAge.cshtml), existem dois formulários. O primeiro formulário submete o valor de Age como um parâmetro de cadeia de caracteres de consulta: 99https://localhost:5001/Users/CheckAge?Age=99.
Quando um parâmetro formatado age corretamente da cadeia de caracteres de consulta é enviado, o formulário é validado.
O segundo formulário na página Verificar Idade envia o valor Age no corpo do pedido e a validação falha. A vinculação falha porque o age parâmetro deve vir de uma cadeia de caracteres de consulta.
Erros máximos
A validação para quando o número máximo de erros é atingido (200 por padrão). Você pode configurar esse número com o seguinte código em Program.cs:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
Recursão máxima
ValidationVisitor percorre o gráfico de objetos do modelo que está sendo validado. Para modelos que são profundos ou são infinitamente recursivos, a validação pode resultar em estouro de pilha.
MvcOptions.MaxValidationDepth Fornece uma maneira de interromper a validação antecipadamente se a recursão do visitante exceder uma profundidade configurada. O valor padrão de MvcOptions.MaxValidationDepth é 32.
Curto-circuito automático
A validação é automaticamente anulada (ignorada) se o gráfico do modelo não exigir validação. Os objetos para os quais o tempo de execução ignora a validação incluem coleções de primitivos (como byte[], , string[]Dictionary<string, string>) e gráficos de objetos complexos que não têm validadores.
Validação do lado do cliente
A validação do lado do cliente impede o envio até que o formulário seja válido. O botão Enviar executa JavaScript que envia o formulário ou exibe mensagens de erro.
A validação do lado do cliente evita uma viagem de ida e volta desnecessária ao servidor quando há erros de entrada em um formulário. As seguintes referências de script em _Layout.cshtml e _ValidationScriptsPartial.cshtml suportam a validação no lado do cliente:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.js"></script>
O script de Validação Não Intrusivo do jQuery é uma biblioteca front-end personalizada da Microsoft que se baseia no popular plugin de Validação do jQuery. Sem o jQuery Unobtrusive Validation, você teria que codificar a mesma lógica de validação em dois lugares: uma vez nos atributos de validação do lado do servidor nas propriedades do modelo e, em seguida, novamente nos scripts do lado do cliente. Em vez disso, os Auxiliares de Tag e Auxiliares HTML utilizam os atributos de validação e os metadados de tipo das propriedades do modelo para renderizar atributos HTML 5 data- nos elementos de formulário que precisam de validação. jQuery Unobtrusive Validation analisa os data- atributos e passa a lógica para jQuery Validation, efetivamente "copiando" a lógica de validação do lado do servidor para o cliente. Você pode exibir erros de validação no cliente usando auxiliares de tag, conforme mostrado aqui:
<div class="form-group">
<label asp-for="Movie.ReleaseDate" class="control-label"></label>
<input asp-for="Movie.ReleaseDate" class="form-control" />
<span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>
Os auxiliares de tag anteriores renderizam o seguinte HTML:
<div class="form-group">
<label class="control-label" for="Movie_ReleaseDate">Release Date</label>
<input class="form-control" type="date" data-val="true"
data-val-required="The Release Date field is required."
id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
<span class="text-danger field-validation-valid"
data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>
Observe que os data- atributos na saída HTML correspondem aos atributos de validação para a Movie.ReleaseDate propriedade. O data-val-required atributo contém uma mensagem de erro a ser exibida se o usuário não preencher o campo de data de lançamento. jQuery Unobtrusive Validation passa esse valor para o método jQuery Validation required(), que exibe essa mensagem no elemento span< que o acompanha>.
A validação de tipo de dados é baseada no tipo .NET de uma propriedade, a menos que isso seja substituído por um atributo [DataType]. Os navegadores têm as suas próprias mensagens de erro padrão, mas o pacote de Validação Não-Interferente do jQuery pode substituí-las.
[DataType] atributos e subclasses como [EmailAddress] permitem especificar a mensagem de erro.
Validação discreta
Para obter informações sobre validação discreta, consulte este problema do GitHub.
Adicionar validação a formulários dinâmicos
A Validação Não Intrusiva do jQuery passa a lógica e os parâmetros de validação para a Validação do jQuery quando a página é carregada pela primeira vez. Portanto, a validação não funciona automaticamente em formulários gerados dinamicamente. Para habilitar a validação, diga ao jQuery Unobtrusive Validation para analisar o formulário dinâmico imediatamente após criá-lo. Por exemplo, o código a seguir configura a validação do lado do cliente em um formulário adicionado via AJAX.
$.get({
url: "https://url/that/returns/a/form",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add form. " + errorThrown);
},
success: function(newFormHTML) {
var container = document.getElementById("form-container");
container.insertAdjacentHTML("beforeend", newFormHTML);
var forms = container.getElementsByTagName("form");
var newForm = forms[forms.length - 1];
$.validator.unobtrusive.parse(newForm);
}
})
O $.validator.unobtrusive.parse() método aceita um seletor jQuery para seu único argumento. Esse método instrui o jQuery Unobtrusive Validation a analisar os atributos de formulários data- dentro desse seletor. Os valores desses atributos são então passados para o plug-in de Validação do jQuery.
Adicionar validação a controles dinâmicos
O $.validator.unobtrusive.parse() método funciona em um formulário inteiro, não em controles individuais gerados dinamicamente, como <input> e <select/>. Para reanalisar o formulário, remova os dados de validação que já tinham sido adicionados quando o formulário foi analisado anteriormente, como está demonstrado no exemplo a seguir.
$.get({
url: "https://url/that/returns/a/control",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add control. " + errorThrown);
},
success: function(newInputHTML) {
var form = document.getElementById("my-form");
form.insertAdjacentHTML("beforeend", newInputHTML);
$(form).removeData("validator") // Added by jQuery Validation
.removeData("unobtrusiveValidation"); // Added by jQuery Unobtrusive Validation
$.validator.unobtrusive.parse(form);
}
})
Validação personalizada do lado do cliente
A validação personalizada do lado do cliente é feita ao gerar data- atributos HTML que funcionam com um adaptador personalizado do jQuery Validation. O seguinte código de adaptador de exemplo foi escrito para os [ClassicMovie] atributos e [ClassicMovieWithClientValidator] que foram introduzidos anteriormente neste artigo:
$.validator.addMethod('classicmovie', function (value, element, params) {
var genre = $(params[0]).val(), year = params[1], date = new Date(value);
// The Classic genre has a value of '0'.
if (genre && genre.length > 0 && genre[0] === '0') {
// The release date for a Classic is valid if it's no greater than the given year.
return date.getUTCFullYear() <= year;
}
return true;
});
$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
var element = $(options.form).find('select#Movie_Genre')[0];
options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
options.messages['classicmovie'] = options.message;
});
Para obter informações sobre como escrever adaptadores, consulte a documentação de Validação do jQuery.
O uso de um adaptador para um determinado campo é acionado por data- atributos que:
- Sinalizar o campo como sujeito a validação (
data-val="true"). - Identifique o nome de uma regra de validação e o texto da mensagem de erro (por exemplo,
data-val-rulename="Error message."). - Forneça quaisquer parâmetros adicionais que o validador precise (por exemplo,
data-val-rulename-param1="value").
O exemplo a seguir mostra os data- atributos do atributo do aplicativo de exemploClassicMovie:
<input class="form-control" type="date"
data-val="true"
data-val-classicmovie="Classic movies must have a release year no later than 1960."
data-val-classicmovie-year="1960"
data-val-required="The Release Date field is required."
id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
Como observado anteriormente, os Ajudantes de Tag e os Auxiliares de HTML usam informações de atributos de validação para renderizar data- atributos. Há duas opções para escrever código que resulta na criação de atributos HTML personalizados data- :
- Crie uma classe que deriva de AttributeAdapterBase<TAttribute> e uma classe que implementa IValidationAttributeAdapterProvidere registre seu atributo e seu adaptador em DI. Esse método segue o princípio de responsabilidade única em que o código de validação relacionado ao servidor e ao cliente está em classes separadas. O adaptador também tem a vantagem de que, uma vez que está registrado em DI, outros serviços em DI estão disponíveis para ele, se necessário.
- Implemente IClientModelValidator na sua ValidationAttribute classe. Esse método pode ser apropriado se o atributo não fizer nenhuma validação do lado do servidor e não precisar de nenhum serviço da DI.
AttributeAdapter para validação do lado do cliente
Esse método de renderização de atributos data- em HTML é utilizado pelo atributo ClassicMovie no aplicativo de exemplo. Para adicionar a validação do cliente usando este método:
Crie uma classe de adaptador de atributo para o atributo de validação personalizado. Derive a classe de AttributeAdapterBase<TAttribute>. Crie um
AddValidationmétodo que adicionedata-atributos à saída renderizada, conforme mostrado neste exemplo:public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute> { public ClassicMovieAttributeAdapter( ClassicMovieAttribute attribute, IStringLocalizer? stringLocalizer) : base(attribute, stringLocalizer) { } public override void AddValidation(ClientModelValidationContext context) { MergeAttribute(context.Attributes, "data-val", "true"); MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context)); var year = Attribute.Year.ToString(CultureInfo.InvariantCulture); MergeAttribute(context.Attributes, "data-val-classicmovie-year", year); } public override string GetErrorMessage(ModelValidationContextBase validationContext) => Attribute.GetErrorMessage(); }Crie uma classe de provedor de adaptador que implemente o IValidationAttributeAdapterProvider. GetAttributeAdapter No método, passe o atributo personalizado para o construtor do adaptador, conforme mostrado neste exemplo:
public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider { private readonly IValidationAttributeAdapterProvider baseProvider = new ValidationAttributeAdapterProvider(); public IAttributeAdapter? GetAttributeAdapter( ValidationAttribute attribute, IStringLocalizer? stringLocalizer) { if (attribute is ClassicMovieAttribute classicMovieAttribute) { return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer); } return baseProvider.GetAttributeAdapter(attribute, stringLocalizer); } }Registre o provedor do adaptador para DI em
Program.cs:builder.Services.AddRazorPages() .AddMvcOptions(options => { options.MaxModelValidationErrors = 50; options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor( _ => "The field is required."); }); builder.Services.AddSingleton <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
IClientModelValidator para validação do lado do cliente
Esse método de renderização de atributos data- em HTML é utilizado pelo atributo ClassicMovieWithClientValidator no aplicativo de exemplo. Para adicionar a validação do cliente usando este método:
No atributo de validação personalizado, implemente a IClientModelValidator interface e crie um AddValidation método.
AddValidationNo método, adicionedata-atributos para validação, conforme mostrado no exemplo a seguir:public class ClassicMovieWithClientValidatorAttribute : ValidationAttribute, IClientModelValidator { public ClassicMovieWithClientValidatorAttribute(int year) => Year = year; public int Year { get; } public void AddValidation(ClientModelValidationContext context) { MergeAttribute(context.Attributes, "data-val", "true"); MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage()); var year = Year.ToString(CultureInfo.InvariantCulture); MergeAttribute(context.Attributes, "data-val-classicmovie-year", year); } public string GetErrorMessage() => $"Classic movies must have a release year no later than {Year}."; protected override ValidationResult? IsValid( object? value, ValidationContext validationContext) { var movie = (Movie)validationContext.ObjectInstance; var releaseYear = ((DateTime)value!).Year; if (movie.Genre == Genre.Classic && releaseYear > Year) { return new ValidationResult(GetErrorMessage()); } return ValidationResult.Success; } private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value) { if (attributes.ContainsKey(key)) { return false; } attributes.Add(key, value); return true; } }
Desativar a validação do lado do cliente
O código a seguir desabilita a validação do cliente no Razor Pages:
builder.Services.AddRazorPages()
.AddViewOptions(options =>
{
options.HtmlHelperOptions.ClientValidationEnabled = false;
});
Outras opções para desativar a validação do lado do cliente:
- Comente a referência a
_ValidationScriptsPartialem todos os ficheiros.cshtml. - Remova o conteúdo do arquivo Pages\Shared_ValidationScriptsPartial.cshtml .
A abordagem anterior não impedirá a validação do lado do cliente da biblioteca de classes ASP.NET Core IdentityRazor. Para obter mais informações, consulte Scaffold Identity em projetos ASP.NET Core.
Recursos adicionais
Este artigo explica como validar a entrada do usuário em um aplicativo ASP.NET Core MVC ou Razor Pages.
Visualize ou baixe o código de exemplo (como fazer o download).
Estado do modelo
O estado do modelo representa erros provenientes de dois subsistemas: vinculação do modelo e validação do modelo. Os erros que se originam da vinculação de modelo geralmente são erros de conversão de dados. Por exemplo, um "x" é inserido em um campo inteiro. A validação do modelo ocorre após a vinculação do modelo e relata erros em que os dados não estão em conformidade com as regras de negócios. Por exemplo, um 0 é inserido em um campo que espera uma classificação entre 1 e 5.
A vinculação do modelo e a validação do modelo ocorrem antes da execução de uma ação do controlador ou de um Razor método manipulador Pages. Para aplicativos Web, é responsabilidade do aplicativo inspecionar ModelState.IsValid e reagir adequadamente. Os aplicativos Web normalmente reexibem a página com uma mensagem de erro:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Os controladores de API da Web não precisam verificar ModelState.IsValid se têm o atributo [ApiController]. Nesse caso, uma resposta HTTP 400 automática contendo detalhes de erro é retornada quando o estado do modelo é inválido. Para obter mais informações, consulte Respostas HTTP 400 automáticas.
Executar novamente a validação
A validação é automática, mas convém repeti-la manualmente. Por exemplo, você pode calcular um valor para uma propriedade e querer executar novamente a validação depois de definir a propriedade para o valor calculado. Para executar novamente a validação, chame ModelStateDictionary.ClearValidationState para limpar a validação específica do modelo que está sendo validado, seguido por TryValidateModel:
public async Task<IActionResult> OnPostTryValidateAsync()
{
var modifiedReleaseDate = DateTime.Now.Date;
Movie.ReleaseDate = modifiedReleaseDate;
ModelState.ClearValidationState(nameof(Movie));
if (!TryValidateModel(Movie, nameof(Movie)))
{
return Page();
}
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Atributos de validação
Os atributos de validação permitem especificar regras de validação para as propriedades do modelo. O exemplo a seguir do aplicativo de exemplo mostra uma classe de modelo que é anotada com atributos de validação. O [ClassicMovie] atributo é um atributo de validação personalizado e os outros são incorporados. Não mostrado é [ClassicMovieWithClientValidator], que mostra uma maneira alternativa de implementar um atributo personalizado.
public class Movie
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; }
[ClassicMovie(1960)]
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; }
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
}
Atributos incorporados
Aqui estão alguns dos atributos de validação internos:
- [ValidateNever]: Indica que uma propriedade ou parâmetro deve ser excluído da validação.
- [Cartão de Crédito]: Valida que a propriedade tem um formato de cartão de crédito. Requer métodos adicionais de validação do jQuery.
- [Comparar]: Valida que duas propriedades em um modelo correspondem.
- [EmailAddress]: Valida se a propriedade tem um formato de e-mail.
- [Telefone]: Valida se a propriedade tem um formato de número de telefone.
- [Intervalo]: Valida que o valor da propriedade está dentro de um intervalo especificado.
- [RegularExpression]: Valida se o valor da propriedade corresponde a uma expressão regular especificada.
-
[Obrigatório]: Valida que o campo não é nulo. Consulte
[Required]atributo para obter detalhes sobre o comportamento desse atributo. - [StringLength]: Valida que um valor de propriedade string não excede um limite de comprimento especificado.
- [Url]: Valida que a propriedade tem um formato de URL.
-
[Remoto]: Valida a entrada no cliente chamando um método de ação no servidor. Consulte
[Remote]atributo para obter detalhes sobre o comportamento desse atributo.
Uma lista completa de atributos de validação pode ser encontrada no System.ComponentModel.DataAnnotations namespace.
Mensagens de erro
Os atributos de validação permitem especificar a mensagem de erro a ser exibida para entrada inválida. Por exemplo:
[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]
Internamente, os atributos chamam String.Format com um espaço reservado para o nome do campo e, às vezes, espaços reservados adicionais. Por exemplo:
[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]
Quando aplicado a uma Name propriedade, a mensagem de erro criada pelo código anterior seria "O comprimento do nome deve estar entre 6 e 8.".
Para descobrir quais parâmetros são passados para a String.Format mensagem de erro de um atributo específico, consulte o código-fonte DataAnnotations.
Tipos de referência não anuláveis e atributo [Obrigatório]
O sistema de validação trata parâmetros não anuláveis ou propriedades acopladas como se tivessem um [Required(AllowEmptyStrings = true)] atributo. Ao habilitar Nullable contextos, o MVC começa implicitamente a validar propriedades ou parâmetros não anuláveis como se tivessem sido atribuídos ao [Required(AllowEmptyStrings = true)] atributo. Considere o seguinte código:
public class Person
{
public string Name { get; set; }
}
Se o aplicativo foi criado com <Nullable>enable</Nullable>, um valor ausente para Name em uma postagem de formulário ou JSON resultará em um erro de validação. Use um tipo de referência anulável para permitir que valores nulos ou ausentes sejam especificados para a Name propriedade:
public class Person
{
public string? Name { get; set; }
}
Esse comportamento pode ser desabilitado configurando SuppressImplicitRequiredAttributeForNonNullableReferenceTypes em Startup.ConfigureServices:
services.AddControllers(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);
Validação [necessária] no servidor
No servidor, um valor necessário é considerado ausente se a propriedade for null. Um campo não anulável é sempre válido e a [Required] mensagem de erro do atributo nunca é exibida.
No entanto, a vinculação de modelo para uma propriedade não anulável pode falhar, resultando em uma mensagem de erro como The value '' is invalid. Para especificar uma mensagem de erro personalizada para validação do lado do servidor de tipos não anuláveis, você tem as seguintes opções:
Torne o campo anulável (por exemplo,
decimal?em vez dedecimal). Nulo<Os tipos de valor T> são tratados como tipos anuláveis padrão.Especifique a mensagem de erro padrão a ser usada pela vinculação de modelo, conforme mostrado no exemplo a seguir:
services.AddRazorPages() .AddMvcOptions(options => { options.MaxModelValidationErrors = 50; options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor( _ => "The field is required."); }); services.AddSingleton<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();Para obter mais informações sobre erros de vinculação de modelo para os quais você pode definir mensagens padrão, consulte DefaultModelBindingMessageProvider.
Validação [necessária] no cliente
Tipos e cadeias de caracteres não anuláveis são tratados de forma diferente no cliente em comparação com o servidor. Sobre o cliente:
- Um valor é considerado presente somente se a entrada for inserida para ele. Portanto, a validação do lado do cliente lida com tipos não anuláveis da mesma forma que tipos anuláveis.
- O espaço em branco num campo de texto é considerado uma entrada válida pelo método required jQuery Validation. A validação do lado do servidor considera um campo de cadeia de caracteres obrigatório inválido se apenas espaço em branco for inserido.
Como observado anteriormente, os tipos não anuláveis são tratados como se tivessem um [Required(AllowEmptyStrings = true)] atributo. Isso significa que você obtém validação do lado do cliente, mesmo que não aplique o [Required(AllowEmptyStrings = true)] atributo. Mas se você não usar o atributo, receberá uma mensagem de erro padrão. Para especificar uma mensagem de erro personalizada, use o atributo.
Atributo [Remoto]
O atributo [Remote] implementa a validação do lado do cliente que requer a chamada de um método no servidor para determinar se a entrada de campo é válida. Por exemplo, o aplicativo pode precisar verificar se um nome de usuário já está em uso.
Para implementar a validação remota:
Crie um método de ação para o JavaScript chamar. O método remoto jQuery Validation espera uma resposta JSON:
-
truesignifica que os dados de entrada são válidos. -
false,undefined, ounullsignifica que a entrada é inválida. Exiba a mensagem de erro padrão. - Qualquer outra cadeia de caracteres significa que a entrada é inválida. Exiba a cadeia de caracteres como uma mensagem de erro personalizada.
Aqui está um exemplo de um método de ação que retorna uma mensagem de erro personalizada:
[AcceptVerbs("GET", "POST")] public IActionResult VerifyEmail(string email) { if (!_userService.VerifyEmail(email)) { return Json($"Email {email} is already in use."); } return Json(true); }-
Na classe model, anote a propriedade com um
[Remote]atributo que aponta para o método de ação de validação, conforme mostrado no exemplo a seguir:[Remote(action: "VerifyEmail", controller: "Users")] public string Email { get; set; }
Campos adicionais
A AdditionalFields propriedade do [Remote] atributo permite validar combinações de campos em relação aos dados no servidor. Por exemplo, se o User modelo tinha FirstName e LastName propriedades, convém verificar se nenhum usuário existente já tem esse par de nomes. O exemplo a seguir mostra como usar AdditionalFields:
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; }
AdditionalFields pode ser definido explicitamente para as cadeias de caracteres "FirstName" e "LastName", mas usar o operador nameof simplifica a refatoração posterior. O método de ação para essa validação deve aceitar ambos os firstNamelastName argumentos:
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
if (!_userService.VerifyName(firstName, lastName))
{
return Json($"A user named {firstName} {lastName} already exists.");
}
return Json(true);
}
Quando o usuário insere um nome ou sobrenome, o JavaScript faz uma chamada remota para ver se esse par de nomes foi usado.
Para validar dois ou mais campos adicionais, forneça-os como uma lista delimitada por vírgula. Por exemplo, para adicionar uma MiddleName propriedade ao modelo, defina o [Remote] atributo como mostrado no exemplo a seguir:
[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }
AdditionalFields, como todos os argumentos de atributo, deve ser uma expressão constante. Portanto, não use uma cadeia de caracteres interpolada ou Join para inicializar AdditionalFields.
Alternativas aos atributos internos
Se você precisar de validação não fornecida por atributos internos, poderá:
Atributos personalizados
Para cenários que os atributos de validação internos não manipulam, você pode criar atributos de validação personalizados. Crie uma classe que herda de ValidationAttributee substitua o IsValid método.
O IsValid método aceita um objeto chamado value, que é a entrada a ser validada. Uma sobrecarga também aceita um objeto ValidationContext, que fornece informações adicionais, como a instância do modelo criada pela ligação de modelos.
O exemplo a seguir valida que a data de lançamento de um filme do gênero Clássico não é posterior a um ano especificado. O [ClassicMovie] atributo:
- Só é executado no servidor.
- Para filmes clássicos, valida a data de lançamento:
public class ClassicMovieAttribute : ValidationAttribute
{
public ClassicMovieAttribute(int year)
{
Year = year;
}
public int Year { get; }
public string GetErrorMessage() =>
$"Classic movies must have a release year no later than {Year}.";
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
var movie = (Movie)validationContext.ObjectInstance;
var releaseYear = ((DateTime)value).Year;
if (movie.Genre == Genre.Classic && releaseYear > Year)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
}
A movie variável no exemplo anterior representa um Movie objeto que contém os dados do envio do formulário. Quando a validação falha, uma mensagem de ValidationResult erro é retornada.
IValidatableObject
O exemplo anterior funciona apenas com Movie tipos. Outra opção para validação de nível de classe é implementar IValidatableObject na classe de modelo, como mostrado no exemplo a seguir:
public class ValidatableMovie : IValidatableObject
{
private const int _classicYear = 1960;
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; }
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; }
[Range(0, 999.99)]
public decimal Price { get; set; }
public Genre Genre { get; set; }
public bool Preorder { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
{
yield return new ValidationResult(
$"Classic movies must have a release year no later than {_classicYear}.",
new[] { nameof(ReleaseDate) });
}
}
}
Validação de nó de nível superior
Os nós de nível superior incluem:
- Parâmetro da ação
- Propriedades do controlador
- Parâmetros do manipulador de página
- Propriedades do modelo de página
Os nós de nível superior vinculados ao modelo são validados, além de validar as propriedades do modelo. No exemplo a seguir da sample app, o método VerifyPhone utiliza o RegularExpressionAttribute para validar o parâmetro de ação phone.
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
[RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
if (!ModelState.IsValid)
{
return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
}
return Json(true);
}
Os nós de nível superior podem usar BindRequiredAttribute com atributos de validação. No exemplo a seguir do aplicativo de exemplo, o CheckAge método especifica que o age parâmetro deve ser vinculado a partir da cadeia de caracteres de consulta quando o formulário é enviado:
[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{
Na página Verificar Idade (CheckAge.cshtml), existem dois formulários. O primeiro formulário submete o valor de Age como um parâmetro de cadeia de caracteres de consulta: 99https://localhost:5001/Users/CheckAge?Age=99.
Quando um parâmetro formatado age corretamente da cadeia de caracteres de consulta é enviado, o formulário é validado.
O segundo formulário na página Verificar Idade envia o valor Age no corpo do pedido e a validação falha. A vinculação falha porque o age parâmetro deve vir de uma cadeia de caracteres de consulta.
Erros máximos
A validação para quando o número máximo de erros é atingido (200 por padrão). Você pode configurar esse número com o seguinte código em Startup.ConfigureServices:
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
_ => "The field is required.");
});
services.AddSingleton<IValidationAttributeAdapterProvider,
CustomValidationAttributeAdapterProvider>();
Recursão máxima
ValidationVisitor percorre o gráfico de objetos do modelo que está sendo validado. Para modelos que são profundos ou são infinitamente recursivos, a validação pode resultar em estouro de pilha.
MvcOptions.MaxValidationDepth Fornece uma maneira de interromper a validação antecipadamente se a recursão do visitante exceder uma profundidade configurada. O valor padrão de MvcOptions.MaxValidationDepth é 32.
Curto-circuito automático
A validação é automaticamente anulada (ignorada) se o gráfico do modelo não exigir validação. Os objetos para os quais o tempo de execução ignora a validação incluem coleções de primitivos (como byte[], , string[]Dictionary<string, string>) e gráficos de objetos complexos que não têm validadores.
Validação do lado do cliente
A validação do lado do cliente impede o envio até que o formulário seja válido. O botão Enviar executa JavaScript que envia o formulário ou exibe mensagens de erro.
A validação do lado do cliente evita uma viagem de ida e volta desnecessária ao servidor quando há erros de entrada em um formulário. As seguintes referências de script em _Layout.cshtml e _ValidationScriptsPartial.cshtml suportam a validação no lado do cliente:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.js"></script>
O script de Validação Não Intrusivo do jQuery é uma biblioteca front-end personalizada da Microsoft que se baseia no popular plugin de Validação do jQuery. Sem o jQuery Unobtrusive Validation, você teria que codificar a mesma lógica de validação em dois lugares: uma vez nos atributos de validação do lado do servidor nas propriedades do modelo e, em seguida, novamente nos scripts do lado do cliente. Em vez disso, os Auxiliares de Tag e Auxiliares HTML utilizam os atributos de validação e os metadados de tipo das propriedades do modelo para renderizar atributos HTML 5 data- nos elementos de formulário que precisam de validação. jQuery Unobtrusive Validation analisa os data- atributos e passa a lógica para jQuery Validation, efetivamente "copiando" a lógica de validação do lado do servidor para o cliente. Você pode exibir erros de validação no cliente usando auxiliares de tag, conforme mostrado aqui:
<div class="form-group">
<label asp-for="Movie.ReleaseDate" class="control-label"></label>
<input asp-for="Movie.ReleaseDate" class="form-control" />
<span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>
Os auxiliares de tag anteriores renderizam o seguinte HTML:
<div class="form-group">
<label class="control-label" for="Movie_ReleaseDate">Release Date</label>
<input class="form-control" type="date" data-val="true"
data-val-required="The Release Date field is required."
id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
<span class="text-danger field-validation-valid"
data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>
Observe que os data- atributos na saída HTML correspondem aos atributos de validação para a Movie.ReleaseDate propriedade. O data-val-required atributo contém uma mensagem de erro a ser exibida se o usuário não preencher o campo de data de lançamento. jQuery Unobtrusive Validation passa esse valor para o método jQuery Validation required(), que exibe essa mensagem no elemento span< que o acompanha>.
A validação de tipo de dados é baseada no tipo .NET de uma propriedade, a menos que isso seja substituído por um atributo [DataType]. Os navegadores têm as suas próprias mensagens de erro padrão, mas o pacote de Validação Não-Interferente do jQuery pode substituí-las.
[DataType] atributos e subclasses como [EmailAddress] permitem especificar a mensagem de erro.
Validação discreta
Para obter informações sobre validação discreta, consulte este problema do GitHub.
Adicionar validação a formulários dinâmicos
A Validação Não Intrusiva do jQuery passa a lógica e os parâmetros de validação para a Validação do jQuery quando a página é carregada pela primeira vez. Portanto, a validação não funciona automaticamente em formulários gerados dinamicamente. Para habilitar a validação, diga ao jQuery Unobtrusive Validation para analisar o formulário dinâmico imediatamente após criá-lo. Por exemplo, o código a seguir configura a validação do lado do cliente em um formulário adicionado via AJAX.
$.get({
url: "https://url/that/returns/a/form",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add form. " + errorThrown);
},
success: function(newFormHTML) {
var container = document.getElementById("form-container");
container.insertAdjacentHTML("beforeend", newFormHTML);
var forms = container.getElementsByTagName("form");
var newForm = forms[forms.length - 1];
$.validator.unobtrusive.parse(newForm);
}
})
O $.validator.unobtrusive.parse() método aceita um seletor jQuery para seu único argumento. Esse método instrui o jQuery Unobtrusive Validation a analisar os atributos de formulários data- dentro desse seletor. Os valores desses atributos são então passados para o plug-in de Validação do jQuery.
Adicionar validação a controles dinâmicos
O $.validator.unobtrusive.parse() método funciona em um formulário inteiro, não em controles individuais gerados dinamicamente, como <input> e <select/>. Para reanalisar o formulário, remova os dados de validação que já tinham sido adicionados quando o formulário foi analisado anteriormente, como está demonstrado no exemplo a seguir.
$.get({
url: "https://url/that/returns/a/control",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add control. " + errorThrown);
},
success: function(newInputHTML) {
var form = document.getElementById("my-form");
form.insertAdjacentHTML("beforeend", newInputHTML);
$(form).removeData("validator") // Added by jQuery Validation
.removeData("unobtrusiveValidation"); // Added by jQuery Unobtrusive Validation
$.validator.unobtrusive.parse(form);
}
})
Validação personalizada do lado do cliente
A validação personalizada do lado do cliente é feita ao gerar data- atributos HTML que funcionam com um adaptador personalizado do jQuery Validation. O seguinte código de adaptador de exemplo foi escrito para os [ClassicMovie] atributos e [ClassicMovieWithClientValidator] que foram introduzidos anteriormente neste artigo:
$.validator.addMethod('classicmovie', function (value, element, params) {
var genre = $(params[0]).val(), year = params[1], date = new Date(value);
// The Classic genre has a value of '0'.
if (genre && genre.length > 0 && genre[0] === '0') {
// The release date for a Classic is valid if it's no greater than the given year.
return date.getUTCFullYear() <= year;
}
return true;
});
$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
var element = $(options.form).find('select#Movie_Genre')[0];
options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
options.messages['classicmovie'] = options.message;
});
Para obter informações sobre como escrever adaptadores, consulte a documentação de Validação do jQuery.
O uso de um adaptador para um determinado campo é acionado por data- atributos que:
- Sinalizar o campo como sujeito a validação (
data-val="true"). - Identifique o nome de uma regra de validação e o texto da mensagem de erro (por exemplo,
data-val-rulename="Error message."). - Forneça quaisquer parâmetros adicionais que o validador precise (por exemplo,
data-val-rulename-param1="value").
O exemplo a seguir mostra os data- atributos do atributo do aplicativo de exemploClassicMovie:
<input class="form-control" type="date"
data-val="true"
data-val-classicmovie="Classic movies must have a release year no later than 1960."
data-val-classicmovie-year="1960"
data-val-required="The Release Date field is required."
id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
Como observado anteriormente, os Ajudantes de Tag e os Auxiliares de HTML usam informações de atributos de validação para renderizar data- atributos. Há duas opções para escrever código que resulta na criação de atributos HTML personalizados data- :
- Crie uma classe que deriva de AttributeAdapterBase<TAttribute> e uma classe que implementa IValidationAttributeAdapterProvidere registre seu atributo e seu adaptador em DI. Esse método segue o princípio de responsabilidade única em que o código de validação relacionado ao servidor e ao cliente está em classes separadas. O adaptador também tem a vantagem de que, uma vez que está registrado em DI, outros serviços em DI estão disponíveis para ele, se necessário.
- Implemente IClientModelValidator na sua ValidationAttribute classe. Esse método pode ser apropriado se o atributo não fizer nenhuma validação do lado do servidor e não precisar de nenhum serviço da DI.
AttributeAdapter para validação do lado do cliente
Esse método de renderização de atributos data- em HTML é utilizado pelo atributo ClassicMovie no aplicativo de exemplo. Para adicionar a validação do cliente usando este método:
Crie uma classe de adaptador de atributo para o atributo de validação personalizado. Derive a classe de AttributeAdapterBase<TAttribute>. Crie um
AddValidationmétodo que adicionedata-atributos à saída renderizada, conforme mostrado neste exemplo:public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute> { public ClassicMovieAttributeAdapter(ClassicMovieAttribute attribute, IStringLocalizer stringLocalizer) : base(attribute, stringLocalizer) { } public override void AddValidation(ClientModelValidationContext context) { MergeAttribute(context.Attributes, "data-val", "true"); MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context)); var year = Attribute.Year.ToString(CultureInfo.InvariantCulture); MergeAttribute(context.Attributes, "data-val-classicmovie-year", year); } public override string GetErrorMessage(ModelValidationContextBase validationContext) => Attribute.GetErrorMessage(); }Crie uma classe de provedor de adaptador que implemente o IValidationAttributeAdapterProvider. GetAttributeAdapter No método, passe o atributo personalizado para o construtor do adaptador, conforme mostrado neste exemplo:
public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider { private readonly IValidationAttributeAdapterProvider baseProvider = new ValidationAttributeAdapterProvider(); public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer) { if (attribute is ClassicMovieAttribute classicMovieAttribute) { return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer); } return baseProvider.GetAttributeAdapter(attribute, stringLocalizer); } }Registre o provedor do adaptador para DI em
Startup.ConfigureServices:services.AddRazorPages() .AddMvcOptions(options => { options.MaxModelValidationErrors = 50; options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor( _ => "The field is required."); }); services.AddSingleton<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
IClientModelValidator para validação do lado do cliente
Esse método de renderização de atributos data- em HTML é utilizado pelo atributo ClassicMovieWithClientValidator no aplicativo de exemplo. Para adicionar a validação do cliente usando este método:
No atributo de validação personalizado, implemente a IClientModelValidator interface e crie um AddValidation método.
AddValidationNo método, adicionedata-atributos para validação, conforme mostrado no exemplo a seguir:public class ClassicMovieWithClientValidatorAttribute : ValidationAttribute, IClientModelValidator { public ClassicMovieWithClientValidatorAttribute(int year) { Year = year; } public int Year { get; } public void AddValidation(ClientModelValidationContext context) { MergeAttribute(context.Attributes, "data-val", "true"); MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage()); var year = Year.ToString(CultureInfo.InvariantCulture); MergeAttribute(context.Attributes, "data-val-classicmovie-year", year); } public string GetErrorMessage() => $"Classic movies must have a release year no later than {Year}."; protected override ValidationResult IsValid(object value, ValidationContext validationContext) { var movie = (Movie)validationContext.ObjectInstance; var releaseYear = ((DateTime)value).Year; if (movie.Genre == Genre.Classic && releaseYear > Year) { return new ValidationResult(GetErrorMessage()); } return ValidationResult.Success; } private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value) { if (attributes.ContainsKey(key)) { return false; } attributes.Add(key, value); return true; } }
Desativar a validação do lado do cliente
O código a seguir desabilita a validação do cliente no Razor Pages:
services.AddRazorPages()
.AddViewOptions(options =>
{
options.HtmlHelperOptions.ClientValidationEnabled = false;
});
Outras opções para desativar a validação do lado do cliente:
- Comente a referência a
_ValidationScriptsPartialem todos os ficheiros.cshtml. - Remova o conteúdo do arquivo Pages\Shared_ValidationScriptsPartial.cshtml .
A abordagem anterior não impedirá a validação do lado do cliente da biblioteca de classes ASP.NET Core IdentityRazor. Para obter mais informações, consulte Scaffold Identity em projetos ASP.NET Core.