Validações customizadas no Asp.Net MVC

Uma das formas de fazer validação em uma aplicação Asp.Net MVC é utilizar as anotações (Attributes) definidos no namespace System.ComponentModel.DataAnnotations. Se quisermos, por exemplo, que o campo nome de um modelo de usuário seja obrigatório utilizamos o RequiredAttribute:

public class Usuario
{
   [Required]
   public String Nome { get; set; }

   public String Password { get; set; }
}

E dentro do controller da aplicação, podemos verificar se as informações enviadas pelo usuário são válidas utilizando o ModelState:

public class UsuarioController : Controller
{
    public ActionResult Cadastra(Usuario usuario)
    {
        if(ModelState.IsValid)
        {
            // Modelo é válido
        }
        else
        {
            // Modelo é inválido
        }
    }
}

Mas e se quiséssemos validar que o usuário preencheu um password forte (contém números, letras maiúsculas e minúsculas). Nesse caso, poderíamos colocar a regra de validação diretamente dentro do controller da aplicação:

public class UsuarioController : Controller
{
    public ActionResult Cadastra(Usuario usuario)
    {
        Regex regex = new Regex(@"((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{4,100})");
        if(regex.IsMatch(usuario.Password))
        {
            ModelState.AddModelError("password.Fraco", "Password muito fraco")
        }
        if(ModelState.IsValid)
        {
            // Modelo é válido
        }
        else
        {
            // Modelo é inválido
        }
    }
}

Mas e se essa validação for utilizada em outros pontos da aplicação? Nesse caso, seria mais interessante se pudéssemos isolar o código da validação dentro de uma nova anotação.

Nova anotação de validação

Vamos então criar uma nova anotação de validação chamada PasswordForteAttribute. Para que possamos utilizar essa classe como uma validação no Asp.Net MVC, ela precisa herdar de ValidationAttribute:

public class PasswordForteAttribute : ValidationAttribute
{

}

Dentro dessa classe, a regra de validação customizada é implementada dentro de um método chamado IsValid:

public class PasswordForteAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {

    }
}

Caso o value seja um valor válido, o método IsValid deve devolver o valor true, senão false:

public class PasswordForteAttribute : ValidationAttribute
{
    private static readonly String PasswordPattern = @"((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{4,100})";

    private static readonly Regex PasswordRegex = new Regex(PasswordPattern);

    public override bool IsValid(object value)
    {
        String password = value.ToString();
        return PasswordRegex.IsMatch(password);
    }
}

Quando o campo não obedecer a regra de validação, precisamos informar qual é a mensagem de validação que deve ser exibida para o usuário. Para isso, precisamos passar a mensagem como argumento do construtor da classe ValidationAttribute:

public class PasswordForteAttribute : ValidationAttribute
{
    public PasswordForteAttribute() : base("Senha muito fraca") { }

    // implementação da validação
}

Agora precisamos simplesmente anotar o modelo com essa nova anotação e depois remover a lógica da validação customizada da action do controller, lembrando que como a classe será utilizada como anotação, não precisamos do sufixo Attribute:

public class Usuario
{
    [Required]
    public String Nome { get; set; }

    [PasswordForte]
    public String Password { get; set; }
}

public class UsuarioController : Controller
{
    public ActionResult Cadastra(Usuario usuario)
    {
        if(ModelState.IsValid)
        {
            // Modelo é válido
        }
        else
        {
            // Modelo é inválido
        }
    }
}

Agora temos um código de validação isolado e reutilizável.

Validação no navegador

Essa validação que criamos será executada quando alguma action de um controller receber um objeto do tipo usuário, ou seja, para executarmos a validação precisamos de uma requisição web! Mas quando utilizamos as anotações do namespace System.ComponentModel.DataAnnotations, a maioria delas define validações que são executadas no navegador do usuário da aplicação, tudo o que precisamos fazer para utilizar essa validação no cliente é utilizar o HtmlHelper para gerar o formulário dentro de uma view fortemente tipada, como fazemos no curso de razor do Alura:

@model Aplicacao.Models.Usuario

@using(Html.BeginForm("Cadastra", "Usuario", FormMethod.Post))
{
    @Html.LabelFor(u => u.Nome)
    @Html.TextBoxFor(u => u.Nome)
    @Html.ValidationMessageFor(u => u.Nome)

    @Html.LabelFor(u => u.Password)
    @Html.PasswordFor(u => u.Password)
    @Html.ValidationMessageFor(u => u.Password)

    <input type="submit" value="Enviar" />
}

Queremos que a nossa validação de password forte também seja executada no navegador do usuário, para isso, precisamos fazer com que a classe PasswordForteAttribute implemente a interface IClientValidatable:

public class PasswordForteAttribute : ValidationAttribute, IClientValidatable

Todas as classes que implementam IClientValidatable precisam informar ao Asp.Net MVC qual é o código javascript que será executado no navegador do cliente. Isso é feito através do método GetValidationRules:

public class PasswordForteAttribute : ValidationAttribute, IClientValidatable
{
    private static readonly String PasswordPattern = @"((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{4,100})";

    // Implementação do IsValid

    public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {

    }
}

O IEnumerable<ModelClientValidationRule> é o objeto que configura qual é o código javascript que será executado para fazer a validação. Dentro dele precisamos informar qual é a mensagem de validação que deve ser exibida para o usuário, o nome da regra de validação (que será utilizado para identificar qual é a função javascript que será executada) e também os parâmetros que devem ser passadas para a função de validação.

public class PasswordForteAttribute : ValidationAttribute, IClientValidatable
{
    private static readonly String PasswordPattern = @"((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{4,100})";

    // Implementação do IsValid

    public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var regra = new ModelClientValidationRule();
        regra.ErrorMessage = "Password muito fraco";
        regra.ValidationType = "passwordforte";
        regra.ValidationParameters.Add("regex", PasswordPattern);
        return new List(){ regra };
    }
}

A implementação das regras de validação no navegador, no Asp.Net MVC, é feita utilizando-se uma biblioteca chamada jquery.validation.unobtrusive, então vamos incluí-la na view do formulário:

@* código do formulário *@

@* Assumindo que a versão do jquery é a 1.10.2 e está na pasta Scripts do projeto *@
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>

E agora precisamos configurar o jquery validate para que ele reconheça a nova regra de validação. Para isso precisamos registrar o nome dessa nova regra de validação e os parâmetros que ela precisa (no caso, o parâmetro é a expressão regular para o password):

@* Importa os scripts do jquery *@
<script>
    // o primeiro argumento é o nome da validação
    // e o segundo é o nome do argumento da validação
    $.validator.unobtrusive.adapters.addSingleVal("passwordforte", "regex");
</script>

Para finalizar precisamos informar qual é a função de validação que será executada:

<script>
    $.validator.unobtrusive.adapters.addSingleVal("passwordforte", "regex");

    // Aqui precisamos falar qual é o nome da regra de validação: passwordforte
    // e a função de validação
    $.validator.addMethod("passwordforte", function (value, element, regex) {
        // implementação da validação
    });
</script>

Na função de validação, recebemos 3 argumentos, o valor que está sendo validado (no caso o campo de texto para o password), o elemento html e, por fim, os parâmetros que foram passados no ValidationParameters do ModelClientValidationRule. Na implementação do método, precisamos devolver algo que avalie para true caso o value seja válido e para false, caso contrário.

<script>
    $.validator.unobtrusive.adapters.addSingleVal("passwordforte", "regex");
    $.validator.addMethod("passwordforte", function (value, element, regex) {
        var passwordRegex = new RegExp(regex);
        return value.match(passwordRegex);
    });
</script>

Isso é tudo que precisamos fazer para criar uma nova validação que será executada no navegador do cliente! Podemos ainda melhorar esse código isolando os códigos de validação dentro de um arquivo javascript e importar esse arquivo dentro do layout da aplicação para que o código não fique repetido em todos os formulários.

Simplificando a validação

A validação da senha é parecida com uma outra validação que já existe no Asp.Net MVC, a RegularExpressionAttribute, e por isso podemos reutilizar uma parte do código que já está pronta no .Net para implementar a validação na view. Ao invés de utilizarmos diretamente o ModelClientValidationRule, podemos utilizar uma de suas classes filhas para utilizarmos a validação na view implementada pelo Asp.Net MVC. Para a validação com expressões regulares, utilizamos a ModelClientValidationRegexRule:

public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
    var rule = new ModelClientValidationRegexRule("Password muito fraco", PasswordPattern);
    return new List() { rule };
}

Utilizando essa implementação para o GetClientValidationRule, só precisamos importar os códigos javascript para o jquery.validate.unobtrusive, sem implementarmos nossa própria função de validação. A implementação dessa validação pode ser encontrada nesse gist.

Para mais informações sobre a criação de validações customizadas, você pode ler o post do Brad Wilson sobre o tema e para mais um exemplo prático, temos esse artigo do code project.

Aprenda mais sobre as tecnologias do .Net em nossos cursos presenciais ou nas formações online do Alura.

4 Comentários

  1. janete clara 22/07/2014 at 11:08 #

    muito bom os exemplos e explicações para as validações! show!

  2. Vitor Cabral 23/07/2014 at 15:05 #

    Muito bom! Estava com algumas dúvidas no emprego de algumas validações. Obrigado Victor!

  3. Filipe Dalepiane 04/08/2014 at 11:30 #

    Excelente post, parabéns!

  4. Emerson 19/10/2016 at 23:09 #

    Boa noite Victor!
    preciso me especializar em mvc , validações, validações JavaScript. Você teria alguma dica de site, curso gratis que eu possa fazer ?

Deixe uma resposta