Validações próprias em ASP.NET MVC

Como criar validações próprias em ASP.NET MVC tanto em servidor quanto em cliente.

Por Eduard Tomás - Tradução CRV


Publicado em: 21/6/12
Valorize este artigo:
No artigo anterior vimos como usar Data Annotations para criar validações e como usar os helpers para mostrar as mensagens de erros das validações falidas. Porém faltou ver como podemos criar nossas próprias validações, caso as que venham de série não nos sirvam. Isso é o que vamos ver neste artigo.

Atributos de validação próprios

A maneira que podemos estender Data Annotations para validações personalizadas é criando um atributo de validação próprio, que mais tarde poderemos aplicar às propriedades do nosso viewmodel que queiramos validar.

Para criar um atributo de validação próprio basta derivar da classe ValidationAttribute e de modo geral redefinir um só método: IsValid. Dito método recebe um object com o valor a validar e deve devolver um booleano indicando se a validação foi correta ou não:

[AttributeUsage(AttributeTargets.Property)]
    public class NumeroParAttribute : ValidationAttribute
    {
      public override bool IsValid(object value)
      {
         try
         {
            var number = Convert.ToInt32(value);
            return number % 2 == 0;
         }
         catch (Exception)
         {
            return base.IsValid(value);
         }
      }
   }

O atributo AtributeUsage se usa para indicar à .NET onde é válido aplicar este atributo (isto é sempre que sejam criados atributos, quer sejam para validações ou para qualquer outra tarefa). Aqui estamos indicando que este atributo se aplica a propriedades, não a métodos ou a parâmetros.

Uma vez criado o atributo, seu uso é como qualquer dos que vêm de série: basta aplicá-lo às propriedades que desejemos validar.

public class DemoModel
    {
      [Range(1, 100, ErrorMessage = "Positivo menor que 100")]
      [NumeroPar(ErrorMessage = "O número deve ser par.")]
      public int ValorPar { get; set; }
   }

Ao derivar da classe ValidationAttribute já obtemos a propriedade ErrorMessage que vimos no artigo anterior e que nos permite especificar uma mensagem de erro a ser mostrada no caso da validação ser falida. Também podemos observar que, apesar da classe que criamos se chamar NumeroParAttribute, para aplicá-la basta decorar a propriedade do viewmodel com [NumeroPar] (sem o sufixo Attribute).

Agora poderíamos criar uma visão para criar objetos DemoModel:

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
      <legend>DemoModel</legend>

      <div class="editor-label">
         @Html.LabelFor(model => model.ValorPar)
      </div>
      <div class="editor-field">
         @Html.EditorFor(model => model.ValorPar)
         @Html.ValidationMessageFor(model => model.ValorPar)
      </div>

      <p>
         <input type="submit" value="Create" />
      </p>
   </fieldset>
}

E poderíamos comprovar como apenas podemos entrar números pares, comprendidos entre 1 e 100:


Figura 1: Validação de número par falida

Validação própria em cliente

A diferença do atributo NumeroPar com os que vêm de série, como p.ex. Range é que os segundos validam em cliente antes de que sejam enviados os dados ao servidor, enquanto que os atributos próprios apenas se validam em servidor (os atributos que vêm de série também se validam em servidor, lembrem que os dados devem sempre ser validados em servidor e que a validação em cliente é só por usabilidade e não por segurança).

É claro que podemos acrescentar validação em cliente para nossos atributos. Chegados a este ponto devo dizer que o mecanismo exato depende da biblioteca de validação que se use em cliente. Aqui vamos ver como fazê-lo usando jQuery Validate que é a biblioteca que se inclui por padrão em ASP.NET MVC. Para usar outras bibliotecas de validação em cliente seria necessário realizar outros passos.

Acrescentando o código javascript

O primeiro que devemos fazer é criar o código javascript que vai validar os dados. Se usamos jQuery Validate isto equivale a acrescentar um validador novo. Para acrescentar um validador novo basta dar lhe um nome e o método javascript a ser executado:

$.validator.addMethod("numeropar", function (value, element, param) {
    return value % 2 == 0;
});

Acrescentamos um validador chamado "numeropar" com a função de validação associada. Com isto agora jQuery Validate sabe que deve chamar este método javascript quando se solicite usar o validador "numeropar".

O passo seguinte é informar a jQuery Validate quando deve chamar este validador "numeropar" que acrescentamos. Para isso necessitaremos código em cliente e em servidor.

A interface IClientValidatable

Comecemos pelo código de servidor: nosso atributo de validação deve implementar uma interface chamada IClientValidatable. Esta interface requer que implementemos um só método chamado GetClientValidationRules, que deve retornar uma coleção de objetos da classe ModelClientValidationRule.

A classe ModelClientValidationRule contém o nome da regra de validação em cliente a aplicar e a mensagem de erro no caso de que tal validação falhe. No nosso caso, uma possível implementação é:

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
      yield return new ModelClientValidationRule
       {
         ErrorMessage = this.ErrorMessage,
         ValidationType = "numeropar"
      };
   }

Estamos devolvendo uma coleção com um só elemento ModelClientValidationRule. O nome da regra de validação em cliente é numeropar e a mensagem de erro é a mesma mensagem que se usa para a validação em servidor.

Nota: Você conhece a palavra chave yield? Essa palavra chave de C# permite retornar coleções sem necessidade de criar uma classe que implemente a coleção (usualmente uma List<T>). Para mais informação remeto ao blog de José Manuel Alarcón onde o conta de forma fenomenal: http://www.jasoft.org/Blog/post/PermaLinkaspxguid=8dfbbe0c-7851-4cb8-8a49-150be21.aspx

Bem, criamos no cliente um validador chamado "numeropar" e modificamos nosso atributo para indicar que se deve usar a regra de validação em cliente chamada "numeropar". Parece que tudo deveria funcionar mas ainda nos falta um último detalhe.

Código javascript não obtrusivo

Por padrão ASP.NET MVC3 não só usa jQuery Validate para as validações como ademais usa a versão unobtrusive. Não sei se você conhece o significado de código javascript não obtrusivo (unobtrusive javascript) mas resumindo posso dizer que se trata de que o código javascript esteja totalmente separado das etiquetas HTML. Isso significa não ver mais os famosos onclick="…" e similares. As etiquetas HTML se mantêm totalmente limpas de código javascript e contêm apenas atributos HTML standard. O uso de javasctipt não obtrusivo se baseia em uma característica nova de HTML5 (embora seja possível usar javascript não obtrusivo em versões anteriores, ainda que não de forma tão elegante). Para mais informação remeto a um post em meu blog onde comento com mais detalhe o que é e como funciona o javascript não obtrusivo: http://geeks.ms/blogs/etomas/archive/2010/11/12/saca-tus-scripts-de-tu-c-243-digo-html.aspx

Se você olhar no código fonte HTML que gera a chamada a Html.EditorFor(x=>ValorPar) vai ver o seguinte:

<div class="editor-field">
    <input class="text-box single-line" data-val="true" data-val-number="The field ValorPar must be a number." data-val-numeropar="El número debe ser par." data-val-range="Positivo menor que 100" data-val-range-max="100" data-val-range-min="1" data-val-required="The ValorPar field is required." id="ValorPar" name="ValorPar" type="text" value="" />
    <span class="field-validation-valid" data-valmsg-for="ValorPar" data-valmsg-replace="true"></span>
</div>

Todos os atributos data-val que o <input> contêm são para as validações usando javascript não obtrusivo. Na verdade você pode observar que existe um atributo chamado data-val-numeropar. Esse atributo foi gerado porque precisamente implementamos IClientValidatable em nosso atributo de servidor. Bem, por um lado temos um validador chamado "numeropar" que registramos em jQuery Validate. Por outro temos o atributo data-val-numeropar que foi gerado ao implementar IClientValidatable em nosso atributo. Só nos falta indicar a jQuery Validate que deve usar o validador chamado "numeropar" em todos aqueles campos que tenham o atributo "data-val-numeropar". Para isso devemos usar o seguinte código javascript:

$.validator.unobtrusive.adapters.addBool("numeropar");

Com este código se conecta o validador numeropar de jQuery Validate, com o atributo data-val-numeropar.

Na verdade, no exemplo o mesmo nome (“numeropar”) para tudo, coisa que eu recomendo a vocês, porém realmente temos dois conceitos:

  1. O nome do validador que registramos em jQuery Validate
  2. O nome da regra de validação, ou seja, o nome do atributo data-val-xx (onde xx se substitui pelo nome da regra de validação).
Não é obrigatório usar o mesmo nome. Com o código seguinte, vocês registram um validador chamado "numeropar" e o vinculam à regra de validação "np":

$.validator.addMethod("numeropar", function (value, element, param) {
    return value % 2 == 0;
    });
    $.validator.unobtrusive.adapters.addBool("np", "numeropar");

Observem o segundo parâmetro do método addBool: indica que a regra "np" deve ser validada usando o validador "numeropar" (se não se coloca o parâmetro se assume que o nome é o mesmo).

Claro que agora a regra se chama "np", e não "numeropar", de modo que quando se implemente IClientValidatable deve- se usar "np":

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
      yield return new ModelClientValidationRule
      {
         ErrorMessage = this.ErrorMessage,
         ValidationType = "np"
      };

Bom, o código completo da visão ficaria:

@model MvcApplication2.Models.DemoModel
<h2>
   ViewPage1</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script type="text/javascript">
    $.validator.addMethod("numeropar", function (value, element, param) {
      return value % 2 == 0;
   });
   $.validator.unobtrusive.adapters.addBool("np", "numeropar");
</script>
@using (Html.BeginForm())
{
   @Html.ValidationSummary(true)
   <fieldset>
      <legend>DemoModel</legend>
      <div class="editor-label">
         @Html.LabelFor(model => model.ValorPar)
      </div>
      <div class="editor-field">
         @Html.EditorFor(model => model.ValorPar)
         @Html.ValidationMessageFor(model => model.ValorPar)
      </div>
      <p>
         <input type="submit" value="Create" />
      </p>
   </fieldset>
}

Agora sim! Criamos uma validação própria e que se valida não só em servidor como também em cliente!

Em resumo…

Vimos como funciona o sistema de validações baseadas em atributos em ASP.NET MVC3. Também vimos o simples que é criar nossas próprias validações usando atributos próprios e como acrescentar validação em cliente em javascript. Claro que animo vocês a que entendam como funciona jQuery Validate (vimos o tipo de validadores mais simples que existem, os que só validam um valor, mas há os que podem receber parâmetros para validar categorias, ou fazer comparações). Não entraremos em mais detalhes em jQuery Validate porque fica fora do âmbito deste manual.

E sobre as validações? Bem, não terminamos ainda. Há mais umas duas coisinhas que creio interessantes e que veremos no artigo seguinte!






Usuários :    login / registro

Manuais relacionados
Categorias relacionadas
O autor

Home | Sobre nós | Copyright | Anuncie | Entrar em contato