ASP.NET Core -  Validação Customizada


 Neste artigo vamos recordar como implementar uma validação customizada na ASP .NET Core MVC.

Vamos recordar como criar uma validação customizada na ASP .NET Core MVC.


A validação do modelo  é feita no namespace System.ComponentModel.DataAnnotations onde os atributos de validação permitem especificar regras de validação para propriedades de modelo.

Podemos criar atributos de validação personalizados, e para isso temos que criar uma classe que herda de ValidationAttribute e substituir o método IsValid.  O método IsValid aceita um objeto denominado value, que é a entrada a ser validada. Uma sobrecarga também aceita um objeto ValidationContext, que fornece informações adicionais, como a instância de modelo criada pela associação de modelo.

A classe ValidationAttribute é a classe base para todos os atributos de validação e possui os seguintes métodos:

Métodos
Descrição
GetValidationResult(Object, ValidationContext)
Verifica se o valor especificado é válido em relação ao atributo de validação atual.
IsDefaultAttribute()
Quando substituído em uma classe derivada, indica se o valor dessa instância é o valor padrão da classe derivada. Herdado do atributo.
IsValid(Object)
Determina se o valor especificado do objeto é válido.
IsValid(Object, ValidationContext)
Valida o valor especificado com relação ao atributo de validação atual.
MemberwiseClone()
Cria uma cópia sombra do objeto atual.
ToString()
Retorna uma string que representa o objeto atual.
Validate(Object, String)
Valida o objeto especificicado.
Validate(Object, ValidationContext)
Valida o objeto especificicado.

O Model state representa erros que vêm de dois subsistemas: vinculação de modelo e validação de modelo.

Erros originados da associaçã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.

Tanto a vinculação de modelo quanto a validação de modelo ocorrem antes da execução de uma ação do controlador. Para aplicativos Web, é responsabilidade do aplicativo inspecionar ModelState.IsValid e reagir adequadamente. Os aplicativos Web normalmente exibem novamente a página com uma mensagem de erro.

Os atributos de validação permitem especificar regras de validação para propriedades de modelo.

Abaixo temos um exemplo de modelo representada pela entidade Aluno que contém as propriedades Nome, DataAdmissao e DataNascimento, onde estamos usando atributos de validação definidos em System.ComponentModel.DataAnnotations sendo que os atributos [CustomDataAdmissao()] e [CustomIdadeMinima] são atributos de validação personalizados que foram implementados no namespace CustomValidation.CustomValidation:

using CustomValidation.CustomValidation;
using System.ComponentModel.DataAnnotations;

 

public class Aluno

{

  [Key]

  public int Id { get; set; }


  [Required(ErrorMessage = "Informe o nome")]

  public string? Nome { get; set; }


  [Required(ErrorMessage = "Escolha a data de admissão.")]

  [Display(Name = "Data de Admissão")]

  [DataType(DataType.Date)]

  [CustomDataAdmissao(ErrorMessage = "A data de admissão deve ser menor ou igual a hoje.")]

  public DateTime? DataAdmissao { get; set; }


  [Display(Name = "Date de Nascimento")]

  [DataType(DataType.Date)]

  [CustomIdadeMinima]

  public DateTime? DataNascimento { get; set; }

}

 

O exemplo usado é uma aplicação ASP.NET Core MVC chamada CustomValidation criada no VS 2022 no ambiente do .NET 6.

O controlador HomeController do projeto possui o seguinte código:

using CustomValidation.Models;
using Microsoft.AspNetCore.Mvc;

namespace CustomValidation.Controllers;

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    public IActionResult Cadastro()
    {
        return View();
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Cadastro(Aluno aluno)
    {
        if (ModelState.IsValid)
        {
            return View("~/Views/Home/OK.cshtml");
        }
        return View();
    }
}

A seguir temos o código da view Cadastro.cshtml na pasta /Views/Home:

@model Aluno

<div class="card">
    <div class="card-header">
        <h4 class="text-uppercase">Cadastro Aluno</h4>
    </div>
    <div class="card-body">
        <form asp-action="Cadastro">
            <div class="form-group">
                <label asp-for="Nome" class="label-control"></label>
                <input asp-for="Nome" class="form-control" />
                <span asp-validation-for="Nome" class="text-danger"></span>
            </div>
            <div class="row">
                 <div class="col-md-6">
                    <div class="form-group">
                        <label asp-for="DataNascimento" class="label-control"></label>
                        <input asp-for="DataNascimento" class="form-control" />
                        <span asp-validation-for="DataNascimento" class="text-danger"></span>
                    </div>
                </div>
                <div class="col-md-6">
                    <div class="form-group">
                        <label asp-for="DataAdmissao" class="label-control"></label>
                        <input asp-for="DataAdmissao" class="form-control" />
                        <span asp-validation-for="DataAdmissao" class="text-danger"></span>
                    </div>
                </div>
            </div>
            <div class="form-group">
                <button type="submit" class="btn btn-sm btn-primary rounded-0">Enviar</button>
            </div>
        </form>
    </div>
</div>

A implementação da validação customizada é feita na pasta CustomValidation do projeto onde criamos duas classes:

  1. CustomDataAdmissao
  2. CustomIdadeMinima

A seguir temos o código usado em cada classe:

1- CustomDataAdmissao

using System.ComponentModel.DataAnnotations;

namespace CustomValidation.CustomValidation;

public class CustomDataAdmissao : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        DateTime dateTime = Convert.ToDateTime(value);
        return dateTime <= DateTime.Now;
    }
}

2- CustomIdadeMinima

using CustomValidation.Models;
using System.ComponentModel.DataAnnotations;

namespace CustomValidation.CustomValidation;

public class CustomIdadeMinima : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var aluno = (Aluno)validationContext.ObjectInstance;

        if (aluno.DataNascimento is null)
            return new ValidationResult("Informe a data de nascimento.");

        var idade = DateTime.Today.Year - aluno.DataNascimento.Value.Year;

        return (idade >= 18)
            ? ValidationResult.Success
            : new ValidationResult("O aluno deve ter no mínimo 18 anos.");
    }
}

Com isso temos a implementação dos dois atributos de validação que estamos usando na classe Aluno para validar o modelo.

Executando o projeto iremos obter o seguinte resultado:

Vemos assim os atributos de validação customizados em ação funcionando.

Tipos de referência não anuláveis e o atributo [Required]

O sistema de validação trata os parâmetros não anuláveis ​​ou propriedades vinculadas como se tivessem um atributo [Required(AllowEmptyStrings = true)].

Ao habilitar contextos anuláveis, o MVC começa implicitamente a validar propriedades não anuláveis ​​em tipos ou parâmetros não genéricos como se tivessem sido atribuídos com o atributo [Required(AllowEmptyStrings = true)]. Considere o seguinte código:

public class Aluno
{
     public string Nome { get; set; }
}

Se o aplicativo foi criado com definindo  <Nullable>enable</Nullable>, um valor ausente para Nome em uma postagem de formulário ou JSON resultará em um erro de validação.

Para evitar isso devemos usar um tipo de referência anulável para permitir que valores nulos ou ausentes sejam especificados para a propriedade Nome:

public class Aluno
{
     public string? Nome { get; set; }
}

Esse comportamento pode ser desabilitado configurando na classe Program do projeto a opção: SuppressImplicitRequiredAttributeForNonNullableReferenceTypes da seguinte maneira:

...
builder.Services.AddControllers(
    options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

...

Pegue o projeto aqui : CustomValidation.zip

"Ai dos que são sábios a seus próprios olhos, e prudentes diante de si mesmos!"
Isaías 5:21

Referências:


José Carlos Macoratti