Neste post, iniciarei uma série sobre o mecanismo do ASP.NET MVC conhecido por Model Binding, mostrando suas principais características. Nesta primeira parte, a intenção é fazê-lo entender o que é o model binding, como funciona o binder default do MVC (DefaultModelBinder) e como ele facilita nossa vida.
O QUE É MODEL BINDING?
Model Binding é o processo de criar objetos .Net a partir de dados enviados pelo browser. Mesmo sem saber, estamos usando Model Binding toda vez que implementamos actions que recebem parâmetros.
Toda vez que uma requisição do tipo “/SeuController/SuaAction/121” é recebida, o framework MVC precisa tratar essa requisição de forma que possa passar valores apropriados nos parâmetros das actions.
De forma sucinta, esse tratamento inicia-se com o componente action invoker, que, como o próprio nome diz, é o responsável por invocar action methods. Antes de chamar a action, o action invoker verifica cada parâmetro e encontra o model binder correspondente para cada tipo de parâmetro.
Todo model binder implementa a interface IModelBinder, definida abaixo:
public interface IModelBinder { object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext); }
Na action abaixo, o action invoker irá procurar pelo model binder responsável por recuperar tipos inteiros (Int32) e chamar seu método BindModel para recuperar o valor do inteiro “id”.
public ViewResult Cliente(int id) { //obter cliente pelo id //.... return View(cliente); }
DEFAULT MODEL BINDER
O framework MVC já possui seu próprio model binder, o objeto DefaultModelBinder, que é usado pelo action invoker sempre que um binder customizado para um determinado tipo não for encontrado.
O default model binder procura por dados em quatro lugares, na ordem abaixo:
1. Request.Form
2. RouteData.Values
3. Request.QueryString
4. Request.Files
Isto é, ao receber um parâmetro de nome “id”, o default binder procurará por Request.Form[“id”], RouteData.Values[“id”], etc. A busca se encerra assim que o valor for encontrado em um desses locais.
Embora uma aplicação possa ter inúmeros model binders, o DefaultModelBinder é muito poderoso, fazendo a conversão de dados para tipos primitivos e complexos, arrays, collections, além de ser capaz de receber arquivos via upload. Sendo assim, grande parte de uma aplicação ASP.Net MVC (ou 100% dela) fará uso exclusivo da classe DefaultModelBinder.
Binding de tipos simples
Processo já mostrado acima no action method que recebe um inteiro. O DefaultModelBinder é capaz de fazer o binding para qualquer tipo primitivo (int, string, DateTime, double, etc..).
Binding de tipos complexos
Em alguns casos, fica ruim passarmos uma série de parâmetros para a action method. Para simplificar, podemos encapsular os dados da requisição em uma classe e passar este tipo na action method.
Vamos tomar por exemplo a seguinte view tipada:
@model Contato <h2>Contato</h2> @using (Html.BeginForm()) { <div>Nome: @Html.TextBoxFor(c => c.NomeCompleto)</div> <div>Fone: @Html.TextBoxFor(c => c.Fone)</div> <div>E-mail: @Html.TextBoxFor(c => c.Email)</div> <div></div> }
O tipo “Contato” utilizado na view encapsula as informações que iremos postar e não passa de uma classe bem simples:
public class Contato { public string NomeCompleto { get; set; } public string Fone { get; set; } public string Email { get; set; } }
E recebemos o objeto Contato na action method:
[HttpPost] public ActionResult Editar(Contato contato) { // aqui temos disponível o objeto "contato" com todos os seus atributos // devidamente preenchidos (contato.NomeCompleto, contato.Fone, contato.Email) return RedirectToAction("Index"); }
Para fazer essa “mágica”, o default model binder verifica as propriedades do objeto Contato. Se a propriedade é um tipo básico do .Net, o binder procura por um dado passado na requisição que tenha o mesmo nome da propriedade (por exemplo, “NomeCompleto”) e preenche a propriedade com o valor recebido.
Caso o tipo da propriedade for um outro tipo complexo – por exemplo, se tivéssemos um “Endereco” na classe “Contato” – o binder faria a verificação das propriedades desta classe aninhada da mesma forma.
Em outras palavras, se “Contato” tivesse uma propriedade de nome “EnderecoResidencial” do tipo “Endereco”:
public class Contato { // demais propriedades public Endereco EnderecoResidencial { get; set; } }
E “Endereco” tivesse as seguintes propriedades:
public class Endereco { public string Rua { get; set; } public string Bairro { get; set; } public string Cidade { get; set; } }
O default model binder procuraria por “EnderecoResidencial.Rua” e assim por diante.
Quando utilizamos os Html.Helpers, os atributos “name” do HTML são gerados na forma que o binder espera, ou seja, quando fazemos @Html.TextBoxFor(c => c.EnderecoResidencial.Rua), teremos um <input> de name “EnderecoResidencial.Rua”.
Portanto, devemos ter atenção com os nomes quando estamos escrevendo o HTML “na mão”.
CONCLUSÃO
Vimos neste post uma introdução ao mecanismo de Model Binding e ao default model binder, que conta com uma convenção de nome para encontrar os dados de uma requisição HTTP e convertê-lo para o tipo desejado.
Com Model Binding, ainda podemos limitar quais propriedades queremos que o binder verifique, podemos definir prefixos para os nomes dos campos, etc.
O Model Binding facilita muito no processo de obtenção dos dados, evitando que os desenvolvedores tenham que “investigar” manualmente nas coleções Request.Form, Request.QueryString, etc. (por consequência deixando o código mais limpo).
No próximo post, falarei de outros tipos de binding suportados pelo default model binder.
_________________________________________________________________
Este artigo faz parte da série:
1) ASP.NET MVC Model Binding (parte 1 – DefaultModelBinder)
2) ASP.NET MVC Model Binding (parte 2 – outras formas de binding)
3) ASP.NET MVC Model Binding (parte 3 – binding manual)
4) ASP.NET MVC Model Binding (parte 4 – custom binders)
Parabéns Robson, muito bom esse post!! Mostrou de forma bem clara como o model binding trabalha… Isso ae!!
CurtirCurtir
Obrigado pelo feedback, Fernando.
Abraços!
CurtirCurtir
Excelente post Robson, Parabéns!! Estou ansioso para ver a 2º parte. Vlw
CurtirCurtir
Obrigado, camarada.
Espero publicar ainda esta semana.
[]s
CurtirCurtir
Muito bom!!
CurtirCurtir
Parabéns pelo conteúdo, realmente facilitou a vida 😀
CurtirCurtir
Obrigado, cara
[]s
CurtirCurtir
Muito bom cara. Bastante esclarecedor. Parabéns.
CurtirCurtir
Obrigado, Fernando.
[]s!
CurtirCurtir
Explicação muito objetiva e clara. Parabéns e obrigado.
CurtirCurtir
Valeu!
CurtirCurtir