Escolha uma Página
Automação de E-mail Marketing (campanhas) no Core MVC usando SendGrid Marketing Automation API V3

Automação de E-mail Marketing (campanhas) no Core MVC usando SendGrid Marketing Automation API V3

É muito fácil achar na Internet como mandar e-mails com a API do Sendgrid. Mas não existe quase nada sobre automação de campanhas de e-mail marketing usado a API v3 do SendGrid!

Campanhas de E-mail Marketing, automatizadas, como você sempre sonhou!

Se você já usa o SendGrid para enviar seus e-mails , já andou metade do caminho (veja meu artigo  Adicionando serviço de E-mail no Core MVC, usando SendGrid).

O fato é que há coisas muito chatas de se fazer no Core MVC. Uma delas é formatar e-mails para envio. Outra é tentar programar eventos para acontecer no tempo. Uma campanha de e-mail marketing essencialmente tem que ter e-mails muito bonitos, responsivos, realmente vendedores. E que possam ser mandados sequencialmente assim que a pessoa se inscreve no seu site (como usuária, como interessada em receber alguma coisa), evento este que “dispara” e-mails de tempos em tempo, dando boas vindas (por exemplo) e depois oferecendo seus serviços ou produtos. Com o SendGrid você tem de graça tudo isso, até 6 mil e-mails por mês.

O que (as novas) Campanhas de Marketing do SendGrid Oferecem

Ferramentas de Criação e Edição de Modelos de E-mails

  • Biblioteca de modelos responsivos (onde você pode guardar seus templates também);
  • Ferramentas de criação e edição dos seus próprios modelos, começando do “zero”. Você pode arrastar e soltar, escrever em HTML puro ou ambos.
  • Importação de imagens Na sua biblioteca particular).

Ferramenta de Automação

Voce cria automações de e-mail de alto impacto em horas ou minutos – e não dias – com gatilhos simples e uma visão centralizada das mensagens e do tempo de sua série de e-mails da Campanha.

O que vamos fazer?

Aqui vamos dar um exemplo completo que dispara uma campanhade e-mails em sequência no SendGrid quando alguém se inscreve no seu site montado em Core MVC.

O e-mail cadastrado pelo seu sistema (no nosso exemplo através do Registro de Usuários do Identity), assim que é validado pelo controlador (e é salvo na tabela ASPNETUsers), é mandado também para uma Lista de Contatos do SendGrid. A essa lista está associada uma Campanha de Marketing, com um cronograma de envios de quantos e-mails você quiser. A grande vantagem é que para cada e-mail enviado, você sabe quem recebeu, quem não recebeu, quem recebeu e abriu o email, quem abriu e clicou no e-mail (e quantaz vezes).Tudo isso de graça e dentro do SendGrid. Veja neste vídeo curto.

Antes de qualquer coisa

Abra uma conta no SendGrid (ou acesse a sua) e:

  • Crie uma API para a Campanha (anote a chave secreta da APIKey, porque ela nnunca mais será mostrada). Depois de criar (ou se você já tem API), certifique-se que ela tenha “plenos poderes” insclusive para marketing.
    • SETTINGS
      • API KEYS
      • CREATE API KEY (se não tiver ainda uma, se tiver pule)
      • CLIQUE NA ENGRENAGEM PARA EDITAR PODERES
      • ESCOLHA EDIT API KEY
      • ESCOLHA FULL ACCESS

Agora vamos para a (NOVA) área de Marketing. No menu esquerdo:

  • Clique em Marketing NEW
    • Clique em Contacts (Você vai ver uma lista chamada GLOBAL, que tem todos os contatos de todas as as listas. Esta lista não nos interessa)
    • Clique, no topo, em Create e escolha New List . De o nome de sua lista e salve. Logo abaixo da sua lista Global vai aparecer o nome de sua nova lista.

Agora que tem a sua lista, clique no nome dela (NAME). Vai aparecer a lista de todos seus e-mails (mas se a lista é nova, nenhum e-mail).

O importante neste momento é que a URL tem o endereço interno da identidade da lista no sistema, que vai ser usada na API. No meu caso, a URL está assim:

https://mc.sendgrid.com/contacts/lists/e8b61063-6f33-4963-b8fc-331723f21f6x

Guarde junto do segredo da chave de api SOMENTE a identidade da Lista:

e8b61063-6f33-4963-b8fc-331723f21f6x

Criando sua primeira Campanha

No menu esquerdo:

  • Marketing NEW 
    • Automations
    • No topo direiro, Create an Automation

Você poderia começar completamente do zero, selecionando a opção CUSTOM.

Escolha WELCOME. Já vem uma microcampanha pronta, que você pode editar e mudar o que quiser. Dê o nome para sua campanha (o default é Untitled Welcome Series)

IMPORTANTE:

  • Não se esqueça de salvar todas mudanças. clicando em SAVE no topo da página (nada intuitivo)
  • Clique em Automation Options e clique em Set Live (põe no ar!). Note que é aí que você pode duplicar uma campanha existente. Também nada intuitivo.

Clique em Automation, no topo da página à esquerda. A nossa campanha Teste vai aparecer como Draft (rascunho) e não como Live (Ao vivo!). É que a campanha não entra ao vivo se não:

  • Tiver um NOME
  • Tiver uma LISTA ESCOLHIDA
  • Ter definido um mode da pessoa sair da lista. Para simplificar, escolho i modo de saída global (válido para todas as listas): GLOBAL UNSUBSCRIBE
  • Em cada email que vai ser mandado, ter definido SUBJECT (ASSUNTO) e SENDER (e-mail de quem está mandando o e-mail). Se você não cadastrou, vai ter que ir em SENDERS PAGE e definir um e-mail de remetente (você vai ter que VERIFICAR esse e-mail para torná-lo válido para o SendGrid). Eu recomendo fazer a verificação do E-mail e do domínio de envio, assim as chances dos e-mails enviados caírem no SPAM dos destinatários caem praticamente a zero!.

Faça tudo que o SendGrid solicitar até que sua campanha, finalmente, saia de DRAFT para LIVE.

Finalmente, começando a programação no CORE MVC

Assim que conseguir isso, vamos finalmente para nosso velho Visual Studio. Para implementar o envio de e-mails, escolho o arquivo REGISTER.CSHMTL.CS, que é o controlador do registro de novos usuários na tabela ASPNETUSERS, do Identity.

Você pode escolher qualquer controlador seu que vai salvar um e-mail dentro da base de dados. Assim que a validação do modelo é feita dentro do controlador, você aproveita o embalo e envia o mesmo e-mail para a lista do SENDGRID, que está amarrada na sua campanha. Funciona que é uma chupeta.

Parte inicial do controlador

Você vai ter de instalar, aplicação, os seguintes pacotes (que inclusive substituem o newtowjson):

using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks;

parte inicial do controlador (no caso register.cshtm.cs) em Areas/Identity/Pages/Account/
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using PoupaTempoDigital.Data;
using PoupaTempoDigital.Models;
using PoupaTempoDigital.Services;
using RestSharp;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks;
//pode ser necessário instalar os 4acima via Packate Manager

namespace PoupaTempoDigital.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class RegisterModel : PageModel
    {
        private readonly SignInManager<AppUser> _signInManager;
        private readonly UserManager<AppUser> _userManager;
        private readonly ILogger<RegisterModel> _logger;
        //_emailSender;
        private readonly IEmailService _emailService;
        private readonly ApplicationDbContext _context;
        private readonly RoleManager<AppRole> _roleManager;
        private readonly IConfiguration _configuration;

        public RegisterModel(
            UserManager<AppUser> userManager,
            SignInManager<AppUser> signInManager,
            ILogger<RegisterModel> logger,
            IEmailService emailService,
            IOptions<EmailConfig> emailConfig,
            ApplicationDbContext context,
           RoleManager<AppRole> roleManager,
            IConfiguration configuration)

        {
            _userManager = userManager;
            _signInManager = signInManager;
            _logger = logger;
            _emailService = emailService;
            _context = context;
            _roleManager = roleManager;
            _configuration = configuration;
        }
          // continua controlador ....

Modelo de dados

Eu prentendo, além do e-mail, passar para a minha lista SENDGRID nome e sobrenome. O Sendgrig tem mais de uma dezena de campos prontos na lista – e você pode definir mais campos customizados, quantos ou quais quiser.

Para isso criei, dentro do próprio controller, uma classe de modelo bem simples, logo após a tarefa Task OnGetAsync; Os nomes da variáveis tem que bater com os nomes padrão que o SendGrid já tem (e/ou com os nomes de campos customizados que você vier a criar para todos e-mails da sua lista):

modelo de dados a serem passados pelo register.cshtm.cs) em Areas/Identity/Pages/Account/
//... 
public async Task OnGetAsync(string returnUrl = null)
        {

            ReturnUrl = returnUrl;
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();

        }
//MODELO DE DADOS
        public class EmailSendgrid
        {
            public string email { get; set; }
            public string first_name { get; set; }

            public string last_name { get; set; }
        }
//...

Dentro do método POST do seu controle, no meu caso:
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
assim que o modelo é VALIDADO:

logo depois de validar o envio do post, gravar o usu&amp;aacute;rio, colocamos a "conversa" com a API da SENDGRID
//o controle tenta criar o usuário
var result = await _userManager.CreateAsync(user, Input.Password);
                if (result.Succeeded)
								//sucesso!!!
                {
                    _logger.LogInformation("O usuário criou uma nova conta com senha.");


//COMEÇO****** COLOCANDO dados no sendgrid
                  
                    var NovoEmail = new EmailSendgrid
                    {
                        email = user.Email,
                        first_name = user.FirstName,
                        last_name = user.LastName
                    };
 //TROQUE PELOS DADOS DE ID DA SUA LISTA, CONFORME DICA NO COMEÇO DO ARTIGO
                    var lista_ID = "e8b61063-6f33-4963-b8fc-331723f2322D3";

                    string jsonString = "{" + ""list_ids":" + " ["" + lista_ID + ""]," + ""contacts":" + "[" + JsonSerializer.Serialize(NovoEmail) + "]}";
//PEGA A SEDRET KEY DA SUA API SEND GRID (QUE ESTA ARMAZENADA NO ARQUIVO APPSETTING.JSON)
                    var API = _configuration.GetSection("Sendgrid").GetSection("ApiKey").Value;
                    var json = JsonSerializer.Deserialize<Object>(jsonString);
                    jsonString = json.ToString();
                    var client = new RestClient("https://api.sendgrid.com/v3/marketing/contacts");
                    var requisicao = new RestRequest(Method.PUT);
                    requisicao.AddHeader("content-type", "application/json");
                    requisicao.AddHeader("authorization", "Bearer " + API);
                    //requisicao.AddHeader("on-behalf-of", "");
                    requisicao.AddParameter("application/json", jsonString, ParameterType.RequestBody);
                    IRestResponse response = client.Execute(requisicao);
//******fim

                    //cria papéis se ainda não existem
                    //admin
                    if (!await _roleManager.RoleExistsAsync(Papeis.AdminEndUser))
                    {
                        await _roleManager.CreateAsync(new AppRole(Papeis.AdminEndUser));
                    }
                    //cliente
                    if (!await _roleManager.RoleExistsAsync(Papeis.ClienteEndUser))
                    {
                        await _roleManager.CreateAsync(new AppRole(Papeis.ClienteEndUser));
                    }
                    //colaborador
                    if (!await _roleManager.RoleExistsAsync(Papeis.ColaboradorEndUser))
                    {
                        await _roleManager.CreateAsync(new AppRole(Papeis.ColaboradorEndUser));
                    }
                   
                      
                      
                      //cria papel padrao para cada novo inscrito = Cliente
                    await _userManager.AddToRoleAsync(user, Papeis.ClienteEndUser);
                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
                        protocol: Request.Scheme);
//ENVIA EMAILS SE NECESSARIO (ISSO PODE ESTAR NA SUA CAMPANHA, MANDANDO E-MAILS BONITOS E RESPONSIVOS)
                    await _emailService.SendEmailAsync(Input.Email, "Confirme seu e-mail no Poupatempo Digital",
                        $"<h4><b>Por favor confime sua conta</b> <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicando aqui.</a>.</h4><h5> Obrigado. Confirmando sua conta, você já terá acesso à sua <b>Agenda Inteligente</b> de Tarefas. Você vai curtir!<h5>", "Html");

                    var mensagem = "O usuário " + Input.FirstName + " " + Input.LastName + "e-mail: " + Input.Email + "Telefone: " + Input.PhoneNumber + " acabou de abrir conta no Poupatempo Digital";
                    var assunto = "o usuário " + Input.FirstName + " " + Input.LastName + " acabou de entrar no Poupatempo Digital";
                    var destino = "xxxxx@clickstobricks.com";
                    await _emailService.SendEmailAsync(destino, assunto, mensagem, "Text");

                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
                    }
                    else
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(returnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

Pronto, é isso. Registre um novo usuário com sufixo mailinator.com

Exemplo: maria@mailinator.com

Acesse https://mailinator.com

Digite maria e dê enter. Se você definiu que o primeiro e-mail de sua campanha é enviado imediatamente após a inscrição do e-mail na lista, Maria já terá recebido seu primeiro e-mail .

Importante

Não se esqueça de ter a chave de sua API SendGrid no arquivo appsettings.json, conforme modelo abaixo.

chave secreta da API da SENDGRID, no arquivo appsettings.json
{
  "Email": {
    "FromName": "Site Poupatempo Digital",
    "FromAddress": "atendimento@poupatempodigital.com.br",
    "LocalDomain": "poupatempodigital.com.br",
    "MailServerAddress": "mail.poupatempodigital.com.br",
    "MailServerPort": "25", //SMTP Port:  25 "or" 8889:,
    "UserId": "atendimento@poupatempodigital.com.br",
    "UserPassword": "xxxxxxxxxxxxxxxxxxxxxxxx"
  },
  "Sendgrid": {
    "ApiKey": "xptodfde7WI5ZzpMinesA.blm98jwSobPj1enFAd2a7kQQrAYULi0-csraFJ0NzRs" //poupatempo digital 09/01 full access
  },
  "Twilio": {
    "accountSid": "ACewrewrwere1b43d312b7601",
    "authToken": "1710ewerewrewrw6c0f589b968cfc5",
    "whatsAppFrom": "whatsapp:+14155238886" //número de saída Twilio
  },
  "reCaptcha": {
    "chaveDeSite": "6Le0jCQaAAAAAHMSbanWiWoZrewrewrewGB4FWZET",
    "chaveSecreta": "6Le0jCQaAAAAAPSNwZeRYywrewrwerwe8wSUSj"
  },
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=SQL.site.net;Initial Catalog=DB_A2A240;User Id=DB_user;Password=password;;Connect Timeout=20000;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Referências que vão de ajudar

  1. API Sendkey, como obter e usar
  2. Guia de Parametrização para enviar e-mails pelo SendGrid
  3. Documentação completa, com exemplos – inclusive c#, da API SendGrid V3 (tipo: tudo o que você pode fazer)

Gostou? Deixe sugestões e comentários!

Obrigado pela sua leitura. Continue visitando este blog e compartilhe artigos em sua rede de relacionamento. Por favor, se quiser, registre sugestões e comentários aqui no final da página.

Google Captcha (reCaptcha) no Core MVC

Google Captcha (reCaptcha) no Core MVC

O Captcha ajuda a evitar que robôs tentem invadir seus sites através de formulários, passando a ser mais uma pedra do caminho de invasões automatizadas. Quem tenta acessar o sistema usando o formulário passa a ter que também (pelo menos) clicar num botão dizendo: “Não sou um robô”.

O Google oferece esse serviço. Chamando-o de reCaptcha. Que pode ser usado com Core MVC.

Se você tiver algumas visualizações com entradas com acesso público, especialmente se forem servidas por meio do controlador com [AllowAnonymous], então provavelmente gostará de adicionar CAPTCHA a essas visualizações. Caso contrário, você sempre corre o risco de ser inundado por spam automatizado e até ser invadido.

Como todo serviço do Google, você precisar ter uma conta de Gmail para acessar o serviço em: https://www.google.com/recaptcha/admin

O site é bem confuso e muito pouco intuitivo. Você nunca sabe por onde começar. Se clicar no topo, vai ler:

O reCAPTCHA usa um mecanismo avançado de análise de risco e desafios adaptativos para impedir que o software malicioso se envolva em atividades abusivas no seu site. Enquanto isso, usuários legítimos poderão fazer login, fazer compras, visualizar páginas ou criar contas e usuários falsos serão bloqueados.

Tem de cllicar,esmo é no sinal de +, para adicionar a URL do seu site.

 

Coloque um nome para lembrar de que site é o reCaptcha, em etiqueta. Escolha a última versão: recaptcha v2. Defina o seu domínio. Importante: clique no botão de + depois que acrescentar seu domínio – e defina também localhost para depois poder testat o reCapcha na sua máquina local, ANTES de subir a sua aplicação para o servidor.

Duas dicas importantes:

  • Além de localhost, defina também 127.0.0.1 (verifique se o IIS do seu windows está ativado como um recurso do windows, se não estiver, ative);
  • Escolha a versão V2 do recapcha. Com a V3 não funcionou de jeito nenhum, nem na máquina local, nem remota,.

O proprietário aparece como seu e-mail do Gmail, mas você pode acrescentar outros (nunca tentei). Aceite os termos do serviço. Clique em enviar, no finalzinho da página.

Pronto, você tem agora os dados do seu recaptcha (sugiro guardar num arquivo txt, no seu micro).

Integração com Core Mvc

Para ter a possibilidade de usar o captcha em qualquer formulário, de qualquer página, é melhor chamar a API do Google do arquivo _layout.cshtml, em Views/Shared, uma linha acima de </head>. no topo da página.

 

chamada da API do Google em todas paginas no arquivo_layout.cshtml, em views/shared

    <link rel="stylesheet" href="~/css/site.css" />
    <script src="https://www.google.com/recaptcha/api.js"></script>
</head>
<body data-rsssl=1 data-rsssl=1 data-rsssl=1>

Outra possibilidade é colocar no final de cada View que terá o formulário com reCaptcha, no final da página, na section de script:

colocar em cada view com recaptcha no final da pagina
@section Scripts {
    <script src='https://www.google.com/recaptcha/api.js'></script>
}

Onde colocar o reCaptcha?

Em qualquer formuário que você quiser. Nas views, será necessário usar a “Chave do Site”, a site key que o Google nos forneceu. Calma, já veremos onde ela será guardada. No momento vamos assumir que ela está guardadinha em ViewBag,ChavedoSite.

Assim, em qualquer formulário onde você quiser usar o reCaptcha, vai ter de colocar antes de </form> o seguinte código:

Onde colocar o reCaptcha?

Em qualquer formuário que você quiser. Nas views, será necessário usar a “Chave do Site”, a site key que o Google nos forneceu. Calma, já veremos onde ela será guardada. No momento vamos assumir que ela está guardadinha em ViewBag.ChavedoSite.

Assim, em qualquer formulário onde você quiser usar o reCaptcha, vai ter de colocar antes de </form> o seguinte código:

linha a ser colocada antes de qualquer submit de formularios com reCaptcha
<div class="g-recapcha" data-siteky="@ViewBag.ChavedoSite">
</div>

Exemplo simplificado

Escolha qualquer formulário. Em geral o formulário está numa página de Create.cshtml se foi criado com scafolding, Neste caso, também deveria colocar o reCaptcha na página de edição, em geral Edit.cshtml.

exemplo simplificado para ser colocado nos formularios de criacao e edicao, Create.cshtml e Edit.cshtml
@model PoupaTempoDigital.Models.TP_BancosCaixas

@{
    ViewData["Title"] = "Cria Banco ou Caixa";
}

<h4>Cria Banco ou Caixa</h4>

<h5>Dados</h5>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="NomeBancoCaixa" class="control-label"></label>
                <input asp-for="NomeBancoCaixa" class="form-control" />
                <span asp-validation-for="NomeBancoCaixa" class="text-danger"></span>
              
        			<div class="g-recapcha" data-sitekey="@ViewBag.ChavedoSite">
</div>
              
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Onde guardar as chaves?

Eu proponho guardar no arquivo appsettings. json, no diretório raiz do site, assim:

chaves do google reCaptcha no appsettings.json
{
  "Email": {
    "FromName": "Site Poupatempo Digital",
    "FromAddress": "atendimento@poupatempodigital.com.br",
    "LocalDomain": "poupatempodigital.com.br",
    "MailServerAddress": "mail.poupatempodigital.com.br",
    "MailServerPort": "25", //SMTP Port:  25 "or" 8889:,
    "UserId": "atendimento@poupatempodigital.com.br",
    "UserPassword": "senha"
  },
  "Sendgrid": {
    "ApiKey": "apiso sendgrid"
  },
  "Twilio": {
    "accountSid": "sid",
    "authToken": "token",
    "whatsAppFrom": "whatsapp:xxxx" //número de saída Twilio
  },

  "reCaptcha": {
    "chaveDeSite": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "chaveSecreta": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
  }
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Controladores

Sempre temos nos controladores um get (que chama o formulário para mostrar na tela) e um post (que recebe os dados do formulário quando você dá um SUBMIT).

No método do get precisamos passar o valor da chave do site, em data-siteky=”@ViewBag.ChavedoSite”

Podemos por no view da página uma ViewBag com a Mensagem de retorno, se quisermos, tendo algo assim: @ViewBag.MensagemDeRetorno:

<div class="alert alert-success" role="alert">
  @ViewBag.MensagemDeRetorno
</div>

Só que quando chamarmos pelo get, essa mensagem estará vazia. Podemos preparar a view para mostrá-la somente quando houver retorno pelo controlador POST.

ajuste o arquivo cshtml de edicao e ou criacao
     <div class="g-recaptcha" data-siteky="@ViewBag.ChavedoSite">
            </div>
            @if (@ViewBag.MensagemDeRetorno != "")
            {
            <div class="alert alert-success" role="alert">
                @ViewBag.MensagemDeRetorno
            </div>
            }
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>

GET

No get temos de pegar um valor que está em appsettings.json. Vamos precisar, no início do contrlador, definir _configuration.

rotina get do controlador de criacao (por exemplo, vale o mesmo para edicao)
  // GET: TP_BancosCaixas/Create
        public IActionResult Create()
        {
            //precisamos pegar o valor que está em Appsettings.json
            ViewBag.ChavedoSite = _configuration.GetSection("reCaptcha").GetSection("chaveDeSite").Value;
            ViewBag.MensagemDeRetorno = "";
            return View();
        }

No topo do arquivo acrescente:

using Microsoft.Extensions.Configuration;

Acrescente na chamada do controlador:

  • private readonly IConfiguration _configuration;
  • public PessoasController(ApplicationDbContext context,
    IConfiguration configuration)
  • _configuration = configuration;

Ficando algo assim:

 

rotina de inicio do controlador com as definicoes _configuration, para pegar os dados em appsettings.json
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using PoupaTempoDigital.Data;
using PoupaTempoDigital.Models;

namespace PoupaTempoDigital.Controllers
{
    [Authorize(Roles = Papeis.AdminEndUser)]
    [Authorize(Roles = Papeis.ColaboradorEndUser)]
    [Authorize(Roles = Papeis.ClienteEndUser)]
    public class TP_BancosCaixasController : Controller
    {
        private readonly ApplicationDbContext _context;
        private readonly IConfiguration _configuration;

        public TP_BancosCaixasController(ApplicationDbContext context
            ,IConfiguration configuration)

        {
            _context = context;
            _configuration = configuration;

        }

POST

 O post é mais chatinho. Tem que pessar para a API do Google as variáveis response e secret.

Acrescentar no topo da página: 

using System.Net;

using System.Net.Http; (provalmente vai ter que instalar no seu projeto)

using Newtonsoft.Json;

 

rotina POST do controlador
        // POST: TP_BancosCaixas/Create

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("IdBancoCaixa,IdUsuario,NomeBancoCaixa,NumeroBanco,NumeroConta,NumeroAgencia,SaldoInicial,DataSaldoInicial,TipoDeConta")] TP_BancosCaixas tP_BancosCaixas)
        {
            if (ModelState.IsValid)
            {
                HttpClient httpClient = new HttpClient();
                var response = Request.Form["g-recaptcha-response"]; // that's how you get it from the Request object
            
                string secretKey = _configuration.GetSection("reCaptcha").GetSection("chaveSecreta").Value;
                var cliente = new WebClient();
               // var res = httpClient.GetAsync($"https://www.google.com/recaptcha/api/siteverify?secret={secretKey}&response={response}").Result;
                var resultado = cliente.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secretKey, response));
                var obj = JObject.Parse(resultado);
                var status = (bool)obj.SelectToken("success");
                if (status)
                {
          					//acrescentar somente se o seu form voltar para a mesma página de entrada de dados
                    //ViewBag.MensagemDeRetorno = "Validação do Google reCaptcha feita com SUCESSO !!!";
                    _context.Add(tP_BancosCaixas);
                    await _context.SaveChangesAsync();
                    return RedirectToAction(nameof(Index));
                }
                else
                {
                    ViewBag.MensagemDeRetorno = "Validação do Google reCaptcha FALHOU !!!";
                    ViewBag.ChavedoSite = _configuration.GetSection("reCaptcha").GetSection("chaveDeSite").Value;
                    return View(tP_BancosCaixas);
                }



                }
            return View(tP_BancosCaixas);
        }
Criar um aplicativo Web com autenticação

Criar um aplicativo Web com autenticação

Criar um aplicativo Web com autenticação

Aqui veremos conceitos básicos sobre a arquitetura MVC (Model-View-Controller). ou seja, um modelo que  separa um aplicativo em três componentes principais: Modelo, Visualizador e Controlador.

Conceito e Benefícios do Modelo MVC (Model-View-Controller)

A arquitetura Model-View-Controller (MVC) separa um aplicativo em três componentes principais: Modelo, Visualizador e Controlador. O padrão MVC ajuda você a criar aplicativos que são mais testáveis e mais fáceis de atualizar do que os aplicativos monolíticos tradicionais. Os aplicativos baseados em MVC contêm: Modelos: Classes que representam os dados do aplicativo. As classes modelo usam a lógica de validação para impor regras de negócios para esses dados. Normalmente, os objetos do modelo recuperam e armazenam o estado do modelo em um banco de dados. Neste tutorial, um modelo de filme (Movie) recupera dados de filmes de um banco de dados, fornece-o para a visualização ou para atualização. Os dados atualizados são gravados em um banco de dados. Os Modelos ficam normalmente armazenados numa pasta Models, em arquivos com NomeDoModelo.cs. Os modelos, além de descreverem os dados da aplicação, podem incorporar várias regras de negócio. Para cada “campo” você pode definir um nome padrão, se é requerido, seu intervalo, se é chave, o tipo de mensagem de erro, a formatção numérica ou de data e assim por diante. Toda vez que você usar o campo, na aplicação, ele assumirá essas regras por definição. Ou seja, você define isso uma única vez e esquece no restante do código). Visualizadores: são os componentes que exibem a interface do usuário (UI) do aplicativo. Geralmente, essas UIs (user interfaces) exibem os dados do modelo (do banco de dados). Os visualizadores (Views) ficar normalmente armazenados numa pasta Views, em arquivos NomeDoVisualizador.cshtml (equivalem às páginas HTML que conhecemos bem, só que permitem acessos diretos a dados manipulados nos controladores, tornando-se dinâmicas e não estáticas. Além disso, permitem uso de lógicas bem interessantes tipo mostre isso se… ou mostre aquilo se…. Outro benefício é que usam auxiliadores de tags (Tag Helpers), que ajudam a escrever a página com pouquíssimo código. Quando ela é compilada, o código se expande e se torna completo para ser interpretado corretamente pelos navegadores HTML5. O Core tem uma fantástica biblioteca de Tags e, se isso não bastar, você pode criar suas próprias Tags, indo muito além do HTML5. Controladores: são classes que manipulam solicitações do navegador. Eles recuperam os dados do modelo e chamam os modelos de exibição (visualizadores ou Views) que retornam uma resposta. Também são os controladores, em geral (se você não interferir com programas em Javascript ou Jquery) que, cada vez que o usuário clica em alguma coisa, captam esse clique e executam uma ação correspondente. Em um aplicativo MVC, em geral a o visualizador apenas exibe informações e fica aguardando uma ação do usuário. Assim que o usuário clica em algo é o controlador faz alguma coisa, devolvendo ao visualizador(View) o que deve ser mostrado ao usuário. Se o usuário clicou por exemplo num item de menu, é o controlador que manipula dados, procurando a rota de navegação e, se forem passados parâmetros nesse link, ele processa os dados com esses parâmetros (no banco de dados), faz cálculos se necessários e devolve para o visualizador (View)o que deve ser mostrado na tela (no caso, outra página, com os dados de consulta solicitados pelo usuário). É sempre o Controlador (Controller) que interage com o banco de dados, e sempre seguindo as regras e definições dos Modelos (Models). Por exemplo (rodando no modo local, em localhost), se o usuário clica num item de que tem http://localhost:1234/Home/About, o controlador sabe que a rota é Home (em geral um subdiretório do diretório Views), onde vai buscar o arquivo About.cshtml. Só que, antes de mostrar o arquivo de visualização, ele consulta uma ação, também definida dentro dele (controlador Home.cs). Essa ação pode ser a mais simples possível, tipo “apenas mostre a página Home.cshtml) (na prática, o código é return View(), ou seja mostre a página e ponto final). Se a página About vai ter que mostrar algum dado do seu banco de dados, o controlador manda ler esses dados, conforme o seu modelo diz que tem que ser, e os entrega para a página de visualização. Nisso, em geral, ele faz o trabalho ompleto: entrega para a página About tudo o que está no seu modelo. Pode ser que os dados da sua empresa tenham CNPJ, Endereço de Entrega, Nome da Empresa, E-mail de Contato. O que vai aparecer na página About? Somente o que você definir que tem que aparecer. Poder só Nome da Empresa, Telefone e E-mail de Contato. Veja a sequência na figura abaixo: MOdelo COre MVC  

O Padrão MVC – Adicionando um Controller

O padrão MVC ajuda você a criar aplicativos que separam os diferentes aspectos do aplicativo (lógica de entrada, lógica de negócios e lógica de UI (user interface ou interface de usuário). Ao mesmo tempo, proporcionam um acoplamento livre entre esses elementos. O padrão especifica onde cada tipo de lógica deve estar localizada na aplicação: a lógica da UI pertence ao Visualizador. A lógica de tratamento dos dados de entrada pertencem ao controlador. A lógica de negócios (e, em geral, de apresentação) pertence ao modelo. Essa separação ajuda você a gerenciar a complexidade na criação de um aplicativo. Porque você trabalhar em um aspecto da implementação sem afetar o código de outro aspecto. Por exemplo, você pode trabalhar no código de exibição (que ficam no Visualizador ou View), sem se preocupar com as lógicas de aquisição e processamento dos dados (que ficam no Controlador ou Controller)  ou de apresentação dos dados (que ficam no Modelo. ou Model). Veremos todos estes conceitos nesta série de artigos deste tutorial e mostraremos como usá-los para criar um aplicativo, no nosso exemplo, de filmes. Um projeto MVC contém pastas específicas para controladores, modelos e visualizadores, como já mencionamos. Para adicionar um controlador:
  • Em Solutions Explorer, clique com o botão direito do mouse em Controllers> Add> New Item
ADICIONA CONTROLLER mvc  
  • Selecione MVC Controller Class
  • No diálogo Add New Item, entre com HelloWorldController
adiciona controller MVC  
Troque o conteúdo de Controllers/HelloWorldController.cs com o seguinte código
C&amp;amp;amp;oacute;digo HelloWOrldController.cs
using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers
{
    public class HelloWorldController : Controller
    {
        // 
        // GET: /HelloWorld/

        public string Index()
        {
            return "This is my default action...";
        }

        // 
        // GET: /HelloWorld/Welcome/ 

        public string Welcome()
        {
            return "This is the Welcome action method...";
        }
    }
}
Todo método público (public) em um controlador é “chamável” como um endereço HTTP. Na exemplo acima, os dois métodos retornam uma “string” (texto). Observe os comentários que precedem cada método. Um ponto final (endpoint) HTTP é um URL endereçável pelo aplicativo da Web, assim como http://localhost:1234/HelloWorld. E combina o protocolo usado: HTTP, a localização na rede do servidor web (incluindo a Porta TCP, no caso da depuração local localhost:1234 e o arquivo de visualização alvo, no caso, HelloWorld. Note que é feita uma chamada tipo GET. Ou seja ,é preciso pegar alguma coisa para mostrar. Por isso é um método HTTP GET, chamado pela adição de “/HelloWorld /” na URL base. O segundo comentário também mostra um método HTTP GET, que é chamado pela adição de “/HelloWorld/Welcome/” à URL. Mais tarde, neste tutorial, você usará o mecanismo de “scaffolding” para gerar métodos HTTP POST (ou seja, que vão devolver dados para os visualizadores). Execute o aplicativo em modo não depurado e acrescente “HelloWorld” ao caminho, na barra de endereços. O método Index retornará uma string (um texto). controlador retorno MVC
O MVC invoca as classes do controlador (e os métodos de ação dentro delas) dependendo da URL recebida. A lógica de roteamento de URL padrão usada pelo MVC usa um formato como esse para determinar o código a ser invocado: /[Controller]/[ActionName]/[Parameters] Você é quem configura o formato de roteamento no arquivo Startup.cs.
Formato do Roteamento no Startup.cs
app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});
Quando você executa o aplicativo e não fornece nenhum segmento de URL, o padrão para o controlador segue o que está definido no padrão acima – no caso “Home” e com o  método “Index“. A “primeira” parte da URL determina a classe do controlador a ser executada. A segunda parte do segmento de URL determina o método de ação (Acction) dwntro da classe. Por exemplo, localhost:xxxx/HelloWorld/15 mapeia para a classe HelloWorldController (arquivo HelloWorldController.cs) .  Observe que você só teve que navegar para localhost: xxxx/HelloWorld. Isso ocorre porque o Index é o método padrão (default)  que será chamado em um controlador se um nome de método não for DIRETAMENTE especificado. A terceira parte do segmento de URL (em geral é uma id) é para dados de rota. Ou seja, é a passagem de um parâmetros, como estamos acostumados em chamadas do tipo htttp:minhauerl.com.br?id=15.  Veremos dados de parâmetros nas rotas mais tarde neste tutorial. Navegue até http://localhost: xxxx/HelloWorld. (xxx é definido no seu Visual Studio). Você verá a tela com as boas-vindas, que retorna a string “This is the Welcome method” (este é o método de boas vindas).  Para esta URL, o controlador é HelloWorld e Welcome é a ação (Action) do método. Neste exemplo simples, ainda não foram utilizados [Parâmetros] na URL. controller MVC
Modifique o código, passando agora algum parâmetro na URL. Por exemplo, /HelloWorld/Welcome?name=Maria&numtimes=4. Mude o método Welcome para que ele aceite (e opere) estes 2 parâmetros, de acordo com o seguinte código:  
Inclus&amp;amp;amp;atilde;o de par&amp;amp;amp;acirc;metros no controlador (controller)
// GET: /HelloWorld/Welcome/ 
// Requires using System.Text.Encodings.Web;
public string Welcome(string name, int numTimes = 1)
{
    return HtmlEncoder.Default.Encode($"Hello {name}, NumTimes is: {numTimes}");
}
O código acima:
  • Usa o recurso C# de parâmetro opcional para indicar que o parâmetro numTimes  tem o valor padrão  = 1 se nenhum valor for passado para esse parâmetro;
  • Utiliza UsesHtmlEncoder.Default.Encode para proteger a aplicação de entrada maliciosa (ou seja, entradas do tipo JavaScript);
  • Usa “Interpolated strings” (textos interpolados), que são acrescentados dentro dos {}.
Rode sua aplicação e navegue para: http://localhost:xxx/HelloWorld/Welcome?name=Rick&numtimes=4 (Troque xxx pelo número da porta que seu Visual Studio definiu). Você pode usarr diferentes valores para nomes e números na URL. O sistema de ligação (binding)  do modelo MVC mapeia automaticamente os parâmetros nomeados da cadeia de consulta na barra de endereços para os parâmetros dentro do seu método. mais tarde veremos com detalhes essa questão de ligação (binding). controlador MVC
Na imagem acima. o segmento da URL (Parameters) não é usado dentro do padrão do Core MVC. Os parâmetros name e numTimes são passados como “query strings” (do jeito como estamos acostumados). É por isso que aparece o ? (interrogação) na URL, que é um separador, que tem os parâmetros depois dele. Como você sabe, o carácter & separa as strings de uma query. Troque o método Welcome pelo seguinte código:    
Mudan&amp;amp;amp;ccedil;a no controlador
public string Welcome(string name, int ID = 1)
{
    return HtmlEncoder.Default.Encode($"Hello {name}, ID: {ID}");
}
Rode a aplicação e entre com a seguinte URL (no seu navegador): http://localhost/xxx/HelloWorld/Welcome/3?name=Rick controller MVC
Desta vez, o terceiro segmento de URL corresponde ao id do parâmetro da rota. O método de Welcome contém um ID de parâmetro que corresponde ao modelo de URL no método MapRoute. A trilha? (em id?) Indica que o parâmetro id é um parâmetro opcional. (é bem importante lembrar disso).
C&amp;amp;amp;oacute;digo t&amp;amp;amp;iacute;pico no Roteador no Startup.cs, onde sua aplica&amp;amp;amp;ccedil;&amp;amp;amp;atilde;o se inicia
app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});
Nestes exemplos, o controlador está fazendo a parte “VC” do MVC – isto é, a visão e o controlador é que trabalham de verdade. O controlador está retornando HTML diretamente. Geralmente você não quer que os controladores retornem o HTML diretamente, já que isso se torna muito pesado ao codificar e manter essa codificação. Em vez disso, você geralmente usa um arquivo de modelo de visualização (View). que é um arquivo tipo template (Razor), separado, para ajudar a gerar a resposta em HTML. Veremos isso no próximo tutorial. No Visual Studio, no modo de não-depuração (Ctrl + F5), você não precisa criar o aplicativo depois de alterar o código. Basta salvar o arquivo, atualizar seu navegador e você pode ver as mudanças. Se estiver no mode de depuração simples. mudanças nos arquivos de visualização, se salvos, podem ser vistos sem a necessidade de recompilar a aplicação. Isso facilta muito, pois nasta dar um “refresh” na página do navegador.

Obrigado pela sua leitura. Continue visitando este blog e compartilhe artigos em sua rede de relacionamento. Por favor, se quiser, registre sugestões e comentários aqui no final da página.

ASP.NET Web Forms Accordion Control – Syncfusion

ASP.NET Web Forms Accordion Control – Syncfusion

O widget EssentialJavaScriptMobileAccordion é uma interface em que as listas de itens são recolhidas ou expandidas. O controle de acordeões tem vários painéis recolhíveis, onde apenas um pode ser expandido por vez. Tem um modelo para seu cabeçalho e seu conteúdo.

Acordeão – Visão Geral

Acordeão - Visão GeralO controle Accordion do ASP.NET Syncfusion fornece painéis colapsáveis verticalmente (acordeão vertical) e cabeçalhos empilhados que expandem ou recolhem um ou mais painéis por vez dentro do espaço disponível.

 

Templates (modelos) do Acordeão

O controle Accordion do ASP.NET permite personalizar o cabeçalho e a aparência do painel de conteúdo usando determinados elementos HTML.

Expandir e recolher

O controle Accordion do ASP.NET permite personalizar as ações de expansão e recolhimento.

Modo de expansão

Um único painel ou vários painéis podem se expandir ou recolher (alternar entre painéis) por vez, clicando no cabeçalho do painel (interações) ou programaticamente.

Modo Recolhível

Recolhe todos os painéis de uma só vez. Com opções de personalização, um painel específico é definido para estar em estado aberto enquanto o restante dos painéis é recolhido.

Um desafio que tive: iniciar o acordeão com TODOS elementos recolhidos. Porque, pela documentação, você pode determinar aquele (ou aqueles) que iniciarão abertos, mas ao menos um deve ser aberto no início. A solução foi colocar uma rotina em Javascript que, ao carregar a página .cshtml, fecha todos elementos (vide código abaixo).  

Rotina para arquivo .cshtm
@section scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
    <script>
        $(document).ready(function () {
		var accordion = $('#basicAccordion').data('ejAccordion'); 
		accordion.collapseAll(); 
            // console.log("página carregada!");
        });
    </script>

}

Obrigado pela sua leitura. Continue visitando este blog e compartilhe artigos em sua rede de relacionamento. Por favor, se quiser, registre sugestões e comentários aqui no final da página.

Populando Papéis (roles),usuários principais (admin,managers) e menus por papel a partir do Startup.cs do Core MVC

Populando Papéis (roles),usuários principais (admin,managers) e menus por papel a partir do Startup.cs do Core MVC

Populando Papéis (roles),usuários principais (admin,managers) e menus por papel a partir do Startup.cs do Core MVC

Muitas vezes você quer pré-definir os papéis (roles) para todos possíveis usuários da sua aplicação, Bem como definir um ou mais usuários com poder administrativo geral, ou também um usuário gerenciador, com menos poderes que o administrador. Este artigo mostra como fazer isso,

Ao rodar o arquivo STARTUP.CS é perfeitamente possível gerar todos os papéis (Roles( desejados para a sua aplicação, bem como definir ao menos um usuário administrativo com superpoderes e outros usuários com menores poderes. Isso vai ajudar, depois, a definir menus seletivos, onde só aparecem opções de navegação a que cada papel (role) dá direito.

Passo 1 – Nova tabela no banco de dados

Parimos do princípio que você já tem seu database com as tabelas que necessita, conforma artigos anteriores, O que vamos precisar agora é de uma nova tabela que contenha o que o menu de cada pape (role( deve conter ou não.. Ou seja, precisamos fazer uma tabela de relacionamento entre a tabela ASP.NET Roles e nossa tabela de menu. Vamos ver em detalhes sobre como criar nossa nova Tabela de Menus que tem um relacionamento com a tabela ASP.NET Identity AspNetRoles. A ess tabela vamos dar o nome MenuMaster, com os seguintes campos:

 

Tabela MenuMaster – Campos e Descrições

MenuIdentity é o campo de identidade usado como linha de ID (KEY)
MenuID é a identidade propriamente dita usada como referência primárias para qualquer campo do Menu
Parente_MenuID para cada top  MenuID será usado o valor “*”. Esse valor serpa usado somente para itens de menu com nível igual a 1 (primeira opção por default). Se na hierarquia de menus houver níveis 2 ou 3, elas farão referência ao menu raiz *MenuID”, que são pais
User_Rool Cria o menu para cada papel de usuário (User Role). O menu serpa mostrado para os usuários que tem esse papel (role) e que estiverem logados. Por exemplo, Admin pode criar diferentes menus para gerentes, consultores e outros papéis, E somente usuários que tenham esses papéis poderão ver esses menus
MenuFileName Este é o nome real para cada opção do menu (o que aparece em tela).  A ideia qui é usar o view nMWdo MV como MenuFileName
MenuURL É o Menu path para cada item do Menu, usado como hiperlink,, Em MVCé o nome do Controlador (controller)
USE_YN Se usado Y, o item de menu vai aparecer, Se usado N, não vai aparecer,  mesmo que ateda os requisitos de papel(role), Em geral é controlado por Viewbags, que definem se em certas condições do item de menu deve ou não aparecer,
CreateDate Data de criação do item do Menu

Passo 2- Criando a tabela no banco de dados, antes de popula-la

No arquivo STARTUP.CS, acrescente em services,addIdentity a parte de código relativo a

//Seting the Account Login page
            services.ConfigureApplicationCookie(options =>
Se ao compilar o Visual Stdio reclamar que não consegue apagar um diretório na pasta OBJ da aplicação, apague manualmente e recompile novamente,
ARQUIVO STARTUP.CS - acrescente metodo services.ConfigureApplicationCooki
services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();
  
  
            //Password Strength Setting
            services.Configure<IdentityOptions>(options =>
            {
                // Password settings
                options.Password.RequireDigit = true;
                options.Password.RequiredLength = 8;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = true;
                options.Password.RequireLowercase = false;
                options.Password.RequiredUniqueChars = 6;
  
                // Lockout settings
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
                options.Lockout.MaxFailedAccessAttempts = 10;
                options.Lockout.AllowedForNewUsers = true;
  
                // User settings
                options.User.RequireUniqueEmail = true;
            });
// COPIE A PARTIR DAQUI  
            //Seting the Account Login page
            services.ConfigureApplicationCookie(options =>
            {
                // Cookie settings
                options.Cookie.HttpOnly = true;
                options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
                options.LoginPath = "/Account/Login"; // If the LoginPath is not set here, ASP.NET Core will default to /Account/Login
                options.LogoutPath = "/Account/Logout"; // If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout
                options.AccessDeniedPath = "/Account/AccessDenied"; // If the AccessDeniedPath is not set here, ASP.NET Core will default to /Account/AccessDenied
                options.SlidingExpiration = true;
            });
  //COPIE ATÉ AQUI

Agora vamos criar o modelo com a classe MenuMaster, na pasta Models:

modelo com a classe MenuMaster, na pasta Models:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace DiagnosticoCultura.Models
{
public class MenuMaster
    {
        [Key]
				[Display(Name = "Id do Menu (chave)")]
        public int MenuIdentity { get; set; }


			  [Display(Name = "Identidade do menu")]
        public string MenuID { get; set; }

				[Display(Name = "Nome de display do Menu")]
        public string MenuName { get; set; }

				[Display(Name = "MenuID do Menu Pai (quando existir)")]
        public string Parent_MenuID { get; set; }

  			[Display(Name = "Papel (Role) em que o menu está permitido")]
        public string User_Roll { get; set; }

	  		[Display(Name = "Nome da ação DENTRO DO CONTROLLER")]
        public string MenuFileName { get; set; }
       
			 [Display(Name = "Nome do CONTROLLER")]
		  	public string MenuURL { get; set; }

			  [Display(Name = "Mostra Menu (Y-N)")]
        public string USE_YN { get; set; }
       
  			//alternativa: colocar no controle yourModel.DataCadastro = DateTime.Now;
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        [Display(Name = "Data de Criação do Menu")]
			  public DateTime CreatedDate { get; set; }

 				public MenuMaster()
    		{
        CreatedDate = DateTime.Now;
   	 		}
    }
}

Passo 3 – Gerando a tabela MASTERMENU no nosso database

Recompile a aplicação para ver se até aqui não deu qualquer erro. O resultado deve ser:

========== Build: 0 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========

No Solution Explorer clique na pasta Controllers e com o botão da direita selecione ADD->New Scaffolded Item. Escolha MVC e à direita escolha MVC Controllers with Views, using Entity Framework. Clique no botão ADD.

Em Model Class escolher  o modelo MenuMaster , que ai aparecer como MenuMaster (DiagnosticoCultura.Models) . Clique no botão ADD.

Agora vamos Rodar a miração na console PMC:

add-migration mastermenu

e depois

update-database

Se compilar o programa e digitar https://localhost:44387/MenuMasters você verá que está tudo pronto para cadastrar os menus.

menu_master

Passo 4 – Criando classes de interface

Agora é hora de criarmos uma interface com um método chamado GetMenuMaster() e outro método chamado GetMenuMaster (String UserRole).

Implementaremos essa interface em nosso serviço para obter todos os detalhes do menu da tabela e também outro método para obter o menu considerando o papel (ROLE) do usuário.

Para criar a interface, adicione uma nova classe à sua pasta de modelos e nomeie a classe como “IMenuMasterService”. Vamos mudar a classe para uma interface, pois vamos criar uma interface para implementar em nosso serviço

interface de modelos de menus ARQUIVO IMenuMasterService.CS PASTA mODELS
public interface IMenuMasterService
    {
        IEnumerable<MenuMaster> GetMenuMaster();
        IEnumerable<MenuMaster> GetMenuMaster(String UserRole);
     }

Passo 5: Criação do Serviço de Menus:

Agora vamos adicionar uma nova pasta de serviços de classe (abrir uma pasta raiz chamada Services) e nomear a classe como “MenuMasterService“.

Nesta classe, estaremos implementando nossa interface IMenuMasterService.

Sabemos que, se implementarmos a interface, devemos declarar o método de interface em nossa classe. Neste serviço, usamos o método de interface e retornamos a lista com detalhes do Menu e também retornamos os detalhes do Menu por função do usuário. Depois estaremos injetando isso diretamente em nossa página de visualização.

code snipet Criacaoo do Servco de Menus:folder SERVICER arquivo MenuMasterService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using DiagnosticoCultura.Data;
using DiagnosticoCultura.Models;



namespace DiagnosticoCultura.Services
{

    public class MenuMasterService : IMenuMasterService
    {
        private readonly ApplicationDbContext _context;
        public MenuMasterService(ApplicationDbContext dbContext)
        {
            _context = dbContext;
        }
        public IEnumerable<MenuMaster> GetMenuMaster()
        {
            return _context.MenuMaster.AsEnumerable();

        }

        public IEnumerable<MenuMaster> GetMenuMaster(string UserRole)
        {
            var result = _context.MenuMaster.Where(m => m.User_Roll == UserRole).ToList();
            return result;
        }
    }
}

Passo 6: Registrando o serviço

Precisamos registrar nosso serviço criado no contêiner. Abra o Startup.cs do seu projeto para adicionar o serviço ao contêiner.

Na classe Startup.cs, encontre o método denominado ConfigureServices e adicione seu serviço “MenuMasterService” como abaixo.

Registrando o servico no startup.cs (acrescentar MenuMasterService em ConfigureServices)
// adicionar using DiagnosticoCultura.Services;
//no topo do arquivo startup.cs

services.AddTransient<MenuMasterService, MenuMasterService>();

Passo 7: Injetando o serviço de menus na página _Layout.cshtml

Agora, é muito mais simples e fácil, já que podemos injetar diretamente o serviço em nossa página de visualização e vincular todo o resultado à nossa página de visualização.

Para injetar o Serviço em nossa Visualização, aqui estaremos usando nossa página _Layout.cshtml existente.

Como vamos exibir o menu no topo do nosso site e usá-lo em todas as nossas páginas, aqui usamos a página _Layout.cshtml para vincular os resultados do menu como um menu baseado no usuário logado.

Aqui primeiro verificamos se o usuário está Autenticado em nosso site. Se o usuário estiver logado, obteremos os detalhes do papel do usuário conectado e vincularemos o menu com base nas funções do usuário.

Aqui no exemplo estamos vinculando 2 nível do menu como Menu Principal e Submenu.

Em nosso resultado de tabela, verificamos todos os Parenut_MenuID = ”*”, já que estaremos exibindo o menu principal com o parent_MenuID como “*” e no próximo loop interno, exibimos o submenu apropriado para o menu principal.

IMPORTANTE> Faça um backup do _Layout.cshtml ANTES de alterar qualquer coisa, Se 

mudancas na pagina _Layout,cshtml (pasta ViewsShared) - veja o que deve ser acrescentado e onde
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Diagnóstico de Cultura</title>


    <!-- Essential JS 2 Base's material theme (Dependency Styles) -->
    <link href="https://cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet" type="text/css" />

    <!-- Essential JS 2 Button's material theme (Control Styles) -->
    <link href="https://cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet" type="text/css" />

    <!-- Essential JS 2 Base's global script (Dependency Script) -->
    <script src="https://cdn.syncfusion.com/ej2/ej2-base/dist/global/ej2-base.min.js" type="text/javascript"></script>

    <!-- Essential JS 2 Button's global script (Control Script) -->
    <script src="https://cdn.syncfusion.com/ej2/ej2-buttons/dist/global/ej2-buttons.min.js" type="text/javascript"></script>

    <environment include="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    </environment>
    <environment exclude="Development">
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
              asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
              crossorigin="anonymous"
              integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" />
    </environment>
    <!-- Syncfusion Essential JS 2 Styles -->
    <link rel="stylesheet" href="https://cdn.syncfusion.com/ej2/material.css" />

    <!-- Syncfusion Essential JS 2 Scripts -->
    <script src="https://cdn.syncfusion.com/ej2/dist/ej2.min.js"></script>
    <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">Diagnóstico de Cultura</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">
 <ul class="nav navbar-nav">
   <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
 @if (User.Identity.IsAuthenticated)
        {
            var UserRoles = "";
            if (@User.IsInRole("Admin"))
            {
                UserRoles = "Admin";
            }
            else
            {
                UserRoles = "Manager";
            }
  
                @if (menus.GetMenuMaster(@UserRoles).Any())
                {
                @if (menus.GetMenuMaster(@UserRoles).Any())
                {
                @foreach (var menuNames in menus.GetMenuMaster(@UserRoles).Where(n => n.Parent_MenuID == "*"))
                {
                        <li>
                            <a asp-area="" asp-controller=@menuNames.MenuURL asp-action=@menuNames.MenuFileName>@menuNames.MenuName</a>
                            <ul class="sub-menu">
                                @foreach (var subMenu in menus.GetMenuMaster(@UserRoles).Where(n => n.Parent_MenuID == @menuNames.MenuID))
                                  {
                                    <li>
                                        <a asp-area="" asp-controller=@subMenu.MenuURL asp-action=@subMenu.MenuFileName>@subMenu.MenuName</a>
                                    </li>
                                   }
                            </ul>
                            </li>
                }
                }
                }
            }
  </ul>
              
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <partial name="_CookieConsentPartial" />
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>


    <footer class="border-top footer text-muted">
        <div class="container">
            © 2019 - Diagnóstico de Cultura - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>

    <environment include="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    </environment>
    <environment exclude="Development">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                asp-fallback-test="window.jQuery"
                crossorigin="anonymous"
                integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=">
        </script>
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
                crossorigin="anonymous"
                integrity="sha384-xrRywqdh3PHs8keKZN+8zzc5TX0GRTLCcmivcbNJWm2rs5C8PRhcEn3czEjhAO9o">
        </script>
    </environment>
    <script src="~/js/site.js" asp-append-version="true"></script>
    
    @RenderSection("Scripts", required: false)
    
    <ejs-scripts></ejs-scripts>
</body>
</html>

Obrigado pela sua leitura. Continue visitando este blog e compartilhe artigos em sua rede de relacionamento. Por favor, se quiser, registre sugestões e comentários aqui no final da página.

Como Criar, Ler, Atualizar e Apagar Usuários com o ASP.NET Core Identity

Como Criar, Ler, Atualizar e Apagar Usuários com o ASP.NET Core Identity

Como Criar, Ler, Atualizar e Apagar Usuários com o ASP.NET Core Identity

Neste tutorial, realizarei operações CRUD para criar, ler, atualizar e excluir usuários na identidade (Identity) do ASP.NET Core. Antes de continuar, você deve instalar e configurar a identidade do ASP.NET Core em seu projeto – se ainda não instalou.

Instale o Visual Studio e o .NET Core

Instale o seguinte:
  • .NET Core 5.0 SDK ou mais recente;
  • Visual Studio 2019 versão 16.8 ou posterior com ASP.NET e carga de trabalho de desenvolvimento web… Instale, pelo menos, os seguintes “workloads”:
    • ASP.NET and web development
    • .NET Core cross-platform development

Crie um aplicativo WEB

No Visual Studio, selecione File > New > Project e escolha o template conforme a figura abaixo:
  • Dê um nome para o seu projeto teste como “MVCMovie” (é importante lembrar que ao dar esse nome, se você copiar código daqui, vai ter de trocar o namespace de todas páginas de controle (.cs) para o nome do seu projeto.
  • Escolha umm local no seu disco para os programas fonte;
  • Escolha a opção de deixar a solução e o projeto no mesmo diretório.
  • Escolha a aplicacão e clique em Change, para escolher o tipo de autenticação
  • Selecione a opção Individual User Accounts e de OK.
  • Escolha a aplicacão e clique em Change, para escolher o tipo de autenticação
  • Selecione a opção Individual User Accounts e de OK.
  • Clique em CREATE. Pronto. O projeto está pronto.

O Visual Studio usou um modelo padrão para o projeto MVC que você acabou de criar, já com identidade (Identity instalada).

No lado direito há as opções de registro e login. Você já possui um aplicativo de trabalho inserindo com o nome de projeto, que permite selecionar algumas opções.

Este é um projeto inicial simples e é um bom lugar para começar. No Visual Studio, clique em F5 para executar o aplicativo no modo de depuração ou Ctrl-F5 no modo de não depuração.

 

Criar novos usuários na identidade (Identity)

Para criar usuários na identidade do ASP.NET Core, você precisará criar uma classe de modelo. Comece tudo criando uma classe chamada AppUser.cs dentro da pasta Models (Modelos). Adicione propriedades públicas a ele, que são, no meu caso:
  • FirstName, LastName, Email (com override, pois já existe em Identity), PhoneNumber (com override, pois já existe em Identity) e Password, do tipo string. Adicione também atributos de validação de modelo [Required] a eles
  • FullName (campo calculado FirstName + LastName
  • Interesse, tipo inteiro, não obrigatório (por isso usei int? – a interrogação torna o campo não obrigatório)

Criando o controlador de todas as operações

Para fazer todas operações de leitura, autalização, criação, deleção – teremos de ter um controlador geral – que vamos incluir na pasta de Controllers (controladores). Vamos dar o nome a esse arquivo controlador de AdminController.cs.

Note que em Create o novo usuário tem variáveis que não existem no arquivo original de Identidade: FirstName, LastName, Interesse e Password. Esses campos serão adicionados à tabela de usuários original do ASP.NET.

O Password = user.Password guardará a password em string, sem afetar a guarda da senha codificada pelo aspnet. Para mim isso é útil, mesmo não sendo seguro. Você pode não incluir essa linha se quiser.

Para o arquivo de pessoas receber os novos campos, entre em Tools, Nuget Package Manager, Package Manager Console e digite:

add-migration Usuarios

update-database

Codigo AdminController.cs. na pasta Controllers
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using PoupaTempoDigital.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;


namespace PoupaTempoDigital.Controllers
{
    public class AdminController : Controller
    {
        private readonly UserManager<AppUser> _userManager;
        private readonly ILogger<AdminController> _logger;
        private readonly RoleManager<AppRole> _roleManager;
        private IPasswordHasher<AppUser> _passwordHasher;
        public AdminController(
            UserManager<AppUser> userManager
            , ILogger<AdminController> logger
            , RoleManager<AppRole> roleManager
            , IPasswordHasher<AppUser> passwordHash
            )
        {
            _userManager = userManager;
            _logger = logger;
            _roleManager = roleManager;
            _passwordHasher = passwordHash;
        }
        public IActionResult Index()
        {
            List<AppUser> novaListaUsuarios = new List<AppUser>();
            var todosUsuarios = (from user in _userManager.Users
                                 select new AppUser
                                 {
                                     Id = user.Id,
                                     FirstName = user.FirstName,
                                     LastName = user.LastName,
                                     PhoneNumber = user.PhoneNumber,
                                     Email = user.Email

                                 });

            ViewBag.DataSource = todosUsuarios;
            return View();

        }

        // GET: Pessoas/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var usuario = await _userManager.Users.FirstOrDefaultAsync(m => m.Id == id);
            //ViewBag.DataSource =  _userManager.Users;
            if (usuario == null)
            {
                return NotFound();
            }

            return View(usuario);
        }


        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            AppUser user = await _userManager.FindByIdAsync(id.ToString());
            if (user != null)
            {
                IdentityResult result = await _userManager.DeleteAsync(user);
                if (result.Succeeded)
                { return RedirectToAction(nameof(Index)); }
                else
                { Errors(result); }
            }
            else
            { 
               ModelState.AddModelError("", "Usuário não Encontado"); 
            }
            //return View("Index", _userManager.Users);
            //ViewBag.DataSource = _userManager.Users;
            return RedirectToAction(nameof(Index));
        }

        public async Task<IActionResult> Update(int Id)
        {
            AppUser user = await _userManager.FindByIdAsync(Id.ToString());
            if (user != null)
                return View(user);
            else
                return RedirectToAction("Index");
        }

        [HttpPost]
        public async Task<IActionResult> Update(int Id, string email, string password, string FirstName, string LastName, string PhoneNumber)
        {
            AppUser user = await _userManager.FindByIdAsync(Id.ToString());
            if (user != null)
            {
                if (!string.IsNullOrEmpty(email))
                    user.Email = email;
                else
                    ModelState.AddModelError("", "O E-mail não pode ser vazio");

                if (!string.IsNullOrEmpty(FirstName))
                    user.FirstName = FirstName;
                else
                    ModelState.AddModelError("", "O Nome não pode ser vazio");

                if (!string.IsNullOrEmpty(LastName))
                    user.LastName = LastName;
                else
                    ModelState.AddModelError("", "O Sobrenome não pode ser vazio");

                if (!string.IsNullOrEmpty(PhoneNumber))
                    user.PhoneNumber = PhoneNumber;
                else
                    ModelState.AddModelError("", "O telefone do WhatsApp não pode ser vazio");


                if (!string.IsNullOrEmpty(password))
                    user.PasswordHash = _passwordHasher.HashPassword(user, password);
                else
                    ModelState.AddModelError("", "A senha não pode ser vazia");

                if (!string.IsNullOrEmpty(email) && !string.IsNullOrEmpty(password))
                {
                    IdentityResult result = await _userManager.UpdateAsync(user);
                    if (result.Succeeded)
                        return RedirectToAction("Index");
                    else
                        Errors(result);
                }
            }
            else
                ModelState.AddModelError("", "Usuário não encontrado");
            return View(user);
        }

        private void Errors(IdentityResult result)
        {
            foreach (IdentityError error in result.Errors)
                ModelState.AddModelError("", error.Description);
        }


        public ViewResult Create() => View();

        [HttpPost]
        public async Task<IActionResult> Create(AppUser user)
        {
            if (ModelState.IsValid)
            {
                var usuario = await _userManager.FindByEmailAsync(user.Email);
                if (usuario != null) //existe usuario
                {
                    ModelState.AddModelError("Email", "Este e-mail já está cadastrado no PoupaTempo. Entre no sistema usando o Acesso, no menu");
                    return View("Create", user);
                }


                AppUser appUser = new AppUser
                {
                    FirstName = user.FirstName,
                    LastName = user.LastName,
                    PhoneNumber = user.PhoneNumber,
                    Email = user.Email,
                    Interesse = 4, //todo definido um valor fixo arbitrário, deveria ser  user.Interesse,
                    Password = user.Password
                };
                appUser.UserName = appUser.Email;
                appUser.EmailConfirmed = true;
                //appUser.SecurityStamp = Guid.NewGuid().ToString();
                IdentityResult result = await _userManager.CreateAsync(appUser, user.Password);
                if (result.Succeeded)
                {
                    _logger.LogInformation("O usuário criou uma nova conta com senha.");
                    //cria papéis se ainda não existem
                    //admin
                    if (!await _roleManager.RoleExistsAsync(Papeis.AdminEndUser))
                    {
                        await _roleManager.CreateAsync(new AppRole(Papeis.AdminEndUser));
                    }
                    //cliente
                    if (!await _roleManager.RoleExistsAsync(Papeis.ClienteEndUser))
                    {
                        await _roleManager.CreateAsync(new AppRole(Papeis.ClienteEndUser));
                    }
                    //colaborador
                    if (!await _roleManager.RoleExistsAsync(Papeis.ColaboradorEndUser))
                    {
                        await _roleManager.CreateAsync(new AppRole(Papeis.ColaboradorEndUser));
                    }
                    //cria papel padrao para cada novo inscrito = Cliente
                    await _userManager.AddToRoleAsync(appUser, Papeis.ClienteEndUser);
                }
                else
                {
                    foreach (IdentityError error in result.Errors)
                        ModelState.AddModelError("Erro:", error.Description);
                    return View("Create", user);
                }
                return RedirectToAction("Index");
            }
            return View("Index");
        }
    }
}

Note que ao adicionar um usuário:

  • testo para ver se esse e-mail já não cadastrado anteriormente;
  • associo a ele uma “role”, ou seja, um papel. No meu caso:
  • testo se os 3 papeis que uso (cliente, colaborador e administrador) já existem no arquivo de roles. Se não existem, são criados;
  • associo o usuário adicionado uma role padrão, no meu caso, cliente.

O arquivo de roles (papeis, que você pode mudar)  fica na pasta models, em papeis.cs

Criando as Views (visualizações)

View de criação

Arquivo Create.cshtml. Pasta Views-&amp;amp;amp;amp;amp;gt;Admin
@model PoupaTempoDigital.Models.AppUser

@{
    ViewData["Title"] = "Cria Usuário";
}

<h1 class="bg-info text-white">&nbspCria Novo Usuário</h1>
<a asp-action="Index" class="btn btn-secondary">Retornar</a>
<div asp-validation-summary="All" class="text-danger"></div>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div class="form-group">
                <label asp-for="FirstName"></label>
                <input asp-for="FirstName" class="form-control" />
                <span asp-validation-for="FirstName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="LastName"></label>
                <input asp-for="LastName" class="form-control" />
                <span asp-validation-for="LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="PhoneNumber"></label>
                <input asp-for="PhoneNumber" class="form-control" />
                <span asp-validation-for="PhoneNumber" class="text-danger"></span>
            </div>

            <div class="form-group">
                <label asp-for="Email"></label>
                <input asp-for="Email" autocomplete="off" class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Password"></label>
                <input asp-for="Password" class="form-control" />
                <span asp-validation-for="Password" autocomplete="off"  class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Create</button>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

View de Atualização

Arquivo Update.cshtml. Pasta Views-&amp;amp;amp;amp;amp;gt;Admin
@model PoupaTempoDigital.Models.AppUser

@{
    ViewData["Title"] = "Atualiza Usuário";
}

<h1 class="bg-info text-white">&nbspAtualiza Usuário</h1>
<a asp-action="Index" class="btn btn-secondary">Back</a>
<div asp-validation-summary="All" class="text-danger"></div>

<hr />
<div class="row">
    <div class="col-md-4">


        <form asp-action="Update" method="post">
            <div class="form-group">
                <label asp-for="Id"></label>
                <input asp-for="Id" class="form-control" disabled />
                <span asp-validation-for="Id" class="text-danger"></span>
            </div>

            <div class="form-group">
                <label asp-for="FirstName"></label>
                <input asp-for="FirstName" class="form-control" />
                <span asp-validation-for="FirstName" class="text-danger"></span>
            </div>

            <div class="form-group">
                <label asp-for="LastName"></label>
                <input asp-for="LastName" class="form-control" />
                <span asp-validation-for="LastName" class="text-danger"></span>
            </div>

            <div class="form-group">
                <label asp-for="PhoneNumber"></label>
                <input asp-for="PhoneNumber" class="form-control" />
                <span asp-validation-for="PhoneNumber" class="text-danger"></span>
            </div>

            <div class="form-group">
                <label asp-for="Email"></label>
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label for="password">Password</label>
                <input name="password" class="form-control" />
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Save</button>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Listagem dos usuários

Para a listagem eu usos pacotes da Syncfusion, que me facilitam muito a paginação, o sort e a pesquisa em cada campo mostrado (veja meu artigo Syncfusion Core: acelerando o seu desenvolvimento em Core MVC com componentes).

Nessa página de listagem eu inclui dois botoes, um de atualização e outro para apagar o usuário. Seria bobagem gerar uma segunda listagem só para escolher o usuário a ser “apagado”.

Esses dois botões são construidos dentro do “template” logo abaixo do grid, entre <script> e </script>.

De qualquer forma deixei comentado o “código tradicional” para este tipo de view, caso não queira usar o Syncfusion. Não tem paginação, nem sort, nem nada, como de hábito.

No topo da página acrescentei um botão para a criação de novos usuários.

Arquivo Index.cshtml. Pasta Views-&amp;amp;amp;amp;amp;gt;Admin
@model IEnumerable<PoupaTempoDigital.Models.AppUser>

@{
    ViewData["Title"] = "Listagem de Usuários";
}

<h1 class="bg-info text-white">&nbspTodos os Usuários</h1>
<a asp-action="Create" class="btn btn-secondary">Cria Novo Usuário</a>
<p></p>
<ejs-grid id="Grid" locale="pt-BR" dataSource="@ViewBag.DataSource" allowPaging="true" allowSorting="true" allowFiltering="true" allowGrouping="true" gridLines="Both">

    <e-grid-pagesettings pageSize="15"></e-grid-pagesettings>

    <e-grid-columns>
        <e-grid-column field="Id" headerText="Id" isPrimaryKey="true" isIdentity="true" width="50"></e-grid-column>
        <e-grid-column field="FullName" headerText="Nome" textAlign="Left" width="120"></e-grid-column>
        <e-grid-column field="PhoneNumber" headerText="WhatsApp" width="100"></e-grid-column>
        <e-grid-column field="Email" headerText="E-mail" width="150"></e-grid-column>

        <e-grid-column field="" headerText="Ação" width="130" textAlign="Center" template="#template"></e-grid-column>

    </e-grid-columns>
</ejs-grid>

<script id="template" type="text/x-template">

    <div>
        @*<a rel='nofollow' href="/Pessoas/Edit/${Id}">${NomeCompleto}</a>*@
    <a rel='nofollow'  class="btn btn-sm btn-primary text-white" href="/Admin/Update/${Id}">
                    Atualiza
                </a>
  &nbsp
        @*<a rel='nofollow' href="/Pessoas/Edit/${Id}">${NomeCompleto}</a>*@
    <a rel='nofollow'  class="btn btn-sm btn-warning text-white" href="/Admin/Delete/${Id}">
                    Apaga
                </a>
    </div>
</script>

@*<table class="table table-sm table-bordered">
    <tr>
        <th>ID</th>
        <th>Nome</th>
        <th>Fone</th>
        <th>E-mail</th>
        <th>Update</th>
    </tr>
    @foreach (AppUser user in Model)
    {
        <tr>
            <td>@user.Id</td>
            <td>@user.FullName</td>
            <td>@user.PhoneNumber</td>
            <td>@user.Email</td>
            <td>
                <a class="btn btn-sm btn-primary" asp-action="Update" asp-route-id="@user.Id">
                    Atualiza
                </a>
            </td>
        </tr>
    }
</table>*@

Tela para Apagar Usuário

Não tem tela para apagar e sim uma tela de confirmação, caso acidentalmente seja clicado o botão “Apagar”.

Arquivo Delete.cshtml. Pasta Views-&amp;amp;amp;amp;amp;gt;Admin
@model PoupaTempoDigital.Models.AppUser

@{
    ViewData["Title"] = "Apagando Usuário";
}

<h1>Apaga Usuário</h1>

<h3>Tem certeza que quer apagar este usuário?</h3>
<div>
    <h4>Usuário</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.FullName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.FullName)
        </dd>
    </dl>

    <form asp-action="Delete">
        <input type="hidden" asp-for="Id" />
        <input type="submit" value="Apagar" class="btn btn-danger" /> |
        <a asp-action="Index">Retorno à Lista</a>
    </form>
</div>

Obrigado pela sua leitura. Continue visitando este blog e compartilhe artigos em sua rede de relacionamento. Por favor, se quiser, registre sugestões e comentários aqui no final da página.