Baixe o app para aproveitar ainda mais
Prévia do material em texto
. 1 25 28 Sumário 1 Introdução ao ASP.NET Core 1.1 Um breve resumo sobre o modelo de comunicação na Web 2 1.2 Criando um projeto ASP.NET Core MVC com o Visual Studio (Windows ou MacOS) 4 1.3 Criando um projeto ASP.NET Core MVC em outros ambientes 6 1.4 A estrutura de uma aplicação ASP.NET Core MVC 6 1.5 Página inicial e convenções do ASP.NET MVC 7 1.6 Rodando a aplicação 9 1.7 O sistema de roteamento do ASP.NET Core MVC 9 1.8 Exercícios 24 1.9 Criando tela de listagem de posts 12 1.10 Geração dinâmica da tabela a partir dos posts do blog 13 1.11 Exercícios 24 1.12 Outra maneira de enviar informações para a view 17 1.13 Exercícios 24 1.14 Incluindo um post através de um formulário HTML 19 1.15 Exercícios 24 1.16 Descobrindo os métodos das requisições HTTP 22 1.17 Exercícios 24 1.18 O que vem pela frente? 24 1.19 Conclusão 24 2 Código de qualidade com o padrão MVC 2.1 Exercícios 27 3 Persistindo posts em um Banco de Dados 3.1 Identificando unicamente um post 29 3.2 Abrindo a conexão com o ADO.NET 29 3.3 Isolando a string de conexão 30 3.4 Isolando a conexão em sua própria classe 31 3.5 Exercícios 41 SumárioCaelum 43 56 66 79 3.6 Listando os posts do banco 34 3.7 DAO - Data Access Object 36 3.8 Exercícios 41 3.9 Inclusão do post no banco de dados 37 3.10 Exercícios 41 3.11 Prevenindo problemas de segurança 40 3.12 Exercícios 41 3.13 Conclusão 42 4 Facilitando o acesso ao banco com o Entity Framework 4.1 O que é uma ferramenta de mapeamento objeto-relacional? 43 4.2 Entity Framework Core 44 4.3 Lendo e gravando informações através do DbSet 47 4.4 Exercícios 54 4.5 Buscando posts de uma categoria 48 4.6 Exercícios 54 4.7 Finalizando o cadastro de um post 51 4.8 Exercícios 54 4.9 Conclusão 55 5 Evoluindo o modelo com Code First Migrations 5.1 Gerenciando as mudanças no banco de dados 56 5.2 Evoluindo o modelo 59 5.3 Exercícios 63 5.4 Modificando a aplicação para refletir as mudanças no modelo 61 5.5 Exercícios 63 5.6 Saiba mais: aplicando as migrations fora do ambiente local 64 5.7 Conclusão 65 6 Validação 6.1 Mantendo os valores preenchidos em caso de erro de validação 71 6.2 Exercícios 77 6.3 Validando o formulário no navegador 74 6.4 Exercícios 77 6.5 Conclusão 78 7 Facilidades do ASP.NET MVC para melhorar e tornar mais seguro o código das views 7.1 Alterando a view de listagem para utilizar os Html helpers 81 7.2 Exercícios 93 7.3 Alterando os formulários de inclusão e alteração 83 CaelumSumário 100 124 131 7.4 Mas e como ficam os outros integrantes do seu time? 86 7.5 Exercícios 93 7.6 Criando um formulário único para o cadastro de posts 91 7.7 Exercícios 93 7.8 Para saber mais: Usando Ajax para criar uma funcionalidade de autocomplete 94 7.9 Exercícios opcionais 97 7.10 Conclusão 99 8 Identidade única na aplicação com Bootstrap e Layout Pages 8.1 Definindo a identidade da aplicação com o Razor 100 8.2 Exercícios 119 8.3 Aplicando um tema ao blog 108 8.4 Exercícios 119 8.5 Para saber mais: Implementando o formulário de busca 115 8.6 Exercícios opcionais 116 8.7 Separando o blog em áreas Pública e Administrativa 116 8.8 Exercícios 119 8.9 Conclusão 123 9 Implantando melhores práticas no projeto usando padrões e frameworks 9.1 Melhorando o gerenciamento da conexão com injeção de dependências 124 9.2 Exercícios 130 9.3 Facilitando a Injeção de Dependências com o ASP.NET Core 127 9.4 Exercícios 130 10 Filtros para Autenticação de Usuários 10.1 Autenticando usuários com a session 131 10.2 Exercícios 147 10.3 Criando o Usuário do blog 134 10.4 Buscando usuário no banco 135 10.5 A comunicação sem estado do HTTP 137 10.6 Aplicando autenticação com Cookies e Sessões 137 10.7 Exercícios 147 10.8 Autorização com filtros 140 10.9 Exercícios 147 10.10 Autor no post 143 10.11 Exercícios 147 10.12 Concluindo o cadastro de usuários 145 10.13 Exercícios 147 SumárioCaelum 150 165 182 187 194 201 10.14 Cadastro de posts com autor 146 10.15 Exercícios 147 10.16 Para saber mais: Extension method para trabalhar com a ISession 147 11 Construindo uma web API com ASP.NET Core 11.1 Integração com Web Services 150 11.2 Construindo uma Web API com ASP.NET Core 152 11.3 Exercícios 164 11.4 Criando cadastro de um post pela API 158 11.5 Exercícios 164 11.6 Finalizando o CRUD com a atualização e a remoção 162 11.7 Exercícios 164 12 Apêndice - Começando com testes de unidade 12.1 Menu lateral do blog 165 12.2 Exercícios 180 12.3 Testando a sua aplicação 173 12.4 Exercícios 180 12.5 O que aconteceu? 173 12.6 xUnit.net 177 12.7 Exercícios 180 13 Apêndice - Testando o que realmente é necessário 13.1 Exercícios 186 14 Apêndice - Praticando Test-Driven Development (TDD) 14.1 Desafio matemático 187 14.2 Implementando a sequência estranha 188 14.3 Exercícios 192 15 Apêndice - Requisições elegantes com AJAX 15.1 A funcionalidade de publicação de posts 194 15.2 Declarando o bloco de código javascript 195 15.3 Ajax com jQuery 195 15.4 Alterando o conteúdo da tela com javascript 196 15.5 Executando uma função com a resposta do servidor 197 15.6 Gerando a url com o UrlHelper 198 15.7 Resposta vazia no controller 198 15.8 Exercícios 199 16 Apêndice - Publicando a aplicação nas nuvens com o App Harbor CaelumSumário 207 217 16.1 Primeiros passos 201 16.2 SQL Server no App Harbor 202 16.3 Publicando a aplicação no App Harbor 203 17 Apêndice - Implementando segurança no blog com o Identity 17.1 Aplicando autenticação e autorização com o Identity 207 17.2 Exercícios 216 17.3 Proibindo acesso anônimo à área administrativa 213 17.4 Exercícios 216 17.5 Cadastro de posts com autor 215 17.6 Exercícios 216 17.7 Conclusão 216 18 Apêndice - Tópicos Adicionais 18.1 Facilitando o uso da View Model com o AutoMapper 217 18.2 Exercício 220 18.3 Classificando posts com tags 221 18.4 Exercícios 225 18.5 Incluindo tags no cadastro de posts 224 18.6 Transformando as tags com o AutoMapper 224 18.7 Exercícios 225 Versão: 22.7.28 SumárioCaelum CAPÍTULO 1 Ao final deste capítulo você vai: conhecer as características do modelo Requisição-Resposta do protocolo HTTP conhecer a estrutura de um projeto ASP.NET Core MVC entender o sistema de roteamento de requisições do ASP.NET Core MVC diferenciar os meios de enviar informações do controlador para a view descobrir como enviar parâmetros HTTP para o servidor aprender sobre o processo de Model Binding do ASP.NET Core MVC distinguir os métodos de requisição HTTP GET e POST Existem muitos programadores que desenvolvem aplicativos web para o sistema operacional Windows. Muitos deles já desenvolveram utilizando o ASP.NET WebForms ou então o ASP.NET MVC, todos do que hoje conhecemos como o .NET Framework. 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, como, por exemplo, imprimir uma mensagem na tela quando houvesse um clique no botão. Como muita gente na época vinha desse tipo de programação de aplicativos Desktop, aprender WebForms era relativamente fácil. Sem contar a 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. Contudo, códigos e aplicações feitos com WebForms ficaram difíceis de manter. O motivo? Apesar de ser altamente produtivo, não existe separação entre 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 "obriga" o desenvolvedor a usar boas práticas de programação. Percebendo isso, a Microsoft resolveu criar oASP.NET MVC! O MVC é um padrão de desenvolvimento muito utilizado no mundo web e conhecido por "forçar" o programador a separar as responsabilidades. Ou seja, código de interface fica separado do código de regra de negócio, que fica separado do código de banco de dados e assim por diante. Isso facilitava a manutenção do projeto e seu INTRODUÇÃO AO ASP.NET CORE . 1 INTRODUÇÃO AO ASP.NET CORE 1 ciclo de vida fica maior! Além do MVC, dentro da plataforma do .NET Framework tínhamos disponíveis outras bibliotecas que aceleravam o processo de desenvolvimento da aplicação. Por exemplo, o Entity Framework para bancos de dados e o Identity para autenticação e autorização. No entanto, o .NET tinha a restrição de obrigar quem programava a ter o sistema operacional Windows. Até que em 2014, com a entrada do seu novo CEO Satya Nadella, a Microsoft passou a investir mais em projetos open source. Inclusive, decidindo abrir o código do próprio .NET. Nesta época, um grupo de engenheiros da Microsoft começou a trabalhar na próxima versão do ASP.NET. A ideia era que este projeto estaria alinhado desde o começo com a nova diretriz da empresa de ser open source. Esta nova versão veio a ser o que conhecemos hoje como o .NET Core, que além de ter o código aberto também é multiplataforma. Esta nova plataforma é um redesign da versão clássica do .NET Framework, mas arquitetada para ser mais modular. Esse curso abordará o framework ASP.NET Core MVC e seus recursos. Como aplicação, será implementado o modelo de um blog cujo tema são críticas de filmes, livros e álbuns de música. Para ver o resultado final do blog que será implementado, acesse o endereço: http://blog1.apphb.com Antes de abordar os detalhes da tecnologia ASP.NET Core MVC, é preciso conhecer mais a fundo o modelo de comunicação Web. 1.1 UM BREVE RESUMO SOBRE O MODELO DE COMUNICAÇÃO NA WEB . 2 1.1 UM BREVE RESUMO SOBRE O MODELO DE COMUNICAÇÃO NA WEB http://blog1.apphb.com Quando alguma página na internet é acessada, utiliza-se um navegador como Google Chrome, Mozilla Firefox ou o Microsoft Edge. Os dados são enviados através de uma rede gigante chamada Internet. O navegador representa nessa comunicação o cliente e a página requisitada é fornecida pelo servidor. O cliente pede informações e o servidor responde. Esse modelo de comunicação, ou essa arquitetura, é chamado de Cliente-Servidor, em inglês Client-Server. Em qualquer comunicação tem que existir algumas regras e vocabulário. Por exemplo, o que acontece quando acessamos alguma página que não existe em determinado site? O servidor não saberá a resposta e precisará indicar para o navegador que não conseguiu carregar os dados. Esse comportamento deve estar definido em algum lugar. É por isso que foi criado um idioma próprio, ou melhor, um protocolo próprio que possui um vocabulário e gramática específica. Este protocolo é quem define as regras de comunicação entre cliente e servidor! O principal protocolo usado na internet é o HTTP. Em resumo, HTTP é um protocolo que define as regras de comunicação entre cliente (em geral o navegador) e servidor na internet. Todo desenvolvedor de aplicações Web deve conhecer como funciona esse protocolo. No mundo HTTP, a mensagem enviada pelo navegador para o servidor é chamada de HTTP REQUEST. Essa mensagem é empacotada e enviada pela rede como texto. Esse pacote, chamado HTTP REQUEST, é dividido em duas partes, cabeçalho e corpo. O principal elemento contido no cabeçalho deste pacote é o endereço para onde a requisição deverá ser enviada. Você já conhece esse endereço. É o mesmo que utiliza para acessar sites como o site da Caelum ( http://www.caelum.com.br ) ou do Alura ( http://www.alura.com.br ). Esse endereço identifica unicamente o servidor que irá tratar aquela requisição. Repare o endereço do site da Caelum. Veja que depois do nome do protocolo vem :// seguido pelo nome do site www.caelum.com.br . No vocabulário de um desenvolvedor o www.caelum.com.br é o domínio (ou domain). Quando usamos o endereço http://www.alura.com.br , abrimos uma conexão com o servidor que roda em algum lugar na internet. Um servidor é como uma casa: para entrar nela existem várias portas disponíveis. Qual porta é utilizada para o protocolo HTTP? A porta reservada para o protocolo HTTP possui código padrão 80 , que o próprio navegador se encarrega de colocar. O protocolo HTTP HTTP REQUEST URLs - Endereços da Web . 1.1 UM BREVE RESUMO SOBRE O MODELO DE COMUNICAÇÃO NA WEB 3 Observe o seguinte endereço: http://www.caelum.com.br/cursos-dotnet . Além do protocolo, do domínio e da porta (omitida, porque o navegador utilizará a 80 , padrão para o HTTP), ainda existe outra informação. O cursos-dotnet é um recurso (resource) do site que gostaríamos de acessar, no caso a página de cursos .NET. Além dele, existem vários outros recursos na Caelum, como a página de contatos ( /contato ), ou o calendário de turmas ( /calendario ). O importante é que cada recurso possua o seu nome único. Navegando em outros sites, percebemos que entre o domínio e o recurso podem vir outras informações. Ou seja, para acessarmos um recurso, usamos um caminho intermediário. Há vários exemplos no site da Alura ( http://www.alura.com.br ) que usam caminhos para chegar ao recurso concreto, como por exemplo /discussion/mine ou /user/45 e navegando na Alura você pode encontrar outros. Uma tarefa fundamental no cotidiano do desenvolvedor Web é definir esses nomes e o que deveria acontecer quando o usuário for acessá-los. Neste curso, vamos executar muito essa tarefa quando estivermos implementando o blog. Em resumo, o endereço que aparece no cabeçalho de uma HTTP REQUEST é formado pelas partes protocolo://dominio:porta/caminho/recurso, e a esse endereço damos o nome de URL, ou Uniform Resource Locator. O tratamento da requisição pelo servidor é chamado de HTTP RESPONSE. Essa resposta também é um pacote e é enviado no sentido servidor -> cliente. Assim como a HTTP REQUEST, ela também possui cabeçalho e corpo. Quando a resposta chegar no destino, ela será lida e interpretada. Em grande parte destas respostas, seu corpo é formado por texto em linguagem HTML. CURSO DE HTTP NA ALURA Para aprofundar seu estudo sobre o protocolo HTTP, faça o curso de HTTP na Alura! https://cursos.alura.com.br/course/fundamentos-http Uma aplicação MVC faz uso do modelo Requisição-Resposta do protocolo HTTP para tratar suas lógicas de negócio. Para aprofundar nessa ideia, vamos criar um projeto ASP.NET MVC no Visual Studio e estudar seus detalhes. HTTP RESPONSE 1.2 CRIANDO UM PROJETO ASP.NET CORE MVC COM O VISUAL STUDIO (WINDOWS OU MACOS) . 4 1.2 CRIANDO UM PROJETO ASP.NET CORE MVC COM O VISUAL STUDIO (WINDOWS OU MACOS) https://cursos.alura.com.br/course/fundamentos-http Caso você esteja trabalhando com o sistema operacional Windows ou MacOS, é comum usar o editor do Visual Studio para desenvolver sua aplicação .NET Core. Quando for criar um novo projeto neste editor, basta escolher a opção ASP.NET Core Web Application , utilizando a linguagem Visual C# . A nossa aplicação se chamará Blog : Para que o projeto seja um MVC, na próxima janela precisamos escolher Web Application (Model-View-Controller) : . 1.2 CRIANDO UM PROJETO ASP.NET CORE MVC COM O VISUAL STUDIO (WINDOWS OU MACOS) 5 Com isso criamos a nossa primeira aplicação ASP.NET Core MVC com o Visual Studio. Se você estiver trabalhando com outros sistemas operacionais de distribuição Linux, como o Ubuntu, por enquanto ainda não temos o Visual Studio disponível. Nestes casos, é comum usarmos como editor o Visual Studio Code ( https://code.visualstudio.com/ ). No entanto, o VS Code é apenas um editor,você ainda precisará instalar na sua máquina a SDK do .NET Core, que se encontra neste link https://www.microsoft.com/net/learn/get- started/linux . Agora para criar o Blog, primeiro precisamos de uma pasta onde ficará toda esta estrutura. Você pode abrir o gerenciador de arquivos e criar uma pasta Caelum em algum lugar do seu computador de sua preferência. Esta pasta servirá como a solução/Solution dos projetos. Só que para tornar esta pasta Caelum em uma solução e criar nosso projeto de Blog dentro dela, precisamos trabalhar com o .NET Core command-line interface (CLI). Abra o seu terminal e navegue até a pasta Caelum . Dentro dela, execute o comando: dotnet new sln Com a solução criada, vamos gerar um projeto no padrão MVC usando a linguagem C#. Para isso, ainda dentro da pasta Caelum faça o comando: dotnet new mvc --name Blog -lang C# Note que foi criada uma pasta Blog com a estrutura do MVC. Mas ela ainda não faz parte da solução de fato, você ainda precisará rodar o comando: dotnet sln Caelum.sln add Blog\Blog.csproj Pronto, como toda a estrutura foi criada podemos dar uma olhada no projeto MVC gerado pelo .NET Core CLI. COMANDOS DO .NET CORE CLI Para ver todos os comandos que conseguimos efetuar com o .NET Core CLI, você pode acessar sua documentação em https://docs.microsoft.com/en-us/dotnet/core/tools 1.3 CRIANDO UM PROJETO ASP.NET CORE MVC EM OUTROS AMBIENTES 1.4 A ESTRUTURA DE UMA APLICAÇÃO ASP.NET CORE MVC . 6 1.3 CRIANDO UM PROJETO ASP.NET CORE MVC EM OUTROS AMBIENTES https://docs.microsoft.com/en-us/dotnet/core/tools Projetos ASP.NET Core MVC possuem uma estrutura de pastas típica: Dentre essas pastas temos: Controllers - é nela que são guardadas as classes responsáveis por tratar as requisições HTTP que vem do navegador, transformando-as em lógicas referentes a alguma funcionalidade específica da aplicação. Views - pasta onde são colocados os arquivos que serão utilizados para renderizar a resposta HTTP para o usuário. Será necessário algum conhecimento de HTML para escrever esses arquivos. Models - pasta onde ficam as classes que representarão o modelo da aplicação, ou seja, as classes que representam o negócio e suas regras. Além dessas pastas, o projeto web possui outras pastas e arquivos de configurações, com alguns deles escritos no formato JSON e outras feitas em classes. Como pode-se observar, além da linguagem C#, o desenvolvedor Web precisará ter conhecimentos de HTML para entregar seus produtos na Web. Vamos desenvolver a página inicial da aplicação que, quando acessada no navegador, mostrará uma lista com os posts do blog. Uma página inicial é acessada quando algum usuário digita o endereço do domínio da aplicação na barra de endereços do navegador, sem que haja necessidade de colocar outros recursos no endereço. Por padrão, o ASP.NET Core MVC encaminha a requisição de página inicial para uma instância da 1.5 PÁGINA INICIAL E CONVENÇÕES DO ASP.NET MVC . 1.5 PÁGINA INICIAL E CONVENÇÕES DO ASP.NET MVC 7 classe chamada HomeController . Todas as requisições de uma aplicação ASP.NET Core MVC devem ser tratadas em classes que estendem uma classe especial do framework chamada Controller . Para organizar melhor as classes dentro do projeto, existe uma pasta denominada Controllers , onde ficará a classe HomeController . Todo método público de um controller é chamado action pelo ASP.NET Core MVC. As actions são responsáveis por tratar uma determinada requisição Web, e devem retornar uma instância de IActionResult . Para atender a requisição de página inicial, o ASP.NET Core MVC cria uma instância de HomeController, e executa o método chamado Index . using Microsoft.AspNetCore.Mvc; namespace Blog.Controllers { public class HomeController : Controller { //GET: Home public IActionResult Index() { return View(); } } } A INTERFACE IACTIONRESULT A interface IActionResult é a interface base para todos os resultados gerados pelas actions. O resultado mais comum é o gerado através do método View() , que cria uma instância da classe ViewResult , responsável por representar uma página web a ser renderizada pelo servidor. Ela é filha da classe ActionResult que implementa a IActionResult . Existem diversas outras classes que implementam a IActionResult, cada uma com um propósito específico. Por exemplo RedirectResult, RedirectToActionResult, RedirectToPageResult, etc. Mais detalhes sobre a interface IActionResult no MSDN: https://docs.microsoft.com/pt- br/dotnet/api/microsoft.aspnetcore.mvc.iactionresult Por enquanto essa action não precisa implementar nenhuma regra de negócio. Seu objetivo será apenas mostrar o HTML devolvido para o navegador como resposta da requisição. Esse HTML fica armazenado em um arquivo dentro da pasta Views e representa a resposta da requisição enviada pelo navegador. Para executar o código da view, a action deve devolver o resultado de um método herdado da classe Controller chamado View . Isto significa dizer que será executada a camada de visualização da aplicação. O componente responsável pela camada de visualização no ASP.NET Core MVC é a View Engine , e a . 8 1.5 PÁGINA INICIAL E CONVENÇÕES DO ASP.NET MVC https://docs.microsoft.com/pt-br/dotnet/api/microsoft.aspnetcore.mvc.iactionresult implementação utilizada é o Razor. O arquivo com o HTML que implementa a visualização de uma determinada action precisa cumprir a seguinte convenção do ASP.NET Core MVC: precisa ser colocado dentro da pasta Views deve ficar em uma pasta com o mesmo nome do controller. No caso, as views do HomeController ficam dentro da pasta Views/Home quando o método View é chamado sem argumentos, o arquivo deve ter o mesmo nome da action com a extensão cshtml (para o Razor). Então, no código da action Index do HomeController , o método View sem argumentos criará uma instância de ActionResult a partir do arquivo Views/Home/Index.cshtml . Por enquanto, vamos apenas fazer nossa versão de Olá mundo em nosso arquivo Index.cshtml : <html> <head></head> <body> <h2>Olá mundo com ASP.NET MVC</h2> </body> </html> Por enquanto, o arquivo possui apenas HTML . Se você estiver usando o Visual Studio, para rodar a aplicação basta apertar o botão F5 . Caso esteja trabalhando com o .NET Core CLI, o comando de terminal é: dotnet run Por padrão, o servidor subirá no endereço http://localhost:5000 . Como acessar, a partir do navegador, o código que acabou de ser criado? Como vimos, uma requisição do navegador é disparada para uma URL. Depois que o servidor é encontrado, a aplicação é chamada para descobrir qual componente responderá pela parte caminho/recurso da URL. O framework ASP.NET MVC, no qual nossa aplicação se baseia, possui um módulo responsável por mapear cada requisição em uma action disponível na aplicação. Este módulo é o sistema de roteamento do ASP.NET Core MVC. Quando uma aplicação ASP.NET Core MVC é criada, ela já está configurada para usar o roteamento configurado. Esta configuração é feita na classe presente no arquivo Startup.cs , no método 1.6 RODANDO A APLICAÇÃO 1.7 O SISTEMA DE ROTEAMENTO DO ASP.NET CORE MVC . 1.6 RODANDO A APLICAÇÃO 9 Configure : public class Startup { //... public void Configure(IApplicationBuilder app, IHostingEnvironment env) { //... app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); } ); } } O método MapRoute adiciona uma única rota chamada default à tabela de rotas. A rota Default mapeia o primeiro segmento de uma URL para o nome de um controlador, o segundo argumento de uma URL para uma actiondo controlador, e o terceiro argumento para um parâmetro chamado id. Imagine que a seguinte URL foi informada na barra de endereços do navegador: http://<dominio:porta>/Home/Index/3 A rota Default mapeia o recurso Home/Index/3 para os seguintes componentes: controller = Home action = Index id = 3 Por exemplo, quando uma requisição com a URL http://<dominio:porta>/Home/Index/3 é feita, o seguinte código é executado: HomeController.Index(3) Além disso, a rota Default inclui valores padrão para os três segmentos (controller, action e id). Se um controller não for especificado na URL, então o parâmetro será o do valor padrão Home . Se uma action não for especificada, então o parâmetro da action será o do valor padrão Index . Da mesma forma, se um id não for especificado, o parâmetro id será o de uma string vazia. Ou seja, os três endereços abaixo serão mapeados para o mesmo código HomeController.Index() : http://<dominio:porta> http://<dominio:porta>/Home http://<dominio:porta>/Home/Index . 10 1.7 O SISTEMA DE ROTEAMENTO DO ASP.NET CORE MVC Recapitulando, o modelo Requisição-Resposta do protocolo HTTP é tratado por uma aplicação ASP.NET Core MVC através do seguinte fluxo: 1. O cliente envia um pacote HTTP REQUEST para a web 2. Servidor e aplicação são encontrados através da primeira parte da URL da requisição, composta pelo protocolo e domínio 3. A segunda parte da URL, composta pelo recurso e opcionalmente pelo caminho, é tratada pelo sistema de roteamento, que é responsável por encontrar uma action disponível em um controller 4. Após ser encontrado, uma instância do controller é criada e, em seguida, o método correspondente à action é executado 5. A action retorna uma instância de IActionResult, que será renderizada pelo motor de visualização e enviada como um pacote HTTP RESPONSE de volta para o cliente 1. A primeira coisa que faremos é escolher uma pasta onde ficarão todos os nossos projetos. Abra o gerenciador de arquivos do seu computador e crie uma pasta chamada Caelum em algum lugar de preferência na sua máquina. 2. Agora precisamos tornar esta pasta uma solução. Para isso, abra o terminal do computador e através do comando cd navegue pelas pastas até entrar no diretório Caelum que você criou. Dentro dele, execute o comando: dotnet new sln 3. Ainda dentro desta pasta Caelum , precisamos criar um novo projeto do tipo ASP.NET Core MVC chamado Blog utilizando a linguagem Visual C# . E depois adicioná-lo na solução Caelum . Para isso execute os comandos: dotnet new mvc --name Blog -lang C# dotnet sln Caelum.sln add Blog\Blog.csproj 4. Se notar, dentro do projeto já foram criadas algumas coisas por conta dos seus templates. Como queremos começar nosso projeto do zero, vamos apagar estas coisas que o editor criou automaticamente para a gente. Mas não se preocupe, vamos refazer muitas dessas coisas mais para frente no curso quando elas fizerem sentido no projeto. Então apague todos os arquivos e diretórios dentro das pastas Controllers , Models e Views , mas não apague estas 3 pastas em si. Precisaremos delas para o nosso sistema MVC. 5. Agora que o projeto está limpo, vamos adicionar o controller inicial da aplicação. Dentro da pasta Controllers do projeto, crie um arquivo HomeController.cs . Esta classe deverá herdar de Controller e ter método de action Index , que servirá para mostrar a página inicial da aplicação. using Microsoft.AspNetCore.Mvc; namespace Blog.Controllers { 1.8 EXERCÍCIOS . 1.7 O SISTEMA DE ROTEAMENTO DO ASP.NET CORE MVC 11 public class HomeController : Controller { public IActionResult Index() { return View(); } } } 6. Para completarmos a página inicial, dentro da pasta Views crie uma pasta chamada Home . Em seguida, crie um novo arquivo chamado Index.cshtml dentro da pasta Views/Home/ . Neste arquivo Index.cshtml , coloque o código HTML: <html> <head></head> <body> <h2>Olá mundo com ASP.NET Core MVC</h2> </body> </html> 7. Agora vamos testar a aplicação. No terminal, para subir o servidor basta entrar no diretório do projeto e dar o comando run : cd Blog dotnet run 8. Abra o navegador e acesse a página http://localhost:5000/Home/Index . Verifique que ela possui o texto de boas vindas. 9. Ainda no browser, retire do final do texto o /Index da URL e aperte Enter. Verifique que a mesma página será exibida. 10. Retire do fim do texto /Home da URL e aperte Enter. 11. Para interromper o servidor, no terminal aperte o comando Ctrl+C . Agora você já está pronto para listar os posts na próxima seção! Agora que já temos uma home page do sistema, vamos começar a trabalhar com todas as telas que vão gerenciar os posts do blog. Como ainda não conseguimos saber quais posts foram cadastrados, vamos começar criando uma tela em que veremos artigos escritos no site. Para isso, podemos criar uma tabela HTML com um post por linha. O exemplo abaixo mostra o HTML de uma tabela com quatro posts: <table> <thead> <tr> <th>Título</th> <th>Resumo</th> <th>Categoria</th> </tr> </thead> <tbody> 1.9 CRIANDO TELA DE LISTAGEM DE POSTS . 12 1.9 CRIANDO TELA DE LISTAGEM DE POSTS <tr> <td>Harry Potter 1</td> <td>Pedra Filosofal</td> <td>Filme, Livro</td> </tr> <tr> <td>Cassino Royale</td> <td>007</td> <td>Filme</td> </tr> <tr> <td>Monge e o Executivo</td> <td>Romance sobre Liderança</td> <td>Livro</td> </tr> <tr> <td>New York, New York</td> <td>Sucesso de Frank Sinatra</td> <td>Música</td> </tr> </tbody> </table> Quando a aplicação executar este HTML, o visitante veria uma tabela com 4 posts relacionados a filmes, livros e músicas. Contudo, o propósito de um blog é ser algo dinâmico, vivo, com atualizações constantes, e do jeito que foi implementado, ele não vai atender esse objetivo. Sempre que a página for exibida, serão mostrados os mesmos quatro posts. Extremamente chato! O blog dificilmente receberá novas visitas seguindo esse comportamento. Para que a lista de posts seja dinâmica, a tabela HTML não deve ter linhas com valores fixos. As linhas devem ser geradas conforme a quantidade de posts do blog. Como gerar automaticamente as linhas sem conhecimento prévio do total? Neste caso, é necessário gerar uma linha ( tr ) para cada post do blog. Para implementar esse comportamento precisamos fazer um loop onde, a cada rodada, será criada a linha desejada. Esse loop deverá ser executado no servidor antes da resposta ser enviada para o cliente. Mas como diferenciar, no próprio arquivo cshtml , o código que é executado no servidor (no caso, escrito em C#) do código que vai para o cliente num pacote HTTP RESPONSE (no caso, HTML)? A maneira que o Razor tem para diferenciar um código HTML de um código C# é através do símbolo arroba ( @ ). Neste caso o código que estiver após o @ será executado no servidor. Uma alternativa de código que podemos usar para executar o loop é o foreach . Coloca-se o foreach logo após o @ . Assim: 1.10 GERAÇÃO DINÂMICA DA TABELA A PARTIR DOS POSTS DO BLOG . 1.10 GERAÇÃO DINÂMICA DA TABELA A PARTIR DOS POSTS DO BLOG 13 @foreach (var p in ??????) { <tr> <td> ??? </td> <td> ??? </td> <td> ??? </td> </tr> } De qual lista serão extraídos os posts para alimentar o foreach? Anteriormente aprendemos que, no fluxo de tratamento de uma requisição pelo ASP.NET Core MVC, o códigocontido numa action de um controller é executado antes do processo de renderização da view. Então vamos criar um novo PostController em que faremos estas lógicas de gerenciamento do post. Neste controller podemos adicionar uma action de Index que será responsável por fornecer a lista para nossa view que ficará em . namespace Blog.Controllers { public class PostController : Controller { public IActionResult Index() { var lista = ??????; return View(); } } } O problema é que o escopo da variável lista existe apenas dentro da action. Para que o Razor consiga enxergar essa variável, ela precisa existir e estar visível no escopo de execução da renderização da view. Para isso, faremos uso de uma propriedade das classes Controller e ViewResult . Já vimos que o método View cria uma instância de ViewResult, classe filha de ActionResult, que será usada para o processo de geração do HTML. A classe ViewResult possui algumas propriedades cujo objetivo é transportar informação entre o controlador e a view. Uma delas é a ViewBag . Ela será utilizada para transportar a variável lista do controlador até a view. Então o código do controlador é alterado para: public IActionResult Index() { ViewBag.Posts = ??????; return View(); } . 14 1.10 GERAÇÃO DINÂMICA DA TABELA A PARTIR DOS POSTS DO BLOG COMO ASSIM Peraí, existe uma propriedade chamada Posts dentro de ViewBag? A resposta é não e sim. Em tempo de compilação, ou seja, no momento em que a propriedade ViewBag foi compilada, ela não tinha uma declaração para Posts. Contudo, no momento da execução da linha ViewBag.Posts , ela assume a propriedade como sua. Esse comportamento dinâmico é possível porque a propriedade ViewBag é um objeto dinâmico, um recurso da linguagem .NET para permitir que o desenvolvedor crie objetos para representar estruturas dinâmicas, como por exemplo a hierarquia de objetos (DOM) em uma página HTML. Como criamos o nosso PostController e definimos a action com a lista, agora podemos criar nosso cshtml com o foreach no arquivo Views/Post/Index.cshtml . E para chamar a nossa ViewBag para recuperar a lista de posts basta escrever: @foreach (var p in ViewBag.Posts) { <tr> <td> ??? </td> <td> ??? </td> <td> ??? </td> </tr> } Agora que a action Index fornece a lista de posts em ViewBag.Posts, precisamos entender como essa lista será populada. public IActionResult Index() { ViewBag.Posts = ??????; return View(); } Essa lista será populada a partir dos posts do blog. Porém, ainda não existe em nossa aplicação o conceito de post. Será necessário criar uma classe que representará esse conceito. Essa classe fará parte do nosso modelo. Sendo assim será criada dentro da pasta Models . Inicialmente essa classe terá as propriedades Titulo , Resumo e Categoria . namespace Blog.Models { public class Post { public string Titulo { get; set; } public string Resumo { get; set; } public string Categoria { get; set; } } } E assim a lista poderá ser criada a partir de uma quantidade arbitrária de posts. O código final do . 1.10 GERAÇÃO DINÂMICA DA TABELA A PARTIR DOS POSTS DO BLOG 15 controller fica assim: namespace Blog.Controllers { public class PostController : Controller { public IActionResult Index() { var listaDePosts = new List<Post>() { new Post() { Titulo = "Harry Potter 1", Resumo = "Pedra Filosofal", Categoria = "Film e, Livro" }, new Post() { Titulo = "Cassino Royale", Resumo = "007", Categoria = "Filme" }, new Post() { Titulo = "Monge e o Executivo", Resumo = "Romance sobre Liderança", Cate goria = "Livro" }, new Post() { Titulo = "New York, New York", Resumo = "Sucesso de Frank Sinatra", Cate goria = "Música" } }; ViewBag.Posts = listaDePosts; return View(); } } } Falta ainda completar a view para mostrar as propriedades dos posts: Título, Resumo e Categoria. O foreach define uma variável para guardar o objeto que representa o item da lista na rodada atual. Usaremos essa variável para mostrar as informações de cada post. Como é um código que deve ser avaliado no servidor, o prefixamos com o símbolo arroba ( @ ). A tabela da view deve ficar parecida com a seguinte: <table> <thead> <tr> <th>Título</th> <th>Resumo</th> <th>Categoria</th> </tr> </thead> <tbody> @foreach (var p in ViewBag.Posts) { <tr> <td>@p.Titulo</td> <td>@p.Resumo</td> <td>@p.Categoria</td> </tr> } </tbody> </table> 1. Crie uma nova classe Post na pasta Models para representar um post. Assim, seu namespace será Blog.Models . 2. Dentro da classe, crie as propriedades de um post, Titulo , Resumo e Categoria , todas do tipo 1.11 EXERCÍCIOS . 16 1.10 GERAÇÃO DINÂMICA DA TABELA A PARTIR DOS POSTS DO BLOG string. 3. Crie um novo controller chamado PostController . Em sua action Index , gere uma lista de posts e a transporte para a view através da propriedade ViewBag . 4. Crie a view Views/Post/Index.cshtml que representará a tabela de posts enviados pelo controller. 5. Na sua View , use um foreach na lista de ViewBag.Posts para criar uma tabela de posts. Na seção anterior, utilizamos a propriedade ViewBag para transportar a lista de posts do controller para a view. Existem outras maneiras de fazer isso, e uma delas é injetando a variável lista diretamente no método View() da action Index . public ActionResult Index() { //código anterior... return View(listaDePosts); } Veja o nome do argumento na assinatura desta sobrecarga do método View na classe Controller : public virtual ViewResult View(object model); Esta sobrecarga do método View cria um objeto do tipo ViewResult usando um modelo que será renderizado na resposta representada pela view. No nosso caso, o modelo na qual a view Views/Home/Index.cshtml se baseia é uma lista de posts. A vantagem de usar essa alternativa em relação à ViewBag é que temos o poder do IntelliSense a nossa disposição ao gerarmos o HTML. Para isso, será necessário declarar qual tipo do modelo será utilizado na view, através da diretiva @model (em letras minúsculas). 1.12 OUTRA MANEIRA DE ENVIAR INFORMAÇÕES PARA A VIEW . 1.12 OUTRA MANEIRA DE ENVIAR INFORMAÇÕES PARA A VIEW 17 No nosso exemplo (Index.cshtml), vamos declarar que a view usa uma lista de posts: @model IList<Blog.Models.Post> E o foreach precisará ser alterado para substituir a chamada à ViewBag pela chamada do modelo. Para fazer isso escrevemos: @foreach (var p in Model) { <tr> <td>@p.Titulo</td> <td>@p.Resumo</td> <td>@p.Categoria</td> </tr> } A propriedade Model representa uma instância do tipo definido na diretiva @model e conterá os valores enviados através do método View . Quando escrevemos o ponto depois da variável p , o recurso de IntelliSense do Visual Studio nos ajuda a preencher as propriedades corretamente. Quando uma view usa essa abordagem, isto é, a de declarar qual o tipo de modelo será usado para renderizar as informações, dizemos que estamos usando views fortemente tipadas. Essa é a abordagem recomendada nas aplicações ASP.NET Core MVC. Existem outras vantagens de utilizar as views fortemente tipadas. Elas serão vistas mais à frente no curso, quando formos simplificar o código das views. Concluindo, para gerar a resposta de uma requisição HTTP, o ASP.NET Core MVC usa uma página modelo, chamada view, composta de tags HTML e regiões C# executáveis (definidas por @ ). Os dados usados nestas regiões são transportadospara a view através de duas alternativas: ViewBag ou view fortemente tipada. Podemos resumir a ideia do parágrafo anterior na seguinte fórmula: TEMPLATE + DADOS = RESPOSTA HTTP 1. Na action Index do PostController , remova a chamada para ViewBag e coloque listaDePosts como argumento do método View . 2. Inclua na primeira linha da view Views/Post/Index.cshtml o código @model IList<Blog.Models.Post> , para indicar que esta view é fortemente tipada. 3. Ainda na View, troque o ViewBag.Posts por Model no foreach . 4. Teste a aplicação. Tudo deverá estar funcionando normalmente. 1.13 EXERCÍCIOS . 18 1.12 OUTRA MANEIRA DE ENVIAR INFORMAÇÕES PARA A VIEW Apesar de montarmos dinamicamente a view com o loop foreach , o blog ainda está muito paradão. A aplicação precisa fornecer uma maneira dos autores do blog cadastrarem seus textos diretamente no site. Com esse objetivo em vista, essa seção irá implementar a funcionalidade de inclusão de um post. Para realizar o cadastro, é necessário um formulário que capture os dados do novo post. O código do formulário HTML está listado abaixo: <form action="/Post/Adiciona"> <label> Título: <input name="titulo" /> </label> <label> Resumo: <input name="resumo" /> </label> <label> Categoria: <input name="categoria"/> </label> <button type="submit">Cadastrar</button> </form> Esse formulário faz parte da camada de visualização do ASP.NET Core MVC. Para chegar na camada de visualização, precisamos de uma action. Logo, é preciso criar um método no controller que redireciona para o formulário. Para isso será adicionado um método chamado Novo dentro do PostController . public IActionResult Novo() { return View(); } Desta maneira é criada uma rota que atende a requisição cujo recurso da URL é /Post/Novo . Quando esse endereço for disparado no navegador, o formulário será exibido como resposta. Depois que o usuário preencher os dados, ele vai submeter o form. O que vai acontecer quando o botão Cadastrar for clicado? A tag form possui um atributo chamado action . Esse atributo montará a URL da requisição que submeterá o formulário ao servidor (cujo recurso no caso é /Post/Adiciona ). Portanto, será necessária uma nova action em PostController para atender tal requisição. Essa action será responsável por incluir o post na lista. public IActionResult Adiciona() { return View(); } Mas como obter os valores do post na action? Observe que no formulário HTML existem três tags 1.14 INCLUINDO UM POST ATRAVÉS DE UM FORMULÁRIO HTML . 1.14 INCLUINDO UM POST ATRAVÉS DE UM FORMULÁRIO HTML 19 input com um atributo name , justamente com o nome das propriedades de um post, a saber: titulo, resumo e categoria. A aplicação precisará transportar o valor que foi digitado no campo do formulário para aquele ponto do código. Como isso será feito? Através do próprio HTTP. O protocolo prevê que o valor de campos do tipo input sejam colocados em entradas do tipo chave/valor dentro da requisição. Desta maneira, a requisição que chega no servidor já possui tais parâmetros. A chave de cada parâmetro é determinada pelo atributo name do campo input. Para transportar os parâmetros de uma requisição para determinada action, o ASP.NET Core MVC utiliza um processo chamado Model Binding. Em determinado momento no fluxo de atendimento da requisição, o Model Binding coleta dados da requisição e os injeta diretamente na action como um argumento de entrada no método que representa a action. No exemplo do formulário acima, será necessário alterar a assinatura do método Adiciona para incluir parâmetros de entrada com o mesmo nome que os atributos name do input. public IActionResult Adiciona(string titulo, string resumo, string categoria) { //...resto do código aqui... } Mas a definição de um post possui justamente as propriedades Titulo , Resumo e Categoria . Neste caso, o Model Binding consegue injetar os valores dos parâmetros da requisição diretamente numa instância de Post. Basta que a assinatura da action informe essa necessidade. public IActionResult Adiciona(Post post) { //...resto do código aqui... } Por fim, basta incluirmos o post informado pelo usuário no formulário na lista de posts. Porém a variável lista , que contém os posts, está definida na action Index e não está acessível no método Adiciona. Para resolver essa questão, tornamos a lista um campo privado da classe PostController e a populamos com os 4 posts originais no seu construtor. public class PostController : Controller { private IList<Post> lista; public PostController() { this.lista = new List<Post> () { new Post () { Titulo = "Harry Potter 1", Resumo = "Pedra Filosofal", Categoria = "Fil me, Livro" }, new Post () { Titulo = "Cassino Royale", Resumo = "007", Categoria = "Filme" }, new Post () { Titulo = "Monge e o Executivo", Resumo = "Romance sobre Liderança", Cat egoria = "Livro" }, new Post () { Titulo = "New York, New York", Resumo = "Sucesso de Frank Sinatra", Cat . 20 1.14 INCLUINDO UM POST ATRAVÉS DE UM FORMULÁRIO HTML egoria = "Música" } }; } // resto das actions aqui... } Assim, a lista fica acessível na action Adiciona. public IActionResult Adiciona(Post p) { lista.Add(p); return View(); } A resposta da requisição Post/Adiciona pode ser a própria listagem de posts indicada pelo arquivo Views/Post/Index.cshtml . Já sabemos que quando o método View() é chamado sem argumentos, será procurado um arquivo de mesmo nome que a action. Nossa necessidade agora é diferente: queremos que um arquivo já usado em outra action seja reutilizado numa segunda action. Para essa necessidade, o ASP.NET MVC previu uma sobrecarga do método View() que recebe um argumento de entrada com o nome da view desejada. No nosso caso, Index. Assim modificamos nosso código para usar a sobrecarga deste método: public IActionResult Adiciona(Post p) { lista.Add(p); return View("Index"); } Quando executamos a aplicação e cadastramos um post, acontece um erro do tipo System.NullReferenceException . Isso se deve ao fato de que a propriedade Model, que é referenciada no foreach da view, não está instanciada! Mas é claro, esquecemos de transportar a lista para a view! Vamos fazer isso então: public IActionResult Adiciona(Post p) { lista.Add(p); return View("Index", lista); } 1. Crie uma nova action chamada Novo no PostController . Coloque o código abaixo: public IActionResult Novo() { return View(); } 2. Crie uma view a partir da action Novo e coloque o formulário HTML dentro da tag body <form action="/Post/Adiciona"> <label> Título: <input name="titulo" /> 1.15 EXERCÍCIOS . 1.14 INCLUINDO UM POST ATRAVÉS DE UM FORMULÁRIO HTML 21 </label> <label> Resumo: <input name="resumo" /> </label> <label> Categoria: <input name="categoria"/> </label> <button type="submit">Cadastrar</button> </form> 3. Crie um campo privado na classe PostController para armazenar a lista de posts. Inicialize a variável com o código extraído da action Index no construtor da classe. O código deverá ficar conforme abaixo: public class PostController : Controller { private IList<Post> lista; public PostController() { this.lista = new List<Post> { new Post { Titulo = "Harry Potter 1", Resumo = "Pedra Filosofal", Categoria = "Filme, Livro" }, new Post { Titulo = "Cassino Royale", Resumo = "007",Categoria = "Filme" }, new Post { Titulo = "Monge e o Executivo", Resumo = "Romance sobre Liderança", Catego ria = "Livro" }, new Post { Titulo = "New York, New York", Resumo = "Sucesso de Frank Sinatra", Catego ria = "Música" } }; } } 4. Crie uma nova action chamada Adiciona no PostController . Coloque o código abaixo: public IActionResult Adiciona(Post post) { lista.Add(post); return View("Index", lista); } 5. Teste sua aplicação, inserindo um novo post através da rota /Post/Novo . Se tudo correr bem, a lista de posts aparecerá com o post recém incluído. Mantenha esta página aberta para discutirmos a próxima seção. Repare no endereço inserido no navegador após o clique do botão Cadastrar. Por exemplo, para um post com título "Harry Potter 3", resumo "Prisioneiro de Askaban" e categoria "Filme", a parte final do endereço ficaria mais ou menos assim: /Post/Adiciona?titulo=Harry+Potter+3&resumo=Prisioneiro+de+Askaban&categoria=Filme As informações que foram preenchidas no formulário estão no endereço do navegador. 1.16 DESCOBRINDO OS MÉTODOS DAS REQUISIÇÕES HTTP . 22 1.16 DESCOBRINDO OS MÉTODOS DAS REQUISIÇÕES HTTP Isso aconteceu pois, por padrão, o navegador envia as informações de um formulário utilizando um método de envio chamado GET . Esse método coloca todos os parâmetros da requisição no endereço do navegador. Com isso, qualquer um pode ler o que foi preenchido no formulário. Isso é legal? Pensando bem, em algumas situações, pode não ser interessante mostrar essas informações na URL do navegador. Por exemplo, quando fazemos um login em alguma aplicação. Neste caso, é importante usar uma alternativa que esconda esses dados. Para esconder as informações do formulário, deve-se utilizar um novo método de envio chamado POST . Este método esconde as informações do formulário dentro do corpo da requisição. Os métodos de envio GET e POST fazem parte da especificação do protocolo HTTP. Para fazer com que um formulário HTML envie seus dados por POST , basta mudar a declaração da tag form incluindo o atributo method com o valor "post" . <form action="/Post/Adiciona" method="post"> <!-- Declaração dos campos do formulário --> </form> Com essa modificação, os dados do formulário serão enviados por post. No lado do servidor, a action do controller aceita tanto requisições feitas por GET quanto por POST . Para limitar o tipo de requisição que uma determinada action pode atender, é necessário fazer uma configuração no ASP.NET Core MVC. Essa configuração é feita através dos Attributes do C# (durante o texto, os Attributes serão chamados de anotações para evitar confusão com os atributos de um objeto). A anotação que limita o tipo de requisição que uma action pode receber para POST é a HttpPostAttribute : [HttpPostAttribute] public IActionResult Adiciona(Post p) { // Implementação do método } Mas no C#, por convenção, todas as anotações possuem o sufixo Attribute . Por isso, não é necessário colocá-lo no código, o C# gentilmente abrevia a anotação para o prefixo antes de Attribute . Então a declaração do Adiciona ficaria da seguinte forma: [HttpPost] public IActionResult Adiciona(Post p) { // Implementação do método } Da mesma forma que temos o HttpPostAttribute para limitar as requisições para o tipo POST , também temos o HttpGetAttribute para limitar as requisições para o tipo GET . . 1.16 DESCOBRINDO OS MÉTODOS DAS REQUISIÇÕES HTTP 23 1. Modifique a action para só aceitar requisições que forem enviadas através do método POST . Para isso, anote a action Adiciona com [HttpPost] 2. Teste a inclusão do post (usando a rota /Post/Novo ). Sua aplicação deverá enviar uma página de erro indicando recurso não encontrado (404). Porque isso aconteceu? 3. Modifique o formulário para enviar as informações do post através do método POST 4. Teste novamente sua aplicação. Agora tudo deve voltar ao normal! 5. (opcional) Pesquise na internet outras limitações da utilização do método GET em formulários HTML. Discuta em pares. Execute a aplicação algumas vezes e perceberá que a lista inicial de quatro posts só é incrementada uma única vez! Por que isso acontece? Isso se deve ao fato de que o controller PostController é instanciado toda vez que uma requisição for enviada por um cliente. Abra o código do controlador e repare que a lista é criada e populada no seu construtor. Isto é, toda vez que o controlador for instanciado, uma lista com quatro itens será gerada! Na requisição de inclusão de post, a lista com quatro posts teve mais um adicionado e passou para cinco itens. Contudo, quando enviarmos nova requisição, a lista será reinicializada, perdendo o quinto elemento. A causa desse problema é que não podemos usar a memória da aplicação para persistir os objetos do domínio. Esse tipo de memória é volátil. É necessário usar outras estratégias de persistência. A mais comum e utilizada nas aplicações web é o banco de dados relacional. Mais para frente no curso vamos discutir como persistir posts usando algum banco de dados. Neste capítulo inicial você conheceu a solução Microsoft para projetos Web utilizando o famoso padrão arquitetural MVC, o ASP.NET Core MVC. Além disso, conheceu mais a fundo a especificação do protocolo HTTP, a saber, o modelo Requisição-Resposta, como parâmetros são enviados nas requisições, e dois métodos de envio de requisições, GET e POST . Dentro do ASP.NET Core MVC, você conheceu seu sistema de roteamento, usando controllers e actions, e seu sistema de captura das informações vindas das requisições, chamado Model Binding. Por fim, conheceu as alternativas de transporte de dados entre controllers e views com a ViewBag e as views fortemente tipadas. 1.17 EXERCÍCIOS 1.18 O QUE VEM PELA FRENTE? 1.19 CONCLUSÃO . 24 1.8 EXERCÍCIOS CAPÍTULO 2 Muitos sistemas tornam-se difíceis de manter porque existem trechos de código que tem muita responsabilidade, ou seja, fazem muitas 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 alterar a interface do usuário para mostrar que o programa reagiu da maneira esperada (exibindo uma mensagem de sucesso). O problema da falta de divisão de responsabilidades não ocorre apenas no desktop. Na web, temos páginas ASP que contém, além de código HTML (para a apresentação do resultado), código VBScript com a lógica de negócio e código SQL acessando o banco de dados. Tudo isso é difícil de manter. O código fica espalhado e, muitas vezes, repetido. 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 (como adicionar, atualizar ou buscar algo) e depois informações são mostradas na tela. Isso forma três componentes: O Model é responsável somente pelas regras de negócio; a View é responsável unicamente por exibir os dados para o usuário final; por fim, o 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. Veja o desenho abaixo: CÓDIGO DE QUALIDADE COM O PADRÃO MVC . 2 CÓDIGO DE QUALIDADE COM O PADRÃO MVC 25 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 um outro componente faça essa tarefa. Ou seja, ele recebe informações da View e as transforma em invocações de regras de negócio. O padrão MVC tem se mostrado uma maneira adequada de separar esses componentes 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 ASP.NET Core 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 é salvo na pasta /Views . Por fim, as classes controladoras são salvas no diretório /Controllers , e 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! Leia um pouco mais sobre o MVC no site do Martin Fowler no site . 26 2 CÓDIGO DE QUALIDADE COM O PADRÃO MVC http://martinfowler.com/eaaCatalog/modelViewController.html 1. Quais os motivos para não colocar código de lógica junto com o código HTML? 2. Já que o MVC é um padrão e é você quem escreve os Controllers, os Models e as Views, aonde o framework ASP.NET Core MVC te ajuda? 3. Quando uma requisição chega ao servidor, qual a ordem de execução comum? Invoca o controller, que executa alguma lógica no Model e, ao fim, redireciona para uma view Renderiza a view e depois executa uma lógica Mostra a view, que invoca o model, que por sua vez invoca o controller 4. E caso precisássemos executar alguma outra tarefa antes de todas as lógicas, como, por exemplo, abrir uma conexão com o banco de dados? Você repetiria esse código em todos as suas actions? Qual seria o problema disso? 5. Explique agora, com as suas palavras, o que é o padrão MVC e o porquê dele ser útil. 2.1 EXERCÍCIOS . 2.1 EXERCÍCIOS 27 http://martinfowler.com/eaaCatalog/modelViewController.html CAPÍTULO 3 Ao final deste capítulo você vai: conhecer o componente ADO.NET e sua responsabilidade aprender a abrir uma conexão com um banco de dados descobrir como enviar comandos SQL para o banco ler o resultado de um SELECT reconhecer partes de código em sua aplicação que podem ser isoladas conhecer a camada arquitetural de acesso aos dados chamada Data Access Object Ainda existe o problema do blog com posts estáticos. A única maneira de tornar o blog dinâmico é persistir os posts em outra área de memória, separada da memória da aplicação. Desta forma, quando a aplicação sair do ar, os posts continuam registrados nesta outra memória. Como alternativas para estas outras memórias, existem arquivos XML, bancos de dados, outros serviços Web, dentre outros. O banco de dados normalmente é uma aplicação separada que está sendo executada em uma rede de computadores, por exemplo na rede local da sua empresa. Existem diversos bancos de dados no mercado: MySQL, SQL Server, Oracle e Postgre SQL. Neste curso vamos usar o SQL Server para persistir os posts do blog. Como recuperar os posts do blog do banco? A aplicação precisará substituir o código que popula a lista de posts arbitrariamente por alguma instrução que retorne os posts do banco de dados. Além disso, precisará mudar o código da action Adiciona para efetivamente armazenar o post no banco de dados. A linguagem utilizada para a comunicação com os bancos de dados relacionais é o SQL . Por exemplo, para consultar todos os posts existentes numa tabela chamada Posts o seguinte SQL é usado: select * from Posts E para incluir um registro na mesma tabela com colunas iguais às propriedades da classe Post: insert into Posts (Titulo, Resumo, Categoria) values ('Harry Potter 4', 'Cálice de Fogo', 'Filmes') Para enviar estes comandos, é preciso inicialmente abrir uma conexão que será utilizada para toda PERSISTINDO POSTS EM UM BANCO DE DADOS . 28 3 PERSISTINDO POSTS EM UM BANCO DE DADOS comunicação com o banco de dados. Para abrí-la, é preciso comunicar-se com uma aplicação da rede. Isto pode ser feito através de sockets dentro da aplicação, porém toda a comunicação feita com o banco de dados através dos sockets precisa ser feita através de um protocolo proprietário que muda de acordo com o banco de dados utilizado. Para facilitar este trabalho, a Microsoft criou o ADO.NET, que fornece Drivers específicos que implementam o protocolo proprietário de comunicação com cada banco. Esses drivers são conhecidos como Data Providers. O Data Provider é o componente que cuida da tradução dos comandos da aplicação para o banco de dados e também da tradução da resposta devolvida pelo banco de dados para objetos que podem ser utilizados pela aplicação. Um dos principais conceitos dos bancos relacionais é a existência de uma chave primária, que é um subconjunto de colunas de uma tabela que identifica unicamente um registro armazenado em uma tabela. Quais colunas usaremos para representar a chave primária de um post? No nosso modelo atual, podemos ter posts com resumos e categorias iguais, mas dificilmente teremos posts com títulos, resumos E categorias iguais. Apesar destas propriedades serem candidatas a comporem a chave primária da tabela de posts, em geral criamos uma coluna interna específica com esse propósito. Por esse motivo, iremos adicionar uma nova propriedade, do tipo inteiro, no modelo de Post chamada Id . public class Post { public int Id { get; set; } public string Titulo { get; set; } public string Resumo { get; set; } public string Categoria { get; set; } } Uma conexão com o SQL Server exige uma instância de SqlConnection . Para utilizar essa conexão, é necessário passar as informações do servidor desejado. Essas informações são chamadas strings de conexão e podem ser enviadas como argumento do construtor de SqlConnection . No exercício será informado como recuperar as informações de conexão para a ConnectionString . var stringConexao = ???? SqlConnection conexao = new SqlConnection(stringConexao); Essa variável conexao representa uma conexão que ainda não está aberta com o banco de dados. Para abrir a conexão com o banco de dados, o método Open é utilizado. Para terminar de utilizar a conexão, fecha-se com o Close : 3.1 IDENTIFICANDO UNICAMENTE UM POST 3.2 ABRINDO A CONEXÃO COM O ADO.NET . 3.1 IDENTIFICANDO UNICAMENTE UM POST 29 // Abre a conexão: conexao.Open(); // Operações que utilizam a conexão // Fecha a conexão: conexao.Close(); Como a SqlConnection implementa a interface IDisposable , é possível gerenciar o tempo de vida de uma conexão com o using do C#: var stringConexao = ????; using(SqlConnection conexao = new SqlConnection(stringConexao)) { // Inicializa a string de conexão conexao.Open(); // usa a conexão // não é necessário chamar o close pois ao sair do bloco // a conexão será automaticamente fechada. } A abordagem acima consegue facilmente criar uma nova conexão com o banco de dados, mas deixa a string de conexão diretamente no código daaplicação. Em muitas aplicações, a string de conexão utilizada durante o desenvolvimento é diferente de quando a aplicação é publicada. Nesse caso, deixar a string de conexão no código da aplicação não é uma boa ideia. Uma solução melhor é colocar a string de conexão em algum arquivo de configuração, o appsettings.json . Assim, na publicação basta simplesmente modificar a configuração ao invés de modificar todo o código. Dentro do appsettings.json utilizamos uma chave de ConnectionStrings para listar as strings de conexão da aplicação. Para adicionar uma nova connection string, basta adicionar mais uma chave da ConnectionStrings : { //... "ConnectionStrings": { "Blog": "string de conexao" } } Para o caso do SQL Server, sua string de conexão deverá ficar parecido com esta: { //... "ConnectionStrings": { "Blog": "Data Source=<nome do host>;Database=Blog;Integrated Security=True" } } 3.3 ISOLANDO A STRING DE CONEXÃO . 30 3.3 ISOLANDO A STRING DE CONEXÃO Para recuperar a string de conexão do arquivo de configuração, precisamos de uma nova interface do C# chamada IConfiguration que será gerada pela classe ConfigurationBuilder . A instância de IConfiguration tem um dicionário com as strings que foram configuradas no json, sendo que um dos métodos é o GetConnectionString responsável por recuperar chaves dentro da ConnectionStrings . Agora basta informar pelo ConfigurationBuilder que queremos ler o arquivo appsettings.json : var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddEnvironmentVariables(); IConfiguration configuration = builder.Build(); string stringConexao = configuration.GetConnectionString("Blog"); using (SqlConnection conexao = new SqlConnection(stringConexao)) { // Inicializa a string de conexão conexao.Open(); // usa a conexão // não é necessário chamar o close pois ao sair do bloco // a conexão será automaticamente fechada. } Agora que conectamos com o banco, precisaríamos usar os métodos Index e Adiciona do PostController para executar suas responsabilidades. O código para abrir a conexão é duplicado nos dois métodos: public class PostController : Controller { public IActionResult Index() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddEnvironmentVariables(); IConfiguration configuration = builder.Build(); string stringConexao = configuration.GetConnectionString("Blog"); using (SqlConnection conexao = new SqlConnection(stringConexao)) { conexao.Open(); //...busca todos os posts... } } public IActionResult Adiciona(Post post) { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddEnvironmentVariables(); IConfiguration configuration = builder.Build(); string stringConexao = configuration.GetConnectionString("Blog"); using (SqlConnection conexao = new SqlConnection(stringConexao)) 3.4 ISOLANDO A CONEXÃO EM SUA PRÓPRIA CLASSE . 3.4 ISOLANDO A CONEXÃO EM SUA PRÓPRIA CLASSE 31 { conexao.Open(); //...salva post no banco... } } } É muito comum abrir uma conexão com o banco de dados dentro de vários pontos diferentes da aplicação. Deste modo seria necessário copiar o código de criação da conexão para cada ponto. Como já vimos, todo código repetido é fonte de problemas de manutenção. Por exemplo, o que aconteceria se o nome da string de conexão fosse modificado de Blog para ConexaoBlog no appsettings.json ? Ou se a string de conexão passasse a ser obtida não mais através do appsettings.json e sim através de outro método qualquer? Nesses casos, seria preciso mudar o código de abertura de conexões em todos os locais. E o risco de esquecermos algum seria proporcional a quantidade de arquivos que precisariam ser alterados. Para evitar esse problema, uma solução é isolar esse código dentro de uma nova classe especializada apenas em criar conexões: public class CriadorDeConexoes { public static SqlConnection CriaConexaoAberta() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddEnvironmentVariables(); IConfiguration configuration = builder.Build(); string stringConexao = configuration.GetConnectionString("Blog"); SqlConnection conexao = new SqlConnection(stringConexao); conexao.Open(); return conexao; } } Uma vez que não precisamos de qualquer informação da classe CriadorDeConexoes, tornamos o método estático (palavra chave static). Além disso, já retornamos a conexão aberta. Com isso, todos os pontos da aplicação que precisarem de uma conexão com o banco podem simplesmente chamar o CriaConexaoAberta : SqlConnection conexao = CriadorDeConexoes.CriaConexaoAberta(); Classes que têm a responsabilidade de criar objetos, implementam um padrão de projeto chamado Factory Method. Classes que implementam o Factory Method geralmente seguem uma convenção de nomes para facilitar o entendimento do código. O nome da classe fica com o nome do objeto que será . 32 3.4 ISOLANDO A CONEXÃO EM SUA PRÓPRIA CLASSE criado, seguido do sufixo Factory . No nosso caso, vamos adotar o nome ConnectionFactory . 1. O primeiro passo será criar o seu banco de dados do Blog no SQL Server. Você pode usar uma ferramenta como o Microsoft SQL Server Management Studio caso você ainda não tenha uma base para o blog. Ou então, em seu terminal, execute o comando sqlcmd que abrirá o utilitário de linha de comando do SQL Server. Nele podemos executar os seguintes comandos para criar nossa base de dados do Blog create database Blog GO use Blog GO Depois devemos criar uma tabela de Posts nesta base, executando o comando de CREATE TABLE a seguir: CREATE TABLE [dbo].[Posts] ( [Id] INT NOT NULL PRIMARY KEY IDENTITY(1,1), [Titulo] VARCHAR(50), [Resumo] VARCHAR(500), [Categoria] VARCHAR(50) ) GO Para não ficar vazio, insira alguns posts nesta base através do comando de insert: insert into Posts(Titulo, Resumo, Categoria) values ('Título do post', 'Resumo do post', 'Categoria do post') GO 2. Modifique a classe Post para adicionar a propriedade Id , do tipo int, que será a chave primária da tabela de posts. 3. Agora vamos configurar a string de conexão. Abra o arquivo appsettings.json e adicione a chave ConnectionStrings . Dentro dela você adicionará a chave Blog com as configurações do seu banco de dados SQL Server, parecido com o código a seguir: { //... "ConnectionStrings": { "Blog": "Data Source=<nome do host>;Database=Blog;Integrated Security=True" } } 4. Crie uma pasta chamada Infra e crie a classe ConnectionFactory com o método CriaConexaoAberta . 3.5 EXERCÍCIOS . 3.4 ISOLANDO A CONEXÃO EM SUA PRÓPRIA CLASSE 33 namespace Blog.Infra { public class ConnectionFactory { public static SqlConnection CriaConexaoAberta() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).AddEnvironmentVariables(); IConfiguration configuration = builder.Build(); string stringConexao = configuration.GetConnectionString("Blog"); SqlConnection conexao = new SqlConnection(stringConexao); conexao.Open(); return conexao; } } } No PostController , vamos substituir o código que adiciona posts arbitrários na lista de posts, por registros que vêm do banco de dados. Como vimos anteriormente, para selecionar todos os registros da tabela de posts, fazemos: select * from Posts Para enviar esse comando SQL para o banco de dados é necessário uma instância de SqlCommand , que é obtida a partir de uma conexão aberta com o banco de dados, representada por uma instância de SqlConnection . Para informar o SQL desejado usa-se a propriedade CommandText . O código que realiza essa tarefa fica deste jeito: using (SqlConnection cnx = ConnectionFactory.CriaConexaoAberta()) { SqlCommand comando = cnx.CreateCommand(); comando.CommandText = "select * from Posts"; } Para rodar o comando no banco de dados, utiliza-se o método ExecuteReader . Este método executa o comando SQL no banco de dados e devolve um objeto que representa o resultado da busca no banco. Esse objeto devolvido é uma instância de SqlDataReader : SqlDataReader leitor = comando.ExecuteReader(); O SqlDataReader lê linha a linha o resultado da consulta no banco de dados. Para avançar para a próxima linha, usa-se o método Read . Quando esse método consegue avançar com sucesso para a próxima linha, ele devolve true . Caso contrário, devolve false . Enquanto Read devolver true , o código continuará lendo o próximo registro: while(leitor.Read()) 3.6 LISTANDO OS POSTS DO BANCO . 34 3.6 LISTANDO OS POSTS DO BANCO { // lê as informações do registro } O SqlDataReader também funciona como um dicionário, que pode ser utilizado para ler as informações do registro atual. Nesse dicionário, as chaves são os nomes das colunas do banco de dados e os valores são as informações armazenadas em cada coluna. Por exemplo, para recuperar o titulo do post, usa-se o seguinte código: leitor["titulo"]; Porém, o valor devolvido é de um tipo definido pelo banco de dados. Para fazer a conversão do tipo devolvido pelo SqlDataReader para um tipo do C#, utiliza-se a classe Convert : string titulo = Convert.ToString(leitor["titulo"]); As informações devolvidas pela query pertencem a um post. Nada mais natural do que criar um objeto do tipo Post para armazenar as informações devolvidas. Para completar o código, basta apenas guardar os posts dentro da lista do C#: var listaDePosts = new List<Post>(); while(leitor.Read()) { Post post = new Post() { Id = Convert.ToInt32(leitor["id"]), Titulo = Convert.ToString(leitor["titulo"]), Resumo = Convert.ToString(leitor["resumo"]), Categoria = Convert.ToString(leitor["categoria"]) }; listaDePosts.Add(post); } Por fim, o código completo da action Index ficaria assim: public IActionResult Index() { IList<Post> lista = new List<Post>(); using (SqlConnection cnx = ConnectionFactory.CriaConexaoAberta()) { SqlCommand comando = cnx.CreateCommand(); comando.CommandText = "select * from Posts"; SqlDataReader leitor = comando.ExecuteReader(); while (leitor.Read()) { Post post = new Post() { Id = Convert.ToInt32(leitor["id"]), Titulo = Convert.ToString(leitor["titulo"]), Resumo = Convert.ToString(leitor["resumo"]), Categoria = Convert.ToString(leitor["categoria"]) }; lista.Add(post); } } return View(lista); } . 3.6 LISTANDO OS POSTS DO BANCO 35 Já foi possível sentir que colocar código SQL dentro de suas classes de lógica é algo nem um pouco elegante e muito menos viável quando você precisa manter o seu código. Quantas vezes você não ficou bravo com o programador responsável por aquele código ilegível? A ideia a seguir é remover o código de acesso ao banco de dados de suas classes de lógica e colocá- lo em uma classe responsável pelo acesso aos dados. Assim o código de acesso ao banco de dados fica em um lugar só, tornando mais fácil a manutenção. Que tal se pudéssemos chamar um método que lista todos os Posts do banco? Em outras palavras quero que o código a seguir funcione: // lista os posts do banco Misterio bd = new Misterio(); IList<Post> lista = bd.ListaPosts(); Tentaremos chegar ao código anterior: seria muito melhor e mais elegante poder chamar um único método responsável pela listagem, certo? public IActionResult Index() { Misterio bd = new Misterio(); // método elegante IList<Post> lista = bd.ListaPosts(); return View(lista); } O código anterior já mostra o poder que alcançaremos: através de uma única classe seremos capazes de acessar o banco de dados e, mais ainda, somente através dessa classe será possível acessar os dados. Esta ideia, inocente à primeira vista, é capaz de isolar todo o acesso a banco em classes bem simples, cuja instância é um objeto responsável por acessar os dados. Da responsabilidade deste objeto surgiu o nome de Data Access Object ou simplesmente DAO, um dos mais famosos padrões de projeto (design pattern). Vamos isolar o código que lista numa classe chamada PostDAO com um método Lista : public class PostDAO { public IList<Post> Lista() { var lista = new List<Post>(); using (SqlConnection cnx = ConnectionFactory.CriaConexaoAberta()) { SqlCommand comando = cnx.CreateCommand(); comando.CommandText = "select * from Posts"; SqlDataReader leitor = comando.ExecuteReader(); while (leitor.Read()) { Post post = new Post() 3.7 DAO - DATA ACCESS OBJECT . 36 3.7 DAO - DATA ACCESS OBJECT { Id = Convert.ToInt32(leitor["id"]), Titulo = Convert.ToString(leitor["titulo"]), Resumo = Convert.ToString(leitor["resumo"]), Categoria = Convert.ToString(leitor["categoria"]) }; lista.Add(post); } } return lista; } } Agora, no PostController podemos usar a classe PostDAO da seguinte forma: public IActionResult Index() { PostDAO dao = new PostDAO(); // método elegante IList<Post> lista = dao.Lista(); return View(lista); } 1. Crie uma nova pasta /DAO e adicione a classe PostDAO com o método que Lista todos os posts. Este método deve pegar uma conexão aberta usando a ConnectionFactory , fazer a consulta de todos os posts cadastrados no banco e retornar a lista de posts, utilizando o código da seção anterior. 2. Modifique a action Index no PostController para utilizar os registros de posts salvos no banco de dados criado no exercício anterior ao invés dos objetos gerados manualmente dentro da action. 3. Teste a aplicação. Agora a tabela HTML está sendo populada pelos registros do banco de dados que criamos. O blog já mostra a listagem de posts recuperada do banco de dados. Porém, nosso formulário de inclusão ainda inclui o post em memória (na lista). Precisamos modificar o código da action Adiciona para que o post seja incluído no banco de dados. Atualmente o código da action está assim: public IActionResult Adiciona(Post post) { lista.Add(p); <<== CÓDIGO A SER SUBSTITUÍDO! return View("Index", lista); } Precisamos substituir o código da primeira linha pelo código que insere o post no banco de dados. Vimos no capítulo anterior que enviamos comandos para um banco de dados SQL Server através de instâncias da classe SqlCommand. O comando SQL responsável pela inclusão
Compartilhar