Introdução ao ASP.NET MVC 5
Introdução ao ASP.NET MVC 5
Explicação
Downloads
Segue o link do projeto inicial: Visual Studio 2013
Existem muitos programadores que desenvolvem aplicativos web para o sistema operacional
Windows. Muitos deles já desenvolveram utilizando a linguagem ASP ou, nos útimos anos, o
famoso [Link] WebForms.
O WebForms, em sua época, revolucionou a maneira de se programar para Web. Programar com
WebForms parecia programar com Visual Basic ou Delphi! Ou seja, os componentes eram
arrastados pro formulário, e o programador podia colocar ações em eventos desses componentes,
por exemplo, imprimir uma mensagem na tela quando acontecer um click no botão.
Como muita gente na época vinha desse tipo de programação de aplicativos Desktop, aprender
WebForms não era tão difícil. Sem contar a grande sensação de produtividade, já que os
componentes que existiam faziam as mais diversas coisas, como exibir dados de um banco de
dados de maneira elegante, separando em páginas.
Mas infelizmente hoje percebemos que códigos feitos com WebForms são difíceis de manter. O
motivo? Apesar de ser altamente produtivo, não existia alguma coisa que separasse o código que
lida com interface, do código que lida com regra de negócio, do código que lida com banco de
dados, e assim por diante... Na prática, os códigos WebForms são "macarrônicos", ou seja, são
grandes e fazem muita coisa diferente.
No fim, o problema é que o WebForms não nos "obriga" a usar boas práticas de programação.
Percebendo isso, a Microsoft resolveu criar uma alternativa ao WebForms: o chamado [Link]
MVC! O MVC, um padrão de desenvolvimento muito utilizado no mundo web é conhecido por
"forçar" o programador a separar as responsabilidades. Ou seja, código de interface separado de
código de regra de negócio separado de código de banco de dados, e assim por diante. A
vantagem disso? Sua aplicação viverá por mais tempo! Você conseguirá mantê-la pra sempre!
Estudaremos mais sobre o padrão MVC no próximo capítulo.
Depois de fazer o download do instalador, execute-o. Isso abrirá uma nova janela em que o
Visual Studio pergunta se aceitamos o contrato de licença. Leia os termos e depois marque a
opção I agree to the License Terms and Privacy Policy se você aceitar os termos da
Microsoft.
[Imagem [Link]]
E depois clique no botão install. Com isso o instalador começará a fazer o download dos
componentes adicionais do Visual Studio e executará a instalação em sua máquina.
[Imagem [Link]]
Quando a instação for concluída, execute o Visual Studio que acabou de ser instalado. Na
primeira execução ele perguntará se você deseja se logar na sua conta da microsoft para
compartilhar configurações entre máquinas. Se você quiser compartilhar as suas informações
com a Microsoft, faça o login em sua conta, senão clique no link Not now, maybe later. Com
isso estamos prontos para utilizar o Visual Studio para desenvolver nossas aplicações com o
[Link] MVC.
Primeiro Projeto
Vamos criar um projeto do [Link] MVC. Abra o Visual Web Developer e aperte
Ctrl+Shift+N, a janela New Project será aberta, selecione a linguagem Visual C# e [Link]
Web Application.
Na próxima janela, procure o campo Select a template e escolha a opção Empty, isso criará
um projeto web vazio. Agora para que o projeto contenha as bibliotecas do [Link] MVC,
procure o campo Add folders and core references for e nele marque a opção MVC e depois
clique no botão Ok. Isso é tudo que precisamos para criar um novo projeto [Link] MVC 5!
Após criar o projeto, abra o solution explorer (Ctrl+Alt+L), repare que diversos arquivos e pastas
foram automaticamente criados pela IDE. Essa é a estrutura de diretórios esperada de uma
aplicação [Link] MVC. É importante conhecer essa estrutura, pois você precisará sempre
seguir as convenções já pré-definidas. Essas convenções, aliás, são muito comuns ao longo do
[Link] MVC: elas lhe pouparão muito trabalho!
Entre as pastas criadas na estrutura inicial, duas são mais importantes. A pasta Controllers: é
nela que guardaremos nossas classes responsáveis por tratar as requisições que vem do browser
do usuário. É aqui que pegaremos os dados que o usuário nos envia através de formulários, por
exemplo. E a pasta Views, onde os arquivos que serão utilizados para renderizar a resposta para o
usuário são colocadas. Por exemplo, é aqui que guardaremos o HTML que renderiza o
formulário.
Podemos utilizar o próprio Visual Studio para criar o controller. Para isso, clique com o botão
direito na pasta Controllers do projeto e selecione a opção Add > Controller:
Dentro da janela que é aberta, a Add Scaffold selecione a opção MVC 5 Controller - Empty
e depois clique no botão Add.
Na próxima janela, precisamos dar um nome para a classe que será criada. Como esse é o
controller inicial da aplicação, nós o chamaremos de Home, porém, por convenção, controllers do
[Link] MVC tem o nome terminado pela palavra Controller, então o nome final da classe
será HomeController.
Com isso, o Visual Studio criará uma nova classe na pasta Controllers do projeto com o
seguinte código:
using System;
using [Link];
using [Link];
using [Link];
using [Link];
namespace [Link]
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
}
Veja que essa classe herda de uma classe chamada Controller do namespace [Link].
Todo controller do [Link] MVC precisa herdar dessa classe.
Para atender requisições web, o [Link] MVC utiliza os métodos públicos implementados
dentro dos controllers, esses métodos são conhecidos como actions. As actions do MVC sempre
devolver um objeto do tipo ActionResult. Para decidir qual é a action que deve ser utilizada
para atender uma determinada requisição, o [Link] MVC utiliza um conjunto de regras
conhecido como Rota Padrão.
Segundo a rota padrão, quando o navegador tenta utilizar uma url da forma /Home/Index, a
primeira parte da url (Home) indica qual é o nome do controller que atenderá a requisição e a
segunda parte indica o nome da action. Então nesse exemplo, a action Index do
HomeController seria utilizada.
A rota padrão também possui valores padrão para os nomes do controller e da action. Quando
não informamos o nome da action, a action Index é utilizada e quando não informamos o nome
do controller, o HomeController é utilizado. Então podemos acessar o método Index do
HomeController através de três endereços diferentes: /Home/Index, /Home e /.
Agora que já sabemos como criar e acessar controllers, precisamos aprender como implementar
uma action e o como montar a resposta HTML que será devolvida para o navegador que acessou
a aplicação.
Na implementação das actions, precisamos colocar o código que acessa as regras de negócio
implementadas pela aplicação e, ao terminar de executar as lógicas de negócio, precisamos
avisar o [Link] MVC que queremos executar a lógica de visualização da aplicação. Fazemos
isso através do método View que é herdado da classe Controller:
Dentro do cshtml, precisamos colocar todo o código HTML que será devolvido para o
navegador. Todo o código colocado dentro desse arquivo é interpretado por um componente do
[Link] MVC chamado Razor. Vamos então implementar o código da página inicial da
aplicação. Para isso utilizaremos novamente um assistente do Visual Studio.
Clique com o botão direito do mouse sobre o método Index e selecione a opção Add View.
Dentro da janela que é aberta pelo Visual Studio, desmarque a opção Use a layout page e
depois clique em Add.
Com isso o Visual Studio criará um novo arquivo na pasta Views/Home chamado [Link]
com o seguinte código:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
</div>
</body>
</html>
Vamos modificar esse arquivo para mostrar uma mensagem na página inicial da aplicação:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
Página inicial usando [Link] MVC 5!
</body>
</html>
Agora vamos apertar o F5 para executar a aplicação no servidor do Visual Studio. Isso abrirá o
navegador do seu sistema utilizando um endereço da forma: [Link] ou
seja, não estamos informando nem o controller e nem a action nessa url, o [Link] MVC
executará a lógica do método Index do HomeController. A mesma lógica seria executada caso
acessássemos as urls [Link] ou
[Link]
Com isso terminamos a nossa primeira página de uma aplicação [Link] MVC 5!
PADRAO MVC
Explicação
Muitos sistemas tornam-se difíceis de manter porque existem trechos de código que tem muita
responsabilidade, ou seja, fazem muita coisas. Aplicações desktop são um exemplo empírico
desse problema; muitos códigos legados em VB ou Delphi são difíceis de manter porque, um
mesmo trecho de código é responsável por capturar dados do usuário (através de caixas de texto,
botões, etc), tomar ações a partir desses dados (por exemplo, alterar uma tabela no banco de
dados), e por fim alterar a interface do usuário para mostrar que o programa reagiu da maneira
esperada (exibindo uma mensagem de sucesso).
Uma possível solução para o problema é tentar separar as diversas tarefas citadas acima, da
seguinte forma:
Quando um usuário interage com o sistema, ele executa uma ação e obtém visualmente o
resultado da mesma. Isto é, primeiro uma regra de negócios é executada (adicionar algo,
atualizar algo, buscar algo), e depois informações são mostradas na tela.
Isso forma três camadas: A camada chamada de Model é responsável somente pelas regras de
negócio; a camada de View é responsável unicamente por exibir os dados para o usuário final;
por fim, a camada Controller é responsável por receber as ações feitas pelos usuários na View e
convertê-las em chamadas de negócio dentro do Model. O padrão conhecido por MVC (Model-
View-Controller) sugere que o programador faça exatamente essa separação no código.
Todo sistema possui regras de negócios, que podem ser simples ou complexas. Todas
elas devem estar separadas em classes que tem essa regras como única responsabilidade.
Ou seja, toda e qualquer ação de regra de negócio deve ser realizada por exclusivamente
esse conjunto de classes.
Interfaces de usuário também devem ser isoladas. Códigos de interface tendem a ser
grandes e a sofrerem mudanças constantes. Por esse motivo, toda parte responsável pela
exibição das informações para o usuário devem estar isoladas em outro ponto do sistema.
Para conectar as ações que o usuário realiza na interface e fazer com que essas ações
resultem em execuções de regra de negócio, é necessário que uma outra camada faça essa
tarefa. Ou seja, essa camada recebe informações da camada de visualização e as
transforma em invocações de regras de negócio.
O padrão MVC tem se mostrado uma maneira educada de separar essas camadas, e por isso sua
utilização é crescente no mercado. Muitos frameworks, inclusive de outras linguagens de
programação, como é o caso do Ruby on Rails (do mundo Ruby) e VRaptor (do mundo Java),
adotam o MVC como solução para separar responsabilidades.
O [Link] MVC, como o próprio nome diz, segue o padrão MVC. Veja que os nomes
adotados pelo MVC são idênticos aos do padrão MVC. Suas regras de negócio devem ficar em
classes C# convencionais dentro da pasta /Models. O código responsável apenas pela interface
do usuário são salvas na pasta /Views. Por fim, as classes controladoras, salvas no diretório
/Controllers, conectam a view com os modelos.
Perceba a separação! Não há mistura de código! Isso faz do MVC um dos padrões mais
populares para desenvolvimento de aplicações!
CONTROLANDO AS REQUISIÇÕES
Explicação
Para aprendermos o [Link] MVC, desenvolveremos um sistema de controle de estoque. Esse
sistema deve permitir o cadastro, listagem, visualização de detalhes e controle da quantidade de
produtos. Cada produto deve ter um nome, um preço, uma categoria, uma descrição e a
quantidade em estoque.
O controller de produtos
Vamos inicialmente baixar o projeto inicial do curso no link [Link]
online-public/asp-net-mvc5/[Link]. Descompacte o zip e depois abra o arquivo
[Link] com o Visual Studio 2013 Express for Web. Esse arquivo irá importar um
projeto que já fornece uma implementação para os DAOs de produtos (ProdutosDAO), de
categorias (CategoriasDAO) e de usuários (UsuariosDAO).
Vamos agora desenvolver o controller para os produtos da aplicação, da mesma forma que
fizemos no capítulo inicial do curso, chamado ProdutoController:
Dentro do método Index desse controller, executaremos a lógica para listar todos os produtos
cadastrados no banco de dados utilizando uma tabela do html, mas no controller que acabamos
de criar, a action Index simplesmente redireciona o usuário para a camada de visualização.
Como foi explicado na seção anterior, a camada de visualização do MVC deve conter apenas
lógica de visualização. Logo, a lista de produtos deve ser enviada do controlador para a view.
Para conseguirmos a lista de produtos, devemos acessar os dados que estão gravados no banco de
dados e, como mencionado anteriormente, o acesso ao banco é isolado dentro dos DAOs. Logo,
precisamos instanciar um DAO que saiba acessar os produtos no banco, o ProdutosDAO.
return View();
}
Com o DAO, podemos utilizar o auto complete (Ctrl + Espaço) oferecido pela IDE para listar os
métodos e propriedades do objeto.
Vemos que existe um método Lista que retorna um IList<Produto>. Utilizaremos esse
método para recuperar a lista de produtos do banco de dados.
return View();
}
Obtivemos a lista, porém ela está visível apenas dentro do método do controlador. Para
enviarmos a lista de produtos para a camada de visualização, precisamos utilizar uma
propriedade que é herdada da classe Controller chamada ViewBag.
Toda informação que colocamos dentro da ViewBag pode ser acessada pela camada de
visualização, pois ela também tem acesso ao mesmo objeto ViewBag que utilizamos no
controller. Com a ViewBag, podemos enviar qualquer número de objetos para a camada de
visualização.
Com o método do controller pronto, precisamos somente criar nosso arquivo de view, esse
arquivo será o Views/Produto/[Link]
Esse arquivo será utilizado pelo [Link] MVC para apresentar o resultado da requisição.
Queremos exibir o id, o nome e o valor dos produtos de nossa lista dentro de uma tabela, ou seja,
fazer algo equivalente ao código abaixo:
<tr>
<td>[Link]</td>
<td>[Link]</td>
<td>[Link]</td>
</tr>
Não há como saber se o programador queria exibir o texto "[Link]" ou recuperar o valor
do Id da variável produto e, por esse motivo, o [Link] MVC nos obriga a colocar o prefixo @
nas variáveis:
<tr>
<td>@[Link]</td>
<td>@[Link]</td>
<td>@[Link]</td>
</tr>
O código que criamos serve para exibir um produto, porém nosso controller enviou uma lista,
logo precisamos executar o código acima para cada item da lista. Em C#, isso seria equivalente
ao código:
Na view, o foreach é quase igual ao código em C#, precisamos apenas adicionar um @ antes do
foreach:
Agora podemos utilizar a ViewBag para acessar a lista de produtos que foi enviada pelo
controller:
<table>
<thead>
<tr>
<th>Id</th>
<th>Nome do Produto</th>
<th>Preco</th>
</tr>
</thead>
<tbody>
@foreach (var produto in [Link])
{
<tr>
<td>@[Link]</td>
<td>@[Link]</td>
<td>@[Link]</td>
</tr>
}
</tbody>
</table>
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
<table>
<thead>
<tr>
<th>Id</th>
<th>Nome do Produto</th>
<th>Preco</th>
</tr>
</thead>
<tbody>
@foreach (var produto in [Link])
{
<tr>
<td>@[Link]</td>
<td>@[Link]</td>
<td>@[Link]</td>
</tr>
}
</tbody>
</table>
</div>
</body>
</html>
Relembrando a primeira seção, a rota padrão do [Link] MVC faz com que requisições para
/Produto sejam tratadas pelo método Index do ProdutoController, podemos acessar a action
desenvolvida acessando a url /Produto após subir o servidor com o F5.
LIDANDO COM FORMULARIOS
Explicação
Temos um sistema capaz de listar produtos cadastrados, vamos agora incrementá-lo com a
capacidade de cadastrar novos produtos.
<form action="/Produto/Adiciona">
<label for="nome">Nome:</label>
<input id="nome" name="nome" />
<label for="preco">Preço:</label>
<input id="preco" name="preco" />
<label for="quantidade">Quantidade:</label>
<input id="quantidade" name="quantidade" />
<label for="descricao">Descrição:</label>
<input id="descricao" name="descricao" />
<label for="categoria">Categoria:</label>
<input id="categoria" name="categoriaId" />
Mas como mencionado no começo do curso, esse formulário faz parte da camada de visualização
do MVC, logo precisamos criar um método no controller que, por enquanto, não faz nada a não
ser redirecionar para a view com o formulário. Vamos então adicionar um método chamado Form
dentro do ProdutoController.
Dentro dessa action, para utilizarmos as informações que foram enviadas pelo navegador,
precisamos declarar parâmetros no método que possuem o mesmo nome do campo de texto no
html (atributo name da tag input).
Dentro desse método, precisamos instanciar um novo produto com as informações recebidas:
Repare que os parâmetros enviados pelo formulário são automaticamente convertidos para os
tipos certos. Essa conversão é realizada pelo [Link] MVC.
Para que esse código funcione, os campos do formulário devem ter nome da forma
[Link], por exemplo, se quisermos que o atributo Nome do produto
seja preenchido automaticamente, devemos criar um formulário HTML que tenha um campo
chamado [Link]: <input name="[Link]" />.
<form action="/Produto/Adiciona">
<label for="nome">Nome:</label>
<input id="nome" name="[Link]" />
<label for="preco">Preço:</label>
<input id="preco" name="[Link]" />
<label for="quantidade">Quantidade:</label>
<input id="quantidade" name="[Link]" />
<label for="descricao">Descrição:</label>
<input id="descricao" name="[Link]" />
<label for="categoria">Categoria:</label>
<input id="categoria" name="[Link]" />
A variável produto recebida pelo método da classe controladora é instanciada e preenchida por
um componente do [Link] MVC conhecido como Model Binder.
O Get é geralmente utilizado quando desenvolvemos uma busca dentro da aplicação, pois como
todas as informações do formulário de busca estão na url do navegador, podemos facilmente
compartilhar os resultados com outras pessoas.
Mas em um cadastro de informações não queremos deixar todas as informações expostas na url,
nesses casos, podemos utilizar um outro método de envio chamado post. Para utilizarmos o post
como método de envio do formulário, precisamos apenas colocar o atributo method na
declaração da tag form:
Vimos que podemos enviar tanto requisições do tipo Get quanto do tipo Post para a action
Adiciona do ProdutoController que o cadastro continuará funcionando, porém essa action
deveria aceitar apenas requisições do tipo post, pois não estamos implementando uma busca.
Quando queremos limitar o tipo de requisição que uma action recebe para o tipo post, utilizamos
uma anotação (attribute do C#) chamada HttpPostAttribute sobre a declaração do método:
[HttpPostAttribute]
public ActionResult Adiciona(Produto produto)
Como por convenção toda anotação do C# termina com o sufixo Attribute, podemos
simplificar o código para:
[HttpPost]
public ActionResult Adiciona(Produto produto)
Da mesma forma que podemos utilizar o HttpPostAttribute para aceitar apenas requisições do
tipo post, também temos o HttpGetAttribute para aceitar apenas requisições do tipo Get.
Para contornar esse problema, utilizaremos um novo tipo de resultado definido na classe
Controller, o RedirectToAction.
<label for="categoria">Categoria:</label>
<input id="categoria" name="categoriaId" />
Mas o id não é uma informação que o usuário deveria conhecer. Seria mais interessante se
pudéssemos selecionar o nome a partir de uma lista de categorias cadastradas.
Vamos então utilizar a tag html select para exibir os nomes das categorias existentes e enviar o
valor do Id selecionado para o servidor
<label for="categoria">Categoria:</label>
<select id="categoria" name="[Link]">
@foreach(var categoria in [Link])
{
<option value="@[Link]">@[Link]</option>
}
</select>
<form action="/Produto/Adiciona">
<label for="nome">Nome:</label>
<input id="nome" name="[Link]" />
<label for="preco">Preço:</label>
<input id="preco" name="[Link]" />
<label for="quantidade">Quantidade:</label>
<input id="quantidade" name="[Link]" />
<label for="descricao">Descrição:</label>
<input id="descricao" name="[Link]" />
<label for="categoria">Categoria:</label>
<select id="categoria" name="[Link]">
@foreach(var categoria in [Link])
{
<option value="@[Link]">@[Link]</option>
}
</select>
Agora precisamos fazer com que a variável [Link] contenha a lista de categorias.
Para isso, vamos modificar o método Form do ProdutoController:
Explicação
Um problema muito comum enfrentado por desenvolvedores web é a validação dos dados.
Usuários sempre se esquecem de preencher determinados campos, preenchem e-mails inválidos,
data de nascimento incorretas, e assim por diante. A tarefa da aplicação web é perceber esses
pequenos problemas, e avisar o usuário sobre o mal entendido.
Mas o código de validação geralmente é feio e cheio de ifs. Veja o controller abaixo fazendo
validações:
[HttpPost]
public ActionResult Adiciona(Produto produto)
{
if([Link] > 0 && [Link] > 0 && [Link] < 50000 &&
) {
ProdutosDAO dao = new ProdutosDAO();
[Link](produto);
return RedirectToAction("Index");
}
else {
return RedirectToAction("erros");
}
Veja o if que escrevemos para garantir que a quantidade, preço e descrição estão dentro do
esperado. Código feio e difícil de manter.
O [Link] MVC resolve o problema da validação através dos Model Validators. Quando
definimos as classes de modelo, podemos anotar seus atributos com as regras de validação
necessárias para que o modelo seja válido. Por exemplo, para fazer com que o campo Nome
tenha no máximo 20 caracteres, devemos anotá-lo com a classe StringLengthAttribute:
class Produto
{
[StringLengthAttribute(20)]
String Nome { get; set; }
Mas como vimos, por convenção, no C#, todas as anotações são nomeadas utilizando-se o sufixo
Attribute e quando utilizadas como anotação, o sufixo é desnecessário. Logo, podemos
simplificar o código acima para:
class Produto
{
[StringLength(20)]
String Nome { get; set; }
Como vimos na seção anterior, quando enviamos os dados de um modelo para uma action, o
[Link] MVC utilizará os dados da requisição para preencher os campos do modelo no
processo de Model Binding. Além disso, o [Link] MVC também verifica as anotações
presentes nos atributos da classe e executa as validações necessárias. O resultado da validação
fica disponível em uma variável chamada ModelState que é herdada da classe Controller.
O ModelState guarda todas as regras de validação que foram violadas. Para verificar se todas as
regras de validação foram obedecidas utiliza-se o atributo IsValid do ModelState.
if([Link]) {
// O Modelo foi validado corretamente, então pode ser gravado no banco de
dados.
}
else
{
// O Modelo não foi validado corretamente.
}
Vamos modificar a action Adiciona do ProdutoController para que ela cadastre o novo
produto apenas se não houverem erros de validação e, caso contrário, exiba o formulário de
cadastro. Como queremos reutilizar a view da action Form, vamos utilizar uma segunda versão
do método View que recebe o nome da action cuja view queremos utilizar.
[HttpPost]
public ActionResult Adiciona(Produto produto)
{
if([Link]) {
ProdutosDAO dao = new ProdutosDAO();
[Link](produto);
return RedirectToAction("Index");
}
else
{
CategoriasDAO categoriasDAO = new CategoriasDAO();
[Link] = [Link]();
return View("Form");
}
}
O ValidationMessage recebe como argumento o nome da mensagem de validação que deve ser
gerada. As mensagens de validação geradas durante o processo de model binding tem nome da
forma [Link], logo devemos utilizar [Link]
para recuperar as mensagens referentes ao campo Nome do produto.
@[Link]("[Link]")
<label for="preco">Preço:</label>
<input id="preco" name="[Link]" />
<label for="quantidade">Quantidade:</label>
<input id="quantidade" name="[Link]" />
<label for="descricao">Descrição:</label>
<input id="descricao" name="[Link]" />
<label for="categoria">Categoria:</label>
<select id="categoria" name="[Link]">
@foreach(var categoria in [Link])
{
<option value="@[Link]">@[Link]</option>
}
</select>
Tente cadastrar um produto sem preencher o campo nome e veja as mensagens de validação
geradas.
O nome do erro pode ser utilizado na view para se recuperar a mensagem de erro através do
método ValidationMessage do HtmlHelper.
@[Link](nomeDoErro)
int idDaInformatica = 1;
if([Link](idDaInformatica) && [Link] < 100)
{
[Link]("[Link]", "Produtos
da categoria informática devem ter preço maior do que 100");
}
Logo nossa action Adiciona com a regra complexa fica da seguinte forma:
[HttpPost]
public ActionResult Adiciona(Produto produto)
{
int idDaInformatica = 1;
if([Link](idDaInformatica) && [Link] < 100)
{
[Link]("[Link]",
"Produtos da categoria informática devem ter preço maior do que 100");
}
if([Link]) {
ProdutosDAO dao = new ProdutosDAO();
[Link](produto);
return RedirectToAction("Index");
}
else
{
CategoriasDAO categoriasDAO = new CategoriasDAO();
[Link] = [Link]();
return View("Form");
}
}
<label for="preco">Preço:</label>
<input id="preco" name="[Link]" />
<label for="quantidade">Quantidade:</label>
<input id="quantidade" name="[Link]" />
<label for="descricao">Descrição:</label>
<input id="descricao" name="[Link]" />
<label for="categoria">Categoria:</label>
<select id="categoria" name="[Link]">
@foreach(var categoria in [Link])
{
<option value="@[Link]">@[Link]</option>
}
</select>
if([Link])
{
// produto válido
}
else
{
// produto inválido então vamos guardá-lo na ViewBag
[Link] = produto;
// redireciona para o [Link]
}
Falta apenas modificar a view [Link] para exibir os dados do produto. Para isso,
acessaremos os dados armazenados na ViewBag. Por exemplo, para preencher o nome do
produto, preencheremos o atributo value da tag input:
Para o caso da tag select, precisamos indicar qual option deve ser inicialmente selecionado.
Para decidir qual categoria deve ser selecionada, iremos, dentro do @foreach, comparar cada
categoria com a categoria do produto da ViewBag. Se as categorias forem iguais, marcaremos a
opção como selecionada.
Veja que o atributo selected da tag option funciona como um valor booleano indicando se a
opção está ou não selecionada. Quando temos um atributo no Html que funciona como um valor
booleano, no Razor, podemos atribuir diretamente uma expressão booleana (condição) para esse
atributo. Se a expressão for true, o Razor inclui o atributo no Html gerado, senão o atributo é
ignorado. Vamos então utilizar essa característica do Razor para marcar a categoria que foi
selecionada:
<option value="@[Link]"
selected="@[Link]([Link])">
@[Link]
</option>
<form action="/Produto/Adiciona">
@[Link]("[Link]")
<label for="nome">Nome:</label>
<input id="nome" name="[Link]" value="@[Link]" />
@[Link]("[Link]")
<label for="preco">Preco:</label>
<input id="preco" name="[Link]" value="@[Link]" />
@[Link]("[Link]")
<label for="quantidade">Quantidade:</label>
<input id="quantidade" name="[Link]"
value="@[Link]" />
<label for="descricao">Descricao:</label>
<input id="descricao" name="[Link]"
value="@[Link]" />
<label for="categoria">Categoria:</label>
<select id="categoria" name="[Link]">
@foreach(var categoria in [Link])
{
<option value="@[Link]"
selected="@[Link]([Link])">
@[Link]
</option>
}
</select>
Com isso o formulário será preenchido automaticamente caso ocorra um erro de validação,
porém não conseguimos mais acessar a url /Produto/Form! Esse erro ocorre pois a action Form
não atribui um valor ao atributo Produto da ViewBag. Vamos então corrigir a action Form.
Nos capítulos anteriores desenvolvemos uma página que mostra a lista de todos os produtos
cadastrados no banco de dados, porém nessa página, para não colocarmos muitas informações,
listamos o Id, Nome e Quantidade dos produtos. Vamos agora desenvolver uma nova página
onde podemos visualizar todas as informações de um produto cadastrado no banco de dados.
Vamos então adicionar um novo método no ProdutoController que recebe um id, busca no
banco o produto com Id igual ao recebido e envia essa informação para ser exibida na view.
Vamos chamar essa action de Visualiza.
Repare que codificamos o link para a lista de produtos diretamente na view. O problema disso é
que, se um dia o link mudar, precisaremos mudar em todos os HTMLs que apontem para o
mesmo endereço! Muito trabalhoso!
Podemos utilizar um novo método do HtmlHelper, o ActionLink. Esse método cria o código
html de um link de acordo com os parâmetros passados. Queremos definir que o texto do link
deve ser Voltar para a lista de produtos e que esse link deve nos redirecionar para a
action Index da classe ProdutoController, para isso vamos utilizar a versão sobrecarregada de
ActionLink que recebe o texto do link, o nome da action e o nome do controller.
Vamos agora inserir o link para a página de visualização para cada item da lista de produtos.
Queremos criar links que enviem o usuário para a action Visualiza do ProdutoController
passando produtoId como argumento. Como muitas vezes uma action deve receber um
identificador e realizar sua tarefa de acordo com o id recebido, o [Link] MVC configura a
rota padrão com um id genérico como argumento opcional. Dessa forma podemos criar urls da
forma /Produto/Visualiza/1.
Para que a action receba o valor do id capturado pelo [Link] MVC, devemos fazer com que
ela receba um argumento chamado id de qualquer tipo. Em nosso caso, queremos receber um id
inteiro, logo vamos substituir produtoId por id na action Visualiza:
Vamos modificar a lista de produtos para que ela tenha links para a visualização. Abra o arquivo
Views/Produto/[Link] e localize o laço que percorre a lista de produtos:
Queremos que [Link] seja um link para a página de visualização. Para realizar essa
tarefa, podemos criar o link diretamente:
<td><a href="/Produto/Visualiza/@[Link]">@[Link]</a></td>
new { id = [Link] }
Porém temos um problema, o código da view não compila! O problema é que o compilador não
sabe qual é o tipo da variável produto, logo não consegue descobrir que a expressão
[Link] gera uma String e [Link] gera um int. Para resolver esse problema
devemos fazer com que a variável produto seja do tipo Produto:
@foreach([Link] produto in [Link])
Para utilizarmos uma view fortemente tipada, precisamos enviar a a variável para a view de uma
forma diferente. Ela precisa ser enviada como argumento do método View. Por exemplo, na
action Index do ProdutoController, a lista de produtos pode ser enviada através do seguinte
código:
A variável passada como argumento do método View pode ser acessada pela view através de
uma variável chamada Model, essa variável é considerada pelo [Link] MVC como a variável
principal da view. Agora precisamos modificar a lista de produtos
(Views/Produto/[Link]) para que ela utilize a Model ao invés de [Link]:
Mas apenas essa modificação não é suficiente para o [Link] MVC saber o tipo da variável
Model, no início do código da view, precisamos indicar qual é o tipo da variável Model através
do comando @model do Razor:
@model IList<[Link]>
Com isso estamos declarando qual é o tipo da variável Model que está sendo utilizada na view e
por isso podemos utilizar livremente a inferência de tipos no código do foreach:
Explicação
Customização de rotas
Suponha que a empresa em que você trabalha queira modificar o sistema de controle de estoque
para o sistema que você está desenvolvendo nesse curso, porém como exitem diversos clientes
que dependem das URLs do sistema atual, devemos fazer com que as URLs do novo sistema
sejam iguais às antigas.
Vamos fazer com que a listagem de produtos seja servida pela URL /produtos e que a
visualização por /produtos/{produtoId}. A customização de uma url no [Link] MVC 5 é
feita utilizando-se uma definida no framework chamada RouteAttribute, essa anotação é
utilizada sobre actions dos controllers e recebe como argumento a nova url do método.
Para fazermos com que o Index do ProdutoController possa ser acessado através de
/produtos, utilizamos o seguinte código:
[Route("produtos")]
public ActionResult Index()
{
// implementação
}
Podemos fazer o mesmo para a lógica de visualização, porém nela precisamos capturar qual é o
id que será enviado para a lógica do controller. Para capturarmos uma variável da url,
precisamos colocar o nome da variável que queremos capturar dentro de chaves, {id} para
capturar uma variável chamada id.
[Route("produtos/{id}")]
public ActionResult Visualiza(int id)
{
// implementação
}
Como utilizamos o ActionLink para construir os links do capítulo anterior, os endereços dos
links serão automaticamente atualizados para utilizar as urls que acabamos de configurar.
Mas no ActionLink precisamos passar qual é o nome da action e do controller para onde
queremos enviar o usuário, mas o que aconteceria se por algum motivo a equipe decidisse mudar
o nome do método ou do controller? Nesse caso teríamos que modificar todos os ActionLinks
de todo projeto!
[Route("produtos", Name="ListaProdutos")]
public ActionResult Index()
{
// implementação
}
[Route("produtos/{id}", name="VisualizaProduto")]
public ActionResult Visualiza(int id)
{
// implementação
}
No link de visualização, precisamos também passar quais são as informações que devem ser
enviadas para o servidor. Essas informações são enviadas, novamente, através de objetos
anônimos do C#.
No href do link, precisamos passar a url do arquivo css dentro da pasta Content/Css do
projeto. No Razor, podemos conseguir o endereço da pasta da aplicação utilizando o símbolo ~
no código do href. Para chegarmos ao [Link], precisamos, a partir da pasta do projeto,
acessar a pasta Content/Css, então a url fica da seguinte forma: ~/Content/Css. Agora
podemos utilizar essa url na tag link:
<head>
<link rel="stylesheet" href="~/Content/Css/[Link]"/>
<link rel="stylesheet" href="~/Content/Css/[Link]"/>
</head>
Agora precisamos modificar o código do body da página para que ele utilize o twitter bootstrap
para fazer o layout. Começaremos com um menu superior onde poderemos acessar as diferentes
páginas da aplicação. Dentro da tag body, colocaremos uma div com a classe container e
dentro dessa tag, colocaremos um novo div para o cabeçalho da página:
<body>
<div class="container">
<div class="header">
<!-- código do menu do cabeçalho -->
</div>
</div>
</body>
Os links do menu ficarão dentro de uma lista do html, tag ul, com as classes nav nav-pills
pull-right. Além disso, mostraremos também o nome da aplicação dentro de uma tag h3:
<div class="header">
<ul class="nav nav-pills pull-right">
<li>@[Link]("Home", "Index", "Home")</li>
<li>@[Link]("Produtos", "Index", "Produto")</li>
<li>@[Link]("Categorias", "Index", "Categoria")</li>
<li>@[Link]("Usuários", "Index", "Usuario")</li>
</ul>
<h3 class="text-muted">Caelum Estoque</h3>
</div>
Depois de colocarmos o código do cabeçalho da aplicação, vamos colocar mais uma div que
conterá o código da tabela de produtos:
<div class="container">
<div class="header">
<!-- código do menu -->
</div>
<div id="conteudo">
<!-- Aqui fica o código da tabela de produtos -->
</div>
</div>
Com isso terminamos o layout da lista de produtos, agora precisamos replicar esse layout para as
outras páginas da aplicação. O menu superior e a importação dos estilos do bootstrap serão
utilizados em todas as páginas da aplicação, a única parte diferente entre as páginas é o conteúdo
da div que contém a tabela de produtos, então teremos que replicar esse código em todas as
views da aplicação.
Mas o que aconteceria se precisássemos mudar o layout da aplicação, como copiamos o código
para todas as páginas, teríamos que mudar o código de toda aplicação. Para resolvermos esse
problema, podemos utilizar os layout pages do [Link] MVC.
O layout page é um arquivo cshtml comum onde colocamos o código que é comum às views da
aplicação. Esse arquivo é geralmente colocado dentro de uma pasta chamada Views/Shared e
tem um nome começado com o símbolo _. Então, vamos criar uma nova pasta chamada Shared
dentro da pasta views do projeto e dentro dessa pasta, colocaremos o arquivo _Layout.cshtml
com o código que será repetido nas views:
<html>
<head>
<link rel="stylesheet" href="~/Content/Css/[Link]"/>
<link rel="stylesheet" href="~/Content/Css/[Link]"/>
</head>
<body>
<div class="container">
<div class="header">
<ul class="nav nav-pills pull-right">
<li>@[Link]("Home", "Index", "Home")</li>
<li>@[Link]("Produtos", "Index", "Produto")</li>
<li>@[Link]("Categorias", "Index", "Categoria")</li>
<li>@[Link]("Usuários", "Index", "Usuario")</li>
</ul>
<h3 class="muted-text">Caelum Estoque</h3>
</div>
<div id="conteudo">
<!-- Aqui fica o conteúdo da view -->
</div>
</div>
</body>
</html>
<html>
<head>
<link rel="stylesheet" href="~/Content/Css/[Link]"/>
<link rel="stylesheet" href="~/Content/Css/[Link]"/>
</head>
<body>
<div class="container">
<div class="header">
<ul class="nav nav-pills pull-right">
<li>@[Link]("Home", "Index", "Home")</li>
<li>@[Link]("Produtos", "Index", "Produto")</li>
<li>@[Link]("Categorias", "Index", "Categoria")</li>
<li>@[Link]("Usuários", "Index", "Usuario")</li>
</ul>
<h3 class="muted-text">Caelum Estoque</h3>
</div>
<div id="conteudo">
@RenderBody()
</div>
</div>
</body>
</html>
Agora precisamos fazer com que a lista de produtos utilize o layout que acabamos de criar. Para
fazer isso, precisamos abrir um bloco de código na view e dentro desse bloco definiremos uma
variável chamada Layout com o caminho completo para o layout page.
@{
Layout = "caminho completo para o layout page";
}
Na string do caminho para a layout page, podemos utilizar novamente o símbolo ~ para
referenciarmos a pasta inicial do projeto:
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
Agora podemos apagar todo o código de layout que colocamos inicialmente na view da lista de
produtos. Agora só precisamos definir os layouts para as outras views da aplicação, então
teremos que, novamente, replicar o bloco que define a variável layout em todas as views. O que
pode causar problemas caso tenhamos que, por exemplo, mudar o nome do arquivo de layout.
Para resolvermos esse problema, precisamos definir essa variável Layout para todas as views da
aplicação, para isso, podemos criar no projeto um novo arquivo especial do razor chamado
_ViewStart.cshtml dentro da pasta Views da aplicação. O arquivo _ViewStart.cshtml é o
primeiro cshtml que é executado quando o razor gera o html e é geralmente utilizado para
definir variáveis globais para as views da aplicação.
Então vamos utilizar o _ViewStart.cshtml para definir o layout page padrão da aplicação.
Dentro da pasta Views, crie um novo arquivo chamado _ViewStart.cshtml com o código que
define a variável Layout:
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
Além disso, colocaremos a classe form-control também dentro da tag select do cadastro de
produtos:
AJAX
Dentro desse método, precisamos buscar o produto do banco de dados, decrementar sua
quantidade, atualizar as informações e por fim redirecionar o usuário para a página de listagem
de produtos para que ele veja as informações atualizadas:
Agora na listagem de produtos, podemos adicionar mais uma coluna com um link que chama
essa nova action:
Podemos testar essa nova funcionalidade da aplicação acessando a lista de produtos, url
/produtos. Quando clicarmos no link de decrementar a quantidade, podemos ver que a
quantidade é realmente decrementada no banco de dados, porém a página é recarregada.
Esse recarregamento ocorre pois quando fazemos uma requisição web, o navegador sempre
espera a resposta devolvida pelo servidor e depois faz o recarregamento da página, esse é o modo
síncrono de requisições web. Para evitarmos o recarregamento das páginas, precisamos utilizar o
modo assíncrono de navegação.
Cada navegador implementa as requisições assíncronas de uma forma diferente, e para lidar com
essa diferença entre os navegadores do mercado, utilizaremos uma biblioteca javascript chamada
jquery (que é colocada pelo visual studio dentro da pasta Scripts do projeto).
Para podermos utilizar a biblioteca jquery, precisamos inicialmente importá-la dentro do cshtml
da lista de produtos, fazemos isso com o seguinte código:
Agora que importamos a biblioteca jquery, precisamos declarar um bloco de código javascript
que realmente fará a requisição ajax:
<script type="text/javascript">
// o script fica aqui
</script>
Para fazermos uma requisição ajax do tipo get utilizando o jquery, utilizamos a função $.get
definida na bibioteca e para o post, o $.post.
Nessas funções, o primeiro argumento que colocamos é a url para onde queremos enviar a
requisição e o segundo representa os parâmetros que serão enviados para o servidor:
$.post(url, params);
Com esse código, faremos uma requisição ajax para decrementar a quantidade do produto que
tem id igual a 1. Para podermos reutilizá-lo para os outros produtos cadastrados, vamos isolá-lo
dentro de uma função javascript que recebe o id do produto que será decrementado:
function decrementa(produtoId){
var url = "/Produto/DecrementaQtd";
var params = { id: produtoId };
$.post(url, params);
}
Mas veja que dentro do código da função colocamos manualmente a url para a action
DecrementaQtd do servidor. O que aconteceria se essa url fosse modificada (pelo
RouteAttribute, por exemplo)? Nesse caso teríamos que lembrar de modificar também esse
código javascript. Para resolvermos esse problema, podemos utilizar um objeto do [Link]
MVC responsável por gerar o endereço das actions na camada de visualização da aplicação, o
UrlHelper, que pode ser acessado através da variável @Url dentro do código do cshtml.
Para gerarmos uma url com o UrlHelper, utilizamos o método Action informando qual é a
action e qual é o controller que queremos acessar:
@[Link]("DecrementaQtd", "Produto")
function decrementa(produtoId){
var url = "@[Link]("DecrementaQtd", "Produto")";
var params = { id: produtoId };
$.post(url, params);
}
Agora precisamos fazer com que o clique no link de decremento chame a função decrementa
que acabamos de declarar no código javascript. Então vamos inicialmente modificar o link para
que quando clicado, o navegador não saia da lista de produtos:
<a href="#">Decrementar</a>
Para executarmos uma função no clique do link precisamos utilizar o atributo onclick dentro da
declaração da tag a colocando a chamada para o decrementa com o id do produto correto:
Com essa modificação, toda vez que o usuário clicar em um link, o navegador enviará uma
requisição ajax para o servidor para decrementar a quantidade de um produto cadastrado.
Quando o servidor terminar de responder a requisição ajax, caso ela seja bem sucedida,
queremos atualizar o valor da quantidade que está sendo exibida. Para executarmos uma função
quando uma requisição ajax é bem sucedida, precisamos utilizar o terceiro argumento do
$.post. Esse argumento recebe a função que será executada caso a requisição seja bem
sucedida:
function decrementa(produtoId){
var url = "/Produto/DecrementaQtd";
var params = { id: produtoId };
$.post(url, params, atualiza);
}
function atualiza(){
// código que será executado depois do ajax.
}
Dentro do atualiza, podemos, por exemplo, mostrar uma caixa de mensagens se a requisição
foi bem sucedida:
function atualiza(){
alert("Sucesso");
}
Agora quando clicarmos em um dos links de decremento, o navegador fará uma requisição ajax e
o usuário verá a mensagem "Sucesso". Mas ao invés de mostrar uma mensagem, queremos
atualizar a quantidade atual exibida na tabela. Para isso, utilizaremos o jQuery.
Mas com esse código, o mesmo id será colocado para todos os produtos. Para conseguirmos um
id único para cada produto, precisamos, por exemplo, colocar o @[Link]:
<td id="quantidade@[Link]">@[Link]</td>
Mas se executarmos o código, veremos que no html gerado, o id de todos os tds ficou com o
código: quantidade@[Link]. Isso aconteceu porque quantidade@[Link] se parece
com um endereço de e-mail válido e por isso o Razor não interpreta o código para ler o id do
produto. Para forçarmos o Razor a interpretar o código, precisamos colocar parêntesis em volta
da expressão que deve ser executada:
<td id="quantidade@([Link])">@[Link]</td>
Para devolvermos o Json do produto do servidor, utilizamos mais um método herdado da classe
Controller chamado Json passando qual é o objeto que queremos devolver como resposta:
Para podermos utilizar a resposta do servidor dentro da função atualiza, precisamos apenas
colocar um argumento na declaração da função:
function atualiza(resposta) {
// usa a resposta devolvida
}
Dentro da variável resposta temos o Json do produto que foi devolvido pelo servidor. Como a
resposta está no formato Json, podemos ler as propriedades do produto diretamente do Json. Para
lermos, por exemplo, a Quantidade do produto, utilizamos o código: [Link].
Podemos, por exemplo, mostrar a quantidade atualizada com o seguinte código:
function atualiza(resposta) {
alert([Link]);
}
Agora que já sabemos como utilizar o Json, aprenderemos como utilizar o jquery para atualizar a
página. O primeiro passo é buscarmos o elemento que queremos atualizar dentro do html da
página. Para isso precisamos utilizar a função $ definida pelo jquery passando o id do elemento
que queremos buscar com o prefixo #:
function atualiza(resposta) {
var elemento = $("#quantidade" + [Link]);
}
Agora que temos o elemento que será atualizado, precisamos utilizar a função html do jquery
para atualizar o conteúdo do html do elemento:
function atualiza(resposta){
var elemento = $("#quantidade" + [Link]);
[Link]([Link]);
}
Agora nossa página consegue decrementar a quantidade utilizando requisições ajax para o
servidor e também atualizar o conteúdo da página utilizando a resposta que foi devolvida. O
código final do Views/Produto/[Link]:
@model IList<[Link]>
<table>
<thead>
<tr>
<th>Id</th>
<th>Nome</th>
<th>Quantidade</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach(var produto in Model)
{
<tr>
<td>@[Link]</td>
<td>@[Link]</td>
<td id="quantidade@([Link])">@[Link]</td>
<td><a href="#"
onclick="decrementa(@[Link]);">Decrementar</a></td>
</tr>
}
</tbody>
</table>
AUTENTICAR UTILIZADORES
Para suportar esse tipo de estado, o [Link] MVC implementa o conceito de sessão.
No [Link] MVC, a sessão do usuário pode ser acessada de uma action através da variável
Session, que foi herdada da classe Controller. A variável Session funciona como um
dicionário de String para object, logo podemos armazenar diversos nomes com valores
diferentes por usuário.
Session["contador"] = contador;
Assim como em um dicionário do C#, podemos recuperar o valor associado a uma chave com
Session[nomeDaChave], porém como a sessão guarda o tipo object, precisamos converter o
valor devolvido para o tipo correto. No caso do contador, precisamos converter o valor devolvido
para o tipo int, essa converão pode ser feita através da classe Convert:
Caso uma chave não tenha sido inserida na sessão, a busca Session[chaveInexistente]
retorna null. Se passarmos o valor null para o método Convert.ToInt32, ele nos retorna o
valor zero.
Vamos agora implementar nosso contador por usuário. Criaremos um novo controller chamado
ContadorController e adicionaremos o método Index:
public ActionResult Index()
{
return View();
}
Agora, precisamos fazer uma série de operações: recuperar o valor do contador da session,
incrementá-lo, armazenar o valor atualizado na session e, por fim, enviá-lo para a view. Caso o
contador não tenha sido definido, queremos definir o contador como 0.
Tente acessar essa action de diversos navegadores e veja que o valor do contador é diferente para
cada navegador.
O Sistema de autenticação
Queremos que apenas usuários logados no sistema tenham acesso às páginas de listagem e
cadastro de produtos. Então precisamos de uma nova página da aplicação onde faremos o login
dos usuários. A lógica dessa nova tela ficará dentro de um novo controller chamado
LoginController. Dentro dessa classe, a action Index será a responsável por mostrar o
formulário de login para o usuário:
<label for="senha">Senha:</label>
<input name="senha" id="senha" class="form-control" />
Mas com esse código os dados digitados no campo de senha do formulário ficarão expostos. Para
esconder a senha digitada pelo usuário, podemos utilizar um novo tipo de input do html
chamado password:
<label for="senha">Senha:</label>
<input name="senha" id="senha" class="form-control" type="password" />
Nessa action, precisamos verificar se as informações que foram enviadas pelo formulário
realmente existem dentro do banco de dados, para isso utilizaremos o método Busca do
UsuariosDAO. Esse método busca um usuário dado o login e a senha. Se as informações
estiverem corretas, o método devolve o Usuario do banco de dados, senão ele devolve a
referência nula:
Se o usuario tiver uma referência válida, vamos armazená-lo na sessão do servidor e depois
redirecionar para a página inicial da aplicação (a lista de produtos), senão vamos enviar o usuário
de volta para a página de login da aplicação.
Agora podemos testar o login na aplicação entrando na url /Login. No banco de dados do
projeto que foi importado no começo do curso temos um usuário chamado caelum com a senha
caelum que pode ser utilizado para o teste dessa lógica de login.
Mas também precisamos proteger as outras actions do ProdutoController, então essa lógica de
verificação do usuário logado terá que ser copiada para todas as actions do controller.
Para resolvermos esse problema, utilizaremos o filtro do [Link] MVC 5. Filtros são
componentes que conseguem executar lógicas antes e depois do código do controller. Para
criarmos um filtro, precisamos de uma classe que herda de ActionFilterAttribute do
namespace [Link], então no projeto criaremos uma nova pasta chamada Filtros e
dentro dessa pasta criaremos a classe AutorizacaoFilterAttribute:
}
Dentro de um filtro, quando queremos executar uma ação antes do código da action, precisamos
sobrescrever o método OnActionExecuting e para executar uma ação depois do código da
action, sobrescrevemos o OnActionExecuted. Em nosso caso, queremos verificar se o usuário
está logado antes da lógica do controller, então sobrescreveremos o OnActionExecuting:
Nos filtros, quando queremos deixar o usuário executar a lógica do controller, não precisamos
fazer nada. Então precisamos tratar apenas o caso em que o usuário não está logado, nesse caso,
não queremos permitir a execução do código do controller e para isso, precisamos escrever na
propriedade Result do filterContext.
No caso em que o usuário não está logado, queremos escrever no Result um resultado fará um
redirect no navegador do usuário, porém dentro do filtro não podemos utilizar o método
RedirectToAction da mesma forma que fazíamos no controller, precisamos instanciar
diretamente o objeto devolvido pelo RedirectToAction que é o RedirectToRouteResult.
E todo esse código ficará dentro de um if que verifica se o usuário está logado:
Agora que o código do filtro está pronto, precisamos avisar ao [Link] MVC que ele será
utilizado na action Index do ProdutoController, para isso precisamos anotar essa action com a
classe do filtro que foi criado:
Mas como foi dito anteriormente no curso, quando usamos uma classe como anotação não
precisamos colocar o sufixo Attribute no nome da anotação:
Mas se quisermos proteger todas as actions desse controller, teríamos que colocar a anotação em
todos os métodos, ao invés disso, podemos colocar a anotação sobre a declaração da classe
ProdutoController:
[AutorizacaoFilter]
public class ProdutoController : Controller
{
public ActionResult Index()
{
// código para a lista de produtos
}
}
Quando o filtro é colocado sobre um controller, ele será executado antes e depois de cada uma
das actions desse controller.
Também podemos falar para o [Link] MVC que um determinado filtro precisa ser executado
em toda requisição que chega na aplicação. Quando queremos registrar um filtro global na
aplicação, precisamos abrir um arquivo chamado [Link] do projeto, é nele que fazemos as
configurações programáticas globais de uma aplicação [Link] MVC.
Veja que se colocarmos muitas configurações globais dentro do [Link], o arquivo acaba
ficando grande e bagunçado, para resolver esse problema, geralmente colocamos as
configurações dentro de classes separadas e depois simplesmente chamamos a classe de
configuração dentro do [Link]. A classe que é normalmente utilizada para registrar filtros
globais é uma classe chamada FilterConfig que é usualmente criada dentro da pasta
App_Start da aplicação.
Agora o filtro será executado para toda requisição que chega ao servidor.
Agora suponha que um usuário se autentique no seu site e esqueça de fazer o logout. Nesse caso,
para o servidor da aplicação, o usuário continuará logado. Agora esse usuário entra em um site
malicioso que envia automaticamente o seguinte formulário html:
Como o usuário continua logado em nossa aplicação, a requisição será processada normalmente
pelo servidor, que irá trocar o e-mail cadastrado do usuário para [Link]@[Link] e,
utilizando a funcionalidade de recuperação de senhas, o hacker poderá tomar o controle da conta
do usuário.
O problema ilustrado acima é conhecido como cross site request forgery (csrf). A solução dada
pelo [Link] MVC é colocar um token (uma chave secreta que só o browser e o servidor
conheçam naquele momento) entre os dados enviados pelo formulário. Esse token é conhecido
como csrf token e pode ser gerado pelo método AntiForgeryToken do HtmlHelper. Vamos
modificar nosso formulário de cadastro de produtos para utilizar o csrf token.
<form action="/Produto/Adiciona">
@[Link]()
<label>Nome: <input name="[Link]" /></label>
@[Link]("[Link]")
<label>Preco: <input name="[Link]" /></label>
@[Link]("[Link]")
<label>Quantidade: <input name="[Link]" /></label>
@[Link]("[Link]")
<label>Descricao: <input name="[Link]" /></label>
@[Link]("[Link]")
<label>
Categoria:
<select name="[Link]">
@foreach(var categoria in Model) {
<option value="@[Link]">@[Link]</option>
}
</select>
@[Link]("[Link]")
</label>
<input type="submit" />
</form>
Porém, apenas adicionar o token ao formulário não resolve o problema. Devemos agora fazer
com que o servidor verifique se o token enviado é válido. Para isso utilizaremos um filtro
definido pelo [Link] MVC, o ValidateAntiForgeryTokenAttribute. Para utilizá-lo,
devemos apenas anotar nossa action. Vamos modificar o método Adiciona do
ProdutoController para verificar o token:
[ValidateAntiForgeryToken]
public ActionResult Adiciona(Produto produto)
{
// implementação do adiciona
}
}
Agora apenas requisições que enviem um token com valor correto serão tratadas pela action.