MVVM e Asp.Net MVC com KnockoutJS    

Introdução

Hoje vou falar de um tema novo, porém, muito importante para quem desenvolve sistemas Web. Atualmente, os sistemas têm ficado com interfaces cada vez mais ricas e rápidas, o casamento de Asp.Net MVC (ou mesmo WebForms) com Jquery possibilitou trazer muito da experiência de usuário de sistemas Desktop para Web, e quanto mais nós desenvolvedores vemos as possibilidades mais as telas ficam cheias de “efeitos”.

Porém, conforme as telas ficam mais complexas e com mais informações, mais o código para tratar tudo isso fica confuso. É comum em grandes telas de sistemas Web, existirem arquivos JavaScript com algumas centenas de linhas, e para piorar o suporte do Visual Studio para JavaScript, por mais que tenha melhorado muito, continua ainda um pouco restrito.

Se olharmos, muito das linhas de um arquivo JavaScript tem a responsabilidade de ler e escrever em campos HTMLs, muito do arquivo é a utilização do método val() do Jquery com ou sem parâmetros, e por não ter uma bom editor acabamos muitas vezes duplicando trechos de códigos.

Muito do meu esforço nos últimos projetos em que trabalhei era diminuir o esforço para fazer esse mapeamento entre dados (modelo) e tela, tentando mesclando alguns puglins JQuery para resolver o meu problema. Nesse esforço, esbarrei no padrão MVVM, muito utilizado em aplicações Silverlight, e tentei aplicar esse padrão a minha realidade MVC/WebForms X JQuery. Algum tempo tentando “reinventar” a roda, descobri o Knockout, uma bela biblioteca JavaScript projetada para implementar o padrão MVVM em Javascript.

O objetivo deste post é tentar mostrar um pouco das facilidades que essa biblioteca nos dá ao integrar código e interface de maneira simples e limpa. Espero que goste!

O padrão MVVM (Model – View – ViewModel)

Meu conhecimento de MVVM não é amplo, então vou basear essa breve explicação no que você pode encontrar no Wikipedia ou mesmo no site do Knockout.

A definição básica é: “MVVM é um padrão arquitetural criado pela Microsoft como uma espacialização do Design Patter Presentation Model, introduzido originalmente por Martin Fowler”.

A Microsoft introduziu esse padrão com o advento do WPF e Silverlight, então desenvolvedores dessas plataformas já devem estar bem familiarizado com esse padrão.

Basicamente, MVVM é um padrão para criar UI (tela) de forma transparente sofisticada, separando esta tarefa em três partes:

  • Modelo (Model) – Assim como o M do MVC, o modelo representa os dados e operações do domínio da sua aplicação. É totalmente independente de interface e representa o negócio da sua aplicação, que vai muito além de tela. Para conseguir identificar da melhor forma possível o domínio da sua aplicação, que te leva a um bom modelo, recomendo a leitura do livro sobre DDD do Erick Evans, que pode ser algumas páginas online no google.
  • View – Sua tela, com campos, labels e botões, representando o estado da aplicação e permitindo que o usuário interaja com a aplicação, em resumo, a Interface com Usuário – UI (HTML).
  • ViewModel – Uma representação somente de código dos dados e operações de uma UI. Não é a UI de fato, porque não tem nada que lembre botões, campos de texto, etc. Armazena os dados não salvos do usuário e tem conhecimento para conversões de informação. Utilizando Knockout é puro JavaScript.

A técnica de MVVM pode ser aplicada praticamente em qualquer linguagem que tenha interface de usuário rica, com botões, campos de textos, etc. Como comentei, é muito utilizada com Silverlight e WPF, mas pode ser utilizada com WindowsForm, WebForm, VB6, etc, e obviamente não está amarrado ao Knockout.

É importante frisar também que MVVM não substitui MVC, e a View do MVC não tem a mesma função da View do MVVM, visto que no MVC, view pode ser qualquer saída da aplicação, podendo ser relatório, arquivo de texto, prompt de comando, etc; enquanto que no MVVM é sempre uma interface de usuário rica, com botões, campos de textos, combo, etc.

Em geral, em uma aplicação Asp.Net MVC que faz uso de Ajax, a View é um JSON, que muitas vezes é um objeto do próprio Model, ou um objeto ViewModel.

KnockoutJS – MVVM com Javascript

KnockoutJS (KO), como disse anteriormente, é uma biblioteca de MVVM feita em JavaScript puro, ou seja, sem dependências de outras bibliotecas JS para funcionar (na maioria dos casos). Foi criado pelo Steve Sanderson, há pouco tempo atrás e já está ganhando certa notoriedade no mundo Asp.Net, e promete ganhar muito mais (não só no Asp.Net), porque realiza muito bem a tarefa que se dedica a fazer.

Todas as informações necessárias, como documentação, exemplo, releases e download pode ser encontrado no site http://knockoutjs.com/

Os conceitos básicos do KO são:

  • Bindings declarativos;
  • Atualização de UI automática (Observables) e Controle de Dependência;
  • Template (precisa de JQuery);

Neste post, tentarei abordar cada um dele, apenas para se ter uma noção da capacidade dessa biblioteca. Para não me alongar muito, mostrarei apenas um exemplo de cada um. Muitos outros podem ser encontrado no site do KO.

Exemplo 1: Binding declarativo.

Obs.: Para esse exemplo funcionar, é necessário baixar o arquivo do KO, a partir daqui.

O Binding no Knockout é realizado através de atributos HTML5 data-bind, e pode ser utilizado em praticamente todas as TAGs HTMLs. Como o atributo é interpretado pela biblioteca do KO, não importa se o Browser suporta ou não HTML5. Na realidade, o Knockout é suportado por todos os Browsers, inclusive mobile.

Como primeiro exemplo, vou fazer um bem básico. Na minha View Index do Controller Home, vou colocar o seguinte HTML:

   1: <p>Codigo: <span data-bind="text:Codigo"></span></p>
   2: <p><span>Nome:</span><input type="text" data-bind="value:Nome" /></p>
   3: <p><span>Email:</span><input type="text" data-bind="value:Email" /></p>

O código acima é bem simples: Estou dizendo que o atributo text do controle span, por exemplo, será preenchido com o atributo Codigo do meu Modelo. Em outros palavras, seria o mesmo que fazer assim:

   1: <p>Código: <span>1</span></p>

O JavaScript é muito simples para fazer o Knockout funcionar. Vou criar um modelo, que nada mais é que um objeto JSON, e depois invocar o método que efetiva o Bind no formulário. O Código está abaixo:

   1: var usuario = {
   2:     Codigo: 1,
   3:     Nome: "Frederico",
   4:     Email: "blog@fredericoemidio.com"
   5: }
   6:  
   7: ko.applyBindings(usuario);

Com essas poucas linhas de código o Bind já funciona, a única novidade ai é a chamada applyBindings do objeto ko. KO é o objeto de entrada a todas as funções do Knockout. Esse método tem dois overloads, este que estou usando, que a partir de um modelo de dados ele realiza o bind para toda a página onde tiver controller com o atributo data-bind e o outro é com um segundo parâmetro que recebe um container (div por exemplo), informando que o bind só deve ser realizada nos controles dentro do Container.

Com o código acima, o resultado é o seguinte:

 

image

E voilá! Funcionando! Foi um exemplo muito simples, porém, extremamente útil.

Exemplo 2: Implementando atualização automática de UI com Observables

Um dos conceitos centrais do Knockout sãos os Obervables. Observables basicamente é a forma que o Knockout utiliza para que uma atualização feito no JavaScript automaticamente reflita no HTML e vice-versa.

Vou utilizar o mesmo exemplo acima, porém, agora vou simular um botão Salvar, para percebemos como o modelo é automaticamente atualizado conforme o usuário digita nos campos da tela.

Vou alterar o meu modelo para ficar da seguinte forma:

   1: var usuario = {
   2:         Codigo: ko.observable(1),
   3:         Nome: ko.observable("Frederico"),
   4:         Email: ko.observable("blog@fredericoemidio.com"),
   5:  
   6:         Salvar: function() {
   7:         alert("Nome: " + this.Nome() + "\r\nEmail: " + this.Email());
   8:        }
   9:     }

O que eu fiz nesse código: Falei que minhas propriedades são observaveis, com o método ko.observable, veja que também adicionei um método no modelo, para simular um método Salvar.

O legal dessa abordagem é que o código JavaScript fica como um code-behind, e a gente não precisa fazer referência a controles HTMLs, e isso é incrível!

No HTML, para fazer o botão Salvar funcionar, eu também utilizo o atributo data-bind, veja, apenas utilizando o atributo click:

   1: <input type="button" value="Salvar" data-bind="click: Salvar" />

O resultado fica assim:

image

Perceba que alterei o conteúdo e ele refletiu no meus dados, sem eu precisar ficar me preocupando em como ler as informações. Como enviar para o servidor fica a seu critério. Você pode utilizar PageMethods ou métodos AJAX do JQuery.

Exemplo 4 – Mapeamento automático

Mas agora você pode falar: “Eu vou ter que ficar fazendo mapeando das minhas classes no JavaScript? Mas meu cadastro de Pessoa tem 50 campos, isso não vai dar certo!”

Realmente, e por isso o Knockout tem um plugin, que realiza esse mapeamento automaticamente. Para utiliza-lo, você terá que baixa-lo separadamente, neste site.

Para fazer esse exemplo, vou criar uma classe na pasta Models do meu site MVC:

   1: public class Pessoa
   2: {
   3:    public int Codigo { get; set; }
   4:    public string Nome { get; set; }
   5:    public string Email { get; set; }
   6: }

E vou criar uma uma Action no meu Controller para obter um registro de Pessoa do servidor:

   1: public JsonResult ObterPessoa()
   2: {
   3:     return  Json(new Pessoa { Codigo = 1, Nome = "Frederico", Email = "blog@fredericoemidio.com" }, JsonRequestBehavior.AllowGet);
   4: }

E vou alterar meu JavaScript para que ele processe o retorno e realize o bind. Meu HTML não vai mudar nada, apenas o JavaScript: Estou obtendo assincronamente os meus dados com Jquery e invocando o método fromJS do plugin ko.mapping.

   1: $.get("Home/ObterPessoa", function (retorno) {
   2:     var usuario = ko.mapping.fromJS(retorno);
   3:     usuario.Salvar = function () {
   4:         alert("Nome: " + this.Nome() + "\r\nEmail: " + this.Email());
   5:     }
   6:     ko.applyBindings(usuario);
   7: });

Com esse código o resultado é o mesmo do exemplo anterior, com a vantagem que não precisamos realizar o mapping manualmente. Existem outros métodos interessantes nesse plugin que deixarei para outros posts.

Exemplo 4 – Template

O último exemplo do post de hoje será utilizando template do JQuery, mostrando como a mescla de algumas bibliotecas pode fazer o desenvolvimento ficar muito bom. Neste caso, quem fará acesso ao JQuery template será o próprio Knockout, então você não precisa conhecer a fundo o JQuery template, caso queira, acesse o site do JQuery.

Vou criar uma listagem de pessoas e realizar o mesmo processo de binding no JavaScript:

   1: var modelo = { pessoas: [
   2:    { Codigo: 1, Nome: 'Nome 1', Email: 'nome1@email.com' },
   3:    { Codigo: 2, Nome: 'Nome 2', Email: 'nome2@email.com' },
   4:    { Codigo: 3, Nome: 'Nome 3', Email: 'nome3@email.com' }
   5:  
   6: ]
   7: }
   8:  
   9: ko.applyBindings(modelo);

O HTML que muda um pouco, utilizando o padrão do jquery template:

   1: <div data-bind='template: "pessoaTemplate"'>
   2: </div>
   3: <script id='pessoaTemplate' type='text/html'>
   4:     <table>
   5:     <thead>
   6:         <tr>
   7:             <th>
   8:                 Codigo
   9:             </th>
  10:             <th>
  11:                 Nome
  12:             </th>
  13:             <th>
  14:                 Email
  15:             </th>
  16:         </tr>
  17:     </thead>
  18:         <tbody>
  19:             {{each pessoas}}
  20:             <tr>
  21:             <td>${ Codigo }</td>
  22:             <td>${ Nome }</td>
  23:             <td>${ Email }</td>
  24:             </tr>
  25:             {{/each}}
  26:         </tbody>
  27:     </table>
  28:  
  29: </script>

Os templates do JQuery são colocados dentro de blocos Scripts com o tipo ‘text/html’. O resultado será:

image

O template gera uma linha para cada registro. Simples assim.

Para introduzir o Knockout, acredito que esses exemplos são suficientes. Acredito que vale a pena você navegar em http://knockoutjs.com/ e ver todas as possibilidades dessa biblioteca. Tem ajudado muito no meu projeto atual. Faz o seu código JavaScript ficar muito mais claro e simples.

Bom pessoa, por hoje é isso!

Abraços

Projeto de exemplo:

 

Knockout.zip (583,67 kb)

9. May 2011 00:10 by Frederico B. Emídio | Comments (6) | Permalink

Realizando Autenticação (Authentication) e Autorização (Authorization) em Asp.MVC    

Olá pessoal!

Nos últimos posts falei basicamente sobre Asp.Net MVC, que, na minha opinião, é a melhor forma de desenvolver para Web em Asp.Net. Entenda que com “na minha opinião” eu quero dizer que eu me sinto mais confortável utilizando MVC, mas, de uma forma ou de outra, tudo que é feito em MVC pode ser feito em WebForm ou em qualquer outra tecnologia.

Em todos os posts abordei assuntos básicos, pensando numa linha de o que uma pessoa iniciante precisa saber para conseguir fazer uma página inteira em Asp.Net MVC. Acredito que este é o último post necessário para uma pessoa conseguir fazer um site ou um aplicativo web totalmente em Asp.Net MVC.

Naturalmente, na construção de um site, muitas outras coisas são utilizadas, mas o que foi passado aqui já é o suficiente para fazer um funcional.

Como o título do post fala, falarei da parte de segurança de um site.

Qual é a diferença entre Autenticação e Autorização?

Para falar de segurança, é importante começar por conceitos básicos. Por isso vou explicar a diferença entre as duas palavras mais utilizadas nesse assunto: Autenticação e Autorização .

Autenticação

Basicamente, autenticação é a forma do sistema saber se a pessoa é realmente quem ela diz ser. Por exemplo, para acessar um prédio é normal que o segurança peça um documento oficial com foto, para ver se você é realmente a pessoa que diz ser. Nesse processo, ele está realizando a sua autenticação, autenticando o que você diz ser, com o que o documento oficial informa. Se as duas informações baterem, você está autenticado.

Autorização

Tomando como ponto de partida o exemplo anterior, o fato de você realmente ser quem você diz que é, não te garante acesso ao prédio. Imaginemos que você esteja querendo entrar na sede da Microsoft, o segurança já sabe quem você é, e te pergunta: “Com que você gostaria de falar?”, e você: “Steve Ballmer “, o segurança vai responder para você, depois de fazer uma ligação: “Desculpe, mas o sr. Steve não está disponível”, isso se ele for educado, mas na prática ele está te falando: “Você não tem autorização para falar com o chefão”. Autorização é basicamente isso: Depois de saber quem você é (autenticação), saber se você pode fazer o que está querendo fazer (autorização).

Como isso é feito em Asp.Net MVC

Para quem já programa em WebForm, ai vai a boa notícia: Praticamente nada muda!

Em um dos post introdutórios ao Asp.Net, utilizei a figura abaixo para exemplificar como é organizado o Asp.Net:

A parte de autenticação em Asp.Net MVC não muda muito do WebForm porque toda a estrutura de segurança do Asp.Net está no nível Asp.Net da figura, portanto, todas as tecnologias “finais” da plataforma compartilham a mesma infraestrutura de segurança.

No Asp.Net, existem duas formas de realizar a autenticação/autorização:

Windows Authentication: É a forma de utilizar a autenticação do Windows integrado com o IIS. As credenciais utilizadas são as mesmas credenciais do Active Directory (AD) do servidor Web, que utiliza o protocolo LDAP. Caso o site/sistema esteja sendo acessado de uma intranet, o usuário pode já entrar no site autenticado, de acordo com o usuário logado na máquina do cliente. Caso seja um acesso feito da internet, onde o usuário logado na máquina do cliente não está cadastrado no mesmo AD configurado no servidor, então o browser geralmente exibe uma janela popup solicitando o usuário, senha e domínio no momento em que o usuário acessa o site, para o acesso ser autenticado diretamente do AD do servidor, permitindo assim que apenas usuário autenticados consigam acessar o site.

Forms Authentication: Com Forms Authentication, a responsabilidade de identificar quem está acessando o site fica a cargo do próprio site. Nesse processo você configura uma página responsável por obter as credenciais do acesso (Usuário/Senha). Onde o usuário e senha serão validados fica por responsabilidade do desenvolvedor, pode ser feito em qualquer lugar, como Banco de Dados, arquivos, ou mesmo no AD do servidor.

Nos exemplo vou utilizar Forms Autentication, por ser mais comum de ser utilizado. Além do que eu vou mostrar, existem muitas formas estender ou customizar a arquitetura de segurança do Asp.Net, mas isso é assunto para um outro post, nesse mostraremos apenas como configurar a autenticação.

Configurando Forms Autentication em Asp.Net MVC

Tudo que vou mostrar aqui será na visão MVC, para quem conhece o processo para WebForm verá que o que muda, é que você troca a página do WebForm (aspx) por uma Action ou um Controller, se você deseja que a Action Index seja o foco da autenticação.

Para fazer os exemplos, vou utilizar um novo site padrão do Asp.Net MVC 2, e vou forçar que seja feito o login para acessar a página inicial. O Web.Config para configurar o Forms Autentication fica da seguinte forma:

<authentication mode="Forms">
      <forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>

Nessas linhas, adicionado dentro de system.web, que já estão no projeto padrão, estamos informando no atributo loginUrl que a Action responsável por renderizar a View de Login é a Account/LogOn, isso quer dizer que sempre que for necessário que o usuário esteja logado para acessar uma determinada Action, o browser será direcionado para esta Action. Também está definido quanto tempo em minutos que o login ficará ativo se o usuário não tiver nenhuma ação no site, no atributo timeout.

Mas você vai reparar que mesmo com essas configurações, o site não estará exigindo a autenticação. Isso acontece porque você não está definindo as regras de autorização. Ou seja, se nada está definido para autorização, qualquer usuário está autorizado a fazer tudo, logo não precisa estar autenticado.

Vamos então definir as regras de autorização, veja que nada muda até agora do que você já conhecia no WebForms, exceto as trocas de páginas por Actions.

Também no system.web, vamos falar que apenas usuário reconhecidos, ou seja, autenticados, podem acessar o site. Para isso, adicionamos as seguintes linhas ao config:

<authorization>
     <deny users="?"/>
     <allow users="*"/>
</authorization>

Veja que as tags são bem intuitivas. Dentro da tag autorização, eu informo o que eu quero proibir (deny) e o que eu quero permitir (allow). Por sua vez, dentro dessas duas tags (deny e allow), é comum usarmos dois atributos: users, onde coloco os nomes dos usuário que quero permitir ou proibir, e roles, onde defini os grupos de usuários que quero permitir ou proibir.

Na configuração que coloquei acima, estou proibindo todos os usuários anônimos (?), ou seja, não autenticados, e estou permitindo todos os usuários conhecidos (*), ou seja, autenticados.

Caso não queira ser tão genérico, autorizando todo mundo, você pode usar o nome de um usuário ou grupo, por exemplo:

<authorization>
      <deny users="?"/>
      <allow users="fred" roles="admin"/>
</authorization>

Caso você queira adicionar mais de um grupo ou usuário, é só separar por vírgula:

<authorization>
      <deny users="?"/>
      <allow users="fred" roles="admin,users"/>
</authorization>

De qualquer forma, após inserirmos as linhas de autorização, nenhuma página será acessível sem login e o site será redirecionado automaticamente para a página configurada na seção Authentication do web.config. Porém, se você realizar um teste, verá que ainda não funcionará, pois a tela estará toda torta, como a imagem abaixo:

image

Isso acontece porque a segurança do Asp.Net não é apenas para arquivos HTMLs ou Actions, mas para todo o conteúdo do site, incluindo imagens, scripts, css, pastas e sub-pastas, portanto, quando você proíbe o acesso de um usuário ele também não conseguirá acessar as imagens, scripts, etc.

Para que a exibição seja correta, você tem que criar exceções, falando que alguns arquivos ou pastas podem ser acessados mesmo quando o usuário for anônimo, isso é feito adicionando as seguintes linhas ao config,

<location path="Content">
    <system.web>
      <authorization>
        <allow users="?"/>
      </authorization>
    </system.web>
  </location>
  <location path="Views/Shared">
    <system.web>
      <authorization>
        <allow users="?"/>
      </authorization>
    </system.web>
  </location>
  <location path="Scripts">
    <system.web>
      <authorization>
        <allow users="?"/>
      </authorization>
    </system.web>
</location>

Location são colocadas diretamente abaixo da tag Configuration no Web.Config, e servem para configurações específicas para determinadas pastas, arquivos ou Actions. No nosso caso estamos autorizando que usuários anônimos tenham acesso as pastas Content, Shared e Scripts, e se acessarmos o site agora o layout estaria correto:

image

 

Assim já temos uma autorização funcionando. Como disse anteriormente, como será realizada a autenticação fica a cargo do desenvolvedor, o Asp.Net já fornece algumas possiblidades, que você pode ver no projeto padrão que vem com o VisualStudio.

Definindo autorização por Action

Acredito que é necessário apenas falar agora de como restringir acesso para cada Action. No MVC isso é muito fácil, podemos utilizar o atributo Authorize sobre a Action específica, podendo passar na propriedade User ou Roles o usuário específico ou grupo específico.

Por exemplo, digamos que eu queira que apenas pessoas do grupo Admin possam criar um novo usuário, poderíamos decorar a Action Create da seguinte forma:

   1:  // **************************************
   2:  // URL: /Account/Register
   3:  // **************************************
   4:  [Authorize(Roles="admin")]
   5:  public ActionResult Register()
   6:  {
   7:      ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
   8:      return View();
   9:  }
  10:   
  11:  [HttpPost,Authorize(Roles="admin")]
  12:  public ActionResult Register(RegisterModel model)
  13:  {
  14:      if (ModelState.IsValid)
  15:      {
  16:      // Attempt to register the user
  17:      MembershipCreateStatus createStatus = MembershipService.CreateUser(model.UserName, model.Password, model.Email);
  18:   
  19:      if (createStatus == MembershipCreateStatus.Success)
  20:      {
  21:          FormsService.SignIn(model.UserName, false /* createPersistentCookie */);
  22:          return RedirectToAction("Index", "Home");
  23:      }
  24:      else
  25:      {
  26:          ModelState.AddModelError("", AccountValidation.ErrorCodeToString(createStatus));
  27:      }
  28:      }
  29:   
  30:      // If we got this far, something failed, redisplay form
  31:      ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
  32:      return View(model);
  33:  }

Perceba que adicionei o atributo Authorize as Actions e assim o Asp.Net verificará automaticamente se o usuário logado pertence ao grupo informado.

É importante ressaltar que tudo fica automático porque estamos utilizando o Membership Provider do Asp.Net, que já sabe como fazer para verificar se um usuário faz parte ou não de um grupo. Caso você não utilize o AspNetSqlMembershipProvider, isso não vai ficar tão automático assim, pois você terá que criar um Membership Provider, que também não é nada complicado, e é assunto para um próximo post.

Utilizei o AspNetSqlMembershipProvider porque quando criamos um novo projeto em Asp.Net MVC ele automaticamente utiliza esse provider, é muito fácil de utilizar e eu aconselho a utilização dele em qualquer projeto que não tenham regras muito específicas de segurança..

Bom pessoal, é isso! Acredito que com isso você já consiga fazer um site completo em MVC sem muita dificuldade.

Qualquer dúvida é só postar ai nos comentários, não vou postar um arquivo para download porque é exatamente o projeto padrão, com as alterações mostradas nos exemplos.

Até o próximo!

31. March 2011 07:03 by Frederico B. Emídio | Comments (4) | Permalink

Como utilizar validação com Asp.Net MVC    

Olá pessoal!

Hoje vou falar de uma tarefa importante no desenvolvimento de sites: Validação.

Na construção de site nós nunca podemos confiar nas informações que o usuário informa, devemos saber que qualquer pessoa pode errar uma digitação ou esquecer de digitar uma informação qualquer, e por isso é importante saber utilizar Validação nos formulários que criamos.

No Asp.Net MVC existem várias formas de realizar validações, mas hoje vou falar de apenas três:

  • Adicionando informações à propriedade ModelState
  • Utilizando DataAnnotations
  • Utilizando DataAnnotations com JQuery

O processo de validação no Asp.Net MVC ficou muito fácil, eu diria que ficou muito mais fácil que no Asp.Net WebForm, vamos então começar pelo básico:

Validando suas Views com ModelState

A validação no Asp.Net MVC sempre é feita através do estado do modelo (ModelState) recebido na Action de um Controller, o que muda é como esse estado é alterado, pois pode feito manualmente, como faremos agora, ou automaticamente, através de ActionFilter customizado ou automático.

A forma básica é você adicionar manualmente informações de erro no modelo ao ModelState, invocando o método AddModelError(chave,mensagemDeErro).

Vamos aos exemplos! Para começar vou criar meu modelo, bem simples, como em todos os meus outros exemplos: Pessoa

   1:  public class Pessoa
   2:  {
   3:      public string Nome { get; set; }
   4:      public string Email { get; set; }
   5:      public string Telefone { get; set; }
   6:      public int Idade { get; set; }
   7:  }

Naturalmente, para criar uma pessoa no banco de dados, algumas informações devem ser obrigatórias e outras devem seguir algum formato específico, para todos os nossos exemplos usaremos as mesmas validações:

  • O nome é obrigatório.
  • O e-mail é obrigatório.
  • O telefone é obrigatório.
  • O e-mail deve seguir o formatado texto@texto.texto
  • A idade é deve ser maior que 18.
  • O telefone deve seguir o formato ####-####

Vou criar duas Actions de nome Edicao, uma para renderizar a View inicial e outra para persistir os dados, essa segunda terá a responsabilidade de fazer a validação do Modelo, e adicionar os erros ao ModelState. Caso não tenha erro, apenas redirecionará o usuário para a página inicial (persistir em uma base de dados não vem ao caso).

Abaixo segue o código das Actions:

   1:  public ActionResult Edicao()
   2:  {
   3:      return View();
   4:  }
   5:   
   6:  [AcceptVerbs(HttpVerbs.Post)]
   7:  public ActionResult Edicao(Pessoa pessoa)
   8:  {
   9:      //Valida os dados no lado do servidor.
  10:      if(string.IsNullOrEmpty(pessoa.Nome))
  11:      ModelState.AddModelError("Nome","Nome é obrigatório");
  12:      
  13:      if (string.IsNullOrEmpty(pessoa.Email))
  14:      ModelState.AddModelError("Email", "Email é obrigatório");
  15:      else{
  16:      if (!Regex.Match(pessoa.Email,@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$").Success)
  17:      ModelState.AddModelError("Email", "E-mail inválido.");
  18:      }
  19:   
  20:      if (pessoa.Idade <= 18)
  21:      ModelState.AddModelError("Idade", "Idade deve ser maior que 18.");
  22:   
  23:      if (string.IsNullOrEmpty(pessoa.Telefone))
  24:      ModelState.AddModelError("Telefone", "Telefone é obrigatório.");
  25:      else
  26:      {
  27:      if (!Regex.Match(pessoa.Telefone, @"^\d{4}[-]{1}\d{4}$").Success)
  28:          ModelState.AddModelError("Telefone", "Preencha um telefone no formato ####-####");
  29:   
  30:      }
  31:      //Verifica se algum erro acima falhou.
  32:      if (!ModelState.IsValid)
  33:      {
  34:      return View();
  35:      }
  36:      else
  37:      {
  38:      //Se estive certo só redireciono para a página inicial.
  39:      return Redirect("/");
  40:      }   
  41:  }

O código é bem intuitivo, caso tenha adicionado alguma mensagem de erro, a propriedade IsValid do ModelState retornará false, e o modelo estará inválido, assim eu retorno minha mesma View, que exibirá as mensagens de erros. Minha View fica da seguinte forma:

   1:  <%using(Html.BeginForm()){ %>
   2:  <p>
   3:  <%=Html.LabelFor(Model=>Model.Nome)%>
   4:  <%=Html.TextBoxFor(Model=>Model.Nome)%>
   5:  <%=Html.ValidationMessage("Nome")%>
   6:  </p>
   7:  <p>
   8:  <%=Html.LabelFor(Model=>Model.Email)%>
   9:  <%=Html.TextBoxFor(Model=>Model.Email)%>
  10:  <%=Html.ValidationMessage("Email")%>
  11:  </p>
  12:  <p>
  13:  <%=Html.LabelFor(Model=>Model.Telefone)%>
  14:  <%=Html.TextBoxFor(Model=>Model.Telefone)%>
  15:  <%=Html.ValidationMessage("Telefone")%>
  16:  </p>
  17:  <p>
  18:  <%=Html.LabelFor(Model=>Model.Idade)%>
  19:  <%=Html.TextBoxFor(Model=>Model.Idade)%>
  20:  <%=Html.ValidationMessage("Idade")%>
  21:  </p>
  22:  <p>
  23:  <input type="submit" value="Salvar"/>
  24:  </p>
  25:  <%} %>

Veja que a única novidade da minha View a utilização do HTMLHelper ValidationMessage, passando o nome da chave que eu valido no servidor. Ao executar minha tela, caso passe informações inválidas, terei o seguinte resultado:

image

Ou assim:

image

E a sua validação está pronta para ser usada! Eu poderia utilizar um ValidationSummary, apenas utilizando o helper para isso, da seguinte forma: <%=Html.ValidationSummary()%>.

Vamos à nossa segunda forma de validar nosso modelo.

Validando suas Views com DataAnnotarions

DataAnnotations é uma forma de você adicionar validações ao seu modelo através de atributos, incorporada ao .Net 4. O interessante dessa estratégia é que não está ligada ao Asp.Net MVC, portanto, você pode utilizá-la tanto em Asp.Net, como em qualquer outra tecnologia .Net, como WCF. Isso é muito importante, pois você não precisa repetir validação em diversas partes do código, técnica conhecida como DRY (Don’t Repeat Yourself).

Como a validação é centralizada no modelo, nosso modelo sofrerá uma pequena alteração, assim como nossa Action, pois tiraremos a validação da Action e passaremos para o modelo. Nosso modelo ficará assim:

   1:  public class Pessoa
   2:  {
   3:      [Required(ErrorMessage = "O Nome é obrigatório.")]
   4:      public string Nome { get; set; }
   5:   
   6:      [Required(ErrorMessage = "O E-mail é obrigatório.")]
   7:      [RegularExpression(@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", ErrorMessage = "E-mail inválido.")]
   8:      public string Email { get; set; }
   9:   
  10:      [Required(ErrorMessage = "O Telefone é obrigatório.")]
  11:      [RegularExpression(@"^\d{4}[-]{1}\d{4}$", ErrorMessage = "Telefone inválido. Informe ####-####.")]
  12:      public string Telefone { get; set; }
  13:   
  14:      [Range(18, Int16.MaxValue, ErrorMessage = "A idade deve ser maior que 18 anos.")]
  15:      public int Idade { get; set; }
  16:  }

Veja que existem vários tipos de atributos de Validação, e para todos eu posso informar uma mensagem de erro personalizada, através da propriedade ErrorMessage, além de cada um ter propriedades específicas de acordo com o tipo.

Como toda a lógica de validação passou para o modelo, nossa Action ficará bem mais simples:

   1:  [AcceptVerbs(HttpVerbs.Post)]
   2:  public ActionResult Edicao(Pessoa pessoa)
   3:  {
   4:      //O modelo já chega validado
   5:      if (!ModelState.IsValid)
   6:      {
   7:      return View();
   8:      }
   9:      else
  10:      {
  11:      //Se estive certo só redireciono para a página inicial.
  12:      return Redirect("/");
  13:      }
  14:  }

Veja como ficou simples a Action agora, e o resultado nas Views é o mesmo, não mudei nada no meu HTML, apenas adicionei um Summary, para ver que o resultado é o mesmo:

image

E mais uma forma de validação já está funcionando e pronta para publicar!

Mas espera aí! Existe um problema nessas duas formas de validação: Ela são realizadas no servidor. Ou seja, o usuário precisa ser submetido ao servidor para realizar a validação, o que pode prejudicar muito a experiência do usuário, e para isso termos a terceira forma de validação de hoje:

Utilizando DataAnnotations com JQuery para validação no Cliente

O mais interessante da utilização do JQuery com DataAnnotation é o impacto no código do servidor: Nada! Isso mesmo, não tem impacto nenhum no servidor para você adicionar validação no Asp.Net MVC no cliente. Vou fazer a seguinte alteração no HTML:

 

   1:  <% Html.EnableClientValidation();%>
   2:  <%using (Html.BeginForm())
   3:  { %>
   4:  <p>
   5:  <%=Html.LabelFor(Model=>Model.Nome)%>
   6:  <%=Html.TextBoxFor(Model=>Model.Nome)%>
   7:  <%=Html.ValidationMessageFor(Model => Model.Nome)%>
   8:  </p>
   9:  <p>
  10:  <%=Html.LabelFor(Model=>Model.Email)%>
  11:  <%=Html.TextBoxFor(Model=>Model.Email)%>
  12:  <%=Html.ValidationMessageFor(Model => Model.Email)%>
  13:  </p>
  14:  <p>
  15:  <%=Html.LabelFor(Model=>Model.Telefone)%>
  16:  <%=Html.TextBoxFor(Model=>Model.Telefone)%>
  17:  <%=Html.ValidationMessageFor(Model => Model.Telefone)%>
  18:  </p>
  19:  <p>
  20:  <%=Html.LabelFor(Model=>Model.Idade)%>
  21:  <%=Html.TextBoxFor(Model=>Model.Idade)%>
  22:  <%=Html.ValidationMessageFor(Model => Model.Idade)%>
  23:  </p>
  24:  <p>
  25:  <input type="submit" value="Salvar" />
  26:  </p>
  27:  <%} %>

As alterações na view foram pequenas, adicionei o Helper EnableClientValidation e mudei ValidationMessage por ValidationMessageFor, fora isso, foi necessário adicionar referência a alguns arquivos JS (JavaScript), você pode adicionar tanto no seu arquivo de Layout  (Master) ou na própria View. Os arquivos são os seguintes:

 

   1:  <script src="<%=Url.Content("~/Scripts/jquery-1.4.1.min.js")%>" type="text/javascript"></script>
   2:  <script src="<%=Url.Content("~/Scripts/jquery.validate.js")%>" type="text/javascript"></script>
   3:  <script src="<%=Url.Content("~/Scripts/MicrosoftMvcJQueryValidation.js")%>" type="text/javascript"></script>
 

Os arquivos que adicionei são os arquivo do próprio JQuery (a versão desatualizada que vem com o Visual Studio 2010, que você pode mudar para a atual), o plug-in Validate do Jquery e um arquivo fornecido pelo time do Asp.Net para fazer o meio campo entre a validação do Asp.Net com o JQuery de forma totalmente transparente. Esse arquivo pode ser baixado com o Asp.Net MVC Futures, clicando aqui.

O Resultado é o mesmo:

image

E assim sua validação já estará funcionando no cliente. O mais legal dessa abordagem é que você realizará a validação tanto no Cliente como no Servidor, sem repetir uma linha de código (DRY).

No servidor que eu digo não é como no WebForm, onde a validação de um Validator Control também funciona tanto no servidor como no cliente, porque naquele caso, é apenas no servidor Asp.Net, visto que a validação está na página. Nesse caso, a validação está no Modelo, logo, o mesmo código pode ser utilizado em uma classe de negócio, WCF, WWF, etc; sem replicação de código.

Existe uma validação nativa no lado do Cliente no MVC, mas preferi mostrar com o Jquery, porque é possível integrar outros plug-ins com a validação, que podem gerar efeitos bem interessantes à sua página.

Bom, acredito que seja o suficiente para você adicionar validação ao sua página, de forma bem clara sem ter que se esforçar muito.

Abaixo você pode baixar uma solução contendo três projetos, um com cada tipo de validação, para você ver que não existe grandes diferenças entra elas.

Abraços e até o próximo pessoal.

ValidatorsMVC.zip (1,00 mb)

27. February 2011 06:06 by Frederico B. Emídio | Comments (0) | Permalink

Reaproveitando View em Asp.Net MVC    

Olá pessoal!

Hoje vou fazer um post bem pequeno, mas que é uma resposta a uma pergunta deve surgir na cabeça de todo mundo que começa em MVC. Já tive essa dúvida no passado, já ouvi essa pergunta em WebCasts e me fizeram ela semana passada: Para cada Action eu devo ter uma View, mesmo que o formulário seja igual?

Mesmo no meu post de CRUD eu fiz assim, criei uma View para a Action Create e outra para a Action Edit, mas a idéia era realmente possibilitar a evolução do projeto, como fiz aqui e aqui, e agora nesse atual vou continuar aprimorando aquele projeto.

Nesse post vou mostrar que isso não é necessário, e em uma aplicação de verdade deve ser evitado, afinal programar repetidas vezes as mesmas regras de validação sempre é um risco para a manutenção.

Vamos ao código!

Primeiramente, excluí as Action Create, e sua respectiva View, para ser utilizada apenas a Action e View Edit. Vou inicialmente mostrar como ficou o código da View. Como agora não necessariamente ao carregar a View eu terei os dados para preencher os campos, devo fazer um preenchimento condicional, da seguinte forma:

<form name="formCadastro" action="Edit" method="post">
<input type="hidden" id="hdfCodigo" name="id" value="<%=ViewData.Model.Codigo??"" %>" />
<h2>Alterar Contato</h2>
<p>
<span>Nome</span><input type="text" value="<%=ViewData.Model.Nome??"" %>" name="txtNome"
    id="txtNome" /></p>
<p>
<span>E-mail</span><input type="text" value="<%=ViewData.Model.Email??""%>" name="txtEmail"
    id="txtEmail" /></p>
<p>
<span>Telefone</span><input type="text" id="txtTelefone" value="<%=ViewData.Model.Telefone??"" %>"
    name="txtTelefone" /></p>
<p>
<input type="submit" value="Salvar" /></p>
</form>

Perceba que estou utilizando o operador ??, que verificar se o valor à esquerda é nulo e se for, utiliza o da direita. Na View, toda a alteração necessária é essa.

Nas minhas Actions fiz as seguintes mudanças:

   1:  public ActionResult Edit(int? id)
   2:  {
   3:      if (!id.HasValue) return View(new Pessoa());
   4:   
   5:      using (var bd = new bdEntities())
   6:      {
   7:      var pessoa = (from p in bd.Pessoa
   8:                where p.Codigo == id
   9:                select p).First();
  10:   
  11:      return View(pessoa);
  12:      }
  13:  }
  14:   
  15:  [HttpPost]
  16:  public ActionResult Edit(int id, FormCollection collection)
  17:  {
  18:      try
  19:      {
  20:      using (var bd = new bdEntities())
  21:      {
  22:          if (id ==0)
  23:          {
  24:          var pessoa = new Pessoa();
  25:          pessoa.Email = collection["txtEmail"];
  26:          pessoa.Nome = collection["txtNome"];
  27:          pessoa.Telefone = collection["txtTelefone"];
  28:          bd.AddToPessoa(pessoa);
  29:          bd.SaveChanges();
  30:          }
  31:          else
  32:          {
  33:          var pessoa = (from p in bd.Pessoa
  34:                    where p.Codigo == id
  35:                    select p).First();
  36:   
  37:          pessoa.Email = collection["txtEmail"];
  38:          pessoa.Nome = collection["txtNome"];
  39:          pessoa.Telefone = collection["txtTelefone"];
  40:          bd.SaveChanges();
  41:          }
  42:      }
  43:   
  44:      return RedirectToAction("Index");
  45:      }
  46:      catch
  47:      {
  48:      return View();
  49:      }
  50:  }

Na linha 1, mudei o tipo do parâmetro para Nullable<Int>, isso porque quando eu for criar um novo registro, não vou ter o ID ainda. Na linha 3 eu verifico se o ID está preenchido, se não estiver, considero que é um novo registro e retorna a View com a instância de Pessoa sem as informações preenchidas. Caso tenha valor preenchido, continuo o método como era antes, para ter o comportamento de alteração.

No segundo método, verifico se o ID é um ID novo (valor 0) ou se é uma alteração, e realizo o procedimento adequado, com isso eu utilizo a mesma view tanto para as ações de Criar como Editar, e dessa forma, não replico código de interface (View).

Em alguns casos, em vez de eu criar a lógica na Action dessa forma, eu poderia criar duas Actions com nomes distintos (Create e Edit) e na Create, utilizar o método RedirectToAction, passando como parâmetro o nome do método Edit, são apenas abordagens diferentes, mas que teriam o mesmo resultado. Naturalmente, o método RedirectToAction pode ser utilizados em outros contextos, como eu estou fazendo na linha 44, para redirecionar o navegador para a Action Index, e exibir a listagem atualizada.

Bom, como eu disse o post seria curto, e era isso que tinha para falar.

Clique no link abaixo para baixar o projeto atualizado, inclusive corrigindo um bug do arquivo anterior Smiley de boca aberta.

MvcCrud.zip (641,09 kb)

Até o próximo!



  
     
  
25. January 2011 11:23 by Frederico B. Emídio | Comments (0) | Permalink

Utilizando Ajax com Asp.Net MVC    

Olá pessoal, hoje vou falar de um assunto que é de extrema importância para desenvolvimento Web atualmente: Ajax.

Para quem não sabe, Ajax é o acrônimo de Asynchronous Javascript And XML, e que na  prática nada mais é que a capacidade de postar informações ao servidor, sem a necessidade de enviar a página inteira, e receber apenas uma pequena informação, sem a necessidade de atualizar a página toda. Para saber mais, clique no link do nome.

Hoje em dia, Ajax virou praticamente um conceito de atualização parcial de páginas Web, digo isso, porque Ajax em si não é mais utilizado na grande maioria das vezes! É isso mesmo, se reparar no nome completo, o X do Ajax é XML, e normalmente não utilizamos XML para a comunicação, mas sim JSON, ou seja, o que utilizamos de fato é chamado muitas vezes em blog e artigos pela Web de AJAJ (Asynchronous Javascript And JSON).

A responsabilidade de fazer a comunicação de forma assíncrona é do navegador, através do objeto XMLHttpRequest (IE 7+, e demais browsers) ou do Microsoft.XMLHTTP (IE 5 e 6). No nosso caso, não precisamos saber disso, porque utilizaremos JQuery para encapsular toda a lógica de criação da comunicação.

Apesar de usarmos JQuery, nós poderiamos utilizar outros frameworks JavaScript para encapsular a lógica de comunicação. A própria Microsoft disponibilizar nos projetos Web o framework MicrosoftAjax.js, que é extremamente bom. Com esse framework, inclusive, você pode acessar funcionalidades do Asp.Net, como a parte de Authentication.

Bom, mas vamos ao que interessa.

Utilizando JQuery para fazer chamadas assíncronas em Asp.MVC

No Jquery existe uma forma básica de se comunicar assincronamente com o servidor, que é o método $.ajax. Este método contém todas as informações necessárias para uma comunicação, nele você pode definir a url, o método de retorno em caso de sucesso e em caso de falha, tipo do dado passado (onde você pode passar uma sério de Content-Type, como json, xml, text, etc), verbo HTTP (get, post, etc), e mais uma série de opções. Como não utilizaremos esse método, você pode clicar aqui para ver uma lista detalhada de opções.

Não utilizaremos esse método, porque existem dois métodos que abstraem bastante a utilização dele, e que são suficientes para a maioria das situações, são os métodos $.post e $.get.

Estes métodos basicamente invocam o método $.ajax, passando no parâmetro type o tipo post ou get. O único problema é que ele não retorna erro, ou seja, se tiver algum problema no seu código que faça ter algum erro, você vai ficar sem saber até quebrar um pouco a cabeça, o ideal é: Quando tiver algum comportamento estranho, mude o método para $.ajax, e defina o parâmetro error, assim poderá ver qual a mensagem de erro está sendo retornada do servidor.

Nosso site fará quatro testes:

  • Obter informação do servidor via Get
  • Enviar informação para o servidor via Get
  • Enviar informação para o servidor via Post através de parâmetros
  • Enviar informação para o servidor via Post através de um objeto.

Abaixo segue o código do Controller com suas Action:

   1:  public JsonResult ObterGet()
   2:  {
   3:      var json = new { Nome = "Nome do Servidor", Idade = 20 };
   4:      return Json(json, JsonRequestBehavior.AllowGet);
   5:  }
   6:   
   7:  public JsonResult EnviarGet(string nome, int idade)
   8:  {
   9:      var json = new { Nome = nome + " Servidor", Idade = idade + 1 };
  10:      return Json(json, JsonRequestBehavior.AllowGet);
  11:  }
  12:   
  13:  public JsonResult EnviarPost(string nome, int idade)
  14:  {
  15:      var json = new { Nome = nome + " Servidor", Idade = idade + 1 };
  16:      return Json(json);
  17:  }
  18:   
  19:  public JsonResult EnviarObjeto(Pessoa p)
  20:  {
  21:   
  22:      p.Nome += " Servidor";
  23:      p.Idade += 1;
  24:      return Json(p);
  25:  }

Os códigos são bem simples: Os métodos de GET (ObterGet e EnviarGet) precisam passar uma informação a mais no método Json pois o MVC força você a dizer que quer retornar uma chamada get, mesmo sabendo do risco que isso pode ser. É um risco porque um site de outro domínio pode invocar esse método utilizando GET, e se você não informar que isso aconteça, o ASP.NET vai barrar a chamada, retornado  a mensagem:

This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.

Como queremos testar o Get, então permitimos essa comunicação, adicionando o parâmetro JsonRequestBehavior.AllowGet. Para os métodos utilizados através de POST (EnviarPost e EnviarObjeto) não precisamos utilizar esse parâmetro.

Para o método EnviarObjeto funcionar, criei uma classe simples, que segue abaixo:

   1:  public class Pessoa
   2:  {
   3:      public string Nome { get; set; }
   4:      public int Idade { get; set; }
   5:  }

Para consumir esses métodos, criei o HTML abaixo:

<p>Nome:<input type="text" id="txtNome" /></p>
<p>Idade:<input type="text" id="txtIdade" /></p>
<input type="button" value="Enviar Get" onclick="enviarGet();"/>
<input type="button" value="Enviar Post" onclick="enviarPost();"/>
<input type="button" value="Enviar Objeto" onclick="enviarObjeto();"/>
<input type="button" value="Obter" onclick="javascript:obter();"/>

Sua visualização será essa:

image

E o Javascript da página será:

   1:  function obterCallback(retorno,status) {
   2:      $("#txtNome").val(retorno.Nome);
   3:      $("#txtIdade").val(retorno.Idade);
   4:  }
   5:  function obter() {
   6:      $.get("Ajax/ObterGet", obterCallback);
   7:  }
   8:   
   9:  function enviarGet() {
  10:      if ($("#txtNome").val() == "" || $("#txtIdade").val() == "") {
  11:          alert("Preencha todas os campos!");
  12:          return;
  13:      }
  14:      $.get("Ajax/EnviarGet",
  15:      { nome: $("#txtNome").val(), idade: $("#txtIdade").val() },
  16:         obterCallback
  17:      );
  18:  }
  19:   
  20:  function enviarPost() {
  21:      if ($("#txtNome").val() == "" || $("#txtIdade").val() == "") {
  22:          alert("Preencha todas os campos!");
  23:          return;
  24:      }
  25:      $.post("Ajax/EnviarJson",
  26:      { nome: $("#txtNome").val(), idade: $("#txtIdade").val() },
  27:         obterCallback
  28:      );
  29:  }
  30:   
  31:  function enviarObjeto() {
  32:      if ($("#txtNome").val() == "" || $("#txtIdade").val() == "") {
  33:          alert("Preencha todas os campos!");
  34:          return;
  35:      }
  36:      $.post("Ajax/EnviarObjeto",
  37:      { nome: $("#txtNome").val(), idade: $("#txtIdade").val() },
  38:         obterCallback
  39:      );
  40:  }

O JavaScript, utilizando o Jquery, é tão simples que nem precisaria de explicação, mas vamos lá: O primeiro parâmetro é o endereço da Action (Controller/Action), o segundo pode ser o método de retorno (Callback) ou os parâmetros que serão passados, caso seja definido parâmetro, o terceiro método é o método de retorno. Existe um quarto parâmetro que não estou utilizando, que seria para informar o tipo do retorno (json, xml, etc). No meu caso não preciso utilizá-lo.

Os parâmetros estou passando através de um objeto JSON, que o Jquery converte em querystring, caso seja GET ou coloca os parâmetros no corpo da mensagem HTTP, caso seja POST. Relembre essas regras aqui.

Para conseguir testar, basicamente eu altero o valor enviado para o servidor e retorno, preenchendo os campos com o novo valor, dessa forma posso ver que as informações estão realmente chegando no servidor e voltando.

Obervação: A Action que recebe as chamadas Assíncronas não precisa estar no mesmo Controller que retornou a View do formulário.

Conclusão

Quis mostrar nesse post como é simples utilizar Ajax juntando MVC e JQuery, tão simples quanto utilizar PageMethods no WebForms, porém, sendo mais rápido para primeira renderização (porque não precisa de ScriptManager para gerar um Proxy).

Obervação 2: No WebForms é possivel utilizar esses métodos do Jquery com os métodos estáticos da página marcados com o atributo WebMethod, dessa forma você não precisaria utilizar o ScriptManager para gerar o próximo PageMethods.

Acho importante esse tipo de conhecimento, pois hoje em dia não existe site ou sistema sem alguma funcionalidade Assícrona, e isso pode ser incluído nos conhecimentos básicos de Asp.Net MVC.

O Link para o exemplo está abaixo.

MvcAjax.zip (292,92 kb)

Bom, por hoje é isso. Até o próximo.


  
     
  
24. January 2011 23:31 by Frederico | Comments (3) | Permalink

Tarefas Básicas com Asp.Net MVC Pt. III–Grid com plugin JQuery JQGrid    

 

Olá Pessoal!

Depois de um longo tempo sem escrever, devido a um projeto atrasado, vou finalizar a série sobre tarefas básicas com Asp.Net MVC.

Você viu neste e neste post como criar um cadastro em MVC. Porém, eu criei um grid muito simples, apenas usando as simples tags TABLE do HTML. Quando um iniciante em MVC vê isso, ele já se pergunta: “Mas e todas aquelas facilidades do GridView? Vou ter que criar na mão?”. A resposta é não. Existem muitas grids por ai que funcionam muito bem com Asp.Net MVC, e hoje vou mostrar como utilizar a que eu mais gosto, inclusive em WebForms (exatamente, eu não uso GridView a muito tempo).

A Grid que vou apresentar é um plugin Jquery muito bem feito, chamado JQGrid. Esse plugin tem constantes atualizações, com melhorias e correções, além de ter muitas referências na Web. O próprio site do plugin tem uma parte Wiki com todo o conteúdo necessário para você fazer tudo o que for necessário em um sistema, desde a simples grid com paginação até a SubGrid, Agrupamento, Edição inline, edição em popup,  entre muitas outras coisa, tudo em poucas linhas de códigos. As funcionalidades são separadas em módulos, e você pode baixá-los separadamente. Dê uma olhada aqui que você verá quantas possibilidades existem nesta grid. Além disso, o layout da grid é baseado nos temas do JQuery UI, ou seja, criar temas para grid é muito fácil a partir do site do JQuery UI

O objetivo deste post não é explicar amplamente o plugin, apenas mostrar como pode ser feita a integração com uma Action, a partir daí, muito mais pode se feito.

Então vamos lá!

Instalando o Plugin JQGrid

Para baixar a última versão, você pode acessar aqui. Nessa tela, você poderá selecionar quais módulos da grid deseja baixar, no meu caso eu vou selecionar todos, mas você pode ver com mais atenção quais são realmente necessários para um projeto real. Quanto mais módulos, maior o arquivo, e na internet isso ainda faz uma diferença.

Quando baixamos o Plugin, temos uma pasta src, que você pode estudar para ver como a Grid funciona, mas para adicionar em nosso site, precisamos apenas adicionar o arquivo jquery.jqGrid.min.js, que conterá tudo necessário para a Grid rodar. Além desse arquivo, existe também uma pasta chamada i18n, onde você encontrará arquivos de linguagens, adicione a que você achar melhor, eu vou adicionar apenas o arquivo de Português do Brasil.

Adicionado os arquivos de Scripts, você deve também adicionar os arquivos de layout (CSS). Dois arquivos devem ser adicionados, o CSS da Jquery UI, que pode ser baixado aqui, e o arquivo que complementa o CSS do UI, que vem com o download do JQGrid, chamado ui.jqgrid.css

Meu projeto (o mesmo do artigo anterior) ficará assim:

image

image

Após adicionarmos os arquivos ao nosso projeto, devemos adicionar a referência no HTML, tanto para os JS’s como para os CSS’s. Vou fazer isso na View Index, onde tenho a tabela HTML:

<link rel="Stylesheet" href="../../Content/jquery-ui-1.7.2.custom.css" />
<link rel="Stylesheet" href="../../Content/ui.jqgrid.css" />
<script type="text/javascript" src="../../Scripts/jquery-1.4.1.js"></script>
<script type="text/javascript" src="../../Scripts/i18n/grid.locale-pt-br.js"></script>

Como todo plugin Jquery, a configuração da Grid é feito pelo seu construtor, em cima de alguma tag do HTML selecionado pelo seletor do JQuery. Para criar a Grid, utilizei o seguinte código, em cima da table com id=table:

<script type="text/javascript">
$().ready(function () {
    $("#table").jqGrid({
    multiselect: false,
    datatype: "json",
    url: "Listar",
    colNames: ['Nome', 'Email', 'Telefone'],
    colModel: [
    { name: 'Nome', index: 'Nome', width: 100 },
    { name: 'Email', index: 'Email', width: 300 },
    { name: 'Telefone', index: 'Telefone', width: 80 }
     ],
    height: 220,
    caption: "Resultado",
    pager: "#pager",
    jsonReader: { repeatitems: false, id: "Codigo" },
    rowNum: 10,
    sortname: 'NomeEmpresa',
    sortorder: "asc",
    viewrecords: true
    });
 
});
        
</script>
<table id="table"></table>
<div id="pager"></div>

Vou explicar resumidamente alguns parâmetros, mas se você quiser uma explicação mais detalhada de cada opção, acesse a página do plugin, clicando aqui ou na wiki do site, vamos la:

Parâmetro Descrição
datatype Indica que tipo de dado a grid deve tratar, pode ser JSON, XML, local, entre outros.
url Método no servidor que realizará a pesquisa, no caso a Action, poderia ser uma WebService ou um PageMethods do WebForms. Se o tipo do dado for local, você não precisa desse parâmetro.
colNames Nome (Header) de cada Coluna, se você tiver uma coluna sem nome, só com um checkbox, por exemplo, você deve informar espaços em brancos, a quantidade de itens em ColNames deve ser igual a quantidade de colunas no ColModel
colModel Aqui é onde você define como será cada coluna, as opções são inúmeras, então visite o site para ver mais.
pager Indica em qual controle o plugin criará os controles de paginação. Geralmente é uma div, como no exemplo. Existem muitas opções de pager também, visite o site!
jsonReader Informa a estrutura que os dados devem vir do servidor, e qual é o ID dos dados. A variação também é ampla, estou usando o default.

 

A grid acima ficaria como a imagem abaixo.

image

Usando apenas as configurações padrões, minha grid já é totalmente funcional, com paginação e ordenação por coluna. Como disse, cada opção abre uma grande gama de opções, o site do plugin é bem completo e minha intenção não é replicar o site aqui, apenas apresentar a Grid.

Essas configurações delegam a responsabilidade de paginação e ordenação no servidor, mudando um pouco você poderia ter a ordenação e paginação controlada pela própria Grid, porém, é lógico que é muito mais rápido (no sentido de desempenho em tempo de execução) fazer isso tudo no servidor, além de poupar muito a banda de internet transferindo apenas os dados que serão exibidos para o cliente.

Minha action Listar ficou assim (Implementei apenas a paginação para demonstração, o código naturalmente não é um código de produção, afinal, a idéia é apresentar a grid):

[Obs: Código alterado para correção da lógica]

   1:  {
   2:      using (var bd = new bdEntities())
   3:      {
   4:      var query = from pessoas in bd.Pessoa 
   5:              select pessoas;
   6:      var ini = (page - 1) * 10;
   7:      var fim = query.Count()> 10? 10: query.Count();
   8:      if (ini > 0)
   9:      {
  10:          fim = 10;
  11:          fim = fim - 10 >= 0 ? query.Count() % 10 : fim;
  12:      }
  13:   
  14:      var totalPags = query.Count() / 10;
  15:      if (query.Count() % 10 > 0)
  16:      {
  17:          totalPags++;
  18:      }
  19:      var jsonData = new
  20:      {
  21:          total = totalPags, // número de páginas retornadas no reader
  22:          page = page,
  23:          records = query.Count(), // número total de registros na base.
  24:          rows = query.ToList().GetRange(ini,fim)
  25:      };
  26:      return Json(jsonData, JsonRequestBehavior.AllowGet);
  27:      }
  28:  }

Algumas considerações ao código:

Minha Action retorna um JsonResult, afinal é um objeto Json que minha grid espera. O método Json fica responsável por converter meu objeto jsonData em Json. Apesar de eu estar criando a variável jsonData como um objeto dinâmico, eu poderia definir essa classe como uma classe concreta, e utilizar os parâmetros por toda a aplicação, é assim que costumo fazer.

As propriedades do jsonData seguem um padrão nomes de acordo com o definido no jsonReader (parâmetro da Grid), se você mudar alguma coisa no padrão do jsonReader terá que mudar também nas propriedades de retorno, mais uma vez: todas essas inúmeras possibilidades estão no site do plugin.

Os parâmetros da Action também devem seguir essa assinatura, para que o Asp.Net consiga traduzir os parâmetros vindo do JavaScript diretamente para os parâmetros. Você pode mudar  essas regras com um pouco mais de programação, que, alias, eu acho uma boa idéia se você estiver pensando em adotar a Grid como padrão de lista para seus sistemas, quanto menos parâmetros, mais rápido para desenvolver!

A lógica de paginação não faz parte da solução, é claro.Alegre

Conclusão

A idéia desse post foi apresentar o plugin JQGrid, e mostrar que o mundo sem server controls não é tão difícil assim, eu já uso essa grid a muito tempo em WebForms, porque é uma solução muito mais poderosa e versátil que o GridView. Acredite em mim, cada nova possibilidade que você descobre nesse plugin, você fica mais fascinado e confiante.

O exemplo que mostrei, não mostrou nenhuma possibilidade do plugin, mas se você quiser ter uma noção da capacidade dele, clique aqui e veja com seus próprios olhos.

Para baixar o código do exemplo, clique link abaixo

Baixar Arquivo

20. January 2011 23:55 by Frederico B. Emídio | Comments (3) | Permalink

Tarefas Básicas com Asp.Net. Parte II – HTML Helpers.    

Olá pessoal, nessa semana vo fazer um post bem simples, No post anterior, criei um cadastro básico. Nele, quis deixar claro que queria fazer tudo na mão, criando todo o HTML, apenas para tentar deixar mais claro como funciona a integração entre View e Controller.

Neste post, vou mostra como podemos diminuir a tarefa, muitas vezes enfadonha, de ficar digitando HTML constantemente, através da utilização de HTML Helpers

O que é um HTML Helper

HTML Helper são métodos estáticos que retornam simples strings. Lembre que no Asp.Net MVC não tem server controls como no WebForm, portanto, não espere que um HTML Helper te retorne um “controle” HTML onde você pode definir propriedades após a criação do mesmo via C#. Como disse, ele só retorna strings, em geral que represente uma tag HTML. Você pode criar também qualquer tipo de HTML Helper, mas isso é assunto para outro post, neste vamos apenas falar da utilização.

Por padrão, o Asp.Net MVC já vêm com uma série de Helper pré definidos, alguns deles estão na listagem abaixo:

  • Html.BeginForm()
  • Html.EndForm()
  • Html.TextBox()
  • Html.Hidden()
  • Html.ActionLink()
  • Html.CheckBox()
  • Html.DropDownList()
  • Html.ListBox()
  • Html.Password()
  • Html.RadioButton()
  • Html.TextArea()

A lista acima não tem todos os Helper, mas os mais comuns para criar um formulário de cadastro, por exemplo.

A utilização dos Helpers te abstrai das regras da criação dos formulários. Por exemplo, no exemplo do post anterior, o formulário de criação era o seguinte:

   <form name="formCadastro" action="Create" method="post">
    <h2>
        Criar novo Contato</h2>
    <p>
        <span>Nome</span><input type="text" name = "txtNome" id="txtNome" /></p>
    <p>
        <span>E-mail</span><input type="text" name = "txtEmail"  id="txtEmail" /></p>
    <p>
        <span>Telefone</span><input type="text" id="txtTelefone" name = "txtTelefone"  /></p>
    <p>
        <input type="submit" value="Salvar" /></p>
    </form>

Utilizando os Helpers, ele ficaria da seguintes forma:

    
<%using(Html.BeginForm()){ %>
 <h2>
        Criar novo Contato</h2>
    <p<
        <span>Nome</span><%:Html.TextBox("txtNome")%></p>
    <p>
        <span>E-mail</span><%:Html.TextBox("txtEmail")%></p>
    <p>
        <span>Telefone</span><%:Html.TextBox("txtTelefone")%></p>
    <p>
        <input type="submit" value="Salvar" /></p>
    <%} %>

Perceba que nitidamente tem menos HTML criado, e menos conhecimento de infra estrutura Web é necessário, pois eu não precisei definir o tipo do método do formulário, não precisei definir a Action, o Helper Html.BeginForm presumiu todo o necessário para criar o formulário.

É interessante notar também que em vezes de invocar o BeginForm e o EndForm, utilizei uma diretiva using como muitas vezes é utilizado no C# para definir o início e fim de uma instância de um objeto. Dessa forma, o HTML Helper sabe onde deve colocar o a tag final do formulário, sem a necessidade de utilizar o helper EndForm.

E para criar os textboxes, informei apenas um parâmetro e ele já presumiu o id e o name do controle. Se você fizer essas alterações no exemplo do post anterior, verá que a página funcionará do mesmo jeito, sem necessitar de nenhuma alteração no Controller.

Perceba também que, com exceção do BeginForm, os demais helper precisam ser inseridos dentro de uma tag que imprime texto do Asp.Net. No caso, utilizei as tags Asp.Net <%:Html.TextBox("txtNome") %>, poderia também <%= Html.TextBox("txtNome")%>, ou até mesmo um <%Response.Write(Html.TextBox("txtNome"));%>, lembre-se sempre, eu preciso colocar as tags de print ou utilizar o Response.Write, porque o helper retorna apenas uma string.

Outro exemplo de utilização muito útil de um Html Helper, seria para criar links, sem a necessidade de ter que informar o nome do Controller e da Action, como de costume. Por exemplo, o link abaixo:

<a href="Cadastro/Create">Novo</a>

Poderia ser criado utilizando um Helper da seguinte forma:

<%:Html.ActionLink("Nome","Create") %>

Perceba que não preciso assim conhecer quais são os atributos necessários de um link, apenas definir um texto para o link e saber qual Action invocar no meu Controller atual.

Bom, você viu como pode ser utilizado os Helpers HTML do MVC, é bom conhecer toda a lista existente de Helpers, para podermos maximizar o desempenho no desenvolvimento de sites com MVC, e, é claro, é importante saber que é possível, e simples, criar novos Helpers, para criar estruturas mais complexas, ou mesmo criar conjuntos de Tags que você utiliza sempre, como uma tela de acesso, ou um controle JQuery de tabs. Isso falaremos em um próximo post.

É isso, como comentei, o post hoje seria bem simples.

Até o próximo!

12. December 2010 20:03 by Frederico B. Emídio | Comments (1) | Permalink

Tarefas básicas com Asp.MVC (CRUD)    

Olá pessoal!

Continuando os tópicos básicos do Asp.Net MVC, hoje vou fazer um pequeno cadastro para mostrar como interagir entre View e Controller. Vou procurar fazer tudo manualmente, para que você possa entender como funciona de fato essa intereção. Digo isso porque existem muitas formas de facilitar o desenvolvimento de Views, por exemplo, utilizando HTML Helpers, mas não vou fazer uso deles para que possamos, quando formos falar dos Helpers, entender como é o seu funcionamento.

Para nosso post hoje, vou criar apenas uma tabela no SQL Server Express para armazenar um cadastro de pessoas, como o objetivo é mostrar a Interação Controller/View/Controller, não vou tomar cuidado com validações, nem com um modelo elaborado.

Para começar, vamos criar nossa tabela como abaixo, e criar o acesso aos dados utilizando Entity Framework.

image

image

Feito isso, vamos adicionar um Controller no projeto chamado Cadastro, lembre-se que sempre começamos o desenvolvimento do site MVC adicionando o Controller. Coloquei “site” em negrito, porque muitos podem dizer que é pelo Modelo, ou Domínio, que se inicia o desenvolvimento, inclusive é o que prega as práticas de DDD (Domain Driven Development), mas quero deixar claro que o Modelo (Domínio) de fato é o princípio do desenvolvimento do Projeto, mas quando vamos iniciar o site, o Modelo já deve estar definido, portanto, conhecendo o Modelo você deve iniciar o site pelos Controllers necessários para esse Modelo, entendendo que o Modelo não é para o site, mas para o projeto, pois ele pode ser utilizado para qualquer ambiente. No futuro vou falar um pouco de DDD, visto que no MVC, a sua utilização é extremamente útil.

Adicionando o Controller, vamos marcar as opções para adicionar as Actions de Create, Update, Delete e Details, apenas para facilitar, visto que não é necessário utilizar exatamente métodos com esse nome para realizar o CRUD dos registros. Veja que ele criar algumas Actions com o mesmo nome, o motivo é bem simples:

Como para acessar uma View devemos acessar pela URL o Controller que contem a Action que sabe gerar essa View (por exemplo: http://seusite/Cadastro/Create), algumas Actions precisam se chamadas para renderizar a View e outras para Processar a requisição da View renderizadas anteriormente. Por exemplo: Para criar um novo registro, eu devo invocar a Action Create para renderizar os campos em brancos, dessa forma invocando a Action Create sem parâmetros, que só deve renderizar a View pura, e em seguida, após preencher os campos e clicar no botão Salvar, eu devo invocar a Action Create novamente, porém agora passando as informações que foram preenchidas, chamando dessa forma a Action com parâmetros. Isso é possível porque o motor do Asp.Net MVC consegue diferenciar overloads de métodos de acordo com as informações passadas no corpo da mensagem. Você precisa sempre utilizar essa regra de Overloads para Actions com funções ligadas? Não! No Asp.Net MVC você tem o controle de tudo, e pode usar da forma que achar melhor.

Além de criar as Actions do CRUD, ele também cria uma Action chamada Index, que é a invocada pelo Asp.Net se nenhuma Action form informada na URL. Precisa sempre ter o nome Index? Não! Como eu disse, você pode mudar quase qualquer coisa no MVC, apenas mudando as rotas padrões no arquivo Global.asax.

Abaixo segue todo o código do meu Controller. Você verá que cada método é muito simples:

  public ActionResult Index()
        {
            using (var bd = new bdEntities())
            {
            var query = from pessoas in bd.Pessoa select pessoas;

                //ObjectQuery p = new ObjectQuery("select cod_pessoa,nom_pessoa,des_email,num_telefone from  Pessoa", bd);
                //var resultado = p.Execute(MergeOption.NoTracking);

            
                return View(query.ToList());
            }
        }

      
        public ActionResult Create()
        {
            return View();
        } 

        [HttpPost]
        public ActionResult Create(FormCollection collection)
        {
            try
            {

                using (var bd = new bdEntities())
                {


                    var pessoa = new Pessoa();

                    pessoa.Email = collection["txtEmail"];
                    pessoa.Nome = collection["txtNome"];
                    pessoa.Telefone = collection["txtTelefone"];

                    bd.AddToPessoa(pessoa);
                    bd.SaveChanges();
                }


                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }
        
        public ActionResult Edit(int id)
        {

            using (var bd = new bdEntities()){
                var pessoa = (from p in bd.Pessoa
                             where p.Codigo == id
                             select p).First();
                         
            return View(pessoa);
            }
        }

        [HttpPost]
        public ActionResult Edit(int id, FormCollection collection)
        {
            try
            {
                using (var bd = new bdEntities())
                {
                    var pessoa = (from p in bd.Pessoa
                             where p.Codigo == id
                             select p).First();

                    pessoa.Email = collection["txtEmail"];
                    pessoa.Nome = collection["txtNome"];
                    pessoa.Telefone = collection["txtTelefone"];

                    
                    bd.SaveChanges();
                }

                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

        public ActionResult Delete(int id)
        {
            using (var bd = new bdEntities())
            {
                var pessoa = (from p in bd.Pessoa
                              where p.Codigo == id
                              select p).First();

                bd.DeleteObject(pessoa);
                bd.SaveChanges();
                return RedirectToAction("Index");
            }
        }

Você pode excluir a Actions que não foram utilizadas, como Details (não teremos em nosso cadastro e Delete com dois parâmetros, pois o registro será excluído diretamente da lista.

Perceba que alguns trechos de códigos foram repetidos algumas vezes, e obviamente isso não é legal. Para solucionar esse problema, deveríamos criar um Domínio (Modelo) mais elaborado, encapsulando os métodos de pesquisas, mas para simplificar nosso modelo, resolvi fazer assim. Perceba também que alguns métodos recebem como parâmetro a classe FormCollection. Nesse parâmetro é armazenado todos os campos enviados a partir de formulários do HTML para o Controller, como era feito com Request.Form. É daí que leremos as informações fornecidas pelo usuário.

Você pode estar se perguntando: Posso colocar um método de auxilio no Controller, para facilitar a reutilização? A resposta é: não! Lembre que o MVC procura deixar claro o conceito de SoC - Separation of concerns (Separação de Interesses), por isso, todo método dentro de um Controller, tem a intenção de ser um Controlador (Action), e qualquer método (público) que você colocar ali dentro, será uma Action, mesmo que retorno um tipo incompreensivo para o Navagador. Tudo que tenha a ver com regra de negócio, deve ser colocado na camada Model.

Após criarmos nossos Controllers e Actions, vamos criar as Views.

Três actions necessitam ter Views específicas, são elas:

  • ActionResult Index()  - Retornará a lista de registros.
  • ActionResult Create()  - Retornará o formulário de criação de usuário.
  • ActionResult Edit(int id) – Retornará o formulário de edição de usuário já com os dados preechidos.

As demais Actions não precisam de View específica porque elas apenas persistem as informações e redirecionam o usuário para a listagem, criada na Action Index, sendo esse redirecionamento feito através do método RedirectToAction.

Para criar a View, clique no nome do método com o lado direito do método e Add View.

Vamos ver como ficará nossa View de listagem (Index)

    <h2>Lista de Pessoas</h2>
    <table>
		<thead>
			<tr>
				<th>Ação</th>
				<th>Nome</th>
				<th>E-mail</th>
				<th>Telefone</th>
			</tr>
		</thead>
		<tbody>
		<%foreach(var item in ViewData.Model){ %>
			<tr>
				<td><a href="Cadastro/Edit/<%=item.Codigo %>">Editar</a> <a href="Cadastro/Delete/<%=item.Codigo %>">Excluir</a></td>
				<td><%=item.Nome %></td>
				<td><%=item.Email%></td>
				<td><%=item.Telefone %></td>
			</tr>

		<%} %>
		</tbody>
    </table>
    <a href="Cadastro/Create">Novo</a>

Explicando: Estou criando uma tabela e fazendo um foreach no retorno da Action, que sempre fica exposto através da propriedade ViewData.Model. Para criar a coluna com os links de ação, perceba que estou adicionando links, e montando a URL de acordo com a estrutura Controller/Action/Parametros, e o botão para adicionar um novo, passando apenas Controller/Action. O resultado seria isso:

image

Para as duas outras Views, como vou submeter dados ao servidor, preciso criar um formulário, e definir como Action desse formulário, minha Action do Controller que receberá esses dados. Como são no mesmo Controller, posso apenas fornecer o nome da Action, sem passar o nome do Controller. A diferença de uma para outra é que uma eu já preencho os dados, e outra não:

View de Criação:

 <form name="formCadastro" action="Create" method="post">
    <h2>
        Criar novo Contato</h2>
    <p>
        <span>Nome</span><input type="text" name = "txtNome" id="txtNome" /></p>
    <p>
        <span>E-mail</span><input type="text" name = "txtEmail"  id="txtEmail" /></p>
    <p>
        <span>Telefone</span><input type="text" id="txtTelefone" name = "txtTelefone"  /></p>
    <p>
        <input type="submit" value="Salvar" /></p>
    </form>

View de Edição:

 <form name="formCadastro" action="Edit" method="post">
    <input type="hidden" id="hdfCodigo" name="id" value="<%=ViewData.Model.Codigo %>" />
    <h2>
        Alterar Contato</h2>
    <p>
        <span>Nome</span><input type="text" value="<%=ViewData.Model.Nome %>" name="txtNome"
            id="txtNome" /></p>
    <p>
        <span>E-mail</span><input type="text" value="<%=ViewData.Model.Email%>" name="txtEmail"
            id="txtEmail" /></p>
    <p>
        <span>Telefone</span><input type="text" id="txtTelefone" value="<%=ViewData.Model.Telefone %>"
            name="txtTelefone" /></p>
    <p>
        <input type="submit" value="Salvar" /></p>
    </form>

Diferente do WebForm no MVC você pode colocar quantos formulários quiser em uma mesma página, como é normal em qualquer outra plataforma Web, ou mesmo alterar o Action em tempo de execução. Para que as informações aparecem no parâmetro FormCollection, seus controles precisa ter o atributo name definido, pois é de onde o HTTP retira as informações para criar a mensagem (Post ou Get) a ser enviada ao server. No caso das Views acima, estou enviando via POST, como pode ser visto no atributo method do formulário.

Com esses códigos você já tem seu cadastro funcionando. Com certeza, para desenvolvê-lo você levará menos tempo que para ler esse post. Tudo ficou muito rápido e transparente, sem inúmeros arquivos de códigos inseridos pelo Asp.Net como acontece com o WebForm.

Como eu disse, não utilizei nenhum Helper do MVC para gerar HTML, se tivesse utilizado, com certeza o tempo gasto para desenvolver esse cadastro seria ainda menor.

Conclusão

Nesse post procurei mostrar como é simples criar uma página básica de cadastro. Nos próximos posts tentarei mostrar como deixar essa página um pouco mais elaborada.

Clique no arquivo abaixo para baixar o fonte do exemplo

MvcCrud.rar (426,09 kb)

Até o próximo

30. November 2010 14:36 by Frederico | Comments (1) | Permalink

Primeiros passos no Asp.Net MVC    

Olá pessoal, depois de uma pequena série preparando o terreno para começar a falar de Asp.Net MVC, finalmente chegou o momento de falar dessa forma de desenvolver Web.

Vou tentar fazer vários pequenos posts sobre este assunto, e hoje será um bem introdutório, tentando falar um pouco da estrutura do MVC, para que quem nunca criou um novo projeto desse tipo possa ir se habituando. Falarei a principio falarei de Asp.Net MVC 2, porém, conforme for se tornando conveniente, falarei das novas funcionalidades do Asp.Net MVC 3, que já está com a segunda Release Candidate publicada.

Então vamos lá!

Criando um Projeto MVC

Da mesma forma que você cria um projeto WebForm, para criar um MVC você deve ir até o Visual Studio, e seguir os procedimentos padrões, apenas escolhendo o tipo certo de site (Clique nas imagens para ampliar):

image

Quando criamos um projeto Asp.Net MVC, percebemos que uma estrutura bem maior do que a de um WebForm é criada, com diversas pastas e subpastas, como mostrado na imagem abaixo:

image

Vamos explicar um pouco sobre isso. Toda aplicação Web tende a ficar extensa, com inúmeros arquivos. No caso do WebForm, você pode organizar isso da forma que achar melhor, criando vários níveis de pastas e sub-pastas. No MVC, isso é um pouco diferente. Com MVC a Microsoft decidiu trabalhar seguindo convenções, e isso é levado a sério ao desenvolver sites MVC. No caso das pastas, a MS convencionou que deveria existir uma pasta com cada tipo de arquivo, que são: Modelos, Visões e Controladores (Model, View, Controllers – MVC). Existem outras pastas, mas o conteúdo delas é também comum ao WebForms, como Scripts e Content.

Lembre-se: Convenção é um termo que você ouvirá muito no mundo MVC.

É possível mudar essa convenção? Sim, mas nem sempre é interessante, na realidade isso será tema de algum próximo post.

Mas de forma geral, os Controllers você pode colocar em qualquer lugar no projeto que ele será encontrado, os Models (os dados da aplicação) também podem está em qualquer lugar, principalmente em outro projeto, como é comum, apenas as Views que, normalmente, você não trocar de lugar, as view sempre estão na seguinte estrutura de pasta: View/{NomeDoController}/{AçãoDoControle}. O que fugir muito dessa estrutura não será encontrado pela engine de View do MVC. E ai está o ponto, se você realmente quiser mudar essa estrutura, você terá que customizar a ViewEngine do Asp.Net MVC (assunto para outro post).

Ainda existe outra pequena forma de mudar a estrutura de pastas, também tema para um post, que a nova funcionalidade, adicionada a partir do MVC 2, que são as Areas, que resumidamente é uma forma de você agrupar “por funcionalidade” a estrutura de pastas Controllers, Models e Views, ou seja, replicando para cada grupo de Controladores, toda a estrutura padrão de pastas.

Agora que vimos um pouco cada pasta da estrutura do MVC, vamos entender no que consiste cada item do padrão Model View Controller na implementação do Asp.Net MVC.

Controller

O Controller no Asp.Net MVC é representado por uma classe que herda da classe System.Web.Mvc.Controller, é o Controller que é a base do endereço que digitamos na URL, e ele que sabe como deve ser a View que deve ser retornada. Um Controller pode retornar várias Views, de acordo com a Ação (Action) definida.

Action são os métodos do Controller que executam alguma ação e retornam uma View específica, utilizando um Modelo (Model) ou não.

Por exemplo, quando criamos um novo projeto, o Visual Studio cria dois Controllers: Home e Account. O Controller Home tem duas Actions, como mostrado abaixo:

public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            return View();
        }

        public ActionResult About()
        {
            return View();
        }
    }

Cada Action tem uma View relacionada na pasta View/Home, como pode ser visualizado no print abaixo:

image

Não precisa ser sempre assim. Lembre que uma View pode ser qualquer coisa que retorne para um usuário, portanto, pode ser texto simples, arquivo, JSON, comando para o browser redirecionar, enfim, qualquer coisa coisa que pode ser retornada para o Browser. Para deixar isso claro, vamos criar um novo Controller, da seguinte forma:

Clique na pasta Controller –> Add-> Controller e dê o nome de PrimeiroController, conforme prints abaixo:

image

image

Perceba que temos outra Convenção ao criar um controller: Todos devem ter o sufixo Controller, como os atributos devem ter o sufixo Attribute.

Quando criamos um Controller é criado uma Action padrão chamada Index, que é invocado toda vez que não definimos qual ação chamar, então vamos alterar a implementação padrão do método/action Index, para ficar como a seguinte:

public class PrimeiroController : Controller
    {
        //
        // GET: /Primeiro/

        public string Index()
        {
            return "Primeira página!";
        }

    }

Dando F5, vamos digitar /Primeiro após o nome do seu site no endereço e receberemos o retorno de nossa Action Index no Controller Primeiro da seguinte forma (Perceba que não precisamos digitar o sufixo Controller):

image

Neste casso, o controller está retornando todo o conteúdo HTML, que é a nossa View, vamos criar mais uma Action para ver que podemos chamar mais de uma Action dentro de um Controller e ter Views diferentes. Abaixo segue o código da Action, e o endereço http://localhost:50413/Primeiro/BemVindo digitado no endereço IE. Perceba que você pode colocar qualquer código HTML no retorno da View, e neste caso precisamos também colocar o nome da Action, porque agora não estamos chamando a Action padrão:

public string BemVindo()
{
	return "<h1>Minha Página!</h1><p>Olá, seja bem-vindo!</p>"

}

image

Mas calma! Você não precisa ficar digitando HTML em C# dessa forma, porque seria bem cansativo. Para isso você utiliza um ActionResult como retorno. Vamos mudar o tipo do retorno das nossas Actions de string  para ActionResult, e adicionar Views HTMLs para elas, dessa forma ficará mais fácil de controlarmos a geração do HTML. Podemos adicionar uma View clicando em cima do Método da Action e depois em Add View. Vamos fazer isso para os dois métodos. Aparecerá  a seguinte janela:

image

Essa é a janela que você define as informações da View. Vamos conhecê-la com o passar do tempo. Por hoje, precisamos ver apenas que estamos dando o nome da View, que é o mesmo nome da Action, e também estamos dizendo que ela utilizará uma MasterPage (Site.Master). Quanto a MasterPages, o conceito é o mesmo que no WebForm.

Depois de adicionarmos as duas Views, nosso diretório de View ficará assim:

image

Quando trabalhamos com Action do tipo ViewResult, podemos passar dados do controller para view de duas formas: Através do dicionário ViewData, que você pode pode definir qualquer informação, e lê-la a partir da View, ou através de Model, que é a representação dos dados, como você já sabe.

A princípio não existe nada de mais no HTML da View, porém, se você vem do WebForm, deve saber que NÃO EXISTEM NENHUM SERVER CONTROL no MVC. Portanto, você deverá conhecer HTML, se você veio de PHP ou Asp 3, verá que é bem parecido com o que você trabalhava, a principio pelo menos.

Vamos primeiro codificar o Index, tanto Action como View:

Action:

 public ActionResult Index()
        {
            ViewData["Texto"] = "Primeira Página";
            return View();
        }

View:

<asp:content id="Content2" runat="server" contentplaceholderid="MainContent">

    <h2><%=ViewData["Texto"] %></h2>

</asp:content>

Resultado:

image

Agora o comportamento com um Model, e vendo como ser acessado a partir da View, agora com a Action BemVindo. Nesse caso, estou utilizando como modelo um objeto dinâmico,, apenas como exemplo, mas poderia ser qualquer estrutura de dados:

Action:

public ActionResult BemVindo()
        {
            dynamic model = new ExpandoObject();
            model.Texto = "Seja bem-vindo!";
            return View(model);
        }

View:

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2><%=Model.Texto %></h2>

</asp:Content>

Resultado:

image

Perceba que agora  agente não utilizou o View data, mas um objeto chamado Model, que terá as propriedades do seu modelo de acordo com o definido na Action.

No nosso caso, o IntelliSense do Model não funcionou porque o nosso objeto era dinâmico e o tipo da View era dinâmico, como podemos ver na primeira linha da View:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>

No próximo post, veremos como fazer Views fortemente tipadas e como isso pode nos ajudar no desenvolvimento.

Bom, acredito que para um primeiro post, apenas introdutório, o que foi passado já é suficiente para você ir fuçando e descobrindo mais coisas do Asp.Net MVC.

Qualquer dúvida estou a disposição.

Até o próximo!

16. November 2010 07:18 by Frederico B. Emídio | Comments (1) | Permalink

A plataforma Asp.Net    

Olá Pessoal!

Depois de algum tempo impossibilitado de escrever, retomo minha pequena série introdutória sobre MVC.

Na realidade, no primeiro post, comentei que faria uma séria sobre Web, para que quando fosse falar de Asp.Net MVC, pudesse falar para pessoas que entendesse pelo menos de Web e Asp.Net, para saber onde Asp.Net MVC se encaixa, para quem não se lembra, segue a lista:

Tentarei ser breve nesse post, para que inicie a série sobre Asp.Net MVC o quanto antes.

O que seria o Asp.Net? Asp.Net é um framework da Microsoft para auxiliar o desenvolvimento Web. Entendam como framework, uma coleção de classes que auxiliam na solução de uma tarefa, e que tarefa é essa? Encapsular requisições HTTP, contextos de requisições, respostas HTTP, enfim, tudo do que a web pura é feita.

O Asp.Net faz o trabalho de traduzir as requisições por nós, é uma coleção poderosa de classes, que devemos utilizar para nos comunicar de forma melhor com os protocolos da Internet.

E o que é que podemos fazer com o Asp.Net? Tudo que você pensa que o IIS faz. Como assim? O que o IIS faz, em grande parte (não em todas é claro), é delegar o processamento de certas requisições as classes do Asp.Net. Você pode ver exemplos disso aqui e aqui. Quando geralmente desenvolvemos sites sobre o framework Asp.Net, não estamos utilizando ele diretamente, estamos utilizando outros componentes que rodam sobre o Asp.Net, como o WebForm e o MVC, e por isso chamados de Asp.Net WebForm ou Asp.Net MVC.

Conhecendo a arquitetura do Asp.Net, poderemos criar inclusive um servidor Web, nosso próprio IIS (como mostrado no segundo link acima), poderíamos implementar da forma que quiséssemos esse servidor, inclusive utilizando um servidor Linux, e rodando o CLR sobre o Mono.

Para entendermos um pouco mais sobre isso, vale mostrar uma ilustração:

Diagrama Asp.net

Perceba na imagem acima que Asp.Net é construído em cima do .Net, portanto pode ser utilizado em qualquer contexto onde qualquer classe do framework pode ser utilizada.

Você também deve ter reparado que coloquei três conjunto de componentes que rodam sobre a arquitetura do Asp.Net, e que geralmente é por onde nós alcançamos o Asp.Net de fato. Da mesma forma que esses três conjuntos estão sobre o Asp.Net, e não se complementam nem se anulam, podemos desenvolver novos frameworks que rodam sobre o Asp.Net.

Poderia ter colocado outros quadrinhos no nível superior do diagrama, mas não quis fazer isso pois quis focar apenas em componentes de Web (que auxiliam na construção de sites/tela). O Asp.Net também é um framework de auxilio ao desenvolvimento para internet (que é levemente diferente de simples Web), e poderíamos incluir no diagrama quadros como WebServices, que não é nem Asp.Net MVC, nem Asp.Net WebForms, nem Asp.Net Dynamic Data, é Asp.Net WebServices, que ainda existe, mesmo com o uso muito mais comum de serviços WCF rodando sobre o IIS.

A Microsoft, na atual estrutura do “framework Asp.Net”, decidiu por implementar três paradigmas distinto de aplicações, esses paradigmas não são exclusivos de Web: MVC também é implementado em aplicações Windows, da mesma forma que o WebForms procura levar para o desenvolvimento Web a experiência de desenvolver WindowsForm, e o Dynamic Data procura implementar o conceito RAD (Rapid Application Development) que visa desenvolver aplicações de forma rápida, interativa e incremental.

Como disse, da mesma forma que a Microsoft desenvolveu esses conceitos sobre o framework Asp.Net, nós poderíamos desenvolver outro paradigmas de desenvolvimento sobre o Asp.Net, e nossas páginas seriam servidas pelo IIS sem grandes modificações, afinal, o IIS conhece o Asp.Net, não a implementação específica de cada paradigma de desenvolvimento.

Um exemplo para isso é o MVC#, que procura implementar o padrão MVP, muito utilizado hoje no desenvolvimento Web. É um framework independente, não muito conhecido ainda, mas que mostra as possibilidades de se criar sobre o Asp.Net.

Entendendo como funciona o Asp.Net, as discussões do tipo “O MVC vai substituir o WebForms” devem acabar, pois dá para entender que cada forma de desenvolvimento tem o seu objetivo, quando vamos iniciar o projeto, devemos pensar em qual padrão se aplica melhor para a solução que queremos desenvolver, inclusive devemos analisar o uso do Dynamic Data, que pode resolver muitos problemas simples de forma extremamente rápida. Talvez no futuro eu faça algum post sobre o uso de tipo de Projeto.

Além de dá suporte aos tipos de aplicação que citei acima, outras tecnologias da Microsoft que rodam sobre o Framework .Net também fazem uso da arquitetura do Asp.Net, como o Silverlight em algumas situações ou o WCF, onde podemos utilizar sessões e outras características específicas de desenvolvimento de internet (sobre HTTP).

Para dar suporte ao desenvolvimento Web, a Microsoft também desenvolveu outras pequenas bibliotecas. Um exemplo disso é o Asp.Net Ajax, que pode ser utilizada tanto no lado Servidor (exclusivo para aplicações Asp.Net, mas exclusivamente para WebForms), como no lado cliente (podendo ser utilizada em qualquer arquitetura web, como Java, PHP, etc) codificada em JavaScript, que já parece estar com os dias contatos, com a adoção cada vez mais ampla pelo time da Microsoft do JQuery.

Bom, acredito que o que foi dito até aqui já dá para ter uma idéia de como o Asp.Net é divido, e como podemos usá-lo de acordo com a nossa necessidade para cada momento. Espero que esse texto possa te ajudar a ver o Asp.Net de uma forma mais ampla, e não apenas fechado a realidade que você trabalha (MVC ou WebForm em geral).

Até o próximo post.

8. November 2010 06:38 by Frederico | Comments (2) | Permalink

Sobre mim

Minha Imagem

Meu nome é Frederico Batista Emídio, trabalho com desenvolvimento de sistemas profissionalmente a oito anos, porém, já faço sites pessoais a pelo menos dez anos.

Para saber mais clique aqui.

Páginas

Calendário

<<  March 2021  >>
MoTuWeThFrSaSu
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

Visualizar posts em um Calendário
Sigua @fredemidio

MCP Asp.NET