ASP .NET MVC 5 - A misteriosa classe ModelState
Neste
tutorial vou desmistificar a misteriosa classe ModelState mostrando o
seu significado, seus recursos e como devemos tratá-la em aplicações ASP
.NET MVC.
|
O que o ModelState ?
O ModelSate é uma propriedade do Controller e pode ser acessado a partir
das classes que herdam de System.Web.Mvc.Controller.
Ele é um dicionário disponível na classe base do controlador que armazena as
informações adicionais e de estado sobre o modelo.
O ModeState
representa uma coleção de pares nome e valor que são submetidos ao servidor
durante o POST. Ele também contém uma coleção de mensagens de erros para cada
valor submetido.
Apesar de usar o nome ModelState ele não conhece nada sobre qualquer classe de modelo, ele apenas contém nomes, valores e erros.
Então para que serve o ModelState ?
O ModelState têm dois propósitos:
Assim, quando um post acontece, você pode realizar suas verificações e se algo der errado, você pode adicionar um item ao dicionário ModelState e esta informação estará disponível para ser utilizado pela View para exibir um resumo das incoerências.
O ModelStateDictionary tem vários métodos para adicionar entradas:
void Add (KeyValuePair <string,
ModelState> item);
void Add (string chave, valor ModelState);
AddModelError void (string chave, exceção Exception);
AddModelError void (chave string, string errorMessage);
Simples assim.
Vamos então mostrar isso na prática...
Recursos usados :
Desmistificando o ModelState
Abra o VS 2013 Express for web e clique em New Project;
A seguir selecione Visual C# -> ASP .NET Web Application;
Informe o nome Mvc_ModelState e clique no botão OK;
A seguir selecione o template Empty, marque MVC e clique no botão OK;
Será criado um projeto contendo toda a estrutura de pastas criadas pelo framework ASP .NET MVC.
Definindo Model
Vamos definir um view model na pasta Models para representar um usuário.
Não sabe que é um View Model ? Então dê uma olhada nos modelos de domínio do seu projeto, e veja se há códigos que são utilizados exclusivamente pelas Views, não tendo nenhuma relação com o domínio do negócio em questão. Se isto estiver ocorrendo então seu modelo de domínio esta assumindo muitas responsabilidades. O padrão View Model veio justamente para resolver isso. O ViewModel deve conter a lógica da interface do usuário e permite modelar entidades a partir de um ou mais modelos em um único objeto representando um conjunto de um ou mais Models e outros dados que serão representados em uma View. Assim um View Model tem as seguintes características:
- Contém toda lógica de interface e a
referência ao modelo e assim atua como modelo para a View; |
Clique com o botão direito do mouse na pasta Models e a seguir Add Class;
Informe o nome UsuarioViewModel e defina o código abaixo para esta classe:
public class UsuarioViewModel
{
public string Nome { get; set; }
public string Sobrenome { get; set; }
public string Email { get; set; }
}
|
Definindo o Controller
Vamos agora definir o controlador do nosso projeto.
Clique com o botão do mouse sobre a pasta Controllers e a seguir clique em Add -> Controller;
Selecione o Scaffold - MVC 5 Controller Empty - e clique em Add;
Informe o nome HomeController e a seguir defina dois métodos Actions, o get e o post, neste controlador para incluir um usuário usando o nome IncluirUsuario():
using System.Web.Mvc;
using Mvc_ModelState.Models;
namespace Mvc_ModelState.Controllers
{
public class HomeController : Controller
{
[HttpGet]
public ActionResult IncluirUsuario()
{
UsuarioViewModel model = new UsuarioViewModel();
return View(model);
}
[HttpPost]
public ActionResult IncluirUsuario(UsuarioViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
return RedirectToAction("Index");
}
public ActionResult Index() { return View(); } } } |
O código referencia o namespace Mvc_ModelState.Models para ter acesso a modelo definido na pasta Models.
Temos o método IncluirUsuario() que apresenta o formulário ao usuário com base no modelo UsuarioViewModel e a seguir trata o POST deste formulário no mesmo método onde recebe os dados enviados da view para o controlador via ModelBinding.
O método Action Index() irá definir uma view Index bem simples.
Muito bem...
Criando as Views
Vamos criar a view Index().
Clique com o botão direito do mouse sobre o método Index() e a seguir em Add View;
A seguir defina o template como Empty(without model) e clique no botão Add;
Informe apenas um texto para identificar a view pois talvez ela nem será usada.
Vamos agora criar a view IncluirUsuario.
Clique com o botão direito do mouse sobre o método IncluirUsuario() e a seguir em Add View;
A seguir defina o template como Empty e o Model Class igual a UsuarioViewModel e clique no botão Add;
A seguir vamos definir código da view a seguir onde temos um formulário para o usuário informar o nome, o sobrenome e o email:
@model Mvc_ModelState.Models.UsuarioViewModel
<h2>Incluir Usuário</h2>
@using (Html.BeginForm())
{
<div>
<div>
Nome : @Html.TextBoxFor(x => x.Nome)
</div>
<div>
Sobrenome: @Html.TextBoxFor(x => x.Sobrenome)
</div>
<div>
Email: @Html.TextBoxFor(x => x.Email)
</div>
<div>
<input type="submit" value="Salvar Usuário" />
</div>
</div>
}
|
Executando o projeto e preenchendo o formulário, ao clicar no botão -Salvar Usuário - veremos que todos os valores entrados serão exibidos na instância de UsuarioViewModel (model) no controlador.
Nota: Marque um breakpoint no método Action IncluirUsuario(Post) e verifique os valores de model:
Como esses valores foram parar ai ???
Vamos dar uma espiada no código HTML da view IncluirUsuario.cshtml que foi renderizada :
No momento do POST todos os valores nas tags <input> são submetidos para o servidor como pares chave-valor.
Quando o MVC recebe o POST ele recebe todos os parâmetros do POST e os inclui para uma instância ModelStateDictionary.
Quando debugamos o método Action POST do Controlador podemos usar a janela Local para investigar os valores neste dicionário:
Espiando o ModelState no Debug vemos o seguinte:
Cada uma das propriedade tem uma instância de ValueProviderResult que contém os valores atuais submetidos ao servidor.
O MVC cria todas essas instâncias automaticamente para nós quando submetemos os dados, e o método Action POST tem os inputs que mapeia os valores submetidos.
Essencialmente o MVC esta encapsulando os inputs do usuário em em classes no servidor (ModelState).
E os erros onde estão ???
Vamos aplicar atributos Data Annotations em nosso UsuarioViewModel para realizar a validação e espiar os erros.
Altere a classe UsuarioViewModel incluindo os atributos abaixo no código da classe:
using System.ComponentModel.DataAnnotations;
namespace Mvc_ModelState.Models
{
public class UsuarioViewModel
{
[Required(ErrorMessage = "Informe o nome do usuário.")]
[StringLength(20, ErrorMessage = "O nome deve ser menor que {1} caracteres.")]
[Display(Name = "Nome do Usuário:")]
public string Nome { get; set; }
[Required(ErrorMessage = "Informe o sobrenome do usuário.")]
[StringLength(20, ErrorMessage = "O sobrenome não pode ter mais que {1} caracteres.")]
[Display(Name = "Sobrenome do Usuário:")]
public string Sobrenome { get; set; }
[EmailAddress(ErrorMessage = "Email inválido")]
[Required(ErrorMessage = "Informe o endereço de Email.")]
[Display(Name = "Endereço de Email:")]
public string Email { get; set; }
}
}
|
Feito isso vamos alterar a nossa view IncluirUsuario.cshtml incluindo o ValidationSummary() e o ValidationMessageFor() para exibir mensagens de erros se eles ocorrerem.
Veja como deve ficar o código da view:
@model Mvc_ModelState.Models.UsuarioViewModel
<h2>Incluir Usuário</h2>
@using (Html.BeginForm())
{
<div>
@Html.ValidationSummary()
<div>
Nome : @Html.TextBoxFor(x => x.Nome)
@Html.ValidationMessageFor(x => x.Nome)
</div>
<div>
Sobrenome: @Html.TextBoxFor(x => x.Sobrenome)
@Html.ValidationMessageFor(x => x.Sobrenome)
</div>
<div>
Email: @Html.TextBoxFor(x => x.Email)
@Html.ValidationMessageFor(x => x.Email)
</div>
<div>
<input type="submit" value="Salvar Usuário" />
</div>
</div>
}
|
Agora vamos executar novamente e simular um erro (não informar o nome do usuário) e fazer o Debug espiando o ModelState:
Conforme era esperado a instância de ModelState para o nome agora possui um erro na coleção Errors.
Quando o MVC cria o model state para as propriedade submetidas ele vai até cada propriedade no ViewModel e valida a propriedade usando os atributos associados com ela.
Se houver algum erro ele é adicionado na coleção Errors na propriedade do ModelState.
Note também que IsValid agora é false, indicando que existe um erro.
Dessa forma definindo a validação como fizemos permitimos ao MVC trabalhar da forma esperada: O ModelState armazena os valores submetidos e permite que eles sejam mapeados para propriedades de classe mantendo uma coleção de erros para cada propriedade.
É tudo que precisamos e tudo isso ocorre de forma transparente e não precisa de nenhuma configuração extra.
Pegue o projeto completo aqui: Mvc_ModelState.zip (sem as referências)
Porque a
palavra de Deus é viva e eficaz, e mais penetrante do que espada alguma de
dois gumes, e penetra até à divisão da alma e do espírito, e das juntas e
medulas, e é apta para discernir os pensamentos e intenções do coração.
Hebreus 4:12
Veja os
Destaques e novidades do SUPER DVD Visual Basic
(sempre atualizado) : clique e confira !
Quer migrar para o VB .NET ?
Quer aprender C# ??
Quer aprender os conceitos da Programação Orientada a objetos ? Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ? Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ? |
Gostou ? Compartilhe no Facebook Compartilhe no Twitter
Referências: