[PS Além do blog, eu também estou agora utilizando o
Twitter para atualizações rápidas e para compartilhar links. Siga-me em:
twitter.com/scottgu]
Este é o
segundo de uma série
de posts que estou escrevendo sobre o próximo lançamento da ASP.NET
MVC 2. Este post aborda algumas das
melhorias para a validação de dados com a ASP.NET MVC 2.
Validação
de Dados com a ASP.NET MVC 2
Validar a entrada de dados do
usuário e garantir as regras/lógica de negócio é um requisito
fundamental para a maioria das aplicações web. A ASP.NET MVC 2 inclui um conjunto de novas
funcionalidades que tornam a validação dos dados de entrada do usuário e
a aplicação da lógica de validação dos modelos de dados/modelos de visão
significativamente mais fácil. Esses
recursos são projetados de modo que a lógica de validação seja sempre
aplicada no servidor e, opcionalmente, pode também ser aplicada no
cliente através de JavaScript. A
infraestrutura de validação e seus recursos na ASP.NET MVC 2 são
projetados de modo que:
1) Os
desenvolvedores podem facilmente tirar vantagem da validação através do
suporte a DataAnnotation (Anotação de Dados) presente no .NET
Framework. DataAnnotations fornecem
uma maneira muito fácil para adicionar regras de validação
declarativamente em objetos e propriedades com um mínimo de código.
2) Os desenvolvedores podem, opcionalmente,
integrar seu próprio engine (motor) de validação, ou tirar vantagem de frameworks
como Castle Validator ou a Biblioteca de Validação EntLib. Os recursos de validação da ASP.NET MVC 2 são
projetados para tornar mais fácil a adição de qualquer arquitetura de
validação - e ao mesmo tempo te possibilita tirar vantagem da nova
infraestrutura de validação da ASP.NET MVC 2 (incluindo validação no
lado do cliente, validação do modelo de dados, etc).
Isto
significa que habilitar a validação é extremamente fácil em cenários
comuns, permanecendo ainda muito flexível em cenários mais avançados.
Habilitando
a Validação com a ASP.NET MVC 2 e DataAnnotations
Vamos
seguir um passo a passo de um cenário CRUD simples com a ASP.NET MVC 2,
que tira vantagem do suporte à validação através de DataAnnotation. Especificamente, vamos implementar um formulário
"Create" (Criar) que permite que um usuário entre com dados de amigos:
Queremos garantir que as
informações inseridas sejam válidas antes de salvá-las em um banco de
dados - e também queremos apresentar mensagens de erro apropriadas caso
as informações estejam erradas:
Queremos ativar essa
validação para que ela ocorra no servidor e no cliente (via
JavaScript). Nós também queremos
assegurar que o nosso código mantenha o princípio DRY - Don't Repeat
Yourself ("Não Repita Você Mesmo") - o que significa que só devemos
aplicar as regras de validação em um único lugar, e então todos os
nossos controladores, ações e visões devem honrá-las.
A seguir eu
usarei o VS 2010 para implementar o cenário descrito acima usando a
ASP.NET MVC 2. Você também pode
implementar este exato cenário utilizando o VS 2008 e a ASP.NET MVC 2.
Passo
1: Implementando um Controlador FriendsController (sem validação, para
começar)
Vamos começar adicionando uma simples classe
"Person" (Pessoa) em um novo projeto ASP.NET MVC 2 igual a seguir:
Esta classe tem quatro
propriedades (implementadas usando o conceito de propriedades
automáticas (em Inglês) da linguagem C#, as quais são agora suportadas também na
linguagem VB no VS 2010 - viva!).
Vamos então adicionar uma classe
de controlador "FriendsController" em nosso projeto que expõe dois métodos
de ação "Create". O primeiro método
de ação é chamado quando uma requisição HTTP-GET chega para a URL Friends/Create.
Este método irá exibir um formulário em
branco para que possamos inserir dados da pessoa. O
segundo método de ação é chamado quando um pedido HTTP-POST chega para a
URL Friends/Create. Este método
mapeia os dados de entrada do formulário postado para um objeto Person,
verifica que nenhum erro tenha ocorrido, e se os dados forem válidos,
salvará os mesmos no banco de dados (vamos implementar o trabalho com o
banco de dados mais tarde neste tutorial). Se os dados de entrada do formulário postado forem inválidos, o
método de ação exibirá novamente o formulário com mensagens de erro:
Depois de implementarmos
nosso controlador, podemos clicar com o botão direito do mouse dentro
de um dos seus métodos de ação e escolher a opção "Add Vies" (Adicionar Visão)
dentro do Visual Studio - o que fará com que a janela de diálogo "Add
View" apareça. Vamos escolher a
implementação automática de uma visão "Create" para a qual é passado um
objeto Person:
O Visual Studio irá
gerar automaticamente uma visão Create.aspx par nós dentro do diretório
\Views\Friends\ do nosso projeto. Observe
a seguir como o VS tira vantagem dos novos
métodos de ajuda HTML fortemente tipados da ASP.NET MVC 2
(permitindo um melhor suporte à IntelliSense e à verificação do código
no tempo de compilação):
Agora, quando
executamos a aplicação e chegamos na URL Friends/Create, obteremos um
formulário em branco no qual poderemos inserir dados:
Em virtude de não termos
implementado nenhuma validação dentro da aplicação, nada nos impede de
entrar com dados errados dentro do formulário e enviar o mesmo para o
servidor.
Passo 2: Habilitar a Validação usando
DataAnnotations
Vamos agora atualizar nossa aplicação para
aplicar algumas regras de validação básicas. Nós vamos implementar estas regras no nosso modelo de objeto
Person - e não dentro do nosso Controlador ou nossa Visão. O benefício que ganhamos ao implementar as regras
de validação dentro do nosso objeto Person é que isto garantirá que a
validação será aplicada em qualquer cenário dentro da nossa aplicação
que usa um objeto Person (por exemplo: se mais tarde adicionarmos um
cenário de edição). Isto vai nos ajudar a manter nosso código DRY
evitando a repetição de regras de validação em múltiplos lugares.
A ASP.NET MVC 2 permite que os desenvolvedores
adicionem facilmente atributos de validação declarativos nas classes do
modelo ou visões do modelo, fazendo com que estas regras de validação
sejam automaticamente aplicadas sempre que a ASP.NET MVC realizar
operações de ligação de dados dentro da aplicação. Para vermos isto em ação, vamos atualizar nossa
classe Person para termos alguns atributos de validação dentro dela. Para fazermos isso, vamos adicionar uma
declaração "using" para o namespace
"System.ComponentModel.DataAnnotations" no topo do arquivo - e, em
seguida, decoraremos as propriedades do objeto Person com atributos de
validação [Required] (Requerido), [StringLength] (Tamanho da String),
[Range] (Intervalo) e [RegularExpression] (Expressão Regular) (que são
implementados dentro deste namespace):
Nota: Acima nós
estamos explicitamente especificando as mensagens de erro com strings. Alternativamente, você pode definí-las dentro
de arquivos de recursos (com extensão .resx) e, opcionalmente, localizá-las, dependendo do
idioma e cultura do usuário que está acessando o sistema. Você pode aprender mais sobre como localizar
mensagens de erro de validação aqui
(em Inglês).
Agora que nós adicionamos os atributos de
validação em nossa classe Person, vamos reexecutar nossa aplicação para
ver o que acontece quando entramos com valores errados e enviamos os
mesmos para o servidor:
Observe acima como a
nossa aplicação já possui uma experiência de erro aceitável. Os elementos de texto com entrada de dados
inválida são destacados em vermelho, e as mensagens de erro de validação
que especificamos são exibidas para o usuário final. O formulário também está preservando os dados de
entrada do usuário que foram inicialmente digitados - para que ele não
precise preencher tudo novamente. Como
assim, você pode perguntar, isso aconteceu?
Para
entender esse comportamento, vamos olhar o método de ação Create que
trata o cenário de POST do nosso formulário:
Quando nosso formulário
HTML é enviado de volta para o servidor, o método acima será chamado. Em virtude do método de ação aceitar um objeto
"Person" como um parâmetro, a ASP.NET MVC vai criar um objeto Person e
mapeará automaticamente os dados do formulário de entrada para este
objeto. Como parte deste processo, a
ASP.NET MVC também irá verificar se os atributos de validação
DataAnnotation para o objeto Person são válidos. Se tudo for válido, então a verificação de
ModelState.IsValid dentro do nosso
código irá retornar true - e neste caso nós (possivelmente) salvaremos o
objeto Person no banco de dados e então redirecionaremos o usuário de
volta para a home-page.
Se houver algum erro de validação no objeto
Person, nosso método de ação reexibe o formullário com o objeto Person
inválido. Isto é feito através da
última linha de código no trecho de código acima.
As
mensagens de erro são então exibidas dentro da nossa visão porque o
nosso formulário Create tem cahamadas para o método de ajuda <% =
Html.ValidationMessageFor() %> próximo a cada método de ajuda <%=
Html.TextBoxFor() %>.O método de
ajuda <%= Html.ValidationMessageFor() %> mostrará a mensagem de
erro apropriada para cada propriedade inválida do modelo passado para a
visão:
A coisa agradável com
relação a este padrão/abordagem é que ela é bastante fácil de configurar
- e ela nos permite adicionar ou alterar as regras de validação
facilmente em nossa classe Person, sem termos que mudar qualquer
código dentro dos nossos Controladores ou Visões. Esta capacidade de especificar as regras de
validação em um lugar e fazer com elas sejam honradas e respeitadas em
todos os lugares nos permite evoluir rapidamente nossa aplicação e
regras, com um mínimo de esforço possibilitando-nos manter o nosso
código DRY.
Passo 3: Habilitando a Validação no Lado do
Cliente
Nossa aplicação atualmente só realiza a validação
no lado do servidor - o que significa que nossos usuários finais terão
que enviar o formulário para o servidor antes que eles vejam mensagens
de erro de validação.
Uma das coisas legais sobre a arquitetura de
validação da ASP.NET MVC 2 é que ela suporta tanto a validação no lado
do servidor como também no lado do cliente. Para tornar isto possível, tudo o que precisamos fazer é
adicionar duas referências JavaScript em nossa visão, e escrever uma
linha de código:
Quando acrescentamos
estas três linhas, a ASP.NET MVC 2 usará os meta-dados de validação que
adicionamos em nossa classe Person e ligará a lógica de validação
JavaScript no lado do cliente. Isto
significa que os usuários receberão erros de validação imediatamente
quando eles mudarem para outro campo a partir de um campo inválido
através da tecla Tab.
Para ver o suporte JavaScript funcionando no lado
do cliente para a nossa aplicação de amigos, vamos executar novamente a
aplicação e preencher as três primeiras caixas de texto com valores
válidos - clicando no botão "Create" logo em seguida. Observe como nós vamos receber uma mensagem de
erro imediatamente para o valor que falta ser preenchido sem termos que
enviar o formulário para o servidor:
Se entrarmos com algum
texto que não seja um endereço de e-mail válido, a mensagem de erro irá
mudar imediatamente de "Email Required" (Email necessário) para "Not a
valid email" (Não é um e-mail válido) (as quais são as mensagens de erro
que especificamos quando nós adicionamos as regras de validação em
nossa classe Person):
Quando digitamos um
e-mail válido, a mensagem de erro irá desaparecer imediatamente e a cor
de fundo da caixa de texto voltará ao seu estado normal:
O bom é que nós não
temos que escrever qualquer JavaScript personalizado para habilitarmos a
lógica de validação acima. Nosso
código de validação também é ainda muito DRY - nós podemos especificar
as regras em um único lugar e ter as mesmas aplicadas em toda a aplicação - e
em ambos os lugares: no cliente e no servidor.
Note que
por motivos de segurança as regras de validação no lado do servidor
sempre são aplicadas mesmo se você tiver o suporte às regras de
validação no lado do cliente ativado. Isto impede que os hackers tentem enganar o servidor driblando
as regras no lado do cliente.
O suporte à validação JavaScript no lado do
cliente na ASP.NET MVC 2 pode funcionar com qualquer framework/motor de
validação que você venha a usar com a ASP.NET MVC. Ele não requer que você use a abordagem de validação através de
DataAnnotation - toda a infraestrutura funciona de maneira independente
de DataAnnotations e pode trabalhar em conjunto com o Castle validador,
o EntLib Validation Block, ou qualquer outra solução de validação
customizada que você escolha usar.
Se você não quiser usar nossos
arquivos JavaScript no lado do cliente, você também pode substituir o
plugin de validação da jQuery passando então a usar a biblioteca que
você escolher. O download da
biblioteca ASP.NET MVC Futures também incluirá suporte para habilitar a
validação jQuery para o framework de validação no lado do servidor da
ASP.NET MVC 2.
Passo 4: Criando um Atributo [Email]
Personalizado para a Validação
O namespace
System.ComponentModel.DataAnnotations dentro do .NET Framework já possui alguns
atributos de validação que você pode usar. Nós usamos 4 atributos
diferentes no exemplo acima - [Required],
[StringLength], [Range] e [RegularExpression].
Você também
pode, opcionalmente, definir os seus atributos de validação
personalizados e usá-los da mesma forma. Você pode definir atributos de validação completamente
personalizados derivando da classe base ValidationAttribute dentro do
namespace System.ComponentModel.DataAnnotations. Alternativamente, você pode optar por derivar de
qualquer um dos atributos de validação existentes se você quiser
simplesmente extender a funcionalidade da classe base.
Por
exemplo, para ajudar a limpar o código dentro da nossa classe Person nos
podemos criar um novo atributo de validação [E-mail] que encapsula a
expressão regular que faz a verificação de e-mails válidos. Para fazer isso, nós podemos simplesmente derivar
este novo atributo a partir da classe base RegularExpression como
mostrado a seguir, chamando o construtor da classe base
RegularExpression com a regex de e-mail adequada:
Podemos, então,
atualizar a nossa classe Person para usarmos o nosso novo atributo de
validação [E-mail] no lugar da expressão regular que usamos
anteriormente - o que torna o código mais limpo e encapsulado:
Quando criamos atributos
de validação personalizados, podemos especificar a lógica de validação
que roda tanto no servidor como no cliente através de JavaScript.
Além de
criar atributos de validação que se aplicam à propriedades individuais
em um objeto, você também pode aplicar atributos de validação no nível
da classe - o que permite a você executar a lógica de validação em
múltiplas propriedades dentro de um objeto. Para um exemplo disto, você pode analisar o atributo customizado
"PropertiesMustMatchAttribute" que está incluído no arquivo
AccountModels.cs/vb dentro do template de projeto padrão da ASP.NET MVC 2
(vá em File->New ASP.NET MVC 2 Web Project dentro do VS 2010 e
busque por esta classe).
Passo 5: Persistindo/Salvando os dados em um
Banco de Dados
Vamos agora implementar a lógica necessária para
salvar nossos amigos em um banco de dados.
Neste momento estamos
simplesmente trabalhando com uma simples classe C# (por vezes referida
como uma classe "POCO" (em Inglês) - "simples e velho objeto da CLR"). Uma abordagem que poderíamos usar seria escrever
algum código de persistência separado que mapeia esta classe existente
que já gravamos no banco de dados. Soluções
para mapeamento objeto relacional (ORM) como NHibernate suportam muito
bem este estilo de mapeamento POCO/PI. O ADO.NET Entity Framework (EF) que acompanha o .NET 4 também
suportará mapeamento POCO/PI e igualmente à NHibernate, opcionalmente,
também permitirá a definição de mapeamentos de persistência em uma
maneira "somente código" (nenhum arquivo de mapeamento ou designers
serão necessários).
Se nosso objeto Person fosse mapeado para um
banco de dados desta forma, então não teríamos a necessidade de fazer
quaisquer mudanças em nossa classe Person ou em qualquer uma das nossas
regras de validação - que continuariam funcionando muito bem.
Mas,
e se estivermos usando uma ferramenta gráfica para nossos mapeamentos
ORM?
Muitos desenvolvedores que usamo o Visual Studio
hoje não escrevem seus próprios mapeamentos ORM / lógica de persistência
- e ao invés disso usam os designers que acompanham o Visual Studio
para os ajudar a gerenciar esta tarefa.
Uma questão que muitas
vezes surge quando se usa DataAnnotations (ou qualquer outra forma de
validação baseada em atributos) é "como você pode aplicar tais regras de
validação quando o objeto do modelo com o qual você está trabalhando é
criado e mantido por um designer gráfico?". Por exemplo, o que aconteceria se ao invés de termos uma classe
Person no estilo POCO igual a que estamos usando até agora, tivéssemos
definido e mantido a nossa classe Person dentro do Visual Studio através
de uma ferramenta gráfica para mapeamento como LINQ para SQL ou o
designer do ADO.NET EF?
Acima é exibido um
screenshot que mostra uma classe Person definida através do designer do
ADO.NET EF no VS 2010. A janela no
topo define a classe Person, a janela na parte inferior mostra o editor
de mapeamento e como as suas propriedades mapeiam de/para uma tabela
"People" (Pessoas) dentro de um banco de dados. Quando você clica em salvar no designer este gera
automaticamente uma classe Person para você dentro do seu projeto. Isso é ótimo, só que toda vez que você fizer uma
alteração e clicar em salvar, o designer irá recriar a classe Person
- o que faria com que qualquer atributo de validação que você tivesse
adicionado na classe fosse perdido.
Uma maneira para
adicionar meta-dados baseados em atributos (iguais aos atributos de
validação) em uma classe que é criada/mantida automaticamente por um
designer do VS é aplicar uma técnica que chamamos de "buddy classes"
(classes amigas). Basicamente você
cria uma classe separada que contém seus atributos de validação e
meta-dados e então liga a mesma à classe gerada pelo designer aplicando
um atributo "MetadataType" em uma classe parcial que é compilada juntamente com a
classe gerada pela ferramenta, neste caso o designer do VS. Por exemplo, se quiséssemos aplicar as regras de
validação que usamos anteriormente em uma classe Person mantida pelo
designer de LINQ to SQL ou ADO.NET EF, nós poderíamos atualizar nosso
código de validação fazendo com este passe a ficar em uma classe
separada chamada "Person_Validation" que é ligada à classe "Person"
criada automaticamente pelo VS usando o seguinte código:
A
abordagem acima não é tão elegante como uma abordagem POCO pura -, mas
tem a vantagem de trabalhar com praticamente qualquer ferramenta ou
código gerado por designers dentro do Visual Studio.
Última
Passo - Salvando o Amigo no Banco de Dados
Nosso
último passo - independentemente de nós usarmos uma classe Person POCO
ou uma classe Person gerada automaticamente por alguma ferramenta - será
salvar os nossos amigos válidos no banco de dados.
Para fazer
isso, temos simplesmente que trocar a declaração do marcador "Todo"
dentro da nossa classe FriendsController por 3 linhas de código que
salvam o novo amigo no banco de dados. A seguir está o código completo para a classe FriendsController
- usando o ADO.NET EF para fazer a persistência dos dados para nós:
E agora, quando nós
visitamos a URL Friends/Create podemos facilmente adicionar
novas Pessoas no nosso banco de dados de amigos:
A validação de todos os
dados é aplicada tanto no cliente como no servidor. Podemos facilmente adicionar/modificar/excluir
regras de validação em um só lugar, fazendo com que estas sejam
aplicadas em todos os controladores e visões que fazem parte da nossa
aplicação.
Resumo
A ASP.NET MVC 2 torna
muito mais fácil integrar validação de dados em aplicações web. Ela promove uma abordagem de validação baseada no
modelo de dados, a qual permite que você mantenha suas aplicações
focadas no principio DRY, te ajudando a garantir que as regras de
validação sejam aplicadas de forma consistente em toda a aplicação. O suporte integrado para DataAnnotations da
ASP.NET MVC 2 faz com que cenários de validação sejam implementados de
maneira extremamente fácil sem a necessidade de instalar qualquer pacote
adicional. O suporte para extensões
dentro da infraestrutura de validação da ASP.NET MVC 2 permite que você
implemente uma vasta gama de cenários de validação mais avançados -
através da conexão de qualquer framework/motor de validação customizado
ou existente.
Espero que ajude,
Scott
Texto traduzido do post original por
Leniel Macaferi.