Temp - Desenvolvimento Web Com AspNet MVC e AspNet Core PDF
Temp - Desenvolvimento Web Com AspNet MVC e AspNet Core PDF
NET
MVC e ASP.NET Core
COD.: 1891_0
Desenvolvimento Web com ASP.
NET MVC e ASP.NET Core
Créditos
Todos os direitos autorais reservados. Este manual não pode ser copiado, fotocopiado, reproduzido,
traduzido ou convertido em qualquer forma eletrônica, ou legível por qualquer meio, em parte ou
no todo, sem a aprovação prévia, por escrito, da Monte Everest Participações e Empreendimentos
Ltda., estando o contrafator sujeito a responder por crime de Violação de Direito Autoral, conforme
o art.184 do Código Penal Brasileiro, além de responder por Perdas e Danos. Todos os logotipos
e marcas utilizados neste material pertencem às suas respectivas empresas.
"As marcas registradas e os nomes comerciais citados nesta obra, mesmo que não sejam assim identificados, pertencem
aos seus respectivos proprietários nos termos das leis, convenções e diretrizes nacionais e internacionais."
Autoria
Emilio Celso de Souza
Revisão Ortográfica e
Gramatical
Fernanda Monteiro Laneri
Marcos César dos Santos Silva
Diagramação
Bruno de Oliveira Santos
Edição nº 1 | 1891_0
Junho/ 2020
4
Sumário
5
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
6
Sumário
7
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
8
Fundamentos
1 de aplicações
Web
ÃÃ Arquitetura cliente-servidor;
ÃÃ O servidor Web;
ÃÃ O protocolo HTTP;
ÃÃ Tecnologias utilizadas nas páginas Web e no servidor;
ÃÃ IIS – Internet Information Services;
ÃÃ O IIS Express e o Visual Studio;
ÃÃ Criando um projeto Web.
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
1.1. Introdução
Uma aplicação Web consiste essencialmente em duas partes: um cliente e um servidor.
O cliente é o browser, onde as páginas da aplicação são visualizadas, e por meio do
qual ocorrem todas as interações com o usuário. O servidor é a parte do sistema
responsável por receber e processar as requisições realizadas na aplicação (por
intermédio do browser).
1.2. Arquitetura cliente-servidor
Uma arquitetura em que um aplicativo solicita processamento de outro aplicativo ou
serviço é chamada cliente-servidor.
10
Fundamentos de aplicações Web 1
1.4. O protocolo HTTP
O HTTP (HyperText Transfer Protocol) é um protocolo de comunicação que estabelece
transferência de dados entre os navegadores Web e os servidores de aplicação.
•• POST: Este comando é utilizado para enviar ao servidor solicitações com dados
encapsulados dentro da requisição. É usado para processar informações;
•• PUT: Comando semelhante ao POST, usado para incluir ou criar novos dados;
•• TRACE: Comando para exibir a requisição que foi enviada ao servidor. Usado
para investigar processos;
1.4.1. Requisições (Request)
O formato básico de uma requisição HTTP (Request) é o seguinte:
Cookie: usuario=3WrUX
11
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
A linha 3 adiciona uma informação à requisição. Nesse exemplo, apenas um item está
definido (Accept), mas múltiplas informações podem ser inseridas nessa parte. Nesse
caso, o item Accept diz ao servidor o formato de dados esperado pelo cliente nessa
requisição.
nome=Maria&[email protected]
1.4.2. Respostas (Response)
O formato da resposta (Response) no protocolo HTTP é o seguinte:
HTTP/1.1 200 OK
Content-Length: 12260
Content-Type: text/html;
<html><body><h1>Teste</h1></body></html>
•• 200 – OK;
•• 204 – Nenhum conteúdo;
•• 404 – Não encontrado;
•• 401 – Não autorizado;
•• 500 – Erro interno do servidor.
12
Fundamentos de aplicações Web 1
A linha 3 define o tipo de conteúdo (Content-Type). Nesse caso, é uma página HTML.
O servidor pode definir muitos outros dados e inseri-los a partir dessa linha.
13
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Uma página real pode parecer bem mais complexa do que a do exemplo. Isso
ocorre porque, além de o conteúdo ser maior, as folhas de estilo e o JavaScript são
normalmente colocados em arquivos separados, existem referências a imagens,
bibliotecas de terceiros, metadados e objetos incorporados.
14
Fundamentos de aplicações Web 1
1.6. Tecnologias utilizadas no servidor
O servidor Web é o responsável por atender a solicitações do cliente, localizar o
conteúdo solicitado, preparar o documento HTML e retorná-lo. O ASP.NET entra nesse
processo depois que o IIS recebeu a solicitação e antes de retornar o conteúdo para o
cliente. Esse caminho é chamado de pipeline (encanamento) e diversos componentes
podem ser colocados nesse processo.
15
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
16
Fundamentos de aplicações Web 1
•• Localize o item Serviços de Informação da Internet (Internet Information
Services).
17
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Em linhas gerais, o que esse serviço faz é mapear uma pasta física para uma pasta
virtual. A pasta virtual pode ser isolada em uma área de memória protegida e preparada
para executar scripts. Quando uma pasta é preparada dessa forma, é chamada de
pasta de aplicação.
A pasta física é o local real onde está um aplicação Web. Por exemplo:
C:\inetpub\wwwroot\MinhaAppWeb
http://MeuServidor/AppExemplo
Na janela Conexões, existe o nome do servidor e o Web Site padrão, chamado Default
Web Site. Cada aplicação Web pode compartilhar dados de segurança com outras
aplicações. O item Pool de Aplicativos define esses grupos.
18
Fundamentos de aplicações Web 1
1.7.2. IIS – Criando uma aplicação ASP.NET
Para criar uma aplicação ASP.NET, siga os passos descritos a seguir:
1. Crie uma pasta em qualquer drive. Se quiser usar a "pasta oficial" do ASP.NET, o
endereço é C:\inetpub\wwwroot\NomeDoSeuApp;
19
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
4. Informe o apelido do site (Alias) e o caminho físico. O alias pode ser qualquer nome
sem espaços ou acentos. É por meio desse nome que o aplicativo será executado. O
caminho físico deve ser exatamente aquele onde a pasta foi criada;
20
Fundamentos de aplicações Web 1
O arquivo Index.html está na lista dos arquivos que abrem automaticamente, por isso
pode ser omitido.
O exemplo anterior executa e o browser consegue exibir o arquivo, mas não há nenhum
processamento no servidor além do IIS, porque a extensão htm não está mapeada
para nenhum componente. Nesse caso, o IIS apenas entrega o arquivo solicitado.
O Visual Studio disponibiliza uma versão mais simplificada do servidor IIS, que
permite ao desenvolvedor poupar esse tempo de publicações constantes. Trata-se
do IIS Express. Ele age como um serviço temporário, ou seja, um ambiente em Run
Time, que é executado quando um projeto Web é executado.
21
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
22
Fundamentos de aplicações Web 1
Para exemplificar, vamos criar um projeto Asp.Net Web Application (.NET Framework):
23
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Após alguns instantes, o projeto é criado. Temos o projeto com a seguinte estrutura:
24
Fundamentos de aplicações Web 1
Para iniciarmos o desenvolvimento, vamos incluir uma página HTML ao projeto. Um
dos caminhos para esse processo é clicando com o botão direito do mouse sobre o
projeto e escolhendo adicionar uma página HTML (HTML Page):
25
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Outro atalho que poderíamos usar é clicar com o botão direito do mouse sobre o
projeto e selecionar a opção New Item...:
26
Fundamentos de aplicações Web 1
A vantagem dessa opção é que temos acesso a várias outras opções, incluindo a
opção anterior (HTML Page):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Exemplo</title>
<style>
body{ font-family: Verdana; }
h1{ color:red; }
</style>
<script>
function ExibirVersao() {
alert("Versão 1.0");
}
</script>
</head>
<body>
<h1>Exemplo HTML</h1>
<p>Esta é uma simples página</p>
<button type="button" onclick="ExibirVersao();">
Versão
</button>
</body>
</html>
27
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Esse processo ativa o IIS Express, que permitirá que a aplicação seja executada no
browser selecionado. A seguir, temos o resultado da execução antes e após o clique
do botão:
28
Fundamentos de aplicações Web 1
Pontos principais
Atente para os tópicos a seguir. Eles devem ser estudados com muita atenção,
pois representam os pontos mais importantes do capítulo.
29
Fundamentos
1 de aplicações
Web
Teste seus conhecimentos
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
3. Em qual pasta, por padrão, aplicações Web devem ser colocadas para
que o IIS possa executá-las?
☐☐a) C:\Program Files
☐☐b) C:\Windows
☐☐c) C:\wwwroot
☐☐d) C:\inetpub
☐☐e) C:\inetpub\wwwroot
32
Fundamentos de aplicações Web 1
4. Das tecnologias abaixo, qual não é executada pelo browser?
☐☐a) CSS
☐☐b) JavaScript
☐☐c) HTML
☐☐d) C#
☐☐e) Imagem
33
Fundamentos
1 de aplicações
Web
Mãos à obra!
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Laboratório 1
Objetivos:
1. Escolha uma pasta no seu computador. Nessa pasta, defina uma nova pasta
chamada Labs;
36
Fundamentos de aplicações Web 1
3. Defina como nome do projeto Lab01. Como nome do solution, defina
Capitulo01.Labs:
37
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
5. Clique com o botão direito do mouse sobre o projeto Lab01 e adicione uma
nova página HTML. Coloque o nome de Index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset=”utf-8” />
<title>Capitulo 01</title>
<style>
.margem{
margin: 50px;
}
.borda {
border: 1px solid #a9a9a9;
border-radius: 15px;
padding: 20px;
background-color: #d3d3d3;
}
.centraliza {
text-align: center;
}
</style>
</head>
<body class=”margem”>
<div class=”borda centraliza”>
<h3>Bem vindo ao curso de Asp.Net</h3>
</div>
</body>
</html>
38
Fundamentos de aplicações Web 1
7. Abra o Painel de Controle, o grupo Ferramentas Administrativas e
o Gerenciador de Serviços de Informações da Internet (IIS). No painel
Conexões, localize Sites, Default Web Site, abra o menu de contexto e escolha
a opção Adicionar Aplicativo...;
39
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
9. O gerenciador do IIS deve ter criado uma pasta com o nome da aplicação
Web;
40
2 Web Pages com
Razor
ÃÃ O mecanismo Razor.
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
2.1. Introdução
Em uma aplicação Web, somente páginas HTML não são suficientes. As páginas HTML
são denominadas estáticas por não terem nenhuma interação com o servidor. Em
outras palavras, o conteúdo de uma página HTML é apresentado para o usuário no
navegador exatamente como desenhado.
Acontece que, na maioria das aplicações, o que vemos no navegador não é exatamente
um conteúdo estático, e sim algo que varia em função de um evento. Por exemplo,
quando consultamos o saldo de uma conta bancária, temos um resultado baseado
em uma consulta a um banco de dados, e se consultarmos a mesma página em dias
diferentes, os resultados também serão diferentes. Páginas assim interagem com o
servidor, e são chamadas de páginas dinâmicas.
42
Web Pages com Razor 2
2.2.1. Criando um projeto com uma Web Page
usando Razor
Para ilustrar o processo de desenvolvimento com Razor, vamos criar um projeto ASP.
NET Web Application (.NET Framework) vazio chamado Capitulo02.Razor:
Uma vez que o projeto tenha sido criado, devemos clicar com o botão direito do
mouse sobre ele e selecionar a opção Add... / New Item...:
43
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Selecione a opção MVC 5 View Page (Razor). Nomeie o arquivo como Exemplo1.
cshtml:
Observe que o código gerado é semelhante a uma página HTML, com alguns itens
adicionais. Elaboremos o seguinte exemplo (observe o destaque):
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
<p>Hoje é @DateTime.Now</p>
</div>
</body>
</html>
44
Web Pages com Razor 2
O símbolo usado para abrir uma parte do script para ser executada no servidor é o
caractere arroba (@).Não existe tag de fechamento. Quando um caractere que não
deveria estar no local é encontrado, o mecanismo do Razor entende que terminou a
expressão.
Na mesma Web Page, vamos adicionar outro exemplo. Agora, um exemplo de bloco
de código usando chaves ( { } ):
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
@*<p>Hoje é @DateTime.Now</p>*@
@{
var x = 10;
var y = 5;
}
45
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
<hr />
<p>
Contato: [email protected]
</p>
46
Web Pages com Razor 2
2.2.2. Estruturas de repetição
Razor não é uma linguagem de programação e sim uma View Engine que utiliza
qualquer linguagem, contanto que exista um plugin disponível. Usando C#, a sintaxe
para criar loops é a seguinte:
•• Loop for
•• Loop foreach
@{
var numeros = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
foreach (int i in numeros)
{
<p>
O valor de i é
<span>@i</span>
</p>
}
}
•• Loop while
@{ int i = 1;
while (i <= 10)
{
<p>
O valor de i é
<span>@i</span>
</p>
i = i + 1;
}
}
47
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
@{ int i = 1;
while (i <= 10)
{
<p>
O valor de i é
<span>@i</span>
</p>
i = i + 1;
}
}
</div>
</body>
</html>
48
Web Pages com Razor 2
É interessante notar que não existe uma regra para terminar um trecho de código
e iniciar um em HTML. O Razor é inteligente o suficiente para deduzir quando é
um código de servidor e quando é HTML. No exemplo anterior, o fato de existir um
parágrafo (<p>) no meio do código indica que é HTML e, ao encontrar chaves ( } ) ou
uma expressão, ele retorna para o código. É claro que, às vezes, é necessário intervir
nessa interpretação, como o caractere arroba (@) colocado para exibir a variável i, já
que a sua chamada ocorre dentro de elementos HTML. Se não tivéssemos colocado, a
saída seria 10 vezes a letra "i".
2.2.3. Estruturas condicionais
As estruturas condicionais, quando usadas, devem ser precedidas pelo caractere
arroba (@) e seu conteúdo, em um par de chaves ({ }).
•• Estrutura if
Observe que o comando else não foi precedido por arroba (@) porque ele faz parte
do comando if.
•• Estrutura switch
@switch (DateTime.Now.Hour)
{
case 12:
case 13:
<p>Hora do Almoço</p>
break;
case 18:
case 19:
<p>Hora do Jantar</p>
break;
}
49
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
@if (DateTime.Now.DayOfWeek == DayOfWeek.Friday)
{
<span>
Hoje é sexta-feira.
Agora são @DateTime.Now.ToShortTimeString()
</span>
}
else
{
<span>
Hoje não é sexta
</span>
}
</div>
</body>
</html>
50
Web Pages com Razor 2
2.2.4. Render e layout
Normalmente, em uma aplicação Web, grande parte do código HTML é compartilhada
entre todas as páginas: cabeçalho, menus, rodapés. Esses elementos podem ser
isolados em páginas separadas e inseridos em outra página por meio do método
RenderPage().
2.2.4.1. RenderPage()
O método RenderPage() insere um arquivo externo na posição do comando.
•• Arquivo: Exemplo.cshtml
<!DOCTYPE html>
<html>
<head>
<title>Exemplo</title>
</head>
<body>
@RenderPage("Titulo.cshtml")
<p>Bem-vindo ao site</p>
@RenderPage("Rodape.cshtml")
</body>
</html>
•• Arquivo: Titulo.cshtml
<h1>Exemplo WebPage</h1>
•• Arquivo: Rodape.cshtml
<hr/>
Copyright @DateTime.Now.Year © ASP.NET Exemplos
51
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Ao executar a página, o Razor View Engine monta os três arquivos em um só, que é
renderizado para ser enviado ao browser. Veja:
Olhando o código-fonte que foi enviado ao navegador, é possível perceber que não foi
inserido nada além do conteúdo dos arquivos. Veja:
52
Web Pages com Razor 2
O método RenderPage() não é a melhor opção para garantir um layout consistente
em um aplicativo Web com muitas páginas. Em todas as páginas do aplicativo, seria
necessário incluir uma chamada ao método para renderizar o título e outra para
renderizar o rodapé. Se a estrutura padrão mudasse, por exemplo, se fosse incluído
um navegador, todas as páginas teriam que incluir uma renderização para o arquivo
do menu.
2.2.4.2. RenderBody()
O método RenderBody() renderiza uma página configurada como página de conteúdo
dentro de uma página configurada como página de layout.
Para ser uma página que sirva de modelo para as outras, não tem nenhuma
configuração especial, basta ter uma chamada ao método RenderBody(). A página
que será renderizada deve indicar esta página de layout como primeira declaração.
<!DOCTYPE html>
<html>
<head><title>Minha App</title></head>
<body>
<h1>Titulo Minha App</h1>
@RenderBody()
<hr/>
<p>Desenvolvido para o curso de ASP.NET© @DateTime.Now.
Year </p>
</body>
</html>
@{
Layout = "~/_Layout.cshtml";
}
<p>
Aqui é o corpo da página que será renderizado
pelo método RenderBody() da página de Layout...
</p>
53
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Não é obrigatório utilizar o arquivo iniciando com o caractere sublinhado (_), mas
é uma prática comum, porque os arquivos que iniciam dessa forma não podem ser
visualizados pelo usuário, e o arquivo que serve de modelo geralmente é usado apenas
para esse propósito, portanto não teria sentido isolá-lo.
2.2.4.3. RenderSection()
O método RenderSection renderiza o conteúdo de uma seção nomeada. Este método
deve estar dentro de um arquivo de layout. O primeiro parâmetro deste método é
o nome da área nomeada, e o segundo é se essa área é opcional. Se no segundo
parâmetro for marcado false, a página não precisará ter uma área nomeada. Alteramos
o arquivo de layout para incluir o método RenderSection:
54
Web Pages com Razor 2
•• Página de layout alterada: _Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Minha App</title>
</head>
<body>
<div>
<h1>Titulo Minha App</h1>
@RenderBody()
@RenderSection("Mensagem", false)
<hr />
<p>Desenvolvido para o curso de ASP.NET© @DateTime.Now.
Year </p>
</div>
</body>
</html>
@{
Layout = "~/_Layout.cshtml";
}
@section Mensagem{
55
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
2.2.5. Request e Form
Grande parte do sucesso do protocolo HTTP e da linguagem HTML é a versatilidade na
maneira de transmitir e receber informações pela Internet. As Web Pages disponibilizam
propriedades para manipular e/ou consultar informações vindas do navegador.
•• IsPost e Request
56
Web Pages com Razor 2
A propriedade Request retorna os detalhes da requisição, e os dados do formulário
podem ser obtidos por meio da propriedade Request.Form.
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
<form action="" method="post">
<label>Nome:</label>
<input name="nome" />
<button type="submit">Enviar</button>
</form>
@if (IsPost)
{
var nome = Request.Form["nome"];
<p>Bem vindo, @nome</p>
}
</div>
</body>
</html>
57
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Os dados obtidos pela coleção Form podem ser obtidos pela coleção indexadora da
classe Request:
var nome=Request.Form["nome"];
Pode ser:
var nome=Request["nome"];
Procurar em todas as coleções torna o programa mais vulnerável, pois fica muito
fácil passar um parâmetro errado pela QueryString, por exemplo. Para alterações, o
melhor é utilizar POST e Request.Form.
58
Web Pages com Razor 2
•• IsInt(), IsDate(), IsBool(), IsDecimal(), IsFloat()
Estes métodos permitem verificar se o tipo dentro de uma requisição pode ser
convertido em um tipo primitivo como int, DateTime, Decimal ou Boolean. Podem
ser usados em conjunto com o formulário, para validações:
<br />
<button type="submit">Enviar</button>
@if (IsPost)
{
if (Request["Numero"].IsInt())
{
<p>Número OK</p>
}
else
{
<p>Número Inválido</p>
}
if (Request["Data"].IsDateTime())
{
<p>Data OK</p>
}
else
{
<p>Data Inválida!</p>
}
}
</form>
59
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
2.2.6. HTML Helpers
Helpers são pequenos métodos que geram código HTML. Isso facilita a construção de
uma página HTML, principalmente quando há muito código com a mesma estrutura.
Por exemplo, um formulário sempre vai ser constituído de diversos controles Label
seguidos de controles Input. Essas tags HTML podem ser criadas automaticamente
por meio dos métodos fornecidos pelo Helper. Veja o exemplo, usando o HTML:
60
Web Pages com Razor 2
Pontos principais
Atente para os tópicos a seguir. Eles devem ser estudados com muita atenção,
pois representam os pontos mais importantes do capítulo.
61
2 Web Pages com
Razor
Teste seus conhecimentos
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
64
Web Pages com Razor 2
4. O que faz o método RenderSection?
☐☐a) Inclui uma variável de sessão do servidor no cliente.
☐☐b) Substitui o layout por outra página.
☐☐c) Insere um trecho de código no local onde o método é chamado.
☐☐d) Desabilita o Razor.
☐☐e) Permite que um código JavaScript seja inserido no servidor.
65
2 Web Pages
com Razor
Mãos à obra!
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Objetivos:
Laboratório 1
1. No Visual Studio, crie um novo projeto chamado Lab01, em um solution
chamado Capitulo02.Labs. O projeto deve ser do tipo ASP.NET Web
Application, vazio:
68
Web Pages com Razor 2
2. Com o projeto criado, clique com o botão direito do mouse e adicione, como
novo item, a opção MVC 5 View Page (Razor). Coloque o nome de Index:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
</div>
</body>
</html>
69
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Laboratório 2
1. Adicione ao solution Capitulo02.Labs um novo projeto ASP.NET Web
Application vazio chamado Lab02:
2. Torne o projeto Lab02 como projeto Default. Para isso, clique com o botão
direito do mouse sobre o projeto e selecione a opção Set as Startup Project;
70
Web Pages com Razor 2
3. No projeto Lab02, adicione uma Web Page com Razor (MVC 5 View Page
(Razor)) chamada Default.cshtml:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
<header>
</header>
<section>
</section>
<footer>
</footer>
</div>
</body>
</html>
71
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
<header>
<h1>Exemplo Web Page</h1>
<h2>Tecnologias Web - Html, Javascript, Css, Razor e
Web Pages</h2>
</header>
<section>
</section>
<footer>
<hr />
<p>
Desenvolvido para o curso de ASP.NET
© @DateTime.Now.Year - Impacta Tecnologia
</p>
</footer>
</div>
</body>
</html>
6. Adicione ao projeto uma folha de estilo chamada Estilos.css. Para isso, use
o menu Project, Add New Item, Style Sheet;
72
Web Pages com Razor 2
7. Defina os seguintes estilos no arquivo Estilos.css:
body {
font-family: Tahoma;
margin: 0px;
}
header {
padding: 20px;
background-color: #749ca7;
color: #e0eaed;
letter-spacing: -1px;
}
h1, h2 {
margin: 0px;
}
footer {
position: fixed;
bottom: 0px;
width: 100%;
}
footer p {
padding: 5px;
font-size: 70%;
}
section {
max-width: 960px;
padding: 20px;
}
section ul {
padding: 0px;
}
73
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
<link href="~/Estilos.css" rel="stylesheet" />
</head>
<body>
<div>
<header>
<h1>Exemplo Web Page</h1>
<h2>Tecnologias Web - Html, Javascript, Css, Razor e Web
Pages</h2>
</header>
<section>
</section>
<footer>
<hr />
<p>
Desenvolvido para o curso de ASP.NET
© @DateTime.Now.Year - Impacta Tecnologia
</p>
</footer>
</div>
</body>
</html>
74
Web Pages com Razor 2
9. Na página Default.cshtml, insira o conteúdo do item section e visualize a
página:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
<link href="~/Estilos.css" rel="stylesheet" />
</head>
<body>
<div>
<header>
<h1>Exemplo Web Page</h1>
<h2>Tecnologias Web - Html, Javascript, Css, Razor e Web
Pages</h2>
</header>
<section>
<p>
No desenvolvimento Web, diversas tecnologias são
utilizadas.
Algumas são executadas do lado do Servidor(Server) e
outras
são executadas do lado do Cliente(Client).
</p>
<dl>
<dt>
<a href="#">Tecnologias: Client</a>
</dt>
<dd>
<ul>
<li>Html</li>
<li>Css</li>
<li>Javascript</li>
</ul>
</dd>
75
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
<dt>
<a href="#">Tecnologias: Server</a>
</dt>
<dd>
<ul>
<li>C#</li>
<li>ASP.NET</li>
<li>Web Pages</li>
<li>MVC</li>
<li>Razor</li>
</ul>
</dd>
</dl>
</section>
<footer>
<hr />
<p>
Desenvolvido para o curso de ASP.NET
© @DateTime.Now.Year - Impacta Tecnologia
</p>
</footer>
</div>
</body>
</html>
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
<link href="~/Estilos.css" rel="stylesheet" />
</head>
<body>
<div>
<header>
<h1>Exemplo Web Page</h1>
<h2>Tecnologias Web - Html, Javascript, Css, Razor e Web
Pages</h2>
76
Web Pages com Razor 2
</header>
<section>
<p>
No desenvolvimento Web, diversas tecnologias são utilizadas.
Algumas são executadas do lado do Servidor(Server) e outras
são executadas do lado do Cliente(Client).
</p>
<dl>
<dt>
<a href="javascript:exibirEsconderElemento(‘ddClient’)">
Tecnologias: Client
</a>
</dt>
<dd id="ddClient" style="display:none" >
<ul>
<li>Html</li>
<li>Css</li>
<li>Javascript</li>
</ul>
</dd>
<dt>
<a href="javascript:exibirEsconderElemento(‘ddServer’)">
Tecnologias: Server
</a>
</dt>
<dd id="ddServer" style="display:none" >
<ul>
<li>C#</li>
<li>ASP.NET</li>
<li>Web Pages</li>
<li>MVC</li>
<li>Razor</li>
</ul>
</dd>
</dl>
</section>
<footer>
<hr />
<p>
Desenvolvido para o curso de ASP.NET
© @DateTime.Now.Year - Impacta Tecnologia
</p>
</footer>
</div>
77
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
<script>
function exibirEsconderElemento(elementoId) {
var elemento = document.getElementById(elementoId);
if (elemento.style.display == "none") {
elemento.style.display = "block";
}
else {
elemento.style.display = "none";
}
}
</script>
</body>
</html>
78
Web Pages com Razor 2
79
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
<link href="~/Estilos.css" rel="stylesheet" />
</head>
<body>
<div>
<header>
<h1>Exemplo Web Page</h1>
<h2>Tecnologias Web - Html, Javascript, Css, Razor e Web
Pages</h2>
</header>
<section>
<p>
No desenvolvimento Web, diversas tecnologias são
utilizadas.
Algumas são executadas do lado do Servidor(Server) e outras
são executadas do lado do Cliente(Client).
</p>
<dl>
<dt>
<a
href="javascript:exibirEsconderElemento(‘ddClient’)">
Tecnologias: Client
</a>
</dt>
<dd id="ddClient" style="display:none">
<ul>
<li>Html</li>
<li>Css</li>
<li>Javascript</li>
</ul>
</dd>
<dt>
80
Web Pages com Razor 2
<a
href="javascript:exibirEsconderElemento(‘ddServer’)">
Tecnologias: Server
</a>
</dt>
<dd id="ddServer" style="display:none">
<ul>
<li>C#</li>
<li>ASP.NET</li>
<li>Web Pages</li>
<li>MVC</li>
<li>Razor</li>
</ul>
</dd>
</dl>
<p>
<a href="ComentarioForm.cshtml">Deixe seu comentário</a>
</p>
</section>
<footer>
<hr />
<p>
Desenvolvido para o curso de ASP.NET
© @DateTime.Now.Year - Impacta Tecnologia
</p>
</footer>
</div>
<script>
function exibirEsconderElemento(elementoId) {
var elemento = document.getElementById(elementoId);
if (elemento.style.display == "none") {
elemento.style.display = "block";
}
else {
elemento.style.display = "none";
}
}
</script>
</body>
</html>
81
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Lab02.App
{
public static class WebApp
{
public static void ComentarioIncluir(
string nome, string comentario)
{
}
}
15. Na classe WebApp, insira uma propriedade interna para retornar o nome
do arquivo:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Lab02.App
{
public static class WebApp
{
private static string comentarioArquivo
{
get
{
return HttpContext.Current.Server.MapPath("~/
comentarios.txt");
}
}
public static void ComentarioIncluir(
string nome, string comentario)
{
}
82
Web Pages com Razor 2
16. Na classe WebApp, crie o corpo dos métodos. Utilize a diretiva de compilação
using System.IO para acesso às classes StreamWriter e StreamReader e a
diretiva using System.Text para acesso à classe Encoding;
using System;
using System.IO;
using System.Text;
using System.Web;
namespace Lab02.App
{
public static class WebApp
{
private static string comentarioArquivo
{
get
{
return HttpContext.Current.Server.MapPath("~/comentarios.
txt");
}
}
writer.WriteLine($"{nome}: {comentario}\r\n");
}
if (!File.Exists(comentarioArquivo))
{
return texto;
}
return texto;
}
}
}
83
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
<link href="~/Estilos.css" rel="stylesheet" />
</head>
<body>
<div>
<header>
<h1>Exemplo Web Page</h1>
<h2>Html, Javascript, Css, Razor e Web Pages</h2>
<a href="~/Default.cshtml">Voltar</a>
</header>
<section>
<div class="form-grupo">
<label for="nome">Nome:</label>
<input type="text" name="nome" id="nome" />
</div>
<div class="form-grupo">
<label for="comentario">Comentário</label>
<textarea id="comentario" name="comentario">
</textarea>
</div>
<div class="form-botoes">
<input type="submit" value="Enviar" />
</div>
</form>
</section>
</div>
</body>
</html>
84
Web Pages com Razor 2
18. Adicione a página ComentarioProcessar.cshtml. Essa página processa os
dados enviados pelo formulário;
@{
Layout = null;
}
@using Lab02.App;
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
<header>
<h1>Exemplo Web Page</h1>
<h2>Html, Javascript, Css, Razor e Web Pages</h2>
<a href="~/Default.cshtml">Voltar</a>
</header>
<section>
@if (!IsPost)
{
Response.Redirect("~/comentarioForm.cshtml");
}
else
{
if (string.IsNullOrWhiteSpace(nome) ||
string.IsNullOrWhiteSpace(comentario))
{
Response.Redirect("~/comentarioForm.cshtml");
}
else
{
WebApp.ComentarioIncluir(nome, comentario);
}
}
<p>
<a href="~/Default.cshtml">Voltar</a>
<a href="~/ComentarioLista.cshtml">Exibir Comentários</a>
</p>
</section>
</div>
</body>
</html>
85
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@{
Layout = null;
}
@using Lab02.App
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
<header>
<h1>Exemplo Web Page</h1>
<h2>Html, Javascript, Css, Razor e Web Pages</h2>
<a href="~/Default.cshtml">Voltar</a>
</header>
<section>
<p>Comentários:</p>
<pre>
@WebApp.ComentariosObter()
</pre>
</section>
</div>
</body>
</html>
86
Web Pages com Razor 2
20. Teste o projeto completo:
87
ASP.NET MVC
3 – Controllers,
Models e Views
3.1. Introdução
A arquitetura MVC é bem diferente da arquitetura baseada em Web Pages, vista
no Capítulo 2. O mecanismo usado pelo MVC usa as Web Pages não como páginas
executadas no browser, mas como views, ou seja, como componentes que recebem
informações provenientes de algum processamento no servidor. Em outras palavras,
um componente central (o controller) interage com as outras partes do sistema,
como acesso a dados, acesso a arquivos, imagens, etc. e o resultado dessa interação é
apresentado para o usuário na view, através da execução de um método do controller.
Um controller é uma classe como outra qualquer, mas com características especiais
que a definem como tal. Os métodos nessa classe (controller) também recebem outra
denominação: actions. Esses, quando executados, geram algum tipo de resposta para
o usuário, e se essa resposta for um conteúdo HTML, temos as views para apresentá-
la.
90
ASP.NET MVC – Controllers, Models e Views 3
No próximo passo, atribua um nome para o projeto e para o solution. Neste exemplo,
definimos ambos como Capitulo03.MVC. Escolha uma pasta conveniente.
Ao clicar em Create, selecione a opção Empty, mas marque à direita a caixa de seleção
MVC, no grupo nomeado como Add folders & core references. Desmarque a opção
Configure for HTTPS, no grupo Advanced:
91
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
92
ASP.NET MVC – Controllers, Models e Views 3
•• Pasta Models: Esta pasta não faz parte da convenção do MVC, mas recomenda-
se que todas as classes representando os models sejam criadas nesta pasta, por
questões de padronização;
•• Pasta Views: Esta é outra pasta que participa da convenção usada no projeto
MVC. Quando criarmos as views, veremos que deverá haver uma subpasta com
o nome do controller, para conter as Web Pages representando as views para o
controller em questão. Outros arquivos do ciclo de vida da aplicação também
devem estar nesta pasta. Uma subpasta importante (ainda não criada) é a
subpasta Shared. Nela ficam as views comuns a todos os controllers;
Cada uma das partes apresentadas neste tópico será detalhada na medida em que
evoluirmos no desenvolvimento do projeto de exemplo.
3.3. Rotas
Como já apresentado na introdução deste capítulo, em aplicações MVC não executamos
uma página com extensão .cshtml como Web Page. Elas são chamadas pelos actions
dos controllers. Os actions são os métodos desenvolvidos no controller e, quando
executados, produzem uma resposta.
Sendo assim, para que uma resposta seja apresentada para o usuário, devemos
executar, direta ou indiretamente, o action. A chamada ao action acontece por meio
do sistema de rotas.
93
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Capitulo03.MVC
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home",
action = "Index",
id = UrlParameter.Optional }
);
}
}
}
É possível alterá-lo, ou mesmo adicionar novas rotas a esse método. É possível também
alterar os valores padrão especificados na rota default.
94
ASP.NET MVC – Controllers, Models e Views 3
3.3.1. Iniciando a rota
Vimos que o arquivo Global.asax contém métodos que pertencem ao ciclo de vida
da aplicação. Sendo assim, o método Application_Start() é chamado pelo ASP.NET
quando a aplicação é iniciada.
using System.Web.Mvc;
using System.Web.Routing;
namespace Capitulo03.MVC
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
}
3.4. Controllers e Actions
Os controllers compõem a parte fundamental de uma aplicação MVC, principalmente
por conta da arquitetura de mesmo nome.
Como já mencionado, um controller é uma classe contendo métodos capazes de
gerar a camada de interação com o usuário. Estes métodos são chamados de actions.
95
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
3.4.2. Criando o controller
No nosso projeto Capitulo03.MVC, vamos adicionar o controller Home. Vamos utilizar
a rota da forma como definida pelo Visual Studio. Para adicionar um controller, clique
com o botão direito do mouse sobre a pasta Controllers:
96
ASP.NET MVC – Controllers, Models e Views 3
Quando a opção Controller for selecionada, aparecerá esta nova janela:
Como estamos desenvolvendo uma aplicação MVC do zero, utilizaremos a opção MVC
5 Controller – Empty.
Aqui convém uma observação importante: o nome da classe que representa o controller
deve terminar com a palavra Controller. No nosso caso, como pretendemos criar o
controller Home, a classe deve se chamar HomeController.
97
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using System.Web.Mvc;
namespace Capitulo03.MVC.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
}
}
A classe, além de ter o sufixo Controller, deve ser subclasse de Controller, presente
no namespace System.Web.MVC.
Observe que o Visual Studio criou uma nova pasta chamada Home, abaixo da pasta
Views:
98
ASP.NET MVC – Controllers, Models e Views 3
É na pasta Home que ficarão todas as Web Pages que representarão as views produzidas
pelos actions deste controller. Cada controller adicionado ao projeto fará com que
uma pasta com seu nome seja criada como subpasta de Views. Se não fosse pelo
template do Visual Studio, esta pasta deveria ser criada manualmente.
Ele ainda não será usado neste exemplo, pois teremos uma abordagem separada
sobre seu tipo de retorno.
3.4.3. Criando um action
Vamos adicionar nosso primeiro action ao controller Home.
namespace Capitulo03.MVC.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
99
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
http://localhost:53367/
http://localhost:53367/home/index
No caso, a rota /home/index foi admitida como padrão. O erro ocorreu porque não
foi encontrada nenhuma Web Page representando o action Index. Observe que o erro
mostrou o caminho de busca, até que o erro foi gerado, por não ter sido encontrada
nenhuma página para o action mencionado. Observe também que o nome da Web
Page foi buscada com o mesmo nome do action: Index.aspx, Index.ascx, Index.
cshtml ou Index.vbhtml.
No caminho de busca, os arquivos com extensão aspx e ascx foram incluídos por
questões de retrocompatibilidade, já que as versões 1 e 2 do MVC as usavam como
extensão para suas views.
100
ASP.NET MVC – Controllers, Models e Views 3
Para executar o action que criamos – MostrarTexto – vamos escrevê-lo na URL,
seguido do nome do controller:
http://localhost:53367/home/mostrartexto
O retorno especificado como string para este action gerou um conteúdo HTML,
enviado como resposta para o browser.
Este foi um exemplo ilustrativo, e não constitui uma prática comum especificarmos
actions desta forma. A seguir, apresentaremos os retornos de actions mais comuns.
101
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
O retorno deste action é uma chamada ao método View(), que está definido na
classe base Controller, herdado pela classe HomeController. Esse retorno é do tipo
ViewResult, um dos tipos derivados de ActionResult. Essa classe (ActionResult)
representa o resultado de um método disparado por uma URL.
A classe ActionResult é muito importante no ASP.NET MVC, pois é a classe base para
muitas outras classes que retornam dados:
102
ASP.NET MVC – Controllers, Models e Views 3
3.4.5. Criando Views
O método View(), retornado pelo action Index, por ser do tipo ViewResult, requer a
presença de uma view na pasta Views/Home.
O processo de execução de uma view a partir do action pode ser visto na ilustração
a seguir:
Uma maneira prática de criar esta view é clicando com o botão direito do mouse sobre
o método, e selecionado a opção Add View...:
103
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Veja que o nome da view foi considerada como sendo o nome do action (Index).
Clicando em Add, temos o novo arquivo Index.cshtml na pasta Views/Home:
104
ASP.NET MVC – Controllers, Models e Views 3
Index.cshtml
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
</div>
</body>
</html>
É um arquivo com conteúdo HTML, e por ter a extensão .cshtml, admite elementos
Razor.
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
<h1>Bem vindo à view Index</h1>
<h2>Desenvolvida em @DateTime.Now.ToShortDateString()</h2>
<hr/>
</div>
</body>
</html>
105
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Para demonstrar sua utilização, vamos alterar o action Index, enviando um ViewBag
e um ViewData:
namespace Capitulo03.MVC.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
ViewBag.Empresa = "Impacta Tecnologia";
ViewData["endereco"] = "Avenida Paulista, 1009";
return View();
}
106
ASP.NET MVC – Controllers, Models e Views 3
A view recebe estes dados através dos nomes que lhes atribuímos:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
<h1>Bem vindo à view Index</h1>
<h2>Desenvolvida em @DateTime.Now.ToShortDateString()</h2>
<hr />
<span><strong>Empresa:</strong>@ViewBag.Empresa</span> <br/>
<span><strong>Endereço:</strong>@ViewData["endereco"]</span>
</div>
</body>
</html>
Veja o resultado:
107
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Isso nos leva a definir uma view servindo como um template para o objeto desejado.
Esta view, por ter um tipo específico definido, é chamada de View Fortemente Tipada.
3.6.1. Definindo o Model
A camada Model é constituída por classes que fornecem informações e funcionalidades,
de acordo com a regra de negócios requerida na aplicação. As classes que constituem
esta camada são colocadas, preferencialmente, na pasta Models do projeto. O
esquema a seguir ilustra sua utilização:
No projeto, vamos incluir uma classe chamada Produto. O processo para a inclusão
de um model é o mesmo usado para incluir uma classe. Basta selecionar a pasta
Models e, com o botão direito do mouse, adicionar uma nova classe.
namespace Capitulo03.MVC.Models
{
public class Produto
{
public int Id { get; set; }
public string Descricao { get; set; }
public double Preco { get; set; }
}
}
108
ASP.NET MVC – Controllers, Models e Views 3
3.6.2. Consumindo o Model no Controller
Para usar o model no controller, devemos decidir como usá-lo. Podemos ter uma lista
de objetos, uma simples instância ou outras estruturas. No nosso projeto, vamos
adicionar um novo action ao controller Home, o MostrarProduto.
Usamos uma versão sobrecarregada do método View para passar o objeto para a view
– a versão que recebe um object como parâmetro. Veja o esquema a seguir:
109
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Vamos adicionar uma view para o action MostrarProduto. Clique com o botão direito
do mouse sobre o método e selecione a opção Add View (exatamente como fizemos
para a view Index):
Para que a view seja tipada, devemos adicionar a seguinte instrução no início da
página:
@model Capitulo03.MVC.Models.Produto
110
ASP.NET MVC – Controllers, Models e Views 3
Todas as views, independentemente de serem ou não fortemente tipadas, expõem
uma propriedade chamada Model. Seu tipo depende da classe usada na tipagem da
view (no nosso exemplo, a classe Produto).
@model Capitulo03.MVC.Models.Produto
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>MostrarProduto</title>
</head>
<body>
<div>
<h1>Dados do produto recebido:</h1>
<div>
<span><strong>Código:</strong>@Model.Id</span><br />
<span><strong>Descrição:</strong>@Model.Descricao</span><br
/>
<span><strong>Preço:</strong>@Model.Preco.ToString("c")</
span><br />
</div>
</div>
</body>
</html>
A rota executada para esta view, seguindo a convenção do projeto, deverá ser:
http://localhost:53367/Home/MostrarProduto
E o resultado da execução:
111
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
3.7. Elaboração de formulários
Existem situações em que é necessário o usuário fornecer informações em formulários.
Neste caso, estas informações são enviadas também para o Controller.
Na grande maioria das vezes, é utilizado o método GET de uma requisição HTTP, para
exibir dados ou enviar parâmetros de consulta, e o método POST, para enviar dados,
principalmente quando a finalidade do envio é a inclusão ou alteração de informações.
3.7.1. HTML Helpers
A classe HtmlHelper disponibiliza uma série de métodos que podem ser usados para
auxiliar a criação dos elementos do formulário:
Esses métodos não são de uso obrigatório e servem apenas para facilitar a escrita do
código HTML e para evitar a revisão do código quando algum modelo mudar.
Os formulários podem ser escritos com tags HTML puras, mas vale a pena nos
beneficiarmos dos HTML Helpers.
112
ASP.NET MVC – Controllers, Models e Views 3
Visualmente, os componentes duplicados desta forma apresentam o mesmo resultado,
mas são bastante diferentes na sua utilização. Os componentes SEM o sufixo For
não utilizam os Models e, portanto, não constituem uma view fortemente tipada. Já
os componentes COM o prefixo For, quando aplicados, utilizam as propriedades do
Model através de expressões lambda, e são portanto aplicados a views fortemente
tipadas.
Quando executado, este action apenas apresentará a view com os dados do formulário.
Vamos gerar a view para este action, da mesma forma que fizemos nos actions
anteriores. Veja o conteúdo do formulário, usando os HTML Helpers:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>IncluirProduto</title>
</head>
<body>
<div>
<h1>Cadastro de Produtos (simulação)</h1>
@using (Html.BeginForm())
{
@Html.Label("Código")<br />
@Html.TextBox("id")<br />
@Html.Label("Descrição")<br />
@Html.TextBox("descricao")<br />
@Html.Label("Preço")<br />
@Html.TextBox("preco")<br />
113
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Para que o controller receba os dados da view através de uma requisição POST, deve
haver um action apropriado. Vamos escrever este action:
[HttpPost]
public ActionResult IncluirProduto(FormCollection form)
{
//conteudo do action
return View();
}
[HttpPost]
public ActionResult IncluirProduto(FormCollection form)
{
//recebendo os dados do formulário
var id = Convert.ToInt32(form["id"]);
var descricao = form["descricao"];
var preco = Convert.ToDouble(form["preco"]);
114
ASP.NET MVC – Controllers, Models e Views 3
O parâmetro form usa como índice o nome do componente recebido do formulário.
Vale mencionar que, quando usamos a versão do método View que recebe o nome
do action como primeiro parâmetro (de acordo com nosso exemplo), a requisição é a
mesma, só o conteúdo que foi transferido para outra view:
115
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Vamos reproduzir o mesmo exemplo, desta vez definindo um modelo. Para tanto,
adicione o action IncluirProdutoModel no controller Home:
@model Capitulo03.MVC.Models.Produto
@{
Layout = null;
}
116
ASP.NET MVC – Controllers, Models e Views 3
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>IncluirProdutoModel</title>
</head>
<body>
<div>
<h1>Cadastro de Produtos - model (simulação)</h1>
@using (Html.BeginForm())
{
@Html.LabelFor(m => m.Id)<br />
@Html.TextBoxFor(m => m.Id)<br />
[HttpPost]
public ActionResult IncluirProdutoModel(Produto produto)
{
return View("MostrarProduto", produto);
}
117
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Observe que não foi necessário criar um objeto, pois ele já existe! O action recebe um
produto como parâmetro, e este é gerado na view. O processo é ilustrado no esquema
a seguir:
118
ASP.NET MVC – Controllers, Models e Views 3
3.8. Validação e filtros
Validar dados é uma tarefa que exige uma atenção especial em qualquer aplicação. O
processo de verificar e manter a integridade dos dados engloba diversas áreas, como
verificação de tipos de dados, limites, credenciais, criptografia, certificados, meios
de transporte de informação e formatos. Uma parte desse processo é a validação dos
dados que o usuário digita.
3.8.1. Validação
O processo de validação de campos consiste em verificar os seguintes critérios:
•• Faixa: Verificar se o valor está dentro de uma faixa aceitável. Por exemplo, o dia
do mês deve ser um número entre 1 e 31, a data final de um relatório deve ser
maior ou igual à data inicial, e assim por diante;
3.8.2. Data Annotation
O MVC utiliza uma tecnologia chamada Data Annotation, que se resume em declarar
as regras que devem ser aplicadas ao Model. Considere a classe Produto que usamos
no nosso projeto (ela será usada para validação):
namespace Capitulo03.MVC.Models
{
public class Produto
{
public int Id { get; set; }
public string Descricao { get; set; }
public double Preco { get; set; }
}
}
119
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
3.8.2.1. ModelState
Ao clicar no botão Enviar, os dados do formulário são enviados para a URL informada.
O método que receberá os dados do formulário deverá ter as seguintes características:
3. Deve ser verificado se o modelo é válido por meio da propriedade IsValid da classe
ModelStateDictionary, disponibilizada por meio da propriedade ModelState da
classe Controller.
3.8.2.2. Required
Para tornar os campos obrigatórios, é necessário incluir o atributo Required neles.
O namespace em que residem as classes de anotações usadas pelo MVC é o System.
ComponentModel.DataAnnotations.
using System.ComponentModel.DataAnnotations;
namespace Capitulo03.MVC.Models
{
public class Produto
{
[Required]
public int Id { get; set; }
[Required]
public string Descricao { get; set; }
[Required]
public double Preco { get; set; }
}
}
120
ASP.NET MVC – Controllers, Models e Views 3
Na view, adicionamos o elemento @Html.ValidationSummary():
@model Capitulo03.MVC.Models.Produto
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>IncluirProdutoModel</title>
</head>
<body>
<div>
<h1>Cadastro de Produtos - model (simulação)</h1>
@using (Html.BeginForm())
{
@Html.ValidationSummary()
121
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Como já apresentado, não basta incluir este método na view. É necessário verificar
se o modelo é valido quando for enviado para o controller. Usamos para esta tarefa a
propriedade ModelState e, através dela, a propriedade IsValid. A propriedade IsValid
retorna true se o modelo for válido, de acordo com os Data Annotations configurados
no Model, e false quando alguma especificação falhar.
[HttpPost]
public ActionResult IncluirProdutoModel(Produto produto)
{
if (!ModelState.IsValid)
{
return View();
}
return View("MostrarProduto", produto);
}
122
ASP.NET MVC – Controllers, Models e Views 3
@model Capitulo03.MVC.Models.Produto
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>IncluirProdutoModel</title>
</head>
<body>
<div>
<h1>Cadastro de Produtos - model (simulação)</h1>
@using (Html.BeginForm())
{
@Html.ValidationSummary()
123
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
3.8.2.3. StringLength
O atributo StringLength define o tamanho total permitido para um texto. É muito útil
para não truncar informações ao gravar no banco de dados.
namespace Capitulo03.MVC.Models
{
public class Produto
{
[Required]
public int Id { get; set; }
[Required]
[StringLength(30)]
public string Descricao { get; set; }
[Required]
public double Preco { get; set; }
}
}
124
ASP.NET MVC – Controllers, Models e Views 3
É possível alterar a mensagem padrão, definindo o atributo ErrorMessage:
3.8.3. Regular Expression
Usar expressões regulares para validar formatos de entrada é uma das maneiras mais
versáteis que existe. Pode ser usada para qualquer informação que tenha um formato
definido: e-mail, CPF, CNPJ, IP, placa de carro, sigla de estado etc. No exemplo adiante,
a classe Veículo aceita no campo Placa apenas os formatos corretos:
namespace Capitulo03.MVC.Models
{
public class Veiculo
{
[RegularExpression("[A-Z]{3}[0-9]{4}")]
[Required]
public string Placa { get; set; }
[Required]
public int Ano { get; set; }
[Required]
public string Modelo { get; set; }
}
}
3.8.4. Range
O atributo Range define os limites de campos numéricos. Os parâmetros a serem
passados são o valor mínimo e o máximo. A faixa válida inclui esses valores. O
exemplo a seguir exibe um uso típico: o valor mínimo e máximo para a entrada de
um dia do mês.
namespace Capitulo03.MVC.Models
{
public class Pagamento
{
[Range(1, 31)]
public int Mes { get; set; }
125
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
3.8.5. Remote
O atributo Remote permite realizar validações personalizadas no servidor. Por
exemplo, em um programa de vendas, talvez seja necessário consultar o estoque ao
informar uma quantidade. Isso só é possível fazendo uma chamada ao servidor.
3.8.6. Compare
O atributo Compare verifica se duas propriedades de um modelo são iguais. É usado
tipicamente para confirmação de dados:
[Compare("Senha")]
public string RepetirSenha { get; set; }
Observe que, em uma das versões no MVC, existe um conflito entre Remote e
Compare. O atributo Compare estava definido nos namespaces System.Web.Mvc e
System.ComponentModel.DataAnnotations. Não é um problema sério, pois, se isso
acontecer, basta referenciar uma das classes pelo nome completo:
[System.Web.Mvc.Compare("Senha")]
public string RepetirSenha { get; set; }
126
ASP.NET MVC – Controllers, Models e Views 3
3.8.6.1. Display
Alguns atributos interagem com o método Labelfor e TextBoxFor do helper HTML. O
atributo Display determina qual texto será exibido no lugar do nome da propriedade:
•• Model
namespace Capitulo03.MVC.Models
{
public class Produto
{
[Required]
[Display(Name = "Código do produto:")]
public int Id { get; set; }
[Required]
[Display(Name = "Descrição do produto:")]
[StringLength(30)]
public string Descricao { get; set; }
[Required]
[Display(Name = "Preço do produto:")]
public double Preco { get; set; }
}
}
•• Resultado
127
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
•• AutoGeneratedField
Indica se o campo deve ser gerado automaticamente. Isso faz com que o campo fique
somente leitura.
•• Description
using System;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;
namespace Capitulo03.MVC
{
public static class HtmlExtensions
{
public static IHtmlString DescriptionFor<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression)
{
var metadata = ModelMetadata
.FromLambdaExpression(expression, html.ViewData);
var description = metadata.Description;
Essa classe cria o método de extensão DescriptionFor, que pode ser usado para
exibir uma descrição do campo, por exemplo, usando a propriedade Title.
128
ASP.NET MVC – Controllers, Models e Views 3
No caso do helper para exibir a descrição como um toolTip, pode ser usado este
modelo:
@model Capitulo03.MVC.Models.Produto
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>IncluirProdutoModel</title>
</head>
<body>
<div>
<h1>Cadastro de Produtos - model (simulação)</h1>
@using (Html.BeginForm())
{
@Html.ValidationSummary()
129
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using System.ComponentModel.DataAnnotations;
namespace Capitulo03.MVC.Models
{
public class Produto
{
[Required]
[Display(Name = "Código do produto:")]
public int Id { get; set; }
[Required]
[Display(
Name = "Descrição do produto:",
Description = "Fornecer uma breve descrição para o produto")]
[StringLength(30)]
public string Descricao { get; set; }
[Required]
[Display(Name = "Preço do produto:")]
public double Preco { get; set; }
}
}
•• GroupName
130
ASP.NET MVC – Controllers, Models e Views 3
•• Name
•• Order
A ordem em que esse campo aparece. Não precisa ser numerado exatamente. Cada
número atribuído tem um peso. Esta propriedade é utilizada quando o formulário de
um Model é criado automaticamente.
•• Prompt
Texto usado como marca d’água nas caixas de texto. Quando o usuário começa
a digitar, esse texto some. Alguns componentes de interface implementam essa
funcionalidade.
•• ResourceType
Retorna o tipo (Type) do modelo usado nas propriedade Name, Description e Display.
•• ShortName
O nome usado quando a propriedade é exibida em uma tabela, nos títulos das colunas.
Por padrão, o nome da propriedade é exibido ou o texto definido no parâmetro Name
do atributo Display.
•• ReadOnly
3.8.6.2. DisplayFormat
Este atributo define o formato que será usado para exibir os dados de um campo.
namespace Capitulo03.MVC.Models
{
public class Pagamento
{
[Range(1, 31)]
public int Mes { get; set; }
[DisplayFormat(DataFormatString = "{0:c}"]
public decimal Valor { get; set; }
}
}
131
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
3.9. Layout
No Capítulo 2, apresentamos o conceito de layout quando abordamos o Razor. Para
recordar, um layout é uma página comum a todas as views da aplicação, ou de parte
dela. Quando uma view é chamada, seu conteúdo é inserido no layout na parte que
contém a chamada a @RenderBody().
Vamos apresentar o processo de criação de uma view utilizando layout. Para isso,
vamos adicionar um novo action no controller Home chamado IncluirProdutoLayout.
Sua funcionalidade será idêntica a IncluirProdutoModel:
•• No controller
Primeiro, vamos adicionar o action no controller, que será responsável por apresentar
a view:
•• Na View
Quando criarmos a view (clicando com o botão direito do mouse sobre o método),
deixaremos marcada a opção para incluir uma página de layout:
132
ASP.NET MVC – Controllers, Models e Views 3
Observe que não definimos nenhum arquivo de layout, embora possamos selecionar
um arquivo que tenhamos definido.
Quando fizemos esta seleção pela primeira vez, o Visual Studio adicionou diversos
arquivos e pastas no projeto:
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
133
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-
scale=1.0">
<title>@ViewBag.Title - My ASP.NET Application</title>
<link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
<link href="~/Content/bootstrap.min.css" rel="stylesheet" type="text/
css" />
<script src="~/Scripts/modernizr-2.8.3.js"></script>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle"
data-toggle="collapse"
data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@Html.ActionLink("Application name", "Index", "Home",
new { area = "" }, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
</ul>
</div>
</div>
</div>
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
</body>
</html>
134
ASP.NET MVC – Controllers, Models e Views 3
Na view IncluirProdutoLayout.cshtml, temos apenas o conteúdo necessário para ser
inserido no layout:
@{
ViewBag.Title = "IncluirProdutoLayout";
}
<h2>IncluirProdutoLayout</h2>
@model Capitulo03.MVC.Models.Produto
@{
ViewBag.Title = "IncluirProdutoLayout";
}
@using (Html.BeginForm())
{
@Html.ValidationSummary()
135
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
O layout utiliza o bootstrap como framework para a camada de visualização, além das
funcionalidades JavaScript agregadas ao bootstrap. Os arquivos do bootstrap estão na
pasta criada Content, e os arquivos JavaScript, incluindo o JQuery e o próprio arquivo
de script do bootstrap, estão na pasta Scripts. Na pasta fonts estão os ícones do
bootstrap.
136
ASP.NET MVC – Controllers, Models e Views 3
3.9.1. Fluxo de execução do MVC com o layout
@{
Layout = null;
}
137
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Pontos principais
Atente para os tópicos a seguir. Eles devem ser estudados com muita atenção,
pois representam os pontos mais importantes do capítulo.
•• Usamos os Models para especificar o tipo de objeto que uma view irá representar.
Podemos passar objetos do model do controller para a view, bem como criá-los
na view e enviá-los para o controller realizar seu processamento;
•• Formulários não tipados são aqueles cujo conteúdo não possui nenhum vínculo
com algum model; já os formulários tipados possuem seus componentes
vinculados a uma determinada classe, que é o model;
138
ASP.NET MVC
3 – Controllers,
Models e Views
Teste seus conhecimentos
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
140
ASP.NET MVC – Controllers, Models e Views 3
4. Para que uma aplicação MVC siga as convenções usadas neste tipo de
aplicação, o controller deve iniciar com que nome?
☐☐a) Controller
☐☐b) ControllerBase
☐☐c) Action
☐☐d) O nome da classe
☐☐e) O nome do controller
141
ASP.NET MVC
3 – Controllers,
Models e Views
Mãos à obra!
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Objetivos:
Laboratório 1
1. Na pasta Labs, crie um novo projeto chamado Lab.MVC. Use como solution o
nome Capitulo03.Labs. O projeto deverá ser do tipo ASP.NET Web Application,
vazio, com as referencias do MVC:
144
ASP.NET MVC – Controllers, Models e Views 3
145
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
}
}
146
ASP.NET MVC – Controllers, Models e Views 3
5. Clique com o botão direito do mouse sobre o action Index. Selecione a
opção Add View...:
@{
ViewBag.Title = "Home";
}
147
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-
scale=1.0">
<title>@ViewBag.Title - Vendas</title>
<link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
<link href="~/Content/bootstrap.min.css" rel="stylesheet" type="text/
css" />
<script src="~/Scripts/modernizr-2.8.3.js"></script>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle"
data-toggle="collapse" data-target=".navbar-
collapse">
148
ASP.NET MVC – Controllers, Models e Views 3
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@Html.ActionLink("Vendas Impacta", "Index", "Home",
new { area = "" }, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
</ul>
</div>
</div>
</div>
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
</body>
</html>
149
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@{
ViewBag.Title = "Erro";
}
12. Para testar esta nova view, acrescente o seguinte action no controller Home:
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
150
ASP.NET MVC – Controllers, Models e Views 3
13. Execute a aplicação, e chame na URL a seguinte rota (não esqueça de
copiar a sua URL, pois o numero da porta será diferente!):
http://localhost:54401/home/mostrarerro
151
Entity
4 Framework
ÃÃ Preparando o ambiente;
ÃÃ Database First;
ÃÃ Model First;
ÃÃ Code First.
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
4.1. Introdução
O Entity Framework é um componente que facilita o processo de obter informações
de um ou mais bancos de dados e de transferir essas informações para um conjunto
de classes que serão manipuladas por uma aplicação.
•• Code First
•• Model First
154
Entity Framework 4
•• Database First
4.2. Preparando o ambiente
A melhor maneira de preparar um projeto para usar o Entity Framework é usar
o gerenciador de pacotes NuGet. Em um novo projeto chamado Capitulo04.
EntityFramework (Asp.Net MVC), selecione o menu Tools, escolha a opção NuGet
Package Manager e, depois, Manage NuGet Packages for Solution.
155
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
156
Entity Framework 4
Ainda no Solution Explorer, o item packages.config contém a lista de componentes
instalados pelo NuGet.
4.3. Database First
Neste modo, um banco de dados existente é usado, e o Entity Framework gera
as classes. Automaticamente, tabelas, campos, relacionamentos e restrições são
mapeados e se tornam propriedades das classes geradas.
1. No projeto, com o botão direito do mouse sobre ele, escolha a opção Project / Add
New Item;
157
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
158
Entity Framework 4
4. Na janela de conexão, crie uma nova conexão, mantendo a opção para salvar as
configurações no Web.Config marcada:
159
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
160
Entity Framework 4
161
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
9. É possível alterar o modo como são criadas as classes que representam as tabelas
do banco. É importante que toda alteração seja realizada no próprio modelo, pois
não só as classes mas o mapeamento entre estas e o banco de dados também serão
afetados. Vamos alterar os nomes das entidades no modelo e as propriedades de
navegação (aquelas mapeadas como chave primária / estrangeira no banco de dados):
162
Entity Framework 4
A seguir, a listagem das classes Product, Category e Supplier que foi gerada pelo EF:
namespace Capitulo04.EntityFramework
{
using System;
using System.Collections.Generic;
Na classe Product, todo campo que não é obrigatório está marcado como Nullable.
Isso é necessário para não ter um valor imposto como padrão. O banco de dados,
nesse caso, retorna DBNull.Value para um registro não preenchido e o EF converte
para Null. No campo ProductName não há essa abordagem porque o tipo string
aceita Null como um valor válido. String é inerentemente Nullable por ser uma classe.
namespace Capitulo04.EntityFramework
{
using System;
using System.Collections.Generic;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.
Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Product> Products { get; set; }
}
}
163
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
A classe HashSet é a maneira mais rápida de armazenar uma coleção, pois se utiliza
de um Hash (dados criptografados que identificam um usuário). O único problema é
que essa classe não expõe a coleção por acesso com um índice ou chave. Não existe
acesso individual aos elementos.
namespace Capitulo04.EntityFramework
{
using System;
using System.Collections.Generic;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.
Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Product> Products { get; set; }
}
}
164
Entity Framework 4
A classe fornecedores (Supplier) é uma classe simples com uma única associação
(neste diagrama) à outra classe (Products). Um fornecedor pode fornecer diversos
produtos, e cada produto pertence a um único fornecedor. É uma associação do tipo
um-para-muitos.
A seguir, a listagem gerada pelo EF para a classe derivada de DbContext, que centraliza
todas as operações com o banco de dados:
namespace Capitulo04.EntityFramework
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
Repare que o construtor chama o construtor da classe base passando uma string,
que, nesse caso, é uma string de conexão gravada no Web.Config.
Como esse construtor pode ser usado tanto para passar uma string de conexão quanto
o nome de uma string de conexão armazenada, a forma de diferenciar uma da outra é
passar, quando for o nome de uma conexão, a sintaxe name=xxxxx.
•• Na classe DbContext
public NorthwindEntities()
: base("name=NorthwindEntities") { }
165
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
No Web.Config
<connectionStrings>
<add name="NorthwindEntities" connectionString="metadata=res://*/
Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.
Data.SqlClient;provider connection string="data source=.\
SQLEXPRESS;initial catalog=Northwind;integrated security=True;Multiple
ActiveResultSets=True;App=EntityFramework"" providerName="System.
Data.EntityClient" />
</connectionStrings>
166
Entity Framework 4
Vejamos o resultado da execução:
4.4. Model First
Para criar um modelo de domínio em um projeto do Visual Studio, escolha, no menu,
a opção Project / Add New Item. Na caixa de diálogo, escolha, na coluna à esquerda,
o item Data, em seguida o modelo ADO.NET Entity Data Model e insira um nome
para o modelo:
167
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Nesse modo, é possível criar entidades e relacionamentos para depois criar ou utilizar
um banco de dados.
168
Entity Framework 4
4.4.1. Adicionando uma Entity (entidade)
A principal estrutura de dados é a Entity (entidade). Usando o menu de contexto do
EF Designer, escolha Add New / Entity:
169
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
•• Entity Set: O nome do conjunto de elementos para essa entidade. Por padrão, é
o nome do objeto no plural: Produtos, Clientes, Fornecedores;
•• Create key property: Marque esta opção para criar a chave primária;
170
Entity Framework 4
4.4.2. Adicionando propriedades
Clicando com o botão direito sobre o diagrama da classe Produto e, em seguida, em
Add New, existem três tipos de elementos que podem ser adicionados no modelo:
•• Default Value: O valor padrão do campo. Este valor será inserido automaticamente
nos novos registros, se nenhum valor for fornecido para este campo;
•• Getter: Modificador de acesso para leitura. Pode ser Private, Public, Internal e
Protected;
171
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
•• Setter: Modificador de acesso para gravação. Pode ser Private, Public, Internal
e Protected;
172
Entity Framework 4
4.4.3. Adicionando associações
Uma associação define o relacionamento entre duas entidades. No exemplo da
estrutura anterior, cada Produto pertence a uma Categoria, e cada Categoria pode
estar associada a infinitos Produtos. Esse tipo de relação é chamado um-para-muitos.
•• Associação um-para-muitos
Clicando com o botão direito do mouse sobre o design, escolha Add Association
para ter acesso à janela de criação de associação:
173
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
•• Associação um-para-um
O tipo de associação na qual uma instância de uma classe se associa a outra instância
de outra classe é chamado um-para-um.
•• Associação muitos-para-muitos
Neste tipo de associação, uma instância de uma classe pode se referir a diversas
instâncias de outra classe, e essas outras instâncias podem se referir a diversas
instâncias da primeira classe.
Para criar uma relação de muitos-para-muitos é necessária uma tabela (ou entidade)
intermediária (existem outras formas, mas por enquanto o objetivo é ficar o mais
próximo possível da estrutura de um banco de dados).
174
Entity Framework 4
4.4.4. Criando o banco de dados
Uma vez criado o modelo, é hora de associá-lo a um banco de dados. Para criar o
banco de dados, é necessário acionar o menu de contexto do EF Designer e escolher
Generate Database from Model.
A tela de obter conexão aparecerá. Pode ser um banco existente ou um novo banco.
Clicando em New Connection, a tela de criação e conexão com o SQL Server aparecerá.
175
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
O script de criação do banco é gerado. Neste ponto, o script foi gerado em uma nova
janela.
176
Entity Framework 4
Clicando em Finish, o script aparece no Visual Studio e pode ser executado, usando
o menu de contexto:
177
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
4.5. Code First
Em linhas gerais, o Entity Framework, usando o modo Code First, trabalha da seguinte
maneira:
Cada etapa segue algumas convenções, mas fornece, também, todos os recursos
necessários para personalizar a maneira como o banco é criado ou conectado, o
modo como os dados são validados e transferidos para objetos de memória e como
estes são usados para atualizar as informações originais ou criar novas informações.
A seguir, veremos cada uma dessas etapas.
178
Entity Framework 4
4.5.1. Modelo de domínio
Um modelo de domínio é o detalhamento organizado das informações que fazem
parte de um assunto. Esse modelo determina como os dados se relacionam entre si,
a terminologia usada e as características inerentes a cada informação.
namespace Capitulo04.EntityFramework.Models
{
public class Produto
{
public int ProdutoId { get; set; }
public string Nome { get; set; }
public decimal Preco { get; set; }
public int Estoque { get; set; }
}
}
Classes desse tipo, apenas com propriedades, são conhecidas como POCO (Plain Old
CLR Object). Esse tipo de classe é muito usada em arquiteturas que utilizam serviços,
porque é leve e não depende de outras classes, sendo facilmente serializada, ou seja,
transformada em string, XML, stream ou quaisquer dados em série.
namespace Capitulo04.EntityFramework.Models
{
public class Produto
{
public int ProdutoId { get; set; }
public string Nome { get; set; }
public decimal Preco { get; set; }
public int Estoque { get; set; }
179
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Capitulo04.EntityFramework.Models
{
public class Categoria
{
public int CategoriaId { get; set; }
public string Nome { get; set; }
}
}
Cada produto pertence a uma categoria, e cada categoria pode conter diversos
produtos. No modelo baseado em objetos, a categoria à qual um produto pertence
é definida pelo campo do tipo Categoria e não pelo campo CategoriaId. Vejamos o
exemplo:
namespace Capitulo04.EntityFramework.Models
{
public class Produto
{
public int ProdutoId { get; set; }
public string Nome { get; set; }
public decimal Preco { get; set; }
public int Estoque { get; set; }
namespace Capitulo04.EntityFramework.Models
{
public class Categoria
{
public int CategoriaId { get; set; }
public string Nome { get; set; }
180
Entity Framework 4
A coleção de objetos relacionada deve ser uma classe que implementa a interface
ICollection<T> e deve ser sempre inicializada no construtor, conforme o exemplo:
namespace Capitulo04.EntityFramework.Models
{
public class Categoria
{
public Categoria()
{
this.Produtos = new List<Produto>();
}
A interface pode ser declarada no campo relacionado. Isso possibilita criar uma lista
de qualquer classe que implemente aquela interface:
namespace Capitulo04.EntityFramework.Models
{
public class Categoria
{
public Categoria()
{
this.Produtos = new List<Produto>();
}
181
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
4.5.2. Contexto da conexão
Uma vez criado o modelo de domínio, o próximo passo é criar uma classe derivada
de DbContext e propriedades que representem coleções das entidades. Isso equivale
a criar, no banco de dados, tabelas e relacionamentos.
namespace Capitulo04.EntityFramework.Models
{
public class LojaContext : DbContext
{
}
}
Cada propriedade que será mapeada para uma tabela deve ser declarada como uma
coleção do tipo DbSet.
namespace Capitulo04.EntityFramework.Models
{
public class LojaContext : DbContext
{
public DbSet<Categoria> Categorias { get; set; }
public DbSet<Produto> Produtos { get; set; }
}
}
O programa está pronto para ser executado. Para incluir uma categoria de produto na
tabela de produtos, é necessário instanciar a classe derivada DbContext, adicionar
uma instância de uma Entidade (Produto, Categoria) em uma coleção (instância da
classe DbSet<T>) e chamar o método SaveChanges da classe DbContext. O exemplo
a seguir inclui uma categoria:
182
Entity Framework 4
A resposta a todas essas perguntas é a mesma: O Entity Framework usou uma série
de convenções. Vejamos:
•• O nome das tabelas é o nome das classes no plural. Isso nem sempre funciona
em português. A classe Cliente vira a tabela Clientes, mas a classe Produto vira
a tabela Produto e não Produtos;
Um banco de dados é criado no SQL Server com o mesmo nome da classe derivada
de DbContext.
namespace Capitulo04.EntityFramework.Models
{
public class LojaContext : DbContext
{
public LojaContext()
{
183
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Capitulo04.EntityFramework.Models
{
public class LojaContext : DbContext
{
public LojaContext(string conexao) : base(conexao)
{
Usamos a mesma classe DbContext anterior, mas com uma string de conexão definida
no Web.Config:
•• Arquivo Web.Config:
<connectionStrings>
<add name="minhaConexao" providerName="System.Data.SqlClient"
connectionString="Data Source =.\SQLEXPRESS;Initial
Catalog=EmpresaX;Integrated Security=true"/>
</connectionStrings>
184
Entity Framework 4
•• Um parâmetro do tipo DbConnection
namespace Capitulo04.EntityFramework.Models
{
public class LojaContext : DbContext
{
public LojaContext(DbConnection cn) : base(cn, true)
{
Esse construtor aceita dois parâmetros: o primeiro é uma instância de uma classe
derivada de DbConnection, e o segundo é um parâmetro booleano que indica se a
conexão deve ser liberada da memória (método Dispose() da interface IDisposable).
A não ser que haja algum motivo para deixar a conexão aberta, é melhor que o EF
feche a conexão e libere a memória assim que acabar de usar a conexão. Passar o
valor True para o segundo parâmetro deste construtor garante este comportamento.
4.5.3. Mapeamento
É possível definir com exatidão como o .NET Framework vai criar o banco de dados
e quais as definições que existem nos campos. Isso pode ser feito usando atributos
definidos para as classes e propriedades ou sobrepondo o método OnModelCreating
da classe DbContext.
185
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
•• Table
Define o nome da tabela no banco de dados. Se este atributo não foi definido, o nome
da tabela é o nome da classe de entidade no plural.
using System.ComponentModel.DataAnnotations.Schema;
namespace Capitulo04.EntityFramework.Models
{
[Table("Produtos")]
public class Produto
{
public int ProdutoId { get; set; }
public string Nome { get; set; }
public decimal Preco { get; set; }
public int Estoque { get; set; }
•• Column
namespace Capitulo04.EntityFramework.Models
{
[Table("Produtos")]
public class Produto
{
[Column(name: "CodigoDoProduto")]
public int ProdutoId { get; set; }
public string Nome { get; set; }
public decimal Preco { get; set; }
public int Estoque { get; set; }
186
Entity Framework 4
•• Key
Este atributo define um campo da tabela como chave primária. Toda classe no EF
precisa de uma chave primária, mesmo que não exista no banco de dados.
É importante lembrar que, na maioria das vezes, não é preciso indicá-la explicitamente.
Caso o campo seja numérico, tenha o nome Id ou o nome da classe seguido por Id, o
Entity Framework automaticamente o define como chave primária.
namespace Capitulo04.EntityFramework.Models
{
public class Categoria
{
public Categoria()
{
this.Produtos = new List<Produto>();
}
[Key]
public int CategoriaId { get; set; }
public string Nome { get; set; }
[Key]
[Column(Order = 2)]
public int CategoriaId { get; set; }
}
187
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
•• Required
O atributo Required indica que o campo deve ser preenchido com um valor diferente
de Null e de String.Empty (no caso do tipo do campo ser string).
namespace Capitulo04.EntityFramework.Models
{
public class Categoria
{
public Categoria()
{
this.Produtos = new List<Produto>();
}
[Key]
public int CategoriaId { get; set; }
[Required]
public string Nome { get; set; }
Os campos que não são requeridos devem ser declarados como Nullable, pois o
Entity Framework tentará inserir um valor nulo na propriedade:
•• MaxLength
[Required]
[MaxLength(30)]
public string Nome { get; set; }
[MaxLength(-1)]
public byte[] Foto { get; set; }
188
Entity Framework 4
•• NotMapped
Por padrão, todas as propriedades públicas são mapeadas pelo Entity Framework.
Para que uma propriedade não seja gravada ou lida do banco de dados, usa-se o
atributo NotMapped.
[Required]
public string Nome { get; set; }
[NotMapped]
public string NumeroRevisaoProg { get; set; }
}
•• ForeignKey
O atributo ForeignKey define uma chave estrangeira, relacionando duas tabelas (ou
duas entidades). Esse atributo somente é necessário caso não tenha uma propriedade
do tipo da classe relacionada na classe principal.
Por exemplo, no banco de dados Northwind, cada Produto pertence a uma Categoria,
e uma categoria pode pertencer a diversos produtos. Esse relacionamento é gerado
automaticamente ao criarmos, na tabela Produtos, um campo do tipo Categoria:
189
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
No caso da necessidade de usar apenas o Id da tabela, não faz sentido criar um objeto
para cada registro lido. Nesse caso, uma propriedade relacionada a outra tabela
também pode ser mapeada.
[ForeignKey("Categorias")]
public int CategoriaId { get; set; }
•• DatabaseGenerated
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public decimal PrecoPromocao { get; protected set; }
}
•• None = 0;
•• Identity = 1;
•• Computed = 2.
O Entity Framework não aceita campos calculados (até a versão 6.0), mas isso está
previsto para as futuras versões.
190
Entity Framework 4
4.5.4. Mapeamento por código
Tudo que é possível definir aplicando atributos pode ser definido via código. Isso
abre algumas possibilidades interessantes, como criar o mapeamento dinamicamente
em tempo de execução. Uma tabela pode ter um campo requerido para um tipo de
usuário e não obrigatório para outro tipo. Ao fazer login, o sistema pode analisar
as permissões do usuário e definir as regras de negócio que devem ser aplicadas.
Usando atributos, seria necessário duplicar as classes em áreas diferentes ou escrever
o código para cada situação.
4.5.4.1. Método OnModelCreating
O método OnModelCreating da classe DbContext pode ser sobrescrito para alterar
a maneira como o modelo de domínio é criado. Este método espera receber um
parâmetro do tipo DbModelBuilder.
O exemplo a seguir mostra como configurar para definir o campo Nome da tabela
Produto como Required (requerido):
modelBuilder.Entity<Produto>()
.Property(d => d.Nome)
.IsRequired();
}
191
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
•• Classe DbModelBuilder
Esta classe é o ponto central do Entity Framework, quando é usado o modo Code
First. Por meio de uma instância desta classe enviada ao método OnmodelCreating,
é possível configurar todos os mapeamentos.
•• Classe EntityTypeConfiguration<T>
192
Entity Framework 4
Vejamos, a seguir, seus principais métodos:
O exemplo a seguir define o nome de uma tabela e a chave primária do tipo identity:
protected override
void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
193
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
194
Entity Framework 4
4.5.5. Database Initializer
Quando uma classe mapeada é utilizada, o Entity Framework inicia o processo de
criação ou conexão com o banco de dados existente. Este processo inicial pode ser
definido de diversas maneiras: o banco de dados pode ser excluído e criado a cada
mudança de estrutura, pode ter um processo de migração controlado e pode reverter
uma migração que tenha apresentado alguma falha.
Por padrão, o Entity Framework vai criar o banco de dados apenas se este não existir
no servidor informado. Esse processo todo é feito usando classes que implementam a
interface IdatabaseInitializer. As seguintes classes (Providers) fazem parte do Entity
Framework:
static LojaContext()
{
Database.SetInitializer(
new DropCreateDatabaseAlways<LojaContext>());
195
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Pontos principais
Atente para os tópicos a seguir. Eles devem ser estudados com muita atenção,
pois representam os pontos mais importantes do capítulo.
196
4 Entity
Framework
Teste seus conhecimentos
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
198
Entity Framework 4
4. Para sincronizarmos um objeto com o banco de dados, usamos qual
método?
☐☐a) Add
☐☐b) Insert
☐☐c) SaveChanges
☐☐d) Update
☐☐e) Include
199
4 Entity
Framework
Mãos à obra!
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Objetivos:
Laboratório 1
A - Preparação do projeto, do banco de dados e das entidades
1. Na pasta Labs, crie uma solution vazia (Blank solution) chamada Capitulo04.
Labs;
USE MASTER;
GO
USE DB_VENDAS;
GO
202
Entity Framework 4
CREATE TABLE TBPedidos
(
Id int IDENTITY(1,1) not null,
DocCliente varchar(14) not null,
Data datetime not null,
NumeroPedido varchar(20) not null,
PRIMARY KEY (Id),
FOREIGN KEY (DocCliente) REFERENCES TBClientes (Documento)
);
GO
203
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
5. Uma vez que o banco de dados tenha sido criado, vamos definir os recursos
do Entity Framework na aplicação. Na pasta Models, clique com o botão
direito do mouse e selecione Add / New Item / ADO.NET Entity Data Model.
Atribua o nome VendasModel:
204
Entity Framework 4
6. Na próxima etapa, selecione a opção EF Designer From Database:
205
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
206
Entity Framework 4
10. Vale lembrar que, na elaboração deste material, a conexão usada não
utilizou usuário e senha. Caso sejam utilizados, deixe a opção "Yes, include
the sensitive data in the connection string". Esta opção pode ser vista na
tela a seguir. Inclua o nome DbVendasEntities para a conexão:
207
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
208
Entity Framework 4
14. Por questões de regras de nomenclatura, atualizaremos os nomes das
classes, das propriedades de navegação e dos Entity Sets. Para esta tarefa,
é imprescindível que as alterações sejam realizadas no modelo, pois o
mapeamento entre as classes e o banco de dados são alterados pelo Visual
Studio. Jamais devemos realizar alterações diretamente nas classes, sob o
risco de perdermos o benefício da atualização do mapeamento. Faremos as
seguintes alterações:
Propriedades
Tabela Classe Entity Set
de Navegação
TBClientes Cliente Pedidos Clientes
ClienteInfo
TBPedidos Pedido Pedidos
Itens
PedidoInfo
TBItens Item Itens
ProdutoInfo
Itens
TBProdutos Produto Produtos
CategoriaInfo
TBCategorias Categoria Produtos Categorias
209
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
VendasModel.Context.cs
B - Cadastro de Clientes
using Lab.MVC.Models;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace Lab.MVC.Data
{
public class ClientesDao
{
//método para incluir um novo cliente
public static void IncluirCliente(Cliente cliente)
{
using(var ctx = new DbVendasEntities())
{
ctx.Clientes.Add(cliente);
ctx.SaveChanges();
}
}
210
Entity Framework 4
//método para listar todos os clientes
public static IEnumerable<Cliente> ListarClientes()
{
using (var ctx = new DbVendasEntities())
{
return ctx.Clientes.ToList();
}
}
}
}
17. Para que o cadastro seja consistente, vamos adicionar alguns validadores
na classe Cliente. Abra a classe e adicione os atributos indicados:
namespace Lab.MVC.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
[Required]
[StringLength(14)]
public string Documento { get; set; }
211
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
[Required]
public string Nome { get; set; }
[Required]
public string Telefone { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
"CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Pedido> Pedidos { get; set; }
}
}
18. Com estes validadores, e com a classe ClientesDao criada, estamos prontos
para definir os recursos para cadastro de clientes. Na pasta Controllers,
adicione um novo controller chamado ClientesController. O código da classe
gerada deve ser semelhante ao mostrado a seguir:
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class ClientesController : Controller
{
// GET: Clientes
public ActionResult Index()
{
return View();
}
}
}
19. A partir do action Index, adicione uma view sem modelo. Esta view
representará o menu de opções acerca do cadastro de clientes. Nela, teremos
as opções para incluir um novo cliente e para listar os clientes. Clique com o
botão direito do mouse sobre o action e selecione a opção Add View:
212
Entity Framework 4
20. Na view gerada, inclua as seguintes instruções:
@{
ViewBag.Title = "Clientes";
}
<ul>
<li>@Html.ActionLink("Incluir novo cliente", "Incluir")</li>
<li>@Html.ActionLink("Listar Clientes","Listar")</li>
</ul>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - Vendas</title>
<link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
<link href="~/Content/bootstrap.min.css" rel="stylesheet" type="text/
css" />
<script src="~/Scripts/modernizr-2.8.3.js"></script>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@Html.ActionLink("Vendas Impacta", "Index", "Home",
new { area = "" }, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>
@Html.ActionLink("Clientes", "Index", "Clientes")
</li>
</ul>
</div>
</div>
</div>
213
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
</body>
</html>
22. Execute a aplicação para verificar se o novo action está sendo chamado
corretamente:
214
Entity Framework 4
23. Agora vamos preparar para incluir um novo cliente. Adicione o action
Incluir no controller Clientes. A versão GET do método acessará o formulário,
e a versão POST receberá os dados do formulário e os enviará para o banco de
dados. Como usaremos um formulário tipado, nossa view terá a referência à
classe Cliente. Neste caso, ao criar a view, selecionaremos o template Create,
com a classe Cliente como modelo. Primeiro, escreveremos o método no
controller:
using Lab.MVC.Data;
using Lab.MVC.Models;
using System;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class ClientesController : Controller
{
// GET: Clientes
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Incluir()
{
return View();
}
[HttpPost]
public ActionResult Incluir(Cliente cliente)
{
if (!ModelState.IsValid)
{
return View();
}
try
{
ClientesDao.IncluirCliente(cliente);
return RedirectToAction("Listar");
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
}
}
Observe que escrevemos o action Incluir na versão POST com um bloco try
/ catch, transferindo o usuário para a página de erro, caso algum ocorra. Em
caso de inclusão com sucesso, transferimos o usuário para a lista de clientes.
Esta tarefa será executada corretamente quando tivermos o action responsável
pela listagem devidamente implementado;
215
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@model Lab.MVC.Models.Cliente
@{
ViewBag.Title = "Incluir";
}
<h2>Incluir Cliente</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
//código omitido
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Incluir Cliente"
class="btn btn-info" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Voltar para o início", "Index")
</div>
216
Entity Framework 4
Faremos estas mesmas alterações nas demais views;
26. Execute a aplicação e, no menu de opções, chame esta view para verificar
se está tudo correto:
using Lab.MVC.Data;
using Lab.MVC.Models;
using System;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class ClientesController : Controller
{
// GET: Clientes
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Incluir()
{
return View();
}
[HttpPost]
public ActionResult Incluir(Cliente cliente)
{
217
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
if (!ModelState.IsValid)
{
return View();
}
try
{
ClientesDao.IncluirCliente(cliente);
return RedirectToAction("Listar");
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
}
}
218
Entity Framework 4
29. Na view produzida, também realizaremos alguns ajustes funcionais:
@model IEnumerable<Lab.MVC.Models.Cliente>
@{
ViewBag.Title = "Listar";
}
<h2>Listar Clientes</h2>
<p>
@Html.ActionLink("Novo Cliente", "Incluir")
</p>
<table class="table table-striped">
<tr>
<th>
@Html.DisplayNameFor(model => model.Documento)
</th>
<th>
@Html.DisplayNameFor(model => model.Nome)
</th>
<th>
@Html.DisplayNameFor(model => model.Telefone)
</th>
<th>
@Html.DisplayNameFor(model => model.Email)
</th>
<th></th>
</tr>
</table>
219
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
220
Entity Framework 4
using Lab.MVC.Data;
using Lab.MVC.Models;
using System;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class ClientesController : Controller
{
// GET: Clientes
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Incluir()
{
return View();
}
[HttpPost]
public ActionResult Incluir(Cliente cliente)
{
if (!ModelState.IsValid)
{
return View();
}
try
{
ClientesDao.IncluirCliente(cliente);
return RedirectToAction("Listar");
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
221
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
}
}
222
Entity Framework 4
Realizamos as mesmas alterações no botão submit e no link que havíamos
feito na ocasião da inclusão do cliente;
223
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@model Lab.MVC.Models.Cliente
@{
ViewBag.Title = "Remover";
}
<h2>Remover</h2>
<h3 class="text-danger">
Tem certeza que deseja remover este cliente ?
</h3>
<div>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Documento)
</dt>
<dd>
@Html.DisplayFor(model => model.Documento)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Nome)
</dt>
<dd>
@Html.DisplayFor(model => model.Nome)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Telefone)
</dt>
<dd>
@Html.DisplayFor(model => model.Telefone)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Email)
</dt>
<dd>
@Html.DisplayFor(model => model.Email)
</dd>
</dl>
224
Entity Framework 4
@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
using Lab.MVC.Data;
using Lab.MVC.Models;
using System;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class ClientesController : Controller
{
// GET: Clientes
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Incluir()
{
return View();
}
[HttpPost]
public ActionResult Incluir(Cliente cliente)
{
if (!ModelState.IsValid)
{
return View();
}
try
{
ClientesDao.IncluirCliente(cliente);
225
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
return RedirectToAction("Listar");
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
226
Entity Framework 4
[HttpPost]
public ActionResult Alterar(Cliente cliente)
{
try
{
ClientesDao.AlterarCliente(cliente);
return RedirectToAction("Listar");
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
[HttpPost]
public ActionResult Remover(Cliente cliente)
{
try
{
ClientesDao.RemoverCliente(cliente);
return RedirectToAction("Listar");
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
}
}
227
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
C - Cadastro de Produtos
using Lab.MVC.Models;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace Lab.MVC.Data
{
public class ProdutosDao
{
//método para incluir um novo produto
public static void IncluirProduto(Produto produto)
{
using (var ctx = new DbVendasEntities())
{
ctx.Produtos.Add(produto);
ctx.SaveChanges();
}
}
228
Entity Framework 4
//método para alterar um produto
public static void AlterarProduto(Produto produto)
{
using (var ctx = new DbVendasEntities())
{
ctx.Entry<Produto>(produto).State = EntityState.Modified;
ctx.SaveChanges();
}
}
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class ProdutosController : Controller
{
// GET: Produtos
public ActionResult Index()
{
return View();
}
}
}
229
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@{
ViewBag.Title = "Produtos";
}
<ul>
<li>@Html.ActionLink("Incluir novo produto", "Incluir")</li>
<li>@Html.ActionLink("Listar Produtos", "Listar")</li>
</ul>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - Vendas</title>
<link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
<link href="~/Content/bootstrap.min.css" rel="stylesheet" type="text/
css" />
<script src="~/Scripts/modernizr-2.8.3.js"></script>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@Html.ActionLink("Vendas Impacta", "Index", "Home",
new { area = "" }, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>
@Html.ActionLink("Clientes", "Index", "Clientes")
</li>
<li>
@Html.ActionLink("Produtos", "Index", "Produtos")
</li>
</ul>
</div>
</div>
</div>
230
Entity Framework 4
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>© @DateTime.Now.Year - Gestão de Vendas -
Turma ASP.NET - Impacta</p>
</footer>
</div>
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
</body>
</html>
41. Vamos agora escrever o action Incluir e, em seguida, a sua view. Neste
caso, a view permitirá a seleção de uma imagem e, por conta disso, o formulário
deve ser definido como multipart, já que a imagem trafega em um local
diferente do corpo da requisição. Observe que no action nós buscamos as
categorias e enviamos seu conteúdo para a view através de um ViewBag. Esta
tarefa é bastante comum, uma vez que nossa view está tipada para Produto, e
a categoria será uma propriedade do produto:
using Lab.MVC.Data;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class ProdutosController : Controller
{
// GET: Produtos
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Incluir()
{
ViewBag.ListaDeCategorias = new SelectList(
ProdutosDao.ListarCategorias(), "Id", "Descricao");
return View();
}
}
}
231
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Com esses dados prontos, vamos gerar a view para inclusão de produtos:
@model Lab.MVC.Models.Produto
@{
ViewBag.Title = "Incluir Produto";
}
<h2>Incluir Produto</h2>
<div class="form-horizontal">
<hr />
232
Entity Framework 4
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.IdCategoria, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model => model.IdCategoria,
(SelectList)ViewBag.ListaDeCategorias,
new { @class = "form-control" })
<div class="form-group">
@Html.LabelFor(model => model.Descricao, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Descricao, new { htmlAttributes =
new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Descricao, "",
new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Unidade, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Unidade, new { htmlAttributes =
new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Unidade, "",
new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Preco, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Preco, new
{
htmlAttributes =
new { @class = "form-control" }
})
@Html.ValidationMessageFor(model => model.Preco, "",
new { @class = "text-danger" })
</div>
</div>
233
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@*<div class="form-group">
@Html.LabelFor(model => model.MimeType, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.MimeType,
new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.MimeType, "",
new { @class = "text-danger" })
</div>
</div>*@
<div class="form-group">
@Html.LabelFor(model => model.Foto, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" name="Image" class="form-control" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Incluir Produto"
class="btn btn-info" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Voltar para o início", "Index")
</div>
using Lab.MVC.Data;
using Lab.MVC.Models;
using System;
using System.Web;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class ProdutosController : Controller
{
// GET: Produtos
public ActionResult Index()
{
return View();
}
234
Entity Framework 4
[HttpGet]
public ActionResult Incluir()
{
ViewBag.ListaDeCategorias = new SelectList(
ProdutosDao.ListarCategorias(), "Id", "Descricao");
return View();
}
[HttpPost]
public ActionResult Incluir(Produto produto, HttpPostedFileBase
image)
{
if (!ModelState.IsValid)
{
return Incluir();
}
try
{
if (image != null)
{
produto.MimeType = image.ContentType;
byte[] bytes = new byte[image.ContentLength];
image.InputStream.Read(bytes, 0, image.ContentLength);
produto.Foto = bytes;
}
ProdutosDao.IncluirProduto(produto);
return RedirectToAction("Listar");
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
}
}
235
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Lab.MVC.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
[Required]
[Display(Name = "Categoria")]
public int IdCategoria { get; set; }
[Required]
[Display(Name = "Descrição")]
public string Descricao { get; set; }
[Required]
public string Unidade { get; set; }
[MaxLength]
public byte[] Foto { get; set; }
[HiddenInput(DisplayValue = false)]
public string MimeType { get; set; }
[DataType(DataType.Currency)]
public Nullable<double> Preco { get; set; }
O atributo HiddenInput faz com que o elemento fique oculto nas listagens ou
em outras formas de apresentação;
236
Entity Framework 4
45. A inclusão do produto redirecionará a view para a lista de produtos. Então
vamos deixá-la pronta. No controller ProdutosController, vamos incluir o
action Listar e, além deste action, devemos adicionar outro action que retorna
a referência à imagem que será exibida na listagem. Este action se chamará
BuscarFoto. Vamos então escrever os dois:
using Lab.MVC.Data;
using Lab.MVC.Models;
using System;
using System.Web;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class ProdutosController : Controller
{
// GET: Produtos
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Incluir()
{
ViewBag.ListaDeCategorias = new SelectList(
ProdutosDao.ListarCategorias(), "Id", "Descricao");
return View();
}
[HttpPost]
public ActionResult Incluir(Produto produto, HttpPostedFileBase
image)
{
if (!ModelState.IsValid)
{
return Incluir();
}
try
{
if (image != null)
{
produto.MimeType = image.ContentType;
byte[] bytes = new byte[image.ContentLength];
image.InputStream.Read(bytes, 0, image.ContentLength);
produto.Foto = bytes;
}
ProdutosDao.IncluirProduto(produto);
return RedirectToAction("Listar");
}
catch (Exception ex)
{
237
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
46. Agora vamos gerar a listagem de produtos. Incluiremos apenas o link para
alterar o produto. Além disso, na listagem, apresentaremos a foto do produto
em tamanho reduzido:
238
Entity Framework 4
@model IEnumerable<Lab.MVC.Models.Produto>
@{
ViewBag.Title = "Listar";
}
<h2>Listar Produtos</h2>
<p>
@Html.ActionLink("Novo produto", "Incluir")
</p>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.Descricao)
</th>
<th>
@Html.DisplayNameFor(model => model.Unidade)
</th>
<th>
@Html.DisplayNameFor(model => model.Preco)
</th>
<th>
@Html.DisplayNameFor(model => model.Foto)
</th>
<th></th>
</tr>
<td>
@Html.DisplayFor(modelItem => item.Descricao)
</td>
<td>
@Html.DisplayFor(modelItem => item.Unidade)
</td>
<td>
@Html.DisplayFor(modelItem => item.Preco)
</td>
<td>
<img src="@Url.Action("BuscarFoto", "Produtos",
new { id = item.Id })" style="width:100px;"/>
</td>
<td>
@Html.ActionLink("Alterar", "Alterar", new { id=item.Id })
</td>
</tr>
}
</table>
239
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
240
Entity Framework 4
47. O procedimento de alteração é semelhante ao de inclusão no que diz
respeito ao formulário e à listagem. Porém, um detalhe que deve ser observado
é que o usuário pode alterar os dados do produto, mas não alterar a imagem.
Neste caso, devemos manter a mesma imagem. Para isso, devemos guardar
o valor das propriedades Foto e MimeType em um campo oculto na view. Se
a foto for alterada, estas propriedades serão substituídas pela nova. Vamos
então incluir, no controller, o action Alterar, nas versões GET e POST:
using Lab.MVC.Data;
using Lab.MVC.Models;
using System;
using System.Web;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class ProdutosController : Controller
{
// GET: Produtos
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Incluir()
{
ViewBag.ListaDeCategorias = new SelectList(
ProdutosDao.ListarCategorias(), "Id", "Descricao");
return View();
}
[HttpPost]
public ActionResult Incluir(Produto produto, HttpPostedFileBase
image)
{
if (!ModelState.IsValid)
{
return Incluir();
}
try
{
if (image != null)
{
produto.MimeType = image.ContentType;
byte[] bytes = new byte[image.ContentLength];
image.InputStream.Read(bytes, 0, image.ContentLength);
produto.Foto = bytes;
}
241
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
ProdutosDao.IncluirProduto(produto);
return RedirectToAction("Listar");
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
[HttpGet]
public ActionResult Alterar(int? id)
{
try
{
if(id == null)
{
throw new Exception("Nenhum código fornecido");
}
242
Entity Framework 4
[HttpPost]
public ActionResult Alterar(Produto produto, HttpPostedFileBase
image)
{
if (!ModelState.IsValid)
{
return Alterar(produto.Id);
}
try
{
if (image != null)
{
produto.MimeType = image.ContentType;
byte[] bytes = new byte[image.ContentLength];
image.InputStream.Read(bytes, 0, image.ContentLength);
produto.Foto = bytes;
}
ProdutosDao.AlterarProduto(produto);
return RedirectToAction("Listar");
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
}
}
243
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@model Lab.MVC.Models.Produto
@{
ViewBag.Title = "Alterar";
}
<h2>Alterar Produtos</h2>
<div class="form-horizontal">
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.Id)
@Html.HiddenFor(model => model.Foto)
@Html.HiddenFor(model => model.MimeType)
<div class="form-group">
@Html.LabelFor(model => model.IdCategoria, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model => model.IdCategoria,
(SelectList)ViewBag.ListaDeCategorias,
new { @class = "form-control" })
<div class="form-group">
@Html.LabelFor(model => model.Descricao, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Descricao, new {
htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Descricao, "",
new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Unidade, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Unidade,
new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Unidade, "",
new { @class = "text-danger" })
</div>
</div>
244
Entity Framework 4
<div class="form-group">
@Html.LabelFor(model => model.Preco, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Preco, new
{
htmlAttributes =
new { @class = "form-control" }
})
@Html.ValidationMessageFor(model => model.Preco, "",
new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Foto, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" name="Image" class="form-control" />
</div>
</div>
@*<div class="form-group">
@Html.LabelFor(model => model.MimeType, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.MimeType, new
{ htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.MimeType, "",
new { @class = "text-danger" })
</div>
</div>*@
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Alterar Produto"
class="btn btn-Info" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Voltar para o início", "Index")
</div>
245
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
D - Cadastro de Pedidos
namespace Lab.MVC.Models
{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
[Required]
[Display(Name = "Cliente")]
public string DocCliente { get; set; }
[Required]
[Display(Name = "Data do Pedido")]
[DataType(DataType.Date)]
public System.DateTime Data { get; set; }
[Required]
[Display(Name = "Nº do Pedido")]
public string NumeroPedido { get; set; }
246
Entity Framework 4
51. Crie a classe PedidosDao na pasta Data, com os métodos para incluir,
remover, buscar e listar os pedidos. A listagem pode ser por cliente, ou todos
os pedidos (quaisquer métodos necessários ao longo do desenvolvimento, nós
incluiremos oportunamente):
using Lab.MVC.Models;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace Lab.MVC.Data
{
public class PedidosDao
{
//método para incluir um novo pedido
public static void IncluirPedido(Pedido pedido)
{
using (var ctx = new DbVendasEntities())
{
ctx.Pedidos.Add(pedido);
ctx.SaveChanges();
}
}
247
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class PedidosController : Controller
{
// GET: Pedidos
public ActionResult Index()
{
return View();
}
}
}
53. Crie a view para o action Index. Esta view representará a página de gestão
de pedidos, assim como aconteceu com clientes e produtos:
@{
ViewBag.Title = "Pedidos";
}
<ul>
<li>@Html.ActionLink("Incluir novo pedido", "Incluir")</li>
<li>@Html.ActionLink("Listar pedidos por cliente", "Listar")</li>
</ul>
54. Adicione o link para este item no layout, logo após o link para Produtos;
248
Entity Framework 4
55. Em PedidosController, adicione o action Incluir, versões GET e POST. Na
versão GET, devemos levar a lista de clientes para que o usuário o selecione,
de forma a criar o pedido para ele. A lista de clientes será enviada através
de um objeto ViewBag. Escreva também o action Listar, que deverá receber
opcionalmente o documento do cliente, de forma a apresentar a lista dos
pedidos de um determinado cliente:
using Lab.MVC.Data;
using Lab.MVC.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class PedidosController : Controller
{
// GET: Pedidos
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Incluir()
{
ViewBag.ListaDeClientes = new SelectList(
ClientesDao.ListarClientes(), "Documento", "Nome");
return View();
}
[HttpPost]
public ActionResult Incluir(Pedido pedido)
{
pedido.Data = DateTime.Now;
if (!ModelState.IsValid)
{
return Incluir();
}
try
{
PedidosDao.IncluirPedido(pedido);
249
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
Observe que o action Listar também recebe a lista de clientes, pois ela será
usada em um formulário para que o usuário possa selecionar o cliente e
apresentar seus pedidos. Após a inclusão do pedido, o usuário é direcionado
para a listagem, já com o numero do documento passado como parâmetro;
250
Entity Framework 4
@model Lab.MVC.Models.Pedido
@{
ViewBag.Title = "Incluir";
}
<h2>Incluir Pedido</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.DocCliente, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(
model => model.DocCliente,
(SelectList)ViewBag.ListaDeClientes,
new { @class = "form-control" })
<div class="form-group">
@Html.LabelFor(model => model.NumeroPedido, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.NumeroPedido,
new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.NumeroPedido, "",
new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Incluir Pedido"
class="btn btn-info" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Voltar para o menu", "Index")
</div>
251
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@model IEnumerable<Lab.MVC.Models.Pedido>
@{
ViewBag.Title = "Listar";
}
<p>
@Html.ActionLink("Novo pedido", "Incluir")
</p>
<div class="form-group">
@Html.Label("Cliente", htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("id",
(SelectList)ViewBag.ListaDeClientes,
"TODOS",
new { @class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Buscar Pedidos"
class="btn btn-info" />
</div>
</div>
<hr/>
</div>
}
252
Entity Framework 4
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.DocCliente)
</th>
<th>
@Html.DisplayNameFor(model => model.Data)
</th>
<th>
@Html.DisplayNameFor(model => model.NumeroPedido)
</th>
<th></th>
</tr>
</table>
253
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using Lab.MVC.Data;
using Lab.MVC.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class PedidosController : Controller
{
254
Entity Framework 4
// GET: Pedidos
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Incluir()
{
ViewBag.ListaDeClientes = new SelectList(
ClientesDao.ListarClientes(), "Documento", "Nome");
return View();
}
[HttpPost]
public ActionResult Incluir(Pedido pedido)
{
pedido.Data = DateTime.Now;
if (!ModelState.IsValid)
{
return Incluir();
}
try
{
PedidosDao.IncluirPedido(pedido);
255
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
PedidosDao.RemoverPedido(pedido);
return Redirect("/Pedidos/Listar?id=" + doc);
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
}
}
E - Cadastro de Itens
256
Entity Framework 4
Para elucidar estas novas inclusões, iniciaremos com a inclusão de validadores
e formatadores na entidade Item:
namespace Lab.MVC.Models
{
using System.ComponentModel.DataAnnotations;
[Required]
[Display(Name = "Produto")]
public int IdProduto { get; set; }
[Required]
[Display(Name = "Quantidade")]
public double Quantidade { get; set; }
61. Para adicionar um novo item, nós deveremos selecionar o pedido (incluindo
o nome do cliente), o produto e a quantidade. Com estas informações,
adicionaremos o item ao pedido.
namespace Lab.MVC.ViewModels
{
public class ClientePedidoViewModel
{
public string Documento { get; set; }
public string NomeCliente { get; set; }
public string NumeroPedido { get; set; }
public int IdPedido { get; set; }
}
}
257
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Lab.MVC.ViewModels
{
public class ItensPedidoViewModel
{
public int IdItem { get; set; }
public double QuantItens { get; set; }
public double TotalItem { get; set; }
public string DescProduto { get; set; }
public int IdPedido { get; set; }
public string NumeroPedido { get; set; }
}
}
63. Agora vamos criar a classe ItensDao na pasta Data. Nesta classe, incluiremos
os métodos para listar os pedidos com os nomes dos clientes, e para listar os
itens por pedido, conforme descrito. Observe a sintaxe usada nos métodos:
using Lab.MVC.Models;
using Lab.MVC.ViewModels;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace Lab.MVC.Data
{
public class ItensDao
{
public static void IncluirItem(Item item)
{
using(var ctx = new DbVendasEntities())
{
ctx.Entry<Item>(item).State = EntityState.Added;
ctx.SaveChanges();
}
}
258
Entity Framework 4
public static Item BuscarItem(int id)
{
using (var ctx = new DbVendasEntities())
{
return ctx.Itens.FirstOrDefault(p => p.Id == id);
}
}
return lista.ToList();
}
}
259
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class ItensController : Controller
{
// GET: Itens
public ActionResult Index()
{
return View();
}
}
}
65. Assim como fizemos nos demais controllers neste projeto, vamos adicionar
uma view como página inicial do núcleo de inclusão de itens. Para tanto, gere
uma view a partir do action Index:
@{
ViewBag.Title = "Produtos";
}
<ul>
<li>@Html.ActionLink("Incluir novo item", "Incluir")</li>
</ul>
260
Entity Framework 4
66. Inclua o acesso a este action no menu de opções no Layout, logo após o
link par aos pedidos;
using Lab.MVC.Data;
using Lab.MVC.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class ItensController : Controller
{
// GET: Itens
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Incluir(int? idPedido)
{
try
{
ViewBag.ListaDeProdutos = new SelectList(
ProdutosDao.ListarProdutos(), "Id", "Descricao");
ViewBag.ListaDePedidos = new SelectList(
ItensDao.ListarPedidos(), "IdPedido", "NomeCliente");
ViewBag.ListaDeItens = ItensDao.ListarItensPorPedido(idPedido);
return View();
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
261
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
[HttpPost]
public ActionResult Incluir(Item item, int? idPedido)
{
if (!ModelState.IsValid)
{
return Incluir(idPedido);
}
try
{
item.IdPedido = (int)idPedido;
ItensDao.IncluirItem(item);
return RedirectToAction("Incluir", new {
idPedido = (int)idPedido });
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
}
}
262
Entity Framework 4
@model Lab.MVC.Models.Item
@using Lab.MVC.ViewModels;
@{
ViewBag.Title = "Itens";
}
<h2>Inclusão de Itens</h2>
<div class="row">
<div class="col-md-6">
<h3>Cadastro</h3>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger"
})
<div class="form-group">
@Html.LabelFor(model => model.IdPedido, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("idPedido",
(SelectList)ViewBag.ListaDePedidos,
"SELECIONE",
new { @class = "form-control" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.IdProduto, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(
model => model.IdProduto,
(SelectList)ViewBag.ListaDeProdutos,
new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.IdProduto,
"",
new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Quantidade, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Quantidade,
new { htmlAttributes =
new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Quantidade,
"",
new { @class = "text-danger" })
</div>
</div>
263
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Adicionar Item"
class="btn btn-info" />
</div>
</div>
</div>
}
</div>
<div class="col-md-6">
<h3>Lista de Itens do Pedido</h3>
@{
List<ItensPedidoViewModel> lista = ViewBag.ListaDeItens;
if (lista == null || lista.Count == 0)
{
<div class="alert alert-danger">
Nenhum item ou nenhum pedido selecionado
</div>
}
else
{
foreach (var item in lista)
{
<div class="alert alert-info">
<div style="float:right">
@Html.ActionLink("x", "Remover", "Itens", new
{
id = item.IdItem
},
new { role = "button",
style = "text-decoration:none;" })
</div>
<strong>Pedido: </strong>
@item.NumeroPedido
<strong>Produto: </strong>
@item.DescProduto
<br />
<strong>Quant. Itens:</strong>
@item.QuantItens
<strong>Valor: </strong>
@item.TotalItem.ToString("c")
</div>
} }
}
</div>
</div>
@section Scripts{
<script type="text/javascript">
$(document).ready(function () {
$("#idPedido").change(function () {
var selecao = $(this).val();
$(location).attr("href", "/Itens/Incluir?idPedido=" + selecao);
});
});
</script>
264
Entity Framework 4
Vamos entender como esta view foi elaborada:
69. Já que foi definido um bloco section com o nome Scripts, devemos
adicioná-lo no layout da aplicação. Escreva a seguinte instrução no final do
layout (colocamos aqui apenas o trecho necessário):
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
265
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
70. Para finalizar, vamos adicionar o action que, com base no id do item,
permita sua remoção. O action deve se chamar Remover, de acordo com a
instrução colocada na view Incluir:
using Lab.MVC.Data;
using Lab.MVC.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class ItensController : Controller
{
// GET: Itens
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Incluir(int? idPedido)
{
try
{
266
Entity Framework 4
ViewBag.ListaDeProdutos = new SelectList(
ProdutosDao.ListarProdutos(), "Id", "Descricao");
ViewBag.ListaDePedidos = new SelectList(
ItensDao.ListarPedidos(), "IdPedido", "NomeCliente");
ViewBag.ListaDeItens = ItensDao.ListarItensPorPedido(idPedido);
return View();
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
[HttpPost]
public ActionResult Incluir(Item item, int? idPedido)
{
if (!ModelState.IsValid)
{
return Incluir(idPedido);
}
try
{
item.IdPedido = (int)idPedido;
ItensDao.IncluirItem(item);
return RedirectToAction("Incluir", new {
idPedido = (int)idPedido });
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
267
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
ItensDao.RemoverItem(item);
268
Segurança –
5 ASP.NET
Identity
ÃÃ ASP.NET Identity;
ÃÃ OWIN;
ÃÃ Implementações do Visual Studio.
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
5.1. Introdução
O ASP.NET Identity é uma parte do framework cuja finalidade é fornecer recursos de
segurança para a aplicação. Esses recursos consistem em: criar novos usuários, criar
regras de utilização para os usuários, realizar login, entre muitas outras coisas.
5.2. ASP.NET Identity
O ASP.NET Identity permite o uso de qualquer modelo de dados ou provedor de
autenticação (como Google, Facebook, Twitter ou Windows Live). O esquema de dados
é totalmente controlado pela aplicação e todo o processo é modular, permitindo a
substituição ou modificação de qualquer componente do processo. Esse componente
se tornou válido na versão 4.6 no .NET Framework.
270
Segurança – ASP.NET Identity 5
Nada impede, porém, que acrescentemos outras informações na classe básica:
271
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
... outros métodos omitidos
272
Segurança – ASP.NET Identity 5
3. Uma instância de Usuário com os dados do usuário a ser cadastrado é criada.
Neste exemplo, para manter a simplicidade, os dados foram colocados manualmente;
A seguir, vejamos a lista de classes e interfaces utilizadas até o momento, que estão
no namespace Microsoft.AspNet.Identity:
273
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
5.3. OWIN
Para realizar o processo de login, o ASP.NET utiliza componentes compatíveis com
uma especificação chamada OWIN. Essa especificação define regras para que qualquer
programa, em qualquer sistema operacional, possa hospedar aplicações do .NET
Framework. Isso permite criar servidores mais leves do que o IIS e, também, utilizar
autenticação de terceiros, como Facebook ou Google.
274
Segurança – ASP.NET Identity 5
•• Microsoft.AspNet.Identity.Owin;
•• Microsoft.Owin;
•• Microsoft.Owin.Host.SystemWeb;
•• Microsoft.Owin.Security;
•• Microsoft.Owin.Security.Cookies;
•• Microsoft.Owin.Security.Oauth;
•• Newtonsoft.Json;
•• Owin.
O OWIN necessita de um módulo para seu início. Devemos lembrar que o OWIN é uma
especificação, e que possui sua própria configuração.
275
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Portanto, para definir a autenticação OWIN é necessário incluir uma classe declarada
com o atributo OwinStartup. O nome mais comum para essa classe é Startup.cs. No
projeto, clicando com o botão direito do mouse, podemos selecionar o item OWIN
Startup Class, no template General:
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(Capitulo05.AspNetIdentity.Startup))]
namespace Capitulo05.AspNetIdentity
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
}
}
}
276
Segurança – ASP.NET Identity 5
Sendo assim, consideremos o seguinte código:
277
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Está completo o processo. O código que cria o usuário pode ser usado para autenticar
esse usuário no sistema:
if (resposta.Succeeded)
{
var authenticationManager = HttpContext.Current.GetOwinContext().
Authentication;
var tipoAutenticacao=DefaultAuthenticationTypes.ApplicationCookie;
var userIdentity = gerenciador.CreateIdentity(usuario,
tipoAutenticacao);
authenticationManager.SignIn(userIdentity);
//OK
}
Namespace Owin
Nome Tipo Função
Interface que deve ser passada (por meio de uma
IAppBuilder Interface instância que a implemente) para o programa de
inicialização de um site.
Namespace Microsoft.Owin
Nome Tipo Função
OwinStartup Classe Atributo para definir uma classe de inicialização.
278
Segurança – ASP.NET Identity 5
Namespace Microsoft.Owin.Security
Nome Tipo Função
Usado para interagir como
IAuthenticationManager Interface
componentes OWIN.
Namespace Microsoft.Owin.Security.Cookies
Nome Tipo Função
Opções de autenticação por
CookieAuthenticationOptions Classe
cookies.
Namespace Microsoft.AspNet.Identity
Nome Tipo Função
DefaultAuthenticationTypes Classe Tipos de autenticação.
279
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@if (Request.IsAuthenticated)
{
<ul class="nav navbar-nav navbar-right">
<li>@Html.ActionLink(User.Identity.Name, "Index", "Home")</li>
<li>@Html.ActionLink("Logout", "Logout", "Home")</li>
</ul>
}
else
{
<ul class="nav navbar-nav navbar-right">
<li>@Html.ActionLink("Cadastrar-se", "Criar", "Home")</li>
<li>@Html.ActionLink("Login", "Login", "Home")</li>
</ul>
}
280
Segurança – ASP.NET Identity 5
Agora vamos adicionar esta view parcial ao layout, na parte do menu. Sua posição
será do lado direito, em relação ao restante do menu. Localize a parte referente ao
menu e inclua a instrução:
@{Html.RenderPartial("_UsuarioPV");}
</div>
</div>
</div>
281
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
•• Classe LoginView
namespace Capitulo05.AspNetIdentity.Models
{
public class LoginView
{
[Required]
public string Nome { get; set; }
[Required]
[DataType(DataType.Password)]
public string Senha { get; set; }
}
}
•• Classe UsuarioView
namespace Capitulo05.AspNetIdentity.Models
{
public class UsuarioView
{
[Required]
public string Nome { get; set; }
[Required]
[DataType(DataType.Password)]
public string Senha { get; set; }
[Compare("Senha")]
[DataType(DataType.Password)]
public string Confirma { get; set; }
}
}
282
Segurança – ASP.NET Identity 5
No controller Home, definiremos os seguintes actions: Login, Logout e Criar. A view
Login.cshtml usará como modelo a classe LoginView, e a view Criar.cshtml usará a
classe UsuarioView. O action Logout não terá view.
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
283
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
284
Segurança – ASP.NET Identity 5
•• Criação da view Login.cshtml:
285
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
[assembly: OwinStartup(typeof(Capitulo05.AspNetIdentity.Startup))]
namespace Capitulo05.AspNetIdentity
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes
.ApplicationCookie,
LoginPath = new PathString("/Home/Login")
});
}
}
}
<connectionStrings>
<add name="DefaultConnection"
connectionString="Integrated Security=SSPI;Persist Security
Info=False;Initial Catalog=DBUSUARIOS;Data Source=.\SQLEXPRESS"
providerName="System.Data.SqlClient"/>
</connectionStrings>
286
Segurança – ASP.NET Identity 5
5.4.3.2. Implementação do cadastro de usuários
Já temos um action chamado Criar. Vamos agora implementar a versão POST do
método, ou seja, o action que receberá os dados do formulário para cadastro:
[HttpPost]
public ActionResult Criar(UsuarioView usuario)
{
if (!ModelState.IsValid)
{
return View();
}
//Cria o usuário
IdentityResult resultado = usuarioManager.Create(
usuarioInfo, usuario.Senha);
autManager.SignIn(new AuthenticationProperties() { },
identidadeUsuario);
return RedirectToAction("Index");
}
else
{
ViewBag.Erro = resultado.Errors.FirstOrDefault();
return View("_Erro");
}
}
287
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Microsoft.AspNet.Identity.EntityFramework
{
public class IdentityUser<TKey, TLogin, TRole, TClaim> : IUser<TKey>
where TLogin : IdentityUserLogin<TKey>
where TRole : IdentityUserRole<TKey>
where TClaim : IdentityUserClaim<TKey>
{
public IdentityUser();
public virtual string Email { get; set; }
public virtual bool EmailConfirmed { get; set; }
public virtual string PasswordHash { get; set; }
public virtual string SecurityStamp { get; set; }
public virtual string PhoneNumber { get; set; }
public virtual bool PhoneNumberConfirmed { get; set; }
public virtual bool TwoFactorEnabled { get; set; }
public virtual DateTime? LockoutEndDateUtc { get; set; }
public virtual bool LockoutEnabled { get; set; }
public virtual int AccessFailedCount { get; set; }
public virtual ICollection<TRole> Roles { get; }
public virtual ICollection<TClaim> Claims { get; }
public virtual ICollection<TLogin> Logins { get; }
public virtual TKey Id { get; set; }
public virtual string UserName { get; set; }
}
}
288
Segurança – ASP.NET Identity 5
Seu conteúdo será o mostrado a seguir:
@{
ViewBag.Title = "_Erro";
}
5.4.3.3. Implementação do Login
A implementação do login utilizará boa parte do código que usamos para a inclusão
do usuário. A diferença está na busca pelo usuário, em vez da criação. O processo de
autenticação é semelhante.
[HttpPost]
public ActionResult Login(LoginView usuario, string ReturnUrl)
{
if (!ModelState.IsValid)
{
return View();
}
else
{
var usuarioStore = new UserStore<IdentityUser>();
var usuarioManager = new UserManager<IdentityUser>(usuarioSto
re);
if (usuarioInfo != null)
{
var autManager = System.Web.HttpContext
.Current.GetOwinContext().Authentication;
autManager.SignIn(new AuthenticationProperties()
{ IsPersistent = false }, identidadeUsuario);
289
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
5.4.3.4. Implementação do Logout
Como já mencionamos anteriormente, o action Logout não utilizará nenhuma
view. O propósito é desconectar o usuário e encaminhá-lo para a página inicial. Sua
implementação está apresentada a seguir:
return RedirectToAction("Index");
5.4.4. Executando a aplicação
Para executar a aplicação e tentar o acesso a um recurso protegido, criaremos um
action chamado AreaRestrita. O propósito é mostrar o redirecionamento para Login,
se o usuário tentar acessar este action sem estar autenticado. O conteúdo da view
AreaRestrita será apenas um texto informando que se trata de uma área restrita.
[Authorize]
public ActionResult AreaRestrita()
{
return View();
}
O atributo Authorize fará com que este action seja executado apenas por usuários
devidamente autenticados.
290
Segurança – ASP.NET Identity 5
Primeiro, executaremos a aplicação para adicionar um novo usuário.
Por ser a primeira vez que estamos executando no sentido de incluir um novo usuário,
o ASP.NET Identity cria o banco de dados que definimos na string de conexão.
291
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
292
Segurança – ASP.NET Identity 5
Ao fornecer as credenciais, o usuário é autenticado e direcionado para a view desejada
Área Restrita.
293
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Pontos principais
Atente para os tópicos a seguir. Eles devem ser estudados com muita atenção,
pois representam os pontos mais importantes do capítulo.
294
Segurança
5 – ASP.NET
Identity
Teste seus conhecimentos
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
296
Segurança – ASP.NET Identity 5
4. Que tipo é retornado pelo método Create da classe que gerencia os
dados de um usuário?
☐☐a) IdentityResult
☐☐b) User
☐☐c) Principal
☐☐d) Role
☐☐e) ApplicationUser
5. Qual atributo devemos usar para indicar que uma classe é a classe Owin
Startup?
☐☐a) OAuth
☐☐b) Owin
☐☐c) OData
☐☐d) OwinStartup
☐☐e) Startup
297
Segurança
5 – ASP.NET
Identity
Mãos à obra!
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Objetivos:
Laboratório 1
1. Na pasta Labs, crie uma solution vazia (Blank solution) chamada Capitulo05.
Labs;
300
Segurança – ASP.NET Identity 5
5. Escreva o seguinte conteúdo:
@if (Request.IsAuthenticated)
{
<ul class="nav navbar-nav navbar-right">
<li>@Html.ActionLink(User.Identity.Name, "Index",
"Home")</li>
<li>@Html.ActionLink("Logout", "Logout", "Usuarios")</li>
</ul>
}
else
{
<ul class="nav navbar-nav navbar-right">
<li>@Html.ActionLink("Cadastrar-se", "CriarUsuario",
"Usuarios")</li>
<li>@Html.ActionLink("Login", "Login", "Usuarios")</li>
</ul>
}
@{Html.RenderPartial("_UsuarioPV");}
</div>
301
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using System.ComponentModel.DataAnnotations;
namespace Lab.MVC.Models
{
public class UsuarioViewModel
{
[Required]
public string Nome { get; set; }
[Required]
[DataType(DataType.Password)]
public string Senha { get; set; }
[Compare("Senha")]
[DataType(DataType.Password)]
public string Confirma { get; set; }
}
}
using System.ComponentModel.DataAnnotations;
namespace Lab.MVC.Models
{
public class LoginViewModel
{
[Required]
public string Nome { get; set; }
[Required]
[DataType(DataType.Password)]
public string Senha { get; set; }
}
}
302
Segurança – ASP.NET Identity 5
303
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Esse arquivo é usado pelo framework OWIN. Vamos adicionar a sintaxe adiante:
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;
[assembly: OwinStartup(typeof(Lab.MVC.Startup))]
namespace Lab.MVC
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new
CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes
.ApplicationCookie,
LoginPath = new PathString("/Usuarios/Login"),
LogoutPath = new PathString("/Usuarios/Logout")
});
}
}
}
304
Segurança – ASP.NET Identity 5
11. Vamos definir a string de conexão para que o ASP.NET Identity possa usá-
la para criar o banco de dados. Por padrão, o Identity utiliza uma conexão
chamada DefaultConnection. Para defini-la, abra o arquivo Web.config e
localize a string de conexão definida para o banco de dados DB_VENDAS.
Adicione a seguinte instrução ao elemento <connectionStrings>:
<add
name="DefaultConnection"
connectionString="Integrated Security=SSPI;Persist Security
Info=False;Initial Catalog=DB_USUARIOS;Data Source=.\SQLEXPRESS"
providerName="System.Data.SqlClient" />
12. Agora vamos implementar os actions para incluir usuários, realizar login e
logout. Crie o controller UsuariosController com os actions Login, Logout e
CriarUsuario. Apenas o action Logout não terá a versão POST:
using Lab.MVC.Models;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security;
using System;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class UsuariosController : Controller
{
[HttpGet]
public ActionResult CriarUsuario()
{
return View();
}
[HttpPost]
public ActionResult CriarUsuario(UsuarioViewModel usuario)
{
if (!ModelState.IsValid)
{
return View();
}
try
{
305
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
//Cria o usuário
IdentityResult resultado = usuarioManager.Create(
usuarioInfo, usuario.Senha);
if (resultado.Succeeded)
{
//Autentica e volta para a página inicial
var autManager = System.Web.HttpContext
.Current.GetOwinContext().Authentication;
var identidadeUsuario = usuarioManager
.CreateIdentity(usuarioInfo,
DefaultAuthenticationTypes.ApplicationCookie);
autManager.SignIn(new AuthenticationProperties() { },
identidadeUsuario);
return RedirectToAction("Index", "Home");
}
else
{
throw new Exception(resultado
.Errors.FirstOrDefault());
}
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
[HttpGet]
public ActionResult Login()
{
return View();
}
306
Segurança – ASP.NET Identity 5
[HttpPost]
public ActionResult Login(LoginViewModel usuario,
string returnUrl)
{
if (!ModelState.IsValid)
{
return View();
}
try
{
var usuarioStore = new UserStore<IdentityUser>();
var usuarioManager = new
UserManager<IdentityUser>(usuarioStore);
if (usuarioInfo != null)
{
var autManager = System.Web.HttpContext
.Current.GetOwinContext().Authentication;
autManager.SignIn(new AuthenticationProperties()
{ IsPersistent = false }, identidadeUsuario);
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
[HttpGet]
public ActionResult Logout()
{
var autManager = System.Web.HttpContext
.Current.GetOwinContext().Authentication;
autManager.SignOut();
307
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
308
Segurança – ASP.NET Identity 5
@model Lab.MVC.Models.UsuarioViewModel
@{
ViewBag.Title = "Criar Usuario";
}
<h2>Criar Usuario</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.Nome, htmlAttributes: new
{ @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Nome, new
{ htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Nome, "",
new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Senha, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Senha, new { htmlAttributes =
new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Senha, "",
new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Confirma, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Confirma, new { htmlAttributes =
new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Confirma, "",
new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Criar Usuário"
class="btn btn-info" />
</div>
</div>
</div>
}
309
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@model Lab.MVC.Models.LoginViewModel
@{
ViewBag.Title = "Login";
}
<h2>Login</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.Nome, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Nome, new { htmlAttributes =
new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Nome, "",
new { @class = "text-danger" })
</div>
</div>
310
Segurança – ASP.NET Identity 5
<div class="form-group">
@Html.LabelFor(model => model.Senha, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Senha, new {
htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Senha, "",
new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Login"
class="btn btn-info" />
</div>
</div>
</div>
}
311
6
Serviços - Web
API
314
Serviços - Web API 6
6.2. REST na prática
Os princípios REST utilizam todo o potencial do protocolo HTTP. Apesar de não ter
sido concebido para uso exclusivo desse protocolo, todas as restrições impostas são
parte integrante dos recursos disponíveis no ambiente Web e na comunicação entre
o browser e um servidor.
•• Todo recurso deve ter um identificador: Na Web, isso já é a norma. Uma URL
como www.microsoft.com é um identificador único para um recurso, no caso,
a página de entrada da Microsoft;
315
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
nome=Maria+da+Silva
Este outro exemplo usa URL para ter acesso à lista Produtos. O verbo GET juntamente
com a URL api/Produtos define o que deve ser executado. Neste caso, o retorno deve
ser no formato JSON:
316
Serviços - Web API 6
Para apresentar o uso do framework Web API, vamos definir um novo projeto chamado
Capitulo06.WebAPI. Será um projeto vazio modelo ASP.NET Web Application (.NET
Framework), com template Web Api:
317
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
•• Global.asax
using System.Web.Http;
namespace Capitulo06.WebAPI
{
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
}
•• App_Start / WebApiConfig.cs
Dentro da pasta App_Start está definida a classe WebApiConfig. Essa classe registra,
no mecanismo do ASP.NET, uma route (rota), recurso também usado pelo MVC. No
caso da Web API, o modelo de roteamento inclui a pasta api como prefixo.
using System.Web.Http;
namespace Capitulo06.WebAPI
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
318
Serviços - Web API 6
O formato da URL para utilizar a Web API é o seguinte:
Servidor/api/xxxx/yyyy
Servidor/api/Produto
Servidor/api/Produto/1
Servidor/api/Clientes
Servidor/api/NotaFiscal/435930
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Em que:
•• Routes é uma coleção de objetos do tipo IhttpRoute e que serve para identificar
um caminho;
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
319
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
O parâmetro defaults é do tipo object, ou seja, pode ser passado a qualquer classe.
O aplicativo analisa as propriedades do objeto e verifica se coincide com o template.
Nessa configuração gerada pelo Visual Studio, é usado um tipo anônimo com a
propriedade Id tendo o valor da propriedade Opcional da classe RouteParameters.
Como será visto no próximo tópico, usar a Web API é muito mais fácil do que entender
todo o mecanismo envolvido para que tudo funcione. O propósito de existir templates
que criem o ambiente necessário é facilitar a escrita do código e fazer com que o
programador se concentre no que a aplicação deve fazer, e não nos detalhes técnicos
envolvidos.
320
Serviços - Web API 6
Vale lembrar que as regras e convenções para o controller em uma aplicação MVC
também se aplicam ao controller do projeto Web API. Observe que a classe gerada é
subclasse de ApiController.
using System.Web.Http;
namespace Capitulo06.WebAPI.Controllers
{
public class HomeController : ApiController
{
}
}
Além da convenção aplicada aos controllers, o Web API também mantém uma
convenção aplicada aos nomes dos actions que, neste caso, representam verbos
HTTP. A convenção considera o seguinte:
Não é necessário listar todos os métodos aqui, mas a convenção é aplicada aos demais
verbos HTTP.
321
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Capitulo06.WebAPI.Models
{
public class Produto
{
public int Id { get; set; }
public string Descricao { get; set; }
public double Preco { get; set; }
}
}
3. Nessa pasta, crie uma classe chamada DbDados, contendo os seguintes métodos
e atributos:
using Capitulo06.WebAPI.Models;
using System.Collections.Generic;
namespace Capitulo06.WebAPI.Dados
{
public class DbDados
{
public static List<Produto> produtos = new List<Produto>()
{
new Produto(){ Id = 10, Descricao = "Mouse", Preco = 40.00 },
new Produto(){ Id = 20, Descricao = "Camisa", Preco = 59.90 },
new Produto(){ Id = 30, Descricao = "Bike", Preco = 450.00 }
};
322
Serviços - Web API 6
4. No controller Home, escreva os seguintes métodos (actions):
using Capitulo06.WebAPI.Dados;
using System.Collections.Generic;
using System.Web.Http;
namespace Capitulo06.WebAPI.Controllers
{
public class HomeController : ApiController
{
public IEnumerable<string> GetCursos()
{
return DbDados.ListarCursos();
}
}
}
Observe que o action GetCursos inicia com o prefixo Get, significando que sua chamada
será realizada quando realizarmos uma requisição GET, simplesmente chamando o
controller na URL de acordo com a rota estabelecida no arquivo WebApiConfig.cs.
Para verificar, execute a aplicação e adicione /api/home na URL:
323
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
O serviço foi executado e a lista de cursos foi obtida no formato XML. Desejamos que
o formato padrão seja JSON. Para isso, vamos remover a formatação XML e adicionar
a formatação JSON, na classe WebApiConfig (arquivo WebApiConfig.cs), usando o
parâmetro config do método Register:
using System.Web.Http;
namespace Capitulo06.WebAPI
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.Formatters.Add(config.Formatters.JsonFormatter);
}
}
}
324
Serviços - Web API 6
A lista de cursos, definida como uma lista de string, foi consumida e seu resultado foi
retornado como um objeto JSON.
Além desta lista de cursos, definimos também um método com uma lista de objetos
da classe Produto. Para que o serviço disponibilize a consulta a esta lista, devemos
adicionar um action ao controller:
using Capitulo06.WebAPI.Dados;
using Capitulo06.WebAPI.Models;
using System.Collections.Generic;
using System.Web.Http;
namespace Capitulo06.WebAPI.Controllers
{
public class HomeController : ApiController
{
public IEnumerable<string> GetCursos()
{
return DbDados.ListarCursos();
}
325
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Em casos como este, devemos personalizar a rota para um dos dois actions, de forma
a eliminar a ambiguidade. Vamos alterar o último action adicionado ao controller:
GetProdutos:
using Capitulo06.WebAPI.Dados;
using Capitulo06.WebAPI.Models;
using System.Collections.Generic;
using System.Web.Http;
namespace Capitulo06.WebAPI.Controllers
{
public class HomeController : ApiController
{
public IEnumerable<string> GetCursos()
{
return DbDados.ListarCursos();
}
[Route("listaProdutos")]
public IEnumerable<Produto> GetProdutos()
{
return DbDados.ListarProdutos();
}
}
}
O atributo Route permite especificar uma rota para o action que o utilizar. No nosso
caso, somente podemos consumir a lista de produtos através desta nova rota:
listaProdutos. Vamos ver o novo resultado:
Apesar de termos retornado uma coleção em ambos os métodos, é possível que ele
retorne quaisquer tipos válidos no C#. Seu resultado será avaliado pela aplicação que
consumi-lo.
326
Serviços - Web API 6
6.3.4. Testando o método GET com parâmetros
Nos métodos que definimos até agora no controller, nenhum parâmetro foi informado.
Vamos agora definir novos actions capazes de buscar um curso por partes do nome
e um produto pelo seu Id. Aqui também vale a regra de eliminação de ambiguidade,
ou seja, em um dos actions deveremos configurar sua rota:
using Capitulo06.WebAPI.Dados;
using Capitulo06.WebAPI.Models;
using System.Collections.Generic;
using System.Web.Http;
namespace Capitulo06.WebAPI.Controllers
{
public class HomeController : ApiController
{
public IEnumerable<string> GetCursos()
{
return DbDados.ListarCursos();
}
[Route("listaProdutos")]
public IEnumerable<Produto> GetProdutos()
{
return DbDados.ListarProdutos();
}
[Route("buscarProduto/{id}")]
public Produto GetProduto(int id)
{
List<Produto> produtos = (List<Produto>)DbDados.ListarProdutos();
return produtos.Find(p => p.Id == id);
}
}
}
327
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Para testar, vamos buscar um curso que contenha a palavra "script" e o produto com
Id = 20, respectivamente:
No nosso projeto, vamos definir os actions necessários para incluir, alterar e remover
um produto. Manteremos as convenções de nomes para estes três métodos. Em
seguida, criaremos uma aplicação para consumir nosso serviço com as quatro
operações.
328
Serviços - Web API 6
•• Adicionando métodos na classe DbDados:
using Capitulo06.WebAPI.Models;
using System.Collections.Generic;
namespace Capitulo06.WebAPI.Dados
{
public class DbDados
{
public static List<Produto> produtos = new List<Produto>()
{
new Produto() { Id = 10, Descricao = "Mouse", Preco = 40.00 },
new Produto() { Id = 20, Descricao = "Camisa", Preco = 59.90 },
new Produto() { Id = 30, Descricao = "Bike", Preco = 450.00 }
};
329
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using Capitulo06.WebAPI.Dados;
using Capitulo06.WebAPI.Models;
using System.Collections.Generic;
using System.Web.Http;
namespace Capitulo06.WebAPI.Controllers
{
public class HomeController : ApiController
{
public IEnumerable<string> GetCursos()
{
return DbDados.ListarCursos();
}
[Route("listaProdutos")]
public IEnumerable<Produto> GetProdutos()
{
return DbDados.ListarProdutos();
}
[Route("buscarProduto/{id}")]
public Produto GetProduto(int id)
{
List<Produto> produtos = (List<Produto>)DbDados.ListarProdutos();
return produtos.Find(p => p.Id == id);
}
//HTTP POST
public Produto PostProduto(Produto produto)
{
return DbDados.IncluirProduto(produto);
}
//HTTP PUT
public Produto PutProduto(Produto produto)
{
return DbDados.AlterarProduto(produto);
}
//HTTP DELETE
public Produto DeleteProduto(int id)
{
return DbDados.RemoverProduto(id);
}
}
}
330
Serviços - Web API 6
6.4. Criando um projeto para consumir o
serviço
Já estamos prontos para definir uma aplicação capaz de consumir nosso Webservice.
Vamos definir um novo projeto MVC para esta finalidade.
Usando o modelo ASP.NET Web Application (.NET Framework), crie um projeto MVC
vazio chamado Capitulo06.ClienteWebAPI.
Neste projeto, adicione uma classe chamada Produto na pasta Models, com as
mesmas propriedades definidas na classe Produto do projeto Web API.
331
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Capitulo06.ClienteWebAPI.Models
{
public class Produto
{
public int Id { get; set; }
public string Descricao { get; set; }
public double Preco { get; set; }
}
}
Além dos actions, deveremos ter no controller um objeto do tipo HttpClient, disponível
no namespace System.Net.Http, cuja referência deverá ser adicionada ao projeto.
Para adicioná-la, clique com o botão direito sobre o item References, opção Add
Reference:
332
Serviços - Web API 6
Com esta nova referência, inclua o seguinte código no controller:
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web.Mvc;
namespace Capitulo06.ClienteWebAPI.Controllers
{
public class ClienteController : Controller
{
HttpClient client;
public ClienteController()
{
if(client == null)
{
client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:60865/");
client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
}
}
333
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web.Mvc;
namespace Capitulo06.ClienteWebAPI.Controllers
{
public class ClienteController : Controller
{
HttpClient client;
public ClienteController()
{
if(client == null)
{
client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:60865/");
client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
}
}
334
Serviços - Web API 6
Em seguida, adicione uma nova view, com o template Create e model Produto:
335
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Vamos escrever a versão POST do action Incluir. Usaremos uma classe chamada
JsonConvert, presente no namespace Newtonsoft.Json. Este namespace deverá ser
adicionado através do NuGet:
[HttpPost]
public async Task<ActionResult> Incluir(Produto produto)
{
try
{
string json = JsonConvert.SerializeObject(produto);
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Listar");
}
else
{
var mensagem = response.ReasonPhrase;
throw new Exception(mensagem);
}
}
catch (Exception ex)
{
ViewBag.Erro = ex.Message;
return View("_Erro");
}
}
336
Serviços - Web API 6
Neste método, temos as seguintes instruções principais:
O objeto passado como parâmetro para o action está sendo convertido para o formato
JSON.
Um objeto contendo o fluxo de bytes a ser enviado para o serviço esta sendo criado.
Este objeto é referenciado pela variável content.
É nesta instrução que o fluxo de bytes é enviado para o serviço. Observe que o método
é assíncrono, e que o comando await está sendo aplicado. É por isso que nosso action
foi definido como async, com ActionResult tratado como uma tarefa (Task).
Para testar a inclusão, incluiremos os actions Listar e _Erro. Listar ainda não terá
nenhum conteúdo. _Erro.cshtml será colocado na pasta Views/Shared.
@{
ViewBag.Title = "Listar";
}
<h2>Listar - teste</h2>
337
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@{
ViewBag.Title = "_Erro";
}
338
Serviços - Web API 6
O resultado da consulta ao Webservice será algo semelhante ao apresentado a seguir:
•• Listando os produtos
339
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@model IEnumerable<Capitulo06.ClienteWebAPI.Models.Produto>
@{
ViewBag.Title = "Listar";
}
<h2>Listar Produtos</h2>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.Descricao)
</th>
<th>
@Html.DisplayNameFor(model => model.Preco)
</th>
<th></th>
</tr>
</table>
340
Serviços - Web API 6
Na sequência, adicionaremos o código no action Listar para buscar a lista de produtos
no Webservice:
return View(lista);
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
catch (Exception ex)
{
ViewBag.Erro = ex.Message;
return View("_Erro");
}
}
E, logo em seguida, este resultado foi repassado para um array de objetos Produto, já
que o array é o formato geral de coleções nos Webservices REST. O resultado obtido
foi convertido para List<Produto> através do método ToList.
341
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
•• Alterando um produto
Para ilustrar a inclusão, adicionaremos o action Editar, versão GET e POST. Na versão
GET, buscaremos o produto com o Id informado como parâmetro, e no método POST
executaremos a alteração conforme especificado no Webservice. Vamos começar por
implementar o método GET:
return View(produto);
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
catch (Exception ex)
{
ViewBag.Erro = ex.Message;
return View("_Erro");
}
}
342
Serviços - Web API 6
A view a ser criada usará o template Edit com o model Produto:
[HttpPost]
public async Task<ActionResult> Editar(Produto produto)
{
try
{
string json = JsonConvert.SerializeObject(produto);
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Listar");
}
else
{
var mensagem = response.ReasonPhrase;
throw new Exception(mensagem);
}
}
catch (Exception ex)
{
ViewBag.Erro = ex.Message;
return View("_Erro");
}
}
343
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
344
Serviços - Web API 6
Removendo um Produto
return View(produto);
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
catch (Exception ex)
{
ViewBag.Erro = ex.Message;
return View("_Erro");
}
}
345
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@model Capitulo06.ClienteWebAPI.Models.Produto
@{
ViewBag.Title = "Remover";
}
<h2>Remover um produto</h2>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Descricao)
</dt>
<dd>
@Html.DisplayFor(model => model.Descricao)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Preco)
</dt>
<dd>
@Html.DisplayFor(model => model.Preco)
</dd>
</dl>
@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
@Html.HiddenFor(model => model.Id)
<div class="form-actions no-color">
<input type="submit" value="Sim, remover" class="btn btn-
danger" /> |
@Html.ActionLink("Não, retornar para a lista", "Listar")
</div>
}
</div>
346
Serviços - Web API 6
A versão POST do action Remover pode ser definida como:
[HttpPost]
public async Task<ActionResult> Remover(Produto produto)
{
try
{
var response = await client.DeleteAsync("api/home/" + produto.
Id);
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Listar");
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
catch (Exception ex)
{
ViewBag.Erro = ex.Message;
return View("_Erro");
}
}
347
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Para testar, vamos executar o fluxo completo, desde a listagem, a seleção do produto
a ser removido, a confirmação e o retorno à listagem:
348
Serviços - Web API 6
Pontos principais
Atente para os tópicos a seguir. Eles devem ser estudados com muita atenção,
pois representam os pontos mais importantes do capítulo.
•• O framework Web API usa o recurso routes para definir qual método vai ser
executado quando chega uma requisição;
349
6 Serviços - Web
API
Teste seus conhecimentos
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
1. O que é REST?
☐☐a) É um protocolo de comunicação para enviar dados pela Internet.
☐☐b) É uma linguagem de programação para criar serviços.
☐☐c) É um conjunto de princípios para criação de serviços.
☐☐d) É um framework da Microsoft para desenvolver serviços.
☐☐e) É um padrão de arquitetura para criação de Web sites.
352
Serviços - Web API 6
4. Que tipo de aplicativo pode consumir um serviço REST criado com a
Web API?
☐☐a) Windows Form
☐☐b) Web Form
☐☐c) MVC
☐☐d) Console
☐☐e) Todas as alternativas anteriores estão corretas.
353
6 Serviços -
Web API
Mãos à obra!
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Objetivos:
Laboratório 1
A - Desenvolvimento do serviço Web API
356
Serviços - Web API 6
357
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Lab.WebAPI.Models
{
public class Cartao
{
public int Id { get; set; }
public string NumeroCartao { get; set; }
public double Limite { get; set; }
}
}
namespace Lab.WebAPI.Models
{
public class Fatura
{
public int Id { get; set; }
public string NumeroCartao { get; set; }
public string NumeroPedido { get; set; }
public double Valor { get; set; }
public int Status { get; set; }
}
}
<connectionStrings>
<add name="PagamentosConnection"
connectionString="Integrated Security=SSPI;Persist Security
Info=False;Initial Catalog=DB_pagamentos;Data Source=.\SQLEXPRESS"
providerName="System.Data.SqlClient" />
</connectionStrings>
358
Serviços - Web API 6
6. Na pasta Models, adicione a classe PagamentosContext. Esta classe terá as
definições do banco de dados a ser criado e, posteriormente, a ser acessado
no serviço. Sobrescrevemos o método OnModelCreating para especificar os
detalhes de criação do banco:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace Lab.WebAPI.Models
{
public class PagamentosContext : DbContext
{
public PagamentosContext():
base("PagamentosConnection")
{
}
modelBuilder.Entity<Cartao>().ToTable("TBCartoes");
modelBuilder.Entity<Cartao>()
.Property(p => p.NumeroCartao)
.IsRequired()
.HasMaxLength(16);
modelBuilder.Entity<Cartao>()
.Property(p => p.Limite)
.IsRequired();
modelBuilder.Entity<Fatura>().ToTable("TBFaturas");
modelBuilder.Entity<Fatura>()
.Property(p => p.NumeroCartao)
.IsRequired()
.HasMaxLength(16);
modelBuilder.Entity<Fatura>()
.Property(p => p.NumeroPedido)
.IsRequired()
.HasMaxLength(20);
modelBuilder.Entity<Fatura>()
.Property(p => p.Valor)
.IsRequired();
}
}
}
359
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Lab.WebAPI.Enumerations
{
public enum StatusPagamento
{
SALDO_INDISPONIVEL,
PEDIDO_JA_PAGO,
CARTAO_INEXISTENTE,
PAGAMENTO_OK
}
}
using Lab.WebAPI.Enumerations;
using Lab.WebAPI.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Lab.WebAPI.Data
{
public class PagamentosDao
{
//método para listar todas as faturas
public static IEnumerable<Fatura> ListarFaturas()
{
using(var ctx = new PagamentosContext())
{
return ctx.Faturas.ToList();
}
}
360
Serviços - Web API 6
//método para incluir um novo pagamento
//Observe o retorno deste método
public static StatusPagamento IncluirFatura(Fatura fatura)
{
using (var ctx = new PagamentosContext())
{
//verificando se o cartão existe
//Se existir, definimos uma referência a ele
var cartao = ctx.Cartoes.FirstOrDefault(p =>
p.NumeroCartao.Equals(fatura.NumeroCartao));
if(cartao == null)
{
return StatusPagamento.CARTAO_INEXISTENTE;
}
if(fat != null)
{
return StatusPagamento.PEDIDO_JA_PAGO;
}
361
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using System.Web.Http;
namespace Lab.WebAPI.Controllers
{
public class PgamentosController : ApiController
{
}
}
using Lab.WebAPI.Data;
using Lab.WebAPI.Enumerations;
using Lab.WebAPI.Models;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace Lab.WebAPI.Controllers
{
public class PagamentosController : ApiController
{
362
Serviços - Web API 6
//Listando todos os pagamentos (faturas)
public IEnumerable<Fatura> GetFaturas()
{
return PagamentosDao.ListarFaturas();
}
if(status != StatusPagamento.PAGAMENTO_OK)
{
string mensagem = "";
switch (status)
{
case StatusPagamento.CARTAO_INEXISTENTE:
mensagem = "O Cartão informado não existe";
break;
case StatusPagamento.PEDIDO_JA_PAGO:
mensagem = "Já foi realizado pagamento deste pedido";
break;
case StatusPagamento.SALDO_INDISPONIVEL:
mensagem = "O Cartão não possui saldo disponível";
break;
}
363
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
api/pagamentos
Pode demorar um pouco, mas é o tempo para que o banco de dados seja
criado. O resultado é mostrado a seguir:
using System.Web.Http;
namespace Lab.WebAPI
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.Formatters.Add(config.Formatters.JsonFormatter);
}
}
}
364
Serviços - Web API 6
13. Execute novamente. Veja que recebemos uma lista vazia, pois ainda não
temos nenhuma fatura criada.
16. Adicione este projeto ao novo solution. Execute a aplicação para testar se
está tudo OK;
17. Estando tudo OK, vamos criar um novo controller MVC chamado
FaturasController:
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class FaturasController : Controller
{
// GET: Faturas
public ActionResult Index()
{
return View();
}
}
}
365
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
18. Defina a view para o action Index. Esta view será a view de apresentação
para os pagamentos:
@{
ViewBag.Title = "Faturas";
}
<ul>
<li>@Html.ActionLink("Efetuar Pagamento", "EfetuarPagamento")</li>
<li>@Html.ActionLink("Listar Faturas", "ListarFaturas")</li>
</ul>
19. Na pasta Views/Shared, adicione uma nova view chamada _Sucesso. Ela
será apresentada quando o pagamento for efetuado com sucesso. A view _Erro
continuará a ser usada em caso de erro;
366
Serviços - Web API 6
@{
ViewBag.Title = "Erro";
}
@{Html.RenderPartial("_UsuarioPV");}
</div>
21. Na pasta Models, adicionar uma classe chamada Fatura. Esta classe
deve ter as mesmas propriedades da classe Fatura definida no serviço, pois
suas propriedades serão serializadas e enviadas através da rede. Atingindo o
serviço, as informações serão desserializadas e, desta forma, deverá haver uma
correspondência de nomes para que o Web API faça o trabalho de inclusão;
367
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Lab.MVC.Models
{
public class Fatura
{
public class Fatura
{
public int Id { get; set; }
[Display(Name="Cartão")]
public string NumeroCartao { get; set; }
[Display(Name = "Pedido")]
public string NumeroPedido { get; set; }
public double Valor { get; set; }
public int Status { get; set; }
} }
}
using Lab.MVC.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class FaturasController : Controller
{
// GET: Faturas
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult EfetuarPagamento()
{
try
{
ViewBag.ListaDePedidos = new SelectList(
ItensDao.ListarPedidos(), "NumeroPedido", "NomeCliente");
return View();
}
catch (Exception ex)
{
368
Serviços - Web API 6
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
}
}
@model Lab.MVC.Models.Fatura
@{
ViewBag.Title = "Efetuar Pagamento";
}
<h2>Efetuar Pagamento</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
369
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.NumeroCartao, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.NumeroCartao,
new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.NumeroCartao, "",
new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.NumeroPedido, htmlAttributes:
new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model => model.NumeroPedido,
(SelectList)ViewBag.ListaDePedidos,
new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.NumeroPedido, "",
new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Efetuar Pagamento"
class="btn btn-info" />
</div>
</div>
</div>
}
370
Serviços - Web API 6
25. Para determinar o valor total dos itens, necessitaremos do Id do pedido, já
que ele é usado no método ListarItensPorPedido da classe ItensDao. Vamos
acrescentar um método na classe PedidosDao que receba como parâmetro o
número do pedido e retorne seu Id:
return pedido.Id;
}
}
using Lab.MVC.Data;
using Lab.MVC.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class FaturasController : Controller
{
HttpClient client;
public FaturasController()
{
if (client == null)
{
client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:58671/");
client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
}
371
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
// GET: Faturas
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult EfetuarPagamento()
{
try
{
ViewBag.ListaDePedidos = new SelectList(
ItensDao.ListarPedidos(), "NumeroPedido", "NomeCliente");
return View();
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
[HttpPost]
public async Task<ActionResult> EfetuarPagamento(Fatura fatura)
{
try
{
//obtend o id do pedido
int idPedido = PedidosDao.BuscarId(fatura.NumeroPedido);
//serializando o objeto
HttpContent content = new StringContent(
json, Encoding.Unicode, "application/json");
372
Serviços - Web API 6
//enviando o conteúdo serializado para o serviço
var response = await client.PostAsync("api/pagamentos",
content);
if (response.IsSuccessStatusCode)
{
return View("_Sucesso");
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
}
}
using Lab.MVC.Data;
using Lab.MVC.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
namespace Lab.MVC.Controllers
{
public class FaturasController : Controller
{
HttpClient client;
public FaturasController()
{
if (client == null)
{
373
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
}
// GET: Faturas
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult EfetuarPagamento()
{
try
{
ViewBag.ListaDePedidos = new SelectList(
ItensDao.ListarPedidos(), "NumeroPedido", "NomeCliente");
return View();
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
[HttpPost]
public async Task<ActionResult> EfetuarPagamento(Fatura fatura)
{
try
{
//obtend o id do pedido
int idPedido = PedidosDao.BuscarId(fatura.NumeroPedido);
//serializando o objeto
HttpContent content = new StringContent(
json, Encoding.Unicode, "application/json");
374
Serviços - Web API 6
//enviando o conteúdo serializado para o serviço
var response = await client.PostAsync("api/pagamentos",
content);
if (response.IsSuccessStatusCode)
{
return View("_Sucesso");
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
if (response.IsSuccessStatusCode)
{
var resultado = await response.Content
.ReadAsStringAsync();
return View(lista);
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
catch (Exception ex)
{
ViewBag.MensagemErro = ex.Message;
return View("_Erro");
}
}
}
}
375
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@model IEnumerable<Lab.MVC.Models.Fatura>
@{
ViewBag.Title = "Faturas";
}
<h2>Lista de Faturas</h2>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.NumeroCartao)
</th>
<th>
@Html.DisplayNameFor(model => model.NumeroPedido)
</th>
<th>
@Html.DisplayNameFor(model => model.Valor)
</th>
<th>
@Html.DisplayNameFor(model => model.Status)
</th>
</tr>
376
Serviços - Web API 6
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.NumeroCartao)
</td>
<td>
@Html.DisplayFor(modelItem => item.NumeroPedido)
</td>
<td>
@Html.DisplayFor(modelItem => item.Valor)
</td>
<td>
@Html.DisplayFor(modelItem => item.Status)
</td>
</tr>
}
</table>
377
7 ASP.NET Core
7.1. Introdução
Até o último instante, a versão que seria a sucessora da versão 4.6 do ASP.NET estava
sendo chamada de ASP.NET 5. Poucos dias antes do lançamento oficial, a Microsoft
anuncia que a nova versão terá o nome de ASP.NET Core. Essa mudança reflete
exatamente o que houve com a nova versão da plataforma: uma reformulação geral,
partindo para um modelo completamente novo.
•• Cross platform;
Na prática, isso significa que agora é possível criar um aplicativo para dispositivos
Android, iOS ou para um computador executando Linux. Além disso, é possível
distribuir o ASP.NET como parte da aplicação, criando servidores virtuais e serviços de
gerenciamento de informações da Internet que são distribuídos junto com o aplicativo.
380
ASP.NET Core 7
7.2. .NET Core e seus projetos
No que diz respeito a tipos de projetos, o framework .NET é usado no desenvolvimento
de aplicações Windows Forms e WPF, além de aplicações Web com MVC e Web Forms.
O .NET Core suporta as bibliotecas UWP e ASP.NET Core. UWP é usado para o
desenvolvimento na plataforma Windows 10 e o ASP.NET Core é usado em aplicações
Web nas plataformas Windows, Linux e Mac.
Uma vez instalado o SDK, é possível criar e executar aplicativos, seja usando o Visual
Studio, seja através do prompt de comandos.
>dotnet --info
381
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
>dotnet --help
>dotnet new
Se o atributo --name juntamente com o nome sugerido for omitido, será criado um
projeto com o nome da pasta selecionada.
382
ASP.NET Core 7
7.3.2. Usando o Visual Studio Code
Com o projeto criado, podemos analisar seu conteúdo. Como ele foi criado através
do console, não precisa necessariamente ser aberto no Visual Studio. A opção
recomendada é o Visual Studio Code (ou simplesmente VSCode), especialmente
quando estivermos em outros sistemas operacionais diferentes do Windows. O Visual
Studio Code pode ser obtido no link https://code.visualstudio.com/.
O VSCode é um editor bastante leve, que utiliza extensões para uma grande variedade
de aplicações. Com ele, podemos elaborar aplicações em HTML, CSS, JavaScript,
C#, Java, Python, e muitas outras, desde que tenhamos as extensões adequadas.
Diferentemente dos projetos que criamos no Visual Studio, ele não trabalha com
estrutura baseada em projetos, e sim com diretórios.
383
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Para utilizarmos o VSCode, vamos abri-lo e, no menu File, selecionar a opção Open
Folder. Selecione a pasta do projeto que criamos, ou seja, Capitulo07.NetCore.
Console:
Ao abrir o projeto, é possível que o próprio editor reconheça seu conteúdo e tente
baixar as extensões necessárias. Se isso ocorrer, aguarde o final da instalação.
384
ASP.NET Core 7
Para executá-lo, vamos usar o prompt de comandos. Execute a sequência de comandos:
>cd Capitulo07.NetCore.Console
>dotnet build --output build_app
>dotnet build_app/Capitulo07.NetCore.Console.dll
Essa sequência de comandos permite compilar o projeto em uma DLL na pasta build_
app e, em seguida, executá-lo.
Comando Descrição
-v | --verbose Habilita output com bastante detalhes (verbose).
--version Apresenta as informações de versão do .NET CLI.
Comando Descrição
new Inicia um novo projeto .NET básico.
restore Restaura as dependências especificadas no projeto.
build Compila um projeto .NET Core.
Publica um projeto .NET para deployment (incluindo
publish
o runtime).
run Compila e imediatamente executa um projeto.
Executa testes unitários usando o test runner
test
especificado no projeto.
pack Cria um pacote NuGet.
Esta abordagem foi apresentada como forma de demonstrar que não precisamos
obrigatoriamente do Visual Studio para criarmos projetos baseados no .NET Core.
385
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
386
ASP.NET Core 7
3. Na próxima etapa, selecione uma pasta adequada e forneça o nome Capitulo07.
NetCore.ConsoleVS:
Temos nosso projeto Console, de forma semelhante ao que fizemos via linha de
comandos. Analogamente ao projeto anterior, vamos realizar algumas alterações no
arquivo Program.cs:
using System;
namespace Capitulo07.NetCore.ConsoleVS
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Aplicativo Console, .NET Core");
Console.ReadKey();
}
}
}
387
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
1. No menu File / New Project, selecione a opção ASP.NET Core Web Application:
388
ASP.NET Core 7
3. Existem alguns templates disponíveis. Observe que, como camada Front End,
podemos escolher também o Angular e o React. Como nosso objetivo é trabalhar com
a arquitetura MVC, vamos selecionar esta opção (como vantagem, o Visual Studio
adiciona as referências necessárias para o projeto):
4. Crie o projeto.
Analisando a janela Solution Explorer, podemos ver que a estrutura de um projeto ASP.
NET Core é bem diferente daquela gerada no projeto ASP.NET com .NET Framework:
389
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
•• O formato XML não é utilizado mais para arquivos de sistema. O formato agora
é JSON;
•• Mesmo sendo uma aplicação Web, a execução parte do método Main presente
no arquivo Program.cs;
390
ASP.NET Core 7
3. Classe Startup - Método ConfigureServices
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
391
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Esta é uma amostra do arquivo Startup. O .NET Core utiliza extensivamente o recurso
de injeção de dependência, já que o container permite a execução em diferentes
plataformas. Nosso trabalho é especificar as atividades por meio das interfaces. As
instâncias serão fornecidas pelo próprio .NET Core.
7.5. Controllers e Actions
Os controllers compõem a parte fundamental de uma aplicação MVC, principalmente
por conta da arquitetura de mesmo nome.
392
ASP.NET Core 7
Já tivemos a oportunidade de estudar os controllers no capítulo referente ao ASP.NET
MVC. No ASP.NET Core, o procedimento de criação de controllers é bastante similar,
porém existe uma diferença fundamental com relação ao retorno dos actions: foi
introduzida uma camada utilizando uma interface, IActionResult.
393
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Para uma aplicação Web, selecione a opção MVC Controller. Em seguida, atribua o
nome desejado:
using Microsoft.AspNetCore.Mvc;
namespace Capitulo07.NetCore.WebApp.Controllers
{
public class ExemploController : Controller
{
public IActionResult Index()
{
return View();
}
}
}
Podemos verificar que a estrutura é similar, mas a origem das classes é bem diferente.
O .NET Core foi obtido, como já dito antes, através de um "redesenho" do .NET
Framework.
A partir do controller, podemos gerar as views, de acordo com os actions que tivermos.
As views podem ser escritas com conteúdo HTML, com elementos Razor ou com
um tipo especial de elementos introduzidos no .NET Core: as Tag Helpers. Vamos
conhecê-las a seguir.
7.6. Microsoft.AspNet.Mvc.TagHelpers
Algumas novidades foram introduzidas na geração do código HTML. A classe
TagHelpers define alguns novos elementos HTML:
•• environment
394
ASP.NET Core 7
No exemplo adiante, os elementos link serão renderizados apenas se o ambiente de
execução estiver definido como Development:
<environment names="Development">
<link rel="stylesheet" href="css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</environment>
•• asp-controller
Este atributo define o nome de um controlador. É utilizado para criar uma âncora
HTML (hiperlink).
•• asp-action
•• model Produto
namespace Capitulo07.NetCore.WebApp.Models
{
public class Produto
{
public int Id { get; set; }
public string Descricao { get; set; }
public double Preco { get; set; }
}
}
395
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
•• action Incluir
using Microsoft.AspNetCore.Mvc;
namespace Capitulo07.NetCore.WebApp.Controllers
{
public class ExemploController : Controller
{
public IActionResult Index()
{
return View();
}
•• view Incluir.cshtml
396
ASP.NET Core 7
@model Capitulo07.NetCore.WebApp.Models.Produto
@{
ViewData["Title"] = "Incluir";
}
<h1>Incluir</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Incluir">
<div asp-validation-summary="ModelOnly"
class="text-danger"></div>
<div class="form-group">
<label asp-for="Id" class="control-label"></label>
<input asp-for="Id" class="form-control" />
<span asp-validation-for="Id" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Descricao" class="control-label"></label>
<input asp-for="Descricao" class="form-control" />
<span asp-validation-for="Descricao"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Preco" class="control-label"></label>
<input asp-for="Preco" class="form-control" />
<span asp-validation-for="Preco" class="text-danger"></
span>
</div>
<div class="form-group">
<input type="submit" value="Incluir" class="btn btn-
info" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Voltar</a>
</div>
Trata-se de uma view fortemente tipada, onde cada campo de entrada se relaciona
com uma propriedade por meio do atributo asp-for:
<form asp-action="Incluir">
397
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Pontos principais
Atente para os tópicos a seguir. Eles devem ser estudados com muita atenção,
pois representam os pontos mais importantes do capítulo.
398
7 ASP.NET Core
400
ASP.NET Core 7
4. O arquivo de configurações de um projeto .NET Core se chama:
☐☐a) web.config
☐☐b) wpp.Config
☐☐c) wpp.json
☐☐d) settings.json
☐☐e) appsettings.json
401
7 ASP.NET Core
Mãos à obra!
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Objetivos:
Para que o participante efetue o pagamento, ele poderá usar um cartão de crédito.
Uma segunda aplicação simulando uma administradora de cartões de crédito
(similar àquela que fizemos no projeto finalizado no Capítulo 6) será criada,
contemplando a parte que permite interação com aplicações externas, ou seja, a
aplicação representará um Webservice, consumível pela nossa aplicação principal
(o que permite a efetivação do pagamento).
Laboratório 1
1. Na pasta Labs, crie um novo projeto ASP.NET Core Web Application
chamado Lab.NetCore, em um solution chamado Capitulo07.Labs:
404
ASP.NET Core 7
2. Na próxima etapa, selecione a opção Web Application (Model-View-
Controller). Adicione também o recurso para Contas de Usuário Individuais
(Individual User Accounts):
405
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
406
ASP.NET Core 7
5. Se estiver, desmarque a opção e copie o conteúdo do campo App URL para
o campo Launch browser:
407
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Eventos</title>
<link rel="stylesheet"
href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet"
href="~/css/site.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm
navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-controller="Home"
asp-action="Index">Eventos Impacta</a>
<button class="navbar-toggler" type="button"
data-toggle="collapse"
data-target=".navbar-collapse"
aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex
flex-sm-row-reverse">
<partial name="_LoginPartial" />
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area=""
asp-controller="Home"
asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area=""
asp-controller="Eventos"
asp-action="Index">Eventos</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area=""
asp-controller="Participantes"
asp-action="Index">Participantes</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
408
ASP.NET Core 7
@RenderBody()
</main>
</div>
@{
ViewData["Title"] = "Home";
}
<div class="text-center">
<h1 class="display-4">Bem vindo ao sistema de Gestão de Eventos</h1>
<p>Selecione uma opção no menu para iniciar</p>
</div>
using Microsoft.AspNetCore.Mvc;
namespace Lab.NetCore.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
}
409
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
410
Entity
8 Framework
Core
8.1. Introdução
O Entity Framework é parte integrante do framework, responsável pelo acesso a banco
de dados. Não é o único, mas certamente é o mais simples e rápido de usar, além de
ter uma estrutura otimizada que permite desde a criação até operações fundamentais
sobre um banco de dados.
O Entity Framework Core foi desenvolvido para ser extensível, além de bastante leve,
contendo apenas as operações necessárias. Ele utiliza apenas a abordagem Code
First. A ideia é permitir seu uso em uma grande variedade de plataformas.
8.3. Database Providers
O EF Core é uma camada entre a aplicação e o banco de dados. É usado, portanto,
para conectar o código com o banco.
Ou:
install-package nome_provider
Este é o provider mais comumente usado pelo EF Core. Ele conecta a aplicação ao SQL
Server.
412
Entity Framework Core 8
•• SQLite Provider
•• InMemory Provider
O EF Core adicionou o provider InMemory. É importante ter em mente que ele não
conecta a aplicação a um banco de dados real. Ele é usado como uma ferramenta para
testarmos a aplicação.
•• Outros providers
Além dos providers mencionados, existe uma gama de outros providers disponíveis,
como aqueles que permitem a conexão com MySQL, Maria DB ou DB2, por exemplo.
No entanto, estes providers são gerenciados pelos próprios fabricantes, e sua
obtenção deve ser realizada separadamente. Neste caso, a consulta à documentação
é indispensável para sua configuração.
413
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Já dissemos que o EF Core utiliza a abordagem Code First para a criação do banco
de dados. Neste capítulo, apresentaremos os passos para a criação das classes
necessárias à utilização do EF Core.
8.4.1. Definindo a entidade
Nossa entidade se chamará Pessoa. Vamos criá-la na pasta Models:
namespace Capitulo08.NetCore.EFCore.Models
{
public class Pessoa
{
public int PessoaId { get; set; }
public string Nome { get; set; }
public string SobreNome { get; set; }
}
}
414
Entity Framework Core 8
8.4.2. Definindo o contexto
No EF Core, o contexto é usado para estabelecer o modo de acesso ao banco de
dados, sem a necessidade de abstrações adicionais. Além de fornecer recursos para
adicionar, remover, alterar ou buscar informações, ele realiza também as operações
de abertura e fechamento da conexão.
using Microsoft.EntityFrameworkCore;
namespace Capitulo08.NetCore.EFCore.Models
{
public class PessoasContext : DbContext
{
}
}
using Microsoft.EntityFrameworkCore;
namespace Capitulo08.NetCore.EFCore.Models
{
public class PessoasContext : DbContext
{
public DbSet<Pessoa> Pessoas { get; set; }
}
}
415
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
services.AddControllersWithViews();
}
O método UseSqlServer está sendo usado para configurar o provider para o SQL Server.
O seu parâmetro aponta para uma string de conexão cujo nome é PessoasConnection,
que deverá ser definido no arquivo appsettings.json:
{
"ConnectionStrings": {
"PessoasConnection": "Integrated Security=SSPI;Persist Security
Info=False;Initial Catalog=DBPessoas;Data Source=.\\SQLEXPRESS"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
416
Entity Framework Core 8
A configuração especificada no método ConfigureServices é recebida pelo contexto
por injeção de dependência, através do seu construtor. Vamos alterá-lo para ilustrar
este processo:
using Microsoft.EntityFrameworkCore;
namespace Capitulo08.NetCore.EFCore.Models
{
public class PessoasContext : DbContext
{
public PessoasContext(DbContextOptions<PessoasContext> options)
: base(options)
{ }
Se desejarmos que o banco de dados seja criado com uma estrutura estabelecida de
forma diferente das entidades, devemos sobrescrever o método onModelCreating da
classe referente ao contexto. Este processo é similar ao que fizemos na utilização do
EF 6.
using Microsoft.EntityFrameworkCore;
namespace Capitulo08.NetCore.EFCore.Models
{
public class PessoasContext : DbContext
{
public PessoasContext(DbContextOptions<PessoasContext> options)
: base(options)
{ }
modelBuilder.Entity<Pessoa>()
.Property(p => p.Nome)
.IsRequired()
.HasMaxLength(50);
modelBuilder.Entity<Pessoa>()
.Property(p => p.SobreNome)
.IsRequired()
.HasMaxLength(50);
}
}
}
417
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Inclua no projeto uma pasta chamada Dados. Nesta, inclua uma classe estática
chamada DbInitializer:
using Capitulo08.NetCore.EFCore.Models;
namespace Capitulo08.NetCore.EFCore.Dados
{
public class DbInitializer
{
public static void Initialize(PessoasContext context)
{
context.Database.EnsureCreated();
}
}
}
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Capitulo08.NetCore.EFCore
{
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
host.Run();
}
418
Entity Framework Core 8
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Em seguida, escreveremos o código adiante, usando a nova variável host como base:
using Capitulo08.NetCore.EFCore.Dados;
using Capitulo08.NetCore.EFCore.Models;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
namespace Capitulo08.NetCore.EFCore
{
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
host.Run();
}
419
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Teste a aplicação até este ponto. Se tudo estiver correto, teremos nosso banco de
dados criado, e a aplicação pronta para funcionar com este banco.
using Capitulo08.NetCore.EFCore.Models;
using Microsoft.AspNetCore.Mvc;
namespace Capitulo08.NetCore.EFCore.Controllers
{
public class PessoasController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpGet]
public IActionResult Incluir()
{
return View();
}
[HttpPost]
public IActionResult Incluir(Pessoa pessoa)
{
return View();
}
420
Entity Framework Core 8
Usaremos a entidade Pessoa para gerar a view Incluir:
@model Capitulo08.NetCore.EFCore.Models.Pessoa
@{
ViewData["Title"] = "Incluir";
}
<h1>Incluir</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Incluir">
<div asp-validation-summary="ModelOnly"
class="text-danger"></div>
@*<div class="form-group">
<label asp-for="PessoaId" class="control-label"></label>
<input asp-for="PessoaId" class="form-control" />
<span asp-validation-for="PessoaId"
class="text-danger"></span>
</div>*@
<div class="form-group">
<label asp-for="Nome" class="control-label"></label>
<input asp-for="Nome" class="form-control" />
<span asp-validation-for="Nome" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="SobreNome" class="control-label"></label>
<input asp-for="SobreNome" class="form-control" />
<span asp-validation-for="SobreNome"
class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="INcluir" class="btn btn-info" />
</div>
</form>
</div>
</div>
421
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using Capitulo08.NetCore.EFCore.Models;
using Microsoft.AspNetCore.Mvc;
namespace Capitulo08.NetCore.EFCore.Controllers
{
public class PessoasController : Controller
{
private PessoasContext Context { get; set; }
public PessoasController(PessoasContext context)
{
this.Context = context;
}
[HttpGet]
public IActionResult Incluir()
{
return View();
}
[HttpPost]
public IActionResult Incluir(Pessoa pessoa)
{
return View();
}
422
Entity Framework Core 8
Com isso, podemos codificar a versão POST do action Incluir, para adicionar o registro
no banco de dados. Mas, antes, codificaremos o action Listar, com sua view. Após a
inclusão, redirecionaremos o usuário para a lista de pessoas:
@model IEnumerable<Capitulo08.NetCore.EFCore.Models.Pessoa>
@{
ViewData["Title"] = "Listar";
}
<h1>Listar</h1>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.PessoaId)
</th>
<th>
@Html.DisplayNameFor(model => model.Nome)
</th>
<th>
@Html.DisplayNameFor(model => model.SobreNome)
</th>
</tr>
</thead>
423
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.PessoaId)
</td>
<td>
@Html.DisplayFor(modelItem => item.Nome)
</td>
<td>
@Html.DisplayFor(modelItem => item.SobreNome)
</td>
</tr>
}
</tbody>
</table>
using Capitulo08.NetCore.EFCore.Models;
using Microsoft.AspNetCore.Mvc;
using System.Linq;
namespace Capitulo08.NetCore.EFCore.Controllers
{
public class PessoasController : Controller
{
private PessoasContext Context { get; set; }
public PessoasController(PessoasContext context)
{
this.Context = context;
}
[HttpGet]
public IActionResult Incluir()
{
return View();
}
424
Entity Framework Core 8
[HttpPost]
public IActionResult Incluir(Pessoa pessoa)
{
Context.Add<Pessoa>(pessoa);
Context.SaveChanges();
return RedirectToAction("Listar");
}
Vamos executar a aplicação e chamar o action Incluir. A partir dele, inclua uma nova
pessoa e verifique seu resultado na listagem:
425
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Pontos principais
Atente para os tópicos a seguir. Eles devem ser estudados com muita atenção,
pois representam os pontos mais importantes do capítulo.
426
Entity
8 Framework
Core
Teste seus conhecimentos
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
428
Entity Framework Core 8
4. A configuração especificada no método ConfigureServices é recebida
pelo contexto no construtor por injeção de dependência através de um
parâmetro do tipo:
☐☐a) DbContextOptions
☐☐b) DbContext
☐☐c) Options
☐☐d) DependencyInjection
☐☐e) DbInjection
429
Entity
8 Framework
Core
Mãos à obra!
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Objetivos:
Laboratório 1
1. Na pasta Labs, crie um novo solution (blank solution) chamado Capitulo08.
Labs;
5. Na pasta Models (se não existir, crie-a), inclua as duas classes mostradas
adiante. Essas classes são as entidades a serem manipuladas na aplicação:
using System;
using System.Collections.Generic;
namespace Lab.NetCore.Models
{
public class Evento
{
public int Id { get; set; }
public string Descricao { get; set; }
public DateTime Data { get; set; }
public string Local { get; set; }
public double Preco { get; set; }
}
}
432
Entity Framework Core 8
using System;
namespace Lab.NetCore.Models
{
public class Participante
{
public int Id { get; set; }
public int EventoInfoId { get; set; }
public string Nome { get; set; }
public string Email { get; set; }
public string Cpf { get; set; }
public DateTime DataNascimento { get; set; }
}
}
6. No projeto, crie uma pasta chamada Dao. Nessa pasta, defina o contexto do
banco de dados, EventosContext:
using Lab.NetCore.Models;
using Microsoft.EntityFrameworkCore;
namespace Lab.NetCore.Dao
{
public class EventosContext: DbContext
{
public EventosContext(DbContextOptions<EventosContext>
opcoes) : base(opcoes)
{ }
433
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
modelBuilder.Entity<Participante>().ToTable("TBParticipantes");
modelBuilder.Entity<Participante>()
.Property(p => p.Cpf)
.IsRequired()
.HasMaxLength(11);
modelBuilder.Entity<Participante>()
.Property(p => p.Nome)
.IsRequired()
.HasMaxLength(50);
modelBuilder.Entity<Participante>()
.Property(p => p.Email)
.IsRequired()
.HasMaxLength(60);
modelBuilder.Entity<Participante>()
.Property(p => p.DataNascimento)
.IsRequired();
}
}
}
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(options => options
.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
services.AddRazorPages();
}
434
Entity Framework Core 8
8. Agora, vamos abrir o arquivo appsettings.json. Configure a string de
conexão como indicado a seguir (você deve alterá-la para refletir um banco de
dados disponível na sua máquina ou na sua rede). Deixe como está mostrado
a seguir:
{
"ConnectionStrings": {
"DefaultConnection": "Integrated Security=SSPI;Persist Security
Info=False;Initial Catalog=DB_IDENTITY;Data Source=.\\SQLEXPRESS",
"EventosConnection": "Integrated Security=SSPI;Persist Security
Info=False;Initial Catalog=DB_EVENTOS;Data Source=.\\SQLEXPRESS"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
namespace Lab.NetCore.Dao
{
public class DbInitializer
{
public static void Initialize(EventosContext context)
{
context.Database.EnsureCreated();
}
}
}
435
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using Lab.NetCore.Dao;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
namespace Lab.NetCore
{
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
host.Run();
}
436
Entity Framework Core 8
Teste a aplicação até este ponto. Se tudo estiver correto, teremos nosso banco
de dados criado, e a aplicação pronta para funcionar com este banco.
using Microsoft.AspNetCore.Mvc;
namespace Lab.NetCore.Controllers
{
public class EventosController : Controller
{
public IActionResult Index()
{
return View();
}
}
}
437
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
12. A partir do action Index, crie uma view sem modelo, com layout. Deixe
com o conteúdo a seguir:
@{
ViewData["Title"] = "Gestão de Eventos";
}
<h2>Gestão de Eventos</h2>
<ul>
<li>
<a asp-controller ="Eventos" asp-action="IncluirEvento">
Incluir Novo Evento</a>
</li>
<li>
<a asp-controller ="Eventos" asp-action="ListarEventos">
Listar Eventos</a>
</li>
</ul>
438
Entity Framework Core 8
13. No controller EventosController, adicione os actions IncluirEvento e
ListarEventos:
using Microsoft.AspNetCore.Mvc;
namespace Lab.NetCore.Controllers
{
public class EventosController : Controller
{
public IActionResult Index()
{
return View();
}
14. Para permitir o acesso ao banco de dados por meio da aplicação, criaremos
uma estrutura otimizada, tentando unificar as operações comuns em poucos
métodos, pois o Entity Framework Core nos permitirá fazer isso. Na pasta
Dao, crie uma classe chamada GenericDao com o seguinte conteúdo:
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Lab.NetCore.Dao
{
public enum TipoOperacaoBD
{
Detached = 0,
Unchanged = 1,
Deleted = 2,
Modified = 3,
Added = 4
}
439
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
15. Para lidar com as operações relacionadas a eventos, crie a classe EventosDao
na pasta Dao. Esta classe deve ser subclasse da GenericDao:
using Lab.NetCore.Models;
namespace Lab.NetCore.Dao
{
public class EventosDao : GenericDao<Evento>
{
public EventosDao(EventosContext context)
: base(context)
{ }
}
}
440
Entity Framework Core 8
16. Vamos alterar o controller EventosController para receber o contexto
por injeção de dependência. Implemente os actions IncluirEvento (POST) e
ListarEventos deste controller:
using Lab.NetCore.Dao;
using Lab.NetCore.Models;
using Microsoft.AspNetCore.Mvc;
namespace Lab.NetCore.Controllers
{
public class EventosController : Controller
{
private EventosDao eventosDao { get; set; }
[HttpPost]
public IActionResult IncluirEvento(Evento evento)
{
if (!ModelState.IsValid)
{
return View();
}
eventosDao.Executar(evento, TipoOperacaoBD.Added);
return RedirectToAction("ListarEventos");
}
441
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Lab.NetCore.Models
{
public class Evento
{
public int Id { get; set; }
[Required]
[Display(Name = "Descrição")]
public string Descricao { get; set; }
[Required]
[Display(Name = "Data do evento")]
[DataType(DataType.Date)]
public DateTime Data { get; set; }
[Required]
public string Local { get; set; }
[Required]
[DataType(DataType.Currency)]
public double Preco { get; set; }
}
}
using System;
using System.ComponentModel.DataAnnotations;
namespace Lab.NetCore.Models
{
namespace Lab.NetCore.Models
{
public class Participante
{
public int Id { get; set; }
[Display(Name = "Evento")]
public int EventoInfoId { get; set; }
[Required]
public string Nome { get; set; }
442
Entity Framework Core 8
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength(11, MinimumLength = 11)]
public string Cpf { get; set; }
[DataType(DataType.Date)]
[Display(Name = "Data Nascimento")]
public DateTime DataNascimento { get; set; }
}
}}
443
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@model Lab.NetCore.Models.Evento
@{
ViewData["Title"] = "Incluir Evento";
}
<h1>Incluir Evento</h1>
<h4>Incluir Evento</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="IncluirEvento">
<div asp-validation-summary="ModelOnly"
class="text-danger"></div>
@*<div class="form-group">
<label asp-for="Id" class="control-label"></label>
<input asp-for="Id" class="form-control" />
<span asp-validation-for="Id"
class="text-danger"></span>
</div>*@
<div class="form-group">
<label asp-for="Descricao"
class="control-label"></label>
<input asp-for="Descricao"
class="form-control" />
<span asp-validation-for="Descricao"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Data"
class="control-label"></label>
<input asp-for="Data"
class="form-control" />
<span asp-validation-for="Data"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Local"
class="control-label"></label>
<input asp-for="Local"
class="form-control" />
<span asp-validation-for="Local"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Preco"
class="control-label"></label>
<input asp-for="Preco"
class="form-control" />
<span asp-validation-for="Preco"
class="text-danger"></span>
444
Entity Framework Core 8
</div>
<div class="form-group">
<input type="submit" value="Incluir"
class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Voltar para o menu</a>
</div>
@model IEnumerable<Lab.NetCore.Models.Evento>
@{
ViewData["Title"] = "Listar Eventos";
}
<h1>Listar Eventos</h1>
<p>
<a asp-action="IncluirEvento">Novo Evento</a>
</p>
<table class="table">
<thead>
<tr>
<th>
445
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
446
Entity Framework Core 8
20. Execute a aplicação, incluindo eventos:
447
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using Lab.NetCore.Dao;
using Lab.NetCore.Models;
using Microsoft.AspNetCore.Mvc;
namespace Lab.NetCore.Controllers
{
public class EventosController : Controller
{
private EventosDao eventosDao { get; set; }
[HttpPost]
public IActionResult IncluirEvento(Evento evento)
{
if (!ModelState.IsValid)
{
return View();
}
eventosDao.Executar(evento, TipoOperacaoBD.Added);
return RedirectToAction("ListarEventos");
}
448
Entity Framework Core 8
//action HTTP Get Comum
private IActionResult ExecutarEvento(int id, string viewName)
{
var evento = eventosDao.BuscarPorId(id);
if (evento == null)
{
ViewData["MensagemErro"] = "Nenhum evento encontrado";
return View("_Erro");
}
return View(viewName, evento);
}
//Alterando o evento
[HttpGet]
public IActionResult AlterarEvento(int id)
{
return ExecutarEvento(id, "AlterarEvento");
}
[HttpPost]
public IActionResult AlterarEvento(Evento evento)
{
eventosDao.Executar(evento, TipoOperacaoBD.Modified);
return RedirectToAction("ListarEventos");
}
}
}
449
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@model Lab.NetCore.Models.Evento
@{
ViewData["Title"] = "Alterar Evento";
}
<h1>Alterar Evento</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="AlterarEvento">
<div asp-validation-summary="ModelOnly"
class="text-danger"></div>
@*<div class="form-group">
<label asp-for="Id" class="control-label"></label>
<input asp-for="Id" class="form-control" />
<span asp-validation-for="Id"
class="text-danger"></span>
</div>*@
<div class="form-group">
<label asp-for="Descricao"
class="control-label"></label>
<input asp-for="Descricao"
class="form-control" />
<span asp-validation-for="Descricao"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Data"
class="control-label"></label>
<input asp-for="Data"
class="form-control" />
<span asp-validation-for="Data"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Local"
class="control-label"></label>
<input asp-for="Local"
class="form-control" />
<span asp-validation-for="Local"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Preco"
class="control-label"></label>
<input asp-for="Preco"
class="form-control" />
<span asp-validation-for="Preco"
class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Alterar"
class="btn btn-primary" />
</div>
450
Entity Framework Core 8
</form>
</div>
</div>
<div>
<a asp-action="ListarEventos">Voltar para a lista</a>
</div>
using Lab.NetCore.Dao;
using Lab.NetCore.Models;
using Microsoft.AspNetCore.Mvc;
namespace Lab.NetCore.Controllers
{
public class EventosController : Controller
{
private EventosDao eventosDao { get; set; }
[HttpPost]
public IActionResult IncluirEvento(Evento evento)
{
if (!ModelState.IsValid)
{
return View();
}
eventosDao.Executar(evento, TipoOperacaoBD.Added);
return RedirectToAction("ListarEventos");
}
451
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
if (evento == null)
{
ViewData["MensagemErro"] = "Nenhum evento encontrado";
return View("_Erro");
}
return View(viewName, evento);
}
//Alterando o evento
[HttpGet]
public IActionResult AlterarEvento(int id)
{
return ExecutarEvento(id, "AlterarEvento");
}
[HttpPost]
public IActionResult AlterarEvento(Evento evento)
{
eventosDao.Executar(evento, TipoOperacaoBD.Modified);
return RedirectToAction("ListarEventos");
}
//removendo um evento
[HttpGet]
public IActionResult RemoverEvento(int id)
{
return ExecutarEvento(id, "RemoverEvento");
}
[HttpPost]
public IActionResult RemoverEvento(Evento evento)
{
eventosDao.Executar(evento, TipoOperacaoBD.Deleted);
return RedirectToAction("ListarEventos");
}
}
}
452
Entity Framework Core 8
25. Crie a view RemoverEvento:
@model Lab.NetCore.Models.Evento
@{
ViewData["Title"] = "Remover Evento";
}
<h1 class="text-danger">Confirmação</h1>
<h3 class="text-danger">
Tem certeza que deseja remover este evento?
</h3>
<div>
<hr />
<dl class="row">
</dl>
<form asp-action="RemoverEvento">
<input type="hidden" asp-for="Id" />
<input type="submit" value="Sim, remover"
class="btn btn-danger" /> |
<a asp-action="ListarEventos">Não, voltar para a lista</a>
</form>
</div>
453
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
27. Da mesma forma que pudemos incluir e listar eventos, vamos fazer o
mesmo com os participantes. Neste caso, temos duas características distintas:
using Microsoft.AspNetCore.Mvc;
namespace Lab.NetCore.Controllers
{
public class ParticipantesController : Controller
{
public IActionResult Index()
{
return View();
}
}
}
454
Entity Framework Core 8
@{
ViewData["Title"] = "Index";
}
<ul>
<li>
<a asp-action="IncluirParticipante"
asp-controller="Participantes">
Incluir Participante
</a>
</li>
<li>
<a asp-action="ListarParticipantes"
asp-controller="Participantes">
Listar Participantes por Evento
</a>
</li>
</ul>
using Lab.NetCore.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Lab.NetCore.Dao
{
public class ParticipantesDao: GenericDao<Participante>
{
private EventosContext Context { get; set; }
455
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using Lab.NetCore.Dao;
using Lab.NetCore.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Lab.NetCore.Controllers
{
public class ParticipantesController : Controller
{
private EventosDao eventosDao { get; set; }
private ParticipantesDao participantesDao { get; set; }
[HttpGet]
public IActionResult IncluirParticipante()
{
ViewBag.ListaDeEventos = new
SelectList(eventosDao.Listar(), "Id", "Descricao");
return View();
}
[HttpPost]
public IActionResult IncluirParticipante(Participante participante)
{
if (participante.EventoInfoId == 0)
{
ModelState.AddModelError("IdEvento",
"Nenhum evento selecionado ");
}
if (!ModelState.IsValid)
{
return IncluirParticipante();
}
participantesDao.Executar(participante, TipoOperacaoBD.Added);
return RedirectToAction("Index");
}
}
}
456
Entity Framework Core 8
31. Gere a view para o action IncluirParticipante. Observe as alterações
realizadas, especialmente na seleção de eventos:
@model Lab.NetCore.Models.Participante
@{
ViewData["Title"] = "Incluir Participante";
}
<h1>Incluir Participante</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="IncluirParticipante">
<div asp-validation-summary="ModelOnly"
class="text-danger"></div>
<div class="form-group">
<label asp-for="EventoInfoId"
lass="control-label"></label>
<select asp-for="EventoInfoId"
asp-items="(SelectList)ViewBag.ListaDeEventos"
class="form-control">
<option>SELECIONE</option>
</select>
<span asp-validation-for="EventoInfoId"
class="text-danger"></span>
457
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
</div>
<div class="form-group">
<label asp-for="Nome"
class="control-label"></label>
<input asp-for="Nome"
class="form-control" />
<span asp-validation-for="Nome"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email"
class="control-label"></label>
<input asp-for="Email"
class="form-control" />
<span asp-validation-for="Email"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Cpf"
class="control-label"></label>
<input asp-for="Cpf"
class="form-control" />
<span asp-validation-for="Cpf"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DataNascimento"
class="control-label"></label>
<input asp-for="DataNascimento"
class="form-control" />
<span asp-validation-for="DataNascimento"
class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Incluir"
class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Voltar para o menu</a>
</div>
458
Entity Framework Core 8
C - Listando os participantes por evento via JavaScript - Ajax
Teremos a view principal e a view parcial. A view principal será chama quando o
usuário selecionar a lista de participantes; a partir desse ponto, basta escolher
um evento para ver a lista de participantes daquele evento.
using Lab.NetCore.Dao;
using Lab.NetCore.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Lab.NetCore.Controllers
{
public class ParticipantesController : Controller
{
private EventosDao eventosDao { get; set; }
private ParticipantesDao participantesDao { get; set; }
[HttpGet]
public IActionResult IncluirParticipante()
{
ViewBag.ListaDeEventos = new
SelectList(eventosDao.Listar(), "Id", "Descricao");
return View();
}
459
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
[HttpPost]
public IActionResult IncluirParticipante(Participante participante)
{
if (participante.EventoInfoId == 0)
{
ModelState.AddModelError("IdEvento",
"Nenhum evento selecionado ");
}
if (!ModelState.IsValid)
{
return IncluirParticipante();
}
participantesDao.Executar(participante, TipoOperacaoBD.Added);
return RedirectToAction("Index");
}
if (idEvento == 0)
{
return View();
}
else
{
var lista = participantesDao.ListarPorEvento(idEvento);
return PartialView("_ListarParticipantes", lista);
}
}
}
}
460
Entity Framework Core 8
33. Crie a view para o action ListarParticipantes. Observe que a view não terá
nenhum model:
@{
ViewData["Title"] = "Listar Participantes";
}
<form asp-action="ListarParticipantesAjax"
asp-controller="Eventos"
method="get">
<div class="row">
<label class="control-label">ID Evento</label>
<select id="idEvento" name="idEvento"
class="form-control"
asp-items="(SelectList)ViewBag.ListaDeEventos">
<option value="0">SELECIONE</option>
</select>
</div>
</form>
461
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@section Scripts{
<script>
$(document).ready(function () {
$("#idEvento").change(function () {
var selecao = $(this).val();
if (selecao == "0") {
var erro = "<div class=’alert alert-danger’>" +
"Nenhum evento selecionado</div>";
$("#resultado").html(erro);
}
else {
$("#resultado")
.load("/Participantes/ListarParticipantes?idEvento="
+ selecao);
}
});
});
</script>
}
462
Entity Framework Core 8
Mantenha o conteúdo:
@model IEnumerable<Lab.NetCore.Models.Participante>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Nome)
</th>
<th>
@Html.DisplayNameFor(model => model.Email)
</th>
<th>
@Html.DisplayNameFor(model => model.Cpf)
</th>
<th>
@Html.DisplayNameFor(model => model.DataNascimento)
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Nome)
</td>
<td>
@Html.DisplayFor(modelItem => item.Email)
</td>
<td>
@Html.DisplayFor(modelItem => item.Cpf)
</td>
<td>
@Html.DisplayFor(modelItem => item.DataNascimento)
</td>
</tr>
}
</tbody>
</table>
Neste ponto, será inserido o conteúdo da section com o nome Scripts. Podemos
definir na view tantos sections quanto desejarmos e, no layout, especificar um
local adequado para inseri-lo. Este processo é semelhante ao RenderBody(),
só que nomeado, e com a opção de obrigatoriedade ou não;
463
9 ASP.NET Core
Identity
9.1. Introdução
Existem dois processos básicos envolvidos na segurança dos dados disponibilizados
por uma aplicação: autenticação e autorização. A autenticação é o processo de
identificar a pessoa, software ou serviço que realizará alguma tarefa no programa.
Já a autorização é o processo de permitir ou proibir o acesso às funcionalidades do
sistema pelo usuário identificado.
Os usuários podem criar uma conta contendo um nome de usuário e uma senha,
que seguem determinados padrões. Assim como discutido no Capítulo 5, no qual
estudamos o ASP.NET Identity do .NET Framework, o Identity Core também permite
logon em diferentes providers, como Facebook, Google, Microsoft Account, Twitter
ou outros.
Neste capítulo, veremos como incluir recursos do Identity Core na nossa aplicação.
466
ASP.NET Core Identity 9
9.2.1. Definindo o projeto
A melhor maneira de adicionarmos os recursos de autenticação por meio do ASP.
NET Core Identity é definindo um projeto adicionando o recurso de contas de usuário
individuais (Individual User Accounts). Para demonstrar, vamos criar um novo projeto
ASP.NET Core Web Application, com o template MVC com o recurso de contas de
usuário individuais adicionado. O nome do projeto será Capitulo09.IdentityCore:
467
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;
namespace Capitulo09.IdentityCore.Models
{
public class UsuarioApp: IdentityUser
{
[MaxLength(11)]
public string Cpf { get; set; }
}
}
468
ASP.NET Core Identity 9
9.2.3. Definindo o contexto do banco de dados
O ASP.NET Core Identity utiliza como padrão o Entity Framework Core como mecanismo
de acesso a dados. Neste tópico, nós definiremos o contexto que suportará o ASP.NET
Core Identity. Neste caso, devemos criar uma subclasse de IdentityDbContext. Essa
classe é importante, pois encapsula a criação das propriedades do tipo DbSet, comuns
no Entity Framework Core. A lógica empregada para manipular essas propriedades
está implementada na classe IdentityDbContext.
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace Capitulo09.IdentityCore.Models
{
public class UsuariosContext : IdentityDbContext<UsuarioApp>
{
public UsuariosContext(DbContextOptions<UsuariosContext> options)
:
base(options)
{
}
}
}
{
"ConnectionStrings": {
"DefaultConnection": "Integrated Security=SSPI;Persist Security
Info=False;Initial Catalog=DBIdentity;Data Source=.\\SQLEXPRESS"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
469
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
services.AddDbContext<UsuariosContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<UsuarioApp>()
.AddEntityFrameworkStores<UsuariosContext>();
services.AddControllersWithViews();
services.AddRazorPages();
}
Nesse exemplo, podemos ver que os serviços do ASP.NET Core Identity são instanciados
por meio da chamada ao método AddDefaultIdentity. Essa aplicação usa a classe
UsuarioApp para conter os dados do usuário. Ela conecta, então, o service do Identity
ao IdentityDbContext fornecido por AuthenticationContext.
470
ASP.NET Core Identity 9
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
UsuariosContext context)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseStaticFiles();
app.UseRouting();
context.Database.EnsureCreated();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
9.3.1. Definindo o controller
Neste tópico, apresentaremos os procedimentos para criar usuários e efetuar login.
Para sua codificação, criaremos um novo controller chamado ContasController com
os actions necessários.
471
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using Capitulo09.IdentityCore.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace Capitulo09.IdentityCore.Controllers
{
public class ContasController : Controller
{
private readonly UserManager<UsuarioApp> _userManager;
private readonly SignInManager<UsuarioApp> _signInManager;
9.3.2. Definindo os modelos
Vamos, agora, implementar as operações de inclusão de um novo usuário e de login.
Para isso, devemos definir as classes necessárias que representarão os Models do
projeto para o login e para o registro. Para o login, criaremos a classe LogonViewModel
e, para o registro, UsuarioViewModel:
using System.ComponentModel.DataAnnotations;
namespace Capitulo09.IdentityCore.Models
{
public class LogonViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Senha { get; set; }
}
}
472
ASP.NET Core Identity 9
using System.ComponentModel.DataAnnotations;
namespace Capitulo09.IdentityCore.Models
{
public class UsuarioViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Senha { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirma Senha")]
[Compare("Senha")]
public string ConfirmaSenha { get; set; }
}
}
using Capitulo09.IdentityCore.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Capitulo09.IdentityCore.Controllers
{
public class ContasController : Controller
{
private readonly UserManager<UsuarioApp> _userManager;
private readonly SignInManager<UsuarioApp> _signInManager;
473
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
//Métodos auxiliares
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
//Métodos de registro
[HttpGet]
[AllowAnonymous]
public IActionResult Registrar(string returnUrl = null)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Registrar(UsuarioViewModel
model,
string returnUrl = null)
{
ViewBag.ReturnUrl = returnUrl;
if (ModelState.IsValid)
{
var user = new UsuarioApp
{
UserName = model.Email,
Email = model.Email
};
474
ASP.NET Core Identity 9
//Login
[HttpGet]
[AllowAnonymous]
public IActionResult Login(string ReturnUrl = null)
{
ViewBag.ReturnUrl = ReturnUrl;
return View();
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login(LogonViewModel model,
string ReturnUrl)
{
ViewBag.ReturnUrl = ReturnUrl;
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(
model.Email, model.Senha, false, lockoutOnFailure:
false);
if (result.Succeeded)
{
return RedirectToLocal(ReturnUrl);
}
else
{
ModelState.AddModelError(
string.Empty, "Usuário ou senha inválidos.");
return View(model);
}
}
return View(model);
}
//Logout
[HttpGet]
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
return RedirectToAction("Index", "Home");
}
}
}
475
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Uma vez que a autenticação seja realizada com sucesso, o usuário pode ser
redirecionado para uma URL apropriada, que pode ser a página inicial (caso a operação
de login seja a primeira operação no projeto) ou a página desejada (caso o usuário
tente acessar um recurso protegido).
•• Registrar.cshtml
@model Capitulo09.IdentityCore.Models.UsuarioViewModel
@{
ViewData["Title"] = "Registrar";
}
<h1>Registrar</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Registrar">
<div asp-validation-summary="ModelOnly"
class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></
476
ASP.NET Core Identity 9
span>
</div>
<div class="form-group">
<label asp-for="Senha" class="control-label"></label>
<input asp-for="Senha" class="form-control" />
<span asp-validation-for="Senha" class="text-danger"></
span>
</div>
<div class="form-group">
<label asp-for="ConfirmaSenha" class="control-label"></
label>
<input asp-for="ConfirmaSenha" class="form-control" />
<span asp-validation-for="ConfirmaSenha"
class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Incluir Usuário"
class="btn btn-primary" />
</div>
</form>
</div>
</div>
•• Login.cshtml
477
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@model Capitulo09.IdentityCore.Models.LogonViewModel
@{
ViewData["Title"] = "Login";
}
<h1>Login</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Login">
<div asp-validation-summary="ModelOnly"
class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Senha" class="control-label"></label>
<input asp-for="Senha" class="form-control" />
<span asp-validation-for="Senha"
class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Login"
class="btn btn-primary" />
</div>
</form>
</div>
</div>
478
ASP.NET Core Identity 9
9.3.4. Definindo o componente com os itens de
cadastro, login e logout
Quando criamos o projeto, foi adicionada uma Partial View chamada _LoginPartial.
cshtml na pasta Views/Shared. Nossa tarefa é redefinir esse componente de forma a
corresponder aos itens que definimos no projeto até o momento. Deixe esse arquivo
com as configurações a seguir (os itens alterados / implementados estão destacados):
@using Microsoft.AspNetCore.Identity
@inject SignInManager<UsuarioApp> SignInManager
@inject UserManager<UsuarioApp> UserManager
<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
<li class="nav-item">
<a class="nav-link text-dark"
asp-controller="Home"
asp-action="Index">@User.Identity.Name!</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark"
asp-controller="Contas"
asp-action="Logout">Logout</a>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-dark"
asp-controller="Contas"
asp-action="Registrar">Registrar</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark"
asp-controller="Contas"
asp-action="Login">Login</a>
</li>
}
</ul>
479
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
480
ASP.NET Core Identity 9
Vamos acessar o menu Registrar e adicionar um novo usuário:
9.4. Implementando autorização
A forma mais comum de autorização consiste no bloqueio de usuários não autenticados
tentando executar determinados actions. Nesse caso, o usuário normalmente é
redirecionado para o formulário de login para que o efetue.
No caso de o atributo [Authorize] ser aplicado na classe, todos os actions deverão ser
executados mediante autorização. Havendo um action em particular, por exemplo,
que deve ser acessado de forma anônima (sem autenticação), este deve ter o atributo
[AllowAnonymous] definido.
481
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using Capitulo09.IdentityCore.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Capitulo09.IdentityCore.Controllers
{
public class ContasController : Controller
{
private readonly UserManager<UsuarioApp> _userManager;
private readonly SignInManager<UsuarioApp> _signInManager;
[Authorize]
public IActionResult Index()
{
return View();
}
//...
}
}
@{
ViewData["Title"] = "Index";
}
Vamos executar a aplicação e tentar acessar essa página, sem autenticação. O acesso a
um item protegido, por default, redireciona o usuário para Account/Login. Devemos
configurar o redirecionamento para a nossa página de login. Para tanto, devemos
sobrescrever o comportamento padrão.
482
ASP.NET Core Identity 9
Na classe Startup, escreva o seguinte código, após o método AddDefaultIdentity:
services.AddDefaultIdentity<UsuarioApp>()
.AddEntityFrameworkStores<UsuariosContext>();
services.ConfigureApplicationCookie(options =>
{
options.LoginPath = "/Contas/Login";
options.LogoutPath = "/Contas/Logout";
});
services.AddControllersWithViews();
services.AddRazorPages();
}
483
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Pontos principais
Atente para os tópicos a seguir. Eles devem ser estudados com muita atenção,
pois representam os pontos mais importantes do capítulo.
484
9 ASP.NET Core
Identity
Teste seus conhecimentos
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
486
ASP.NET Core Identity 9
4. Para permitir que um usuário seja autenticado no sistema, devemos
injetar no controller um objeto de qual classe?
☐☐a) SignInManager
☐☐b) LoginManager
☐☐c) PrincipalManager
☐☐d) RoleManager
☐☐e) UserManager
487
9 ASP.NET Core
Identity
Mãos à obra!
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Objetivos:
Laboratório 1
A - Adicionando recursos para autenticação e autorização de usuários
using Microsoft.AspNetCore.Identity;
namespace Lab.NetCore.Models
{
public class Usuario : IdentityUser
{
}
}
É sempre uma boa prática criar uma subclasse de IdentityUser, pois esse
recurso permite adicionar propriedades relevantes para a aplicação.
490
ASP.NET Core Identity 9
6. Na pasta Dao, defina a classe que representa o acesso a dados, o DbContext.
Verifique a sua superclasse (o parâmetro options será passado via injeção de
dependência):
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace Lab.NetCore.Dao
{
public class UsuariosDbContext: IdentityDbContext<Usuario>
{
public UsuariosDbContext(DbContextOptions<UsuariosDbContext>
options) : base(options)
{ }
services.AddDbContext<UsuariosDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddControllersWithViews();
services.AddRazorPages();
}
491
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using System.ComponentModel.DataAnnotations;
namespace Lab.NetCore.Models
{
public class UsuarioViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Senha { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirma Senha")]
[Compare("Senha")]
public string ConfirmaSenha { get; set; }
}
}
using System.ComponentModel.DataAnnotations;
namespace Lab.NetCore.Models
{
public class LogonViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Senha { get; set; }
}
}
492
ASP.NET Core Identity 9
9. Retornando ao arquivo Startup.cs, adicionaremos um recurso para que,
quando a aplicação for iniciada, uma lista de perfis (Roles) seja adicionada ao
banco de dados do ASP.NET Identity Core:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.EntityFrameworkCore;
using Lab.NetCore.Data;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Lab.NetCore.Dao;
using Lab.NetCore.Models;
namespace Lab.NetCore
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
//services.AddDbContext<ApplicationDbContext>(options =>
// options.UseSqlServer(
// Configuration.GetConnectionString("DefaultConnection")));
services.AddDbContext<UsuariosDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<Usuario, IdentityRole>()
.AddEntityFrameworkStores<UsuariosDbContext>()
.AddDefaultTokenProviders();
493
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
services.AddControllersWithViews();
services.AddRazorPages();
}
app.UseRouting();
context.Database.EnsureCreated();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
CreateRoles(serviceProvider).Wait();
}
494
ASP.NET Core Identity 9
//definir um método para incluir novos perfis (roles)
private async Task CreateRoles(IServiceProvider serviceProvider)
{
var roleManager = serviceProvider
.GetRequiredService<RoleManager<IdentityRole>>();
IdentityResult result;
foreach (var role in roleNames)
{
var roleExist = await roleManager.RoleExistsAsync(role);
if (!roleExist)
{
result = await
roleManager.CreateAsync(new IdentityRole(role));
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Lab.NetCore.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Lab.NetCore.Controllers
{
public class UsuariosController : Controller
{
private readonly UserManager<Usuario> _userManager;
private readonly SignInManager<Usuario> _signInManager;
private readonly RoleManager<IdentityRole> _roleManager;
495
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
//métodos auxiliares
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty,
error.Description);
}
}
//métodos de registro
[HttpGet]
public IActionResult Registrar(string returnUrl = null)
{
ViewBag.ReturnUrl = returnUrl;
[HttpPost]
public async Task<IActionResult> Registrar(
UsuarioViewModel model,
string perfil,
string returnUrl = null)
{
ViewBag.ReturnUrl = returnUrl;
if (ModelState.IsValid)
{
var user = new Usuario
{
UserName = model.Email,
Email = model.Email
};
496
ASP.NET Core Identity 9
if (result.Succeeded)
{
var appRole = await _roleManager
.FindByNameAsync(perfil);
if (appRole != null)
{
await _userManager.AddToRoleAsync(user, perfil);
}
await _signInManager.SignInAsync(user,
isPersistent: false);
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
return View(model);
}
[HttpPost]
public async Task<IActionResult> Login(
LogonViewModel model, string ReturnUrl)
{
ViewBag.ReturnUrl = ReturnUrl;
if (ModelState.IsValid)
{
var result = await _signInManager
.PasswordSignInAsync(
model.Email, model.Senha, false,
lockoutOnFailure: false);
if (result.Succeeded)
{
return RedirectToLocal(ReturnUrl);
}
else
{
ModelState.AddModelError(
string.Empty, "Usuário ou senha inválidos.");
return View(model);
}
}
return View(model);
}
497
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@using Microsoft.AspNetCore.Identity
@using Lab.NetCore
@using Lab.NetCore.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
498
ASP.NET Core Identity 9
@model Lab.NetCore.Models.UsuarioViewModel
@{
ViewData["Title"] = "Registrar";
}
<h1>Registrar Usuários</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Registrar">
<div asp-validation-summary="ModelOnly"
class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Senha" class="control-label"></label>
<input asp-for="Senha" class="form-control" />
<span asp-validation-for="Senha" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmaSenha" class="control-label"></label>
<input asp-for="ConfirmaSenha" class="form-control" />
<span asp-validation-for="ConfirmaSenha"
class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label">Perfil</label>
<select name="perfil" class="form-control"
asp-items="(SelectList)ViewBag.Roles"></select>
</div>
<div class="form-group">
<input type="submit" value="Registrar"
class="btn btn-primary" />
</div>
</form>
</div>
</div>
499
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
@model Lab.NetCore.Models.LogonViewModel
@inject SignInManager<Usuario> SignInManager
@{
ViewData["Title"] = "Login";
}
<h1>Login</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-route-returnurl="@ViewBag.ReturnUrl">
<div asp-validation-summary="ModelOnly"
class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Senha" class="control-label"></label>
<input asp-for="Senha" class="form-control" />
<span asp-validation-for="Senha"
class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Login"
class="btn btn-primary" />
</div>
</form>
</div>
</div>
500
ASP.NET Core Identity 9
14. Na pasta Views/Shared, inclua a view _Erro:
@{
ViewData["Title"] = "Erro";
}
@using Microsoft.AspNetCore.Identity
@inject SignInManager<Usuario> SignInManager
@inject UserManager<Usuario> UserManager
<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
<li class="nav-item">
<a class="nav-link text-dark"
asp-controller="Home"
asp-action="Index">@User.Identity.Name!</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark"
asp-controller="Usuarios"
asp-action="Logout">Logout</a>
</li>
}
else
501
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
{
<li class="nav-item">
<a class="nav-link text-dark"
asp-controller="Usuarios"
asp-action="Registrar">Registrar</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark"
asp-controller="Usuarios"
asp-action="Login">Login</a>
</li>
}
</ul>
16. Altere o arquivo _Layout.cshtml de forma a incluir essa view parcial (na
execução, observe no menu à direita):
</div>
17. Uma vez definidos os actions para registro e login, vamos, agora, controlar
o que deve ser executado por um usuário autenticado. Por exemplo: Considere
que, para realizar um cadastro de eventos, o usuário deva ser, por exemplo,
um administrador devidamente logado. No action IncluirEvento, devemos
incluir o atributo:
[HttpGet]
[Authorize(Roles = "ADMIN")]
public IActionResult IncluirEvento()
{
return View();
}
502
ASP.NET Core Identity 9
18. Confira a string de conexão no arquivo appsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Integrated Security=SSPI;Persist Security
Info=False;Initial Catalog=DB_IDENTITY;Data Source=.\\SQLEXPRESS",
"EventosConnection": "Integrated Security=SSPI;Persist Security
Info=False;Initial Catalog=DB_EVENTOS;Data Source=.\\SQLEXPRESS"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
503
10 Web API Core
10.1. Introdução
Web API Core é a tecnologia da Microsoft para criar e consumir serviços baseados em
REST. Houve diversas melhorias em relação à Web API baseada no .NET Framework.
Os conceitos de serviços REST que são sustentados pelo Web API Core serão descritos
na sequência.
Então, Web Services RESTful exigem não apenas recursos para representar, mas
também operações chamadas pelo cliente desses recursos. No centro da abordagem
RESTful está a percepção de que HTTP é uma API e não simplesmente um protocolo
de transporte. HTTP tem seus verbos bem conhecidos, oficialmente chamados de
métodos.
Considere que uma aplicação Web necessite se comunicar com outra aplicação. O
Web service é o canal de comunicações entre as aplicações.
506
Web API Core 10
10.3. Media Types
O protocolo HTTP foi concebido originalmente para transferir hipertexto. No entanto,
alguns recursos possuem outros formatos. É o caso das imagens e dos vídeos, por
exemplo, que utilizam o HTTP para transportar diferentes tipos de formatos.
Para essa tarefa, o HTTP utiliza tipos baseados em Multipurpose Internet Mail
Extensions (MIME), também chamados de media types.
Media types são informados como parte do cabeçalho da requisição. Ou seja, quando
o cliente realiza uma requisição, ele pode enviar uma lista de media types, assim pode
recebê-los como resposta.
10.4. Desenvolvimento de serviços
baseados em Web API Core
Em um projeto Web API no .NET Core, o processo de requisição e resposta ocorre
de forma análoga ao que vimos quando estudamos Web API no .NET Framework.
Existem similaridades na definição dos actions (que são as tarefas), mas a arquitetura
é diferente:
•• O objeto HttpClient, usado por uma aplicação cliente para consumir a Web API,
pode ser obtido por injeção de dependência, desde que devidamente registrado
na classe Startup e configurado como parâmetro do construtor do controller;
507
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
508
Web API Core 10
Uma das configurações que podemos fornecer ao controller é a rota, representada pelo
atributo Route. Veja que a classe possui a configuração [Route("api/[controller]"),
indicando a forma como o acesso ao serviço é realizada. No nosso exemplo, o acesso
seria realizado como api/home. Veja a classe gerada nesse processo:
using Microsoft.AspNetCore.Mvc;
namespace Capitulo10.WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
}
}
Se desejarmos especificar o método HTTP em um método que não siga esses padrões,
devemos usar um dos seguintes atributos: HttpGet, HttpPut, HttpPost ou HttpDelete,
conforme o objetivo. Considere o action a seguir:
using Microsoft.AspNetCore.Mvc;
namespace Capitulo10.WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpGet]
public string MostrarNome()
{
return "Asp.Net Core";
}
}
}
Ele será executado quando uma requisição HTTP GET for realizada por conta do seu
atributo.
509
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Um problema surge quando desejarmos que mais de um action seja executado usando
o mesmo verbo HTTP:
using Microsoft.AspNetCore.Mvc;
namespace Capitulo10.WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpGet]
public string MostrarNome()
{
return "Asp.Net Core";
}
[HttpGet]
public string MostrarEmpresa()
{
return "Impacta Tecnologia";
}
}
}
Nesse caso, devemos especificar um nome a ser usado na sua chamada. O exemplo a
seguir ilustra esse procedimento, e, na sequência, temos uma execução:
using Microsoft.AspNetCore.Mvc;
namespace Capitulo10.WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpGet("nome")]
public string MostrarNome()
{
return "Asp.Net Core";
}
[HttpGet("empresa")]
public string MostrarEmpresa()
{
return "Impacta Tecnologia";
}
}
}
510
Web API Core 10
[Route("api/[controller]/[action]")]
using Microsoft.AspNetCore.Mvc;
namespace Capitulo10.WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
[Route("dadosEmpresa")]
public string MostrarEmpresa()
{
return "Impacta Tecnologia";
}
}
}
511
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using Microsoft.AspNetCore.Mvc;
namespace Capitulo10.WebAPI.Controllers
{
[Route("api/[controller]/{id}")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpGet]
public string MostrarNome(int id)
{
return "Asp.Net Core - código = " + id;
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace Capitulo10.WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpGet("{id}")]
public string MostrarNome(int id)
{
return "Asp.Net Core - código = " + id;
}
}
}
512
Web API Core 10
É possível, também, informarmos mais de um parâmetro no action:
using Microsoft.AspNetCore.Mvc;
namespace Capitulo10.WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpGet("{id}/{ch}")]
public string MostrarNome(int id, int ch)
{
return "Asp.Net Core - código = " + id +
", com carga horária de " + ch;
}
}
}
Um action pode retornar listas, objetos ou, mesmo, IActionResult. Veja os exemplos
de actions a seguir:
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace Capitulo10.WebAPI.Controllers
{
513
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
private static readonly IEnumerable<string> cursos = new
List<string>()
{
"Javascript", "PHP", "Angular","Jogos Digitais"
};
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace Capitulo10.WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
private static readonly List<string> cursos = new List<string>()
{
"Javascript", "PHP", "Angular","Jogos Digitais"
};
514
Web API Core 10
public IEnumerable<string> GetCursos()
{
return cursos;
}
[HttpGet("{id}")]
public ActionResult<string> GetCurso(int id)
{
if(id >= cursos.Count)
{
return NotFound();
}
return cursos[id];
}
}
}
Para exemplificar, vamos considerar uma classe chamada Pessoa com as propriedades
Nome e Idade. O action receberá um objeto, cujas informações são originadas no
corpo de uma requisição POST:
515
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
•• Classe Pessoa:
namespace Capitulo10.WebAPI
{
public class Pessoa
{
public string Nome { get; set; }
public int Idade { get; set; }
}
}
•• Controller:
using Microsoft.AspNetCore.Mvc;
namespace Capitulo10.WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpPost]
public void PostPessoa([FromBody] Pessoa pessoa)
{
//processa a operação
}
}
}
Além disso, a classe HttpClient mantém uma integração consistente com Web API Core
por meio dos objetos HttpRequestMessage e HttpResponseMessage, responsáveis
pela manipulação das mensagens HTTP. Ela possui diversos métodos assíncronos
para o processamento dos serviços:
516
Web API Core 10
No ASP.NET Core, para definir uma instância de HttpClient, podemos usar o serviço
IHttpClientFactory. Para registrá-lo, devemos chamar o método de extensão
AddHttpClient no método ConfigureServices, na classe Startup. O código a seguir
mostra como registrar o serviço IHttpClientFactory:
Uma vez injetado, podemos usar o método CreateClient para obter uma instância de
HttpClient. Para demonstrar, criaremos o controller ClientesController no projeto.
Em seguida, adicionaremos o construtor que receberá o IHttpClientFactory:
using Microsoft.AspNetCore.Mvc;
using System.Net.Http;
namespace Capitulo10.ClienteWebAPI.Controllers
{
public class ClientesController : Controller
{
HttpClient client;
517
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Assim, podemos chamar um serviço Web API Core por meio dos métodos apresentados.
Vamos escrever um action chamado Buscar e, nele, o código para consumir o Web
service desenvolvido neste capítulo:
using Microsoft.AspNetCore.Mvc;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
namespace Capitulo10.ClienteWebAPI.Controllers
{
public class ClientesController : Controller
{
HttpClient client;
if (response.IsSuccessStatusCode)
{
//processa a resposta do Web service
return View();
}
else
{
return Content("Ocorreu um erro");
}
}
518
Web API Core 10
Para finalizar o exemplo, vamos criar uma classe chamada Pessoa, com as mesmas
propriedades da pessoa que definimos no projeto Capitulo10.WebAPI. Essa classe
será usada para ilustrar a busca por pessoas na API e para enviar um objeto para ela.
Vamos criar a classe Pessoa na pasta Models:
namespace Capitulo10.ClienteWebAPI.Models
{
public class Pessoa
{
public string Nome { get; set; }
public int Idade { get; set; }
}
}
Agora, vamos completar o método Buscar de forma a trazer a lista de pessoas da API:
if (response.IsSuccessStatusCode)
{
var pessoas = await response.Content.ReadAsAsync<Pessoa[]>();
return View(pessoas.ToList());
}
else
{
return Content("Ocorreu um erro");
}
}
Fizemos uma chamada assíncrona e, para isso, tornamos nosso action assíncrono.
•• PostAsJsonAsync: Envia uma requisição POST para uma URI específica com o
objeto serializado como JSON;
•• PostAsXmlAsync: Envia uma requisição POST para uma URI específica com o
objeto serializado como XML;
•• PutAsJsonAsync: Envia uma requisição PUT para uma URI específica com o
objeto serializado como JSON;
•• PutAsXmlAsync: Envia uma requisição PUT para uma URI específica com o
objeto serializado como XML.
519
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
[HttpPost]
public async Task<IActionResult> Incluir(Pessoa pessoa)
{
client.BaseAddress = new Uri("http://localhost:62291/");
client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
if (response.IsSuccessStatusCode)
{
return Content("Incluído com sucesso");
}
else
{
return Content("Ocorreu um erro");
}
}
using Microsoft.AspNetCore.Mvc;
namespace Capitulo10.WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpPost]
public void PostPessoa([FromBody] Pessoa pessoa)
{
//processa a operação
}
}
}
520
Web API Core 10
Pontos principais
Atente para os tópicos a seguir. Eles devem ser estudados com muita atenção,
pois representam os pontos mais importantes do capítulo.
•• Assim como todas as tarefas no .NET Core, o Web API Core também utiliza os
serviços por meio de injeção de dependência;
•• Para que um action no Web API Core receba objetos da requisição, podemos usar
os seguintes Binding Sources: FromBody, FromQuery, FromRoute, FromForm
e FromHeader;
521
10 Web API Core
Teste seus conhecimentos
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
524
Web API Core 10
4. Para criar uma instância de HttpClient no controller, podemos usar qual
método dessa classe?
☐☐a) CreateClient
☐☐b) CreateHttp
☐☐c) CreateHttpClient
☐☐d) CreateInstance
☐☐e) GetInstance
525
10 Web API Core
Mãos à obra!
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
Laboratório 1
Objetivos:
528
Web API Core 10
529
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Lab.WebAPICore.Models
{
public class Pagamento
{
public int Id { get; set; }
public int IdEvento { get; set; }
public string Cpf { get; set; }
public string NumeroCartao { get; set; }
public double Valor { get; set; }
public int Status { get; set; }
}
}
using Microsoft.EntityFrameworkCore;
namespace Lab.WebAPICore.Models
{
public class PagamentoContext : DbContext
{
public PagamentoContext(DbContextOptions<PagamentoContext>
options) : base(options) { }
modelBuilder.Entity<Pagamento>().ToTable("TBpagamentos");
modelBuilder.Entity<Pagamento>()
.Property(p => p.Cpf)
.IsRequired()
.HasMaxLength(11);
modelBuilder.Entity<Pagamento>()
.Property(p => p.NumeroCartao)
.IsRequired()
.HasMaxLength(16);
}
}
}
530
Web API Core 10
6. Vamos registrar o DbContext com o container de injeção de dependência.
Esses serviços estarão disponíveis para os controladores. Esse trabalho será
realizado na classe Startup.cs, método ConfigureServices:
using Lab.WebAPICore.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Lab.WebAPICore
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
services.AddControllers();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
531
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
{
"ConnectionStrings": {
"ApiConnection": "Integrated Security=SSPI;Persist Security
Info=False;Initial Catalog=DB_PAGAMENTOS_CORE;Data Source=.\\SQLEXPRESS"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
namespace Lab.WebAPICore.Models
{
public class DBInitializer
{
public static void Initialize(PagamentoContext context)
{
context.Database.EnsureCreated();
}
}
}
using Lab.WebAPICore.Models;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
namespace Lab.WebAPICore
{
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
532
Web API Core 10
var services = scope.ServiceProvider;
try
{
var context = services
.GetRequiredService<PagamentoContext>();
DBInitializer.Initialize(context);
}
catch (Exception ex)
{
var logger = services
.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, ex.Message);
throw;
}
}
host.Run();
}
533
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Lab.WebAPICore.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Lab.WebAPICore.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class PagamentosController : ControllerBase
{
private readonly PagamentoContext _context;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Lab.WebAPICore.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Lab.WebAPICore.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class PagamentosController : ControllerBase
{
private readonly PagamentoContext _context;
534
Web API Core 10
//HTTP POST - Inclusão de um pagamento
public IActionResult PostPagamento([FromBody] Pagamento pagto)
{
var pagamento = _context.Pagamentos.FirstOrDefault(p =>
p.IdEvento == pagto.IdEvento && p.Cpf.Equals(pagto.Cpf));
if (pagamento == null)
{
pagto.Status = 1;
_context.Pagamentos.Add(pagto);
_context.SaveChanges();
return Ok();
}
else
{
return BadRequest();
}
}
}
}
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:61396",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/Pagamentos",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Lab.WebAPICore": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/Pagamentos",
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
535
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
namespace Lab.NetCore.Models
{
public class PagamentoEvento
{
public int IdEvento { get; set; }
public string Cpf { get; set; }
public string NumeroCartao { get; set; }
public double Valor { get; set; }
public int Status { get; set; }
}
}
536
Web API Core 10
5. No arquivo _ListarParticipantes.cshtml (pasta Views/Shared), defina um
link direcionando o usuário para o pagamento:
@model IEnumerable<Lab.NetCore.Models.Participante>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Nome)
</th>
<th>
@Html.DisplayNameFor(model => model.Email)
</th>
<th>
@Html.DisplayNameFor(model => model.Cpf)
</th>
<th>
@Html.DisplayNameFor(model => model.DataNascimento)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Nome)
</td>
<td>
@Html.DisplayFor(modelItem => item.Email)
</td>
<td>
@Html.DisplayFor(modelItem => item.Cpf)
</td>
<td>
@Html.DisplayFor(modelItem => item.DataNascimento)
</td>
<td>
@Html.ActionLink("Efetuar Pagamento", "EfetuarPagamento",
new { id = item.Id, idEvento = item.EventoInfoId })
</td>
</tr>
}
</tbody>
</table>
537
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
using Lab.NetCore.Dao;
using Lab.NetCore.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace Lab.NetCore.Controllers
{
public class ParticipantesController : Controller
{
private EventosDao eventosDao { get; set; }
private ParticipantesDao participantesDao { get; set; }
client = httpClient.CreateClient();
}
[HttpGet]
public IActionResult IncluirParticipante()
{
ViewBag.ListaDeEventos = new
SelectList(eventosDao.Listar(), "Id", "Descricao");
return View();
}
538
Web API Core 10
[HttpPost]
public IActionResult IncluirParticipante(Participante participante)
{
if (participante.EventoInfoId == 0)
{
ModelState.AddModelError("IdEvento",
"Nenhum evento selecionado ");
}
if (!ModelState.IsValid)
{
return IncluirParticipante();
}
participantesDao.Executar(participante, TipoOperacaoBD.Added);
return RedirectToAction("Index");
}
if (idEvento == 0)
{
return View();
}
else
{
var lista = participantesDao.ListarPorEvento(idEvento);
return PartialView("_ListarParticipantes", lista);
}
}
[HttpGet]
public IActionResult EfetuarPagamento(int id, int idEvento)
{
var participante = participantesDao.BuscarPorId(id);
var evento = eventosDao.BuscarPorId(idEvento);
[HttpPost]
public async Task<IActionResult> EfetuarPagamento(
PagamentoEvento pagamento)
{
try
{
client.BaseAddress = new Uri("http://localhost:62291/");
client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
539
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
//return RedirectToAction("Index");
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
else
{
string msg = response.StatusCode + " - " +
response.ReasonPhrase;
throw new Exception(msg);
}
}
catch (Exception ex)
{
ViewData["MensagemErro"] = ex.Message;
return View("_Erro");
}
}
}
}
540
Web API Core 10
@model Lab.NetCore.Models.PagamentoEvento
@{
ViewData["Title"] = "Efetuar Pagamento";
}
<h1>Efetuar Pagamento</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="EfetuarPagamento">
<div asp-validation-summary="ModelOnly"
class="text-danger"></div>
<input type="hidden" asp-for="IdEvento" />
@*<div class="form-group">
<label asp-for="IdEvento" class="control-label"></label>
<input asp-for="IdEvento" class="form-control" />
<span asp-validation-for="IdEvento"
class="text-danger"></span>
</div>*@
<div class="form-group">
<label asp-for="Cpf" class="control-label"></label>
<input asp-for="Cpf" class="form-control" />
<span asp-validation-for="Cpf"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="NumeroCartao" class="control-label"></label>
<input asp-for="NumeroCartao" class="form-control" />
<span asp-validation-for="NumeroCartao"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Valor" class="control-label"></label>
<input asp-for="Valor" class="form-control"
readonly="readonly" />
<span asp-validation-for="Valor"
class="text-danger"></span>
</div>
@*<div class="form-group">
<label asp-for="Status" class="control-label"></label>
<input asp-for="Status" class="form-control" />
<span asp-validation-for="Status"
class="text-danger"></span>
</div>*@
<div class="form-group">
<input type="submit" value="Efetuar Pagamento"
class="btn btn-primary" />
</div>
</form>
</div>
</div>
541
Desenvolvimento Web com ASP.NET MVC e ASP.NET Core
//services.AddDbContext<ApplicationDbContext>(options =>
// options.UseSqlServer(
// Configuration.GetConnectionString("DefaultConnection")));
services.AddDbContext<UsuariosDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddHttpClient();
services.AddControllersWithViews();
services.AddRazorPages();
}
•• O link mostrado nesse exemplo deve ser alterado para o link correto no
momento da sua execução, pois o número da porta certamente mudará
de projeto para projeto.
542