Vimos no post anterior (aqui) como fazer validação de dados (client e server-side) no ASP.NET MVC 3.

Continuando no tema, hoje vou mostrar como é simples criar atributos de validação (Data Annotations) personalizados.

Ainda no exemplo dado no post anterior, citado acima, utilizei o atributo [RegularExpression] para validar o formato de e-mail da classe Pessoa. Imagine agora utilizar um atributo personalizado, como [EmailEmFormatoValido], no lugar do [RegularExpression]. Ficaria muito mais claro, além de encapsular a lógica de validação em um único lugar.

Para criar esse atributo personalizado é muito simples. Basta implementar uma classe que herde de ValidationAttribute (do namespace System.ComponentModel.DataAnnotations) e sobrescrever o método IsValid da mesma. Fica assim:

public class EmailEmFormatoValido : ValidationAttribute
{
    public EmailEmFormatoValido()
    {
        ErrorMessage = "E-mail deve estar em formato válido.";
    }

    public override bool IsValid(object value)
    {
        var email = value as string;
        if (email != null)
            return Regex.IsMatch(email, @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*");
        return true;
    }
}

Simples não é? No construtor da classe, defini uma mensagem padrão e no método IsValid implementei a lógica de validação desejada.

Note que o método IsValid retorna “true” sempre que o valor do campo estiver nulo. Isso é recomendável, senão você estaria deixando a propriedade obrigatória mesmo que o atributo [Required] não estivesse sendo usado.

Agora que temos o atributo criado, vamos utilizá-lo na propriedade Email da nossa classe Pessoa, no lugar do atributo [RegularExpression]:

public class Pessoa
{
    [Required(ErrorMessage = "Nome deve ser preenchido")]
    public string Nome { get; set; }

    [Required(ErrorMessage = "E-mail deve ser preenchido")]
    [EmailEmFormatoValido]
    public string Email { get; set; }
}

Notem que usei o atributo [EmailEmFormatoValido] sem especificar a mensagem ErrorMessage, pois já defini uma mensagem padrão no construtor da classe como mostrado anteriormente. No entanto, você pode especificá-la, sobrescrevendo a mensagem padrão.

Isso é tudo que precisamos para termos uma validação server-side usando nosso atributo personalizado. No entanto, caso você rode a aplicação, notará que a validação client-side não funciona. Isto ocorre porque os scripts client-side são gerados somente para os atributos básicos [Required], [RegularExpression], [Range] e [StringLength].

Sendo assim, é preciso implementar a validação client-side para seu atributo personalizado.

O primeiro passo é implementar um ModelValidator associado ao nosso atributo EmailEmFormatoValido. Fazemos isso, herdando de DataAnnotationsModelValidator<T> (do namespace System.Web.Mvc), onde T é nosso atributo:

public class EmailValidator : DataAnnotationsModelValidator
{
    private readonly string _mensagem;

    public EmailValidator(ModelMetadata metadata, ControllerContext context, EmailEmFormatoValido attribute)
        : base(metadata, context, attribute)
    {
        _mensagem = attribute.ErrorMessage;
    }

    public override IEnumerable GetClientValidationRules()
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = _mensagem,
            ValidationType = "email"
        };
        return new[] { rule };
    }
}

Notem que precisamos sobrescrever o método GetClientValidationRules, o qual retorna um array de regras que servirão como metadata para a validação em javascript que iremos escrever.

No exemplo acima, estou retornando apenas uma regra (ModelClientValidationRule) com a mensagem de erro igual à mensagem de erro definida em nosso atributo EmailEmFormatoValido (a mensagem de erro é obtida de attribute.ErrorMessage no construtor da classe). Definimos também na regra, o ValidationType para “email”, que mais tarde usaremos em nosso javascript.

O segundo passo é registrar esse novo validator, vinculando-o ao atributo EmailEmFormatoValido. Fazemos isso em Application_Start, no Global.asax:

protected void Application_Start()
{
    // mantenha o codigo existente como está

    // vinculando nosso novo validator ao nosso atributo
    DataAnnotationsModelValidatorProvider.
        RegisterAdapter(typeof(EmailEmFormatoValido), typeof(EmailValidator));
}

Por fim, precisamos implementar a validação em javascript:


Sys.Mvc.ValidatorRegistry.validators.email = function (rule) {
    return function (value, context)
    {
        if (!value || EhEmail(value)) return true;
        return rule.ErrorMessage;
    };
};

function EhEmail(email)
{
    regex = new RegExp("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?");
    return regex.test(email);
}

O mais importante do código acima está em “….ValidatorRegistry.validators.email“. Ele está adicionando um validator ao dicionário de validators, usando a chave “email”, que deve ser o mesmo nome que definimos no ValidationType da classe EmailValidator.

Ufa! Terminamos! Após criar nosso validator EmailValidator (1), registrá-lo no Global.asax (2) e implementar a validação javascript (3), podemos ver a nosso atributo EmailEmFormatoValido funcionando corretamente no lado cliente.

Claro, vale lembrar que sua view (ou master) deve referenciar os seguintes scripts (juntamente com o script de validação mostrado acima).

Com isso, vimos como é fácil customizar os Data Annotations, podendo criar validadores para formato de e-mail, validadores de CPF/CNPJ e mais uma infinidade, deixando nossa validação simples e padronizada.

Obs.: tudo que foi mostrado aqui pode ser utilizado no ASP.NET MVC 2.