Escolha uma Página
Syncfusion Core: como instalar Identidade e Autorização da Microsoft

Syncfusion Core: como instalar Identidade e Autorização da Microsoft

Antigamente, ao definir um projeto novo em core MVC, você tinha que definir se o projeto teria ou não identificação/autorização. A partir do core 2.1 isso não é mais necessário. Você pode definir isso depois. Melhor ainda: você pode criar um projeto novo com o Syncfusion, com todos componentes que deseja usar –  e depois acrescentar Autenticação e Identidade da Microsoft

Passo 1: Instale o Gerenciador (Painel de Controle) da Syncfusion

Ao instalar, escolha ao menos a opção Syncfusion ASP.NET Core versão JS 2. Importante: pegue sua licença comunitária na Syncfusion antes… Ela serve para todos seus projetos.

Passo 2: Instale as extensões “Syncfusion Visual Studio extension”.

Você pode definir se quer as extensões para o Visual Studio 2017, 2019 ou ambas. Notar que na verdão 2017 as opções do Syncfusion estarão na barra superior, primeiro nível. Já no 2019. procure em Extesões -> Syncfusion. Veja aqui como fazer.

Passo 3: Gere sua aplicação inicial

Duas opções para gerar:

  1. logo syncfusionA partir do Visual Studio: procure o template CORE MVC com a marca da SYNCFUSION (deve ser o último da lista) e gere a aplicação.
  2. A partir do Painel de Controle da Syncfusion. Neste caso, você pode pré-selecionar todos os componentes que pretende usar em seus projetos.

Nos dois casos você pode escolher se quer arquivos auxiliares locais ou em CDN (eu escolhi CDN) e o tipo de tema que vai querer usar (eu escolhi Bootstrap).

Uma vez que você criou o projeto, rode em debug para ver se está OK na máquina local.

Passo 4: Instale a Identidade/Autorização da Microsoft

  • Clique (com o botão da direita) no nome de seu  projeto no Solution Explorer do Visual Studio. Você deverá rodar o Identity Scafolder;  Add > New Scaffolded Item.
  • No painel esquerdo do diálogo Add Scafold,  selecione  Identity > ADD;
  • No Add Identity, selecione as opções que você vai querer usar (tipo págias de deleção, de troca de senha, de confirmação de e-mail, etc – além da página de login);
    • Selecione a página de layout que você já possui (_layout.cshtm, normalmente em Views/Shared). Do contrário, seu arquivo de layout será escrito com códigos incorretos. Por exemplo , escolha ~/Pages/Shared/_Layout.cshtml  se você tiver páginas Razor. Ou <~/Views/Shared/_Layout.cshtml para projetos MVC (seu caso deve ser este)
    • Selecione o botá + para criar uma Classe de Contexto de Dados (Data context class) . O sistema vai sugerir um, use esse. Se não sugerir, defina um,
  • Selecione ADD.

Passo 5: Acertando o arquivo de Layout (_layout.cshtm)

Para poder “ver” os dados de login assim que rodar a aplicação, é fundamental adicionar no seu arquivo  Views/Shared/_Layout.cshtml o login parcial ( (_loginPartial). Para isso terá que inserir <partial name=“_LoginPartial” />. Como a aplicação usa cookies, também terá de inserir <partial name=”_CookieConsentPartial” />. Veja no arquido de exemplo abaixo onde  deve inserir esses comandos. No SEU próprio arquivo, nã mude MAIS NADA a não ser incluir essas duas linha. Veja um exemplo abaixo (não copie todas as linhas para o seu arquivo  Views/Shared/_Layout.cshtml.

Exemplo de c&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;oacute;digo: veja onde inserir as linhas de login parcial e cookies no arquivo _layout.cshtm
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - DiagnosticoCultura</title>

    <environment names="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
        <link rel="stylesheet" href="~/css/site.css" />
    </environment>
    <environment names="Staging,Production">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
              integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
              crossorigin="anonymous" />
        <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    </environment>
  <link rel="stylesheet" href="https://cdn.syncfusion.com/ej2/17.2.34/bootstrap4.css" />

  <script src="https://cdn.syncfusion.com/ej2/17.2.34/dist/ej2.min.js"></script>

</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <a class="navbar-brand" href="#">DiagnosticoCultura</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item active">
                    <a class="nav-link" asp-controller="Home" asp-action="Index">Home<span class="sr-only">(current)</span></a>
                    <!-- <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a> -->
                </li>
                <li class="nav-item">
                    <a class="nav-link" asp-controller="Home" asp-action="About">About</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" asp-controller="Home" asp-action="Contact">Contact</a>

                </li>
            </ul>
            @* https://docs.microsoft.com/pt-br/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-2.2&tabs=visual-studio *@
            <partial name="_LoginPartial" />



            <form class="form-inline my-2 my-lg-0">
                <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
                <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
            </form>
        </div>
    </nav>

    @* https://docs.microsoft.com/pt-br/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-2.2&tabs=visual-studio *@
    <partial name="_CookieConsentPartial" />



    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>© @DateTime.Now.Year - DiagnosticoCultura</p>
        </footer>
    </div>

    <environment names="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/js/site.js" asp-append-version="true"></script>
    </environment>
    <environment names="Staging,Production">
        <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
                crossorigin="anonymous">
        </script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
                integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
                crossorigin="anonymous">
        </script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
                integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
                crossorigin="anonymous">
        </script>
        <script src="~/js/site.min.js" asp-append-version="true"></script>
    </environment>
    <ejs-scripts></ejs-scripts>
    @RenderSection("Scripts", required: false)
</body>
</html>

Você tem muitas outras opções de incluir ou alterar a identidade. Basta conferir no artigo “Scaffold Identity in ASP.NET Core projects” da Microsoft.

Passo 6 – Preparando o banco de dados

Muito provavelmente sua aplicação vai rodar em um servidor externo MYSQL ou MSSQL.

Crie o database em vazio (sem tabelas ainda) e pegue a definição da string de conexão. Exemplo:

“Data Source=EndereçoDoServidor;Initial Catalog=NomeDOBancoDeDados;User Id=NomeDoUsuario;Password=SenhaDoUsuario;”

No arquivo appsetings,json TROQUE a string de conexão gerada na criação do seu aplicativo pela sua string de conexão.

Passo 7 – Fazendo o Scafolding acertar seu banco de dados, criando as tabelas para identificação e autorização

A identidade é essencialmente configurada em Areas/Identity/IdentityHostingStartup.cs. Para mais informações, consulte IHostingStartup.

As tabelas do banco de dados para Identidade e Autorização requerem o uso do Entity Framework Core Migrations. Será necessário fazer uma migração e autulizar o banco de dados. Isso pode ser feito em Tools->NudgePackageManager->PacketManagerConsole (usuamente chamada de PMC). Rode os seguintes comando:

 

Rodando o Scafolding no PMC
Add-Migration CreateIdentitySchema
Update-Database

O parâmetro “CreateIdentitySchema” psrs o comando Add-Migration descreve no nome da migração. Ele é opcional, mas é sempre últil dar um nome para você lembrar depois o que essa migração fez (criou nova tabela, modificou uma tabela, acrescentou um campo, eliminou um campo, etc.

No arquivo startup.cs é fundamental ter certeza que estão as seguintes linhas de configuração da aplicação:

app.UseStaticFiles();

app.UseAuthentication();

A segunda linha é fundamental. Veja abaixo onde encaixar essas 2 linhas:

Incluindo app.UseStaticFiles() e app.UseAuthentication(); no arquivo startup.cs
public class Startup
{

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseAuthentication();
        app.UseMvcWithDefaultRoute();
    }
}

Agora você pode rodar sua aplicação localmente e fazer os ajustes que quiser. Os 2 artigos da referência dão muitas dicas, inclusive para implementar o serviço de e-mail e alterar páginas Razor.

Modificando e ampliando o Gerenciamento de Identidades do ASP.NET Core 2.2 e implementar Componentes Synfusion

Modificando e ampliando o Gerenciamento de Identidades do ASP.NET Core 2.2 e implementar Componentes Synfusion

Vimos no artigo “Como instalar Identidade e Autorização da Microsoft criar um projeto novo com o Syncfusion“, com todos componentes que deseja usar –  e depois como acrescentar Autenticação e Identidade da Microsoft.

Neste artigo veremos como modificar e ampliar o arquivo de “usuários” (“AspNetUsers”) – personalizando-o para ter mais campos e também para usar uma identidade (ID) que seja numérica – e não string.

Passo 1: Instale a sua aplicação Core MVC 2.2

Estou Assumindo que você já tenha um aplicativo da Web com a autenticação do usuário ativada, montada pelo próprio template do Visual Studio 2017 ou 2019. Na geração da aplicação você pode configurá-lo selecionando “Contas de usuário individuais” como o tipo de autenticação ao criar seu projeto.

definindo autenticação e identidade

Ou você pode adicioná-lo manualmente a um aplicativo seguindo as instruções do artigo:

Como instalar Identidade e Autorização da Microsoft criar um projeto novo com o Syncfusion

 

Passo 2: O que pode ser alterado no arquivo de Identidade?

Há duas coisas que não gosto quando a Microsoft instala seu Microsoft Identity Manager – mas devo reconhecer que essa funcionalidade torna realmente fácil adicionar usuários ao seu site ASP.NET Core.

Há sempre duas coisas que quero sempre mudar imediatamente.

A primeira é pedir o nome de um usuário (para inclusive poder usá-lo depois na barra de navegação com um “Olá NOME DE USUÁRIO” (no lugar de “Ola EMAIL DO USUÁRIO”. A segunda é alterar aquela detestável chave primária do usuário em “string”, substituindo-a por valores “inteiros”. – tornando muito mais fácil integrar a tabela de usuários (“ASPNETUSERS”) com as demais tabelas da aplicação. Nos próximos passos deste artigo vamos ver como fazer isso.

No exemplo, ao registra um usuário vou perguntar seu NOME COMPLETO. Na sua implementação você pode optar por perguntar separadamente o nome e sobrenome (e criar, no modelo, um campo só de leitura que junta o nome com o sobrenome, criando o nome completo automaticamente (a vantagem de fazer isso é que a pessoa é sempre obrigada a digitar os 2 campos… Quando você pede somente o nome completo, ela pode desconsiderar e digitar somente o primeiro nome).

Você pode acrescentar quantos campos quiser no padrão de gerenciamento de identidade padrão da Microsoft, que por default utiliza apenas de endereços de e-mail.

Finalmente, como já falei,  gosto de mudar a chave primária de uma string para um inteiro para corresponder ao resto do meu modelo de dados. Costumo preferir números inteiros em GUIDs, e não strings, difíceis de conferir. O pressuposto aqui é que você já tenha um aplicativo da Web com a autenticação do usuário ativada. Ao criá-lo, você pode configurá-lo selecionando Contas de usuário individuais como sendo o tipo de autenticação do projeto. Ou você pode adicioná-lo manualmente a um aplicativo seguindo nossas instruções no artigo do Passo 1.

 

Passo 3: Adicionando novos modelos para Autorização / Identidade

Vamos começar adicionando dois novos modelos ao nosso aplicativo. Nas suas pastas Modelos (Models), crie uma nova classe chamada AppRole que estende o IdentityRole. Isso pode ser usado para atribuir funções especiais a usuários como “admin” e outras categorias de usuários, com papéis diferentes na sua aplicação.

Script para roles a ser colocado na pasta Models
using Microsoft.AspNetCore.Identity;
 
namespace IdentityExample.Models
{
    public class AppRole : IdentityRole<int>
    {
        public AppRole() { }
 
        public AppRole(string name)
        {
            Name = name;
        }
    }
}

Em seguida, adicione uma classe AppUser que amplia o IdentityUser para que possamos coletar mais dados sobre eles (você pode acrescentar mais campos se quiser). Note que o nome completo é montado a partir de nome sobrenome, neste exemplo.

Script para AppUser a ser colocado na pasta Models
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;
 
namespace IdentityExample.Models
{
    public class AppUser : IdentityUser<int>
    {
        [PersonalData, Required, StringLength(20)]
        public string FirstName { get; set; }
 
        [PersonalData, Required, StringLength(20)]
        public string LastName { get; set; }
 
        public string FullName { get { return $"{FirstName} {LastName}"; } }
    }
}

Note que as duas classes são marcadas com <int>.

Isso informa ao Gerenciamento de Identidades que queremos usar chaves primárias baseadas em números inteiros.

O AppUser marca suas propriedades com algumas anotações de dados padrão para tornar o primeiro e o último nome necessários com um comprimento máximo de 20 caracteres. Essas propriedades também são marcadas com PersonalData, ficando disponíveis automaticamente para download e exclusão.

Tornar os dados capazes de serem baixados e excluídos ajuda a atender aos requisitos GDPR. Também foi uma propriedade FullName para formatar de forma consistente o nome completo de um usuário em todo o aplicativo.

Passo 4 – Preparando o banco de dados

Muito provavelmente sua aplicação vai rodar em um servidor externo MYSQL ou MSSQL.

Crie o database em vazio (sem tabelas ainda) e pegue a definição da string de conexão. Exemplo:

“Data Source=EndereçoDoServidor;Initial Catalog=NomeDOBancoDeDados;User Id=NomeDoUsuario;Password=SenhaDoUsuario;”

No arquivo appsetings,json TROQUE a string de conexão gerada na criação do seu aplicativo pela sua string de conexão.

Passo 5 – Atualizando o Context Database (diretório Data, arquivo ApplicationDbContext.cs

O contexto do banco de dados estende IdentityDbContext, precisamos atualizá-lo para informá-lo sobre nossas novas classes e estratégia de chave primária.

 

Arquivo ApplicationDbContext.cs no folder DATA
public class ApplicationDbContext : IdentityDbContext<AppUser, AppRole, int>

Nós também queremos adicionar ou atualizar OnModelCreating para ignorar a propriedade FullName que adicionamos. É uma propriedade computada que não queremos armazenar no banco de dados.

ignorando fullName em ApplicatioBbContext
protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);
    builder.Entity<AppUser>().Ignore(e => e.FullName);
}

Nossa classe de inicialização está fazendo referência à identidade padrão. Precisamos atualizá-lo para nossa identidade personalizada. Antes, no arquivo startup.cs:

Antes em STARTUP.CS
services.AddDefaultIdentity<IdentityUser>()
    .AddDefaultUI(UIFramework.Bootstrap4)
    .AddEntityFrameworkStores<ApplicationDbContext>();

Depois:

Depois em STARTUP.CS
services.AddIdentity<AppUser, AppRole>()
    .AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();

Passo 6 – Atualizando _loginPartial.cshtml

O controle de login na barra de ferramentas também possui referências que precisam ser atualizadas. (Não se esqueça de adicionar uma declaração usando à sua pasta de modelos aqui.)

 

Lembre-se de mudar IdentityExample para o nome so seu NameSpace
@using Microsoft.AspNetCore.Identity
@using IdentityExample.Models
@inject SignInManager<AppUser> SignInManager
@inject UserManager<AppUser> UserManager

Lembre-se não vamos mais mostrar ao usuário seu endereço de e-mail. Em vez disso, vamos mostrar seu nome.

Antes:

Linha que originalmente mostra e-mail no menu superior
Hello @User.Identity.Name!

Depois:

Linha que agora mostra e-mail no menu superior. Poderia ser nome completo (fullname)
Hello @((await UserManager.FindByNameAsync(User.Identity.Name)).FirstName)!

Possível Problema com caracteres alienígenas

Se a linha do navegador apresentar caracteres internacionais com símbolos alienígena, é porque o Visual Studio salvou o arquivo _loginPartial.cshtml em ANSI – e não em UTF-8.

O artigo aqui explica isso. Abra o arquivo _loginPartial.cshtml  com o notepad++  (instale esse editor gratuito que é fantástico, dá de 100 a 0 no notepad da Microsoft). Salve o arquivo com ENCODING UTF-8 e rode a aplicação para ver como “sumiram os caracteres alienígenas”.

Possível Problema com caracteres alienígenas

 

Passo 7 – Substituir páginas de identidade padrão (Default Identity Pages)

  1. Clique com o botão direito no nome do projeto.
  2. Selecione ADD> New Scaffolded Item.
  3. Selecione Identity (Identidade) no menu à esquerda.
  4. Selecione ADD.
  5. Escolha Account/Manage/Index e também a opção Account/Register.
  6. Selecione sua classe de contexto do banco de dados existente.

Isso criará novas páginas para o registro do usuário e a página do perfil do usuário, onde podemos solicitar o primeiro e o último nome.
Nota> se quiser usar o primeiro nome, o ultimo no ou ainda o nome completo, em outras páginas, escolha essas páginas já no item 5, acima,

 

Passo 8 – Arquivo _ValidationScriptsPartial.cshtml

Copie o HTML do seu arquivo atual, localizado na pasta Pages/Shared, para o novo na pasta Areas/Identity/Pages, para ter certeza de que são os mesmos.

 

Passo 9 – Arquivo Register.cshtml.cs

Adicione propriedades de primeiro e último nome à classe InputModel. Se não encontrar o arquivo, é porque se esqueceu de fazer o ADD NEW SCAFFOLDED do passo 7. Este arquivo Fica na pasta Areas;Identity/Pages/Account e dentro de Register.cshtml

 

Acrescentando primeiro e ultimo nome na classe INPUT MODEL
[Required, DataType(DataType.Text), Display(Name = "First Name")]
public string FirstName { get; set; }
 
[Required, DataType(DataType.Text), Display(Name = "Last Name")]
public string LastName { get; set; }

Adicione esses valores de propriedade à declaração de variável do usuário em OnPostAsync

valores de propriedade na declaracao OnPostAsync
var user = new AppUser { UserName = Input.Email, Email = Input.Email, FirstName = Input.FirstName, LastName = Input.LastName };

Passo 10 – Arquivo Register.cshtml

Adicione os campos de entrada para primeiro e último nome, logo abaixo do controle asp-validation-summary.

 

definindo nome e sobrenome para tela de registro
<div class="form-group">
    <label asp-for="Input.FirstName"></label>
    <input asp-for="Input.FirstName" class="form-control" />
    <span asp-validation-for="Input.FirstName" class="text-danger"></span>
</div>
<div class="form-group">
    <label asp-for="Input.LastName"></label>
    <input asp-for="Input.LastName" class="form-control" />
    <span asp-validation-for="Input.LastName" class="text-danger"></span>
</div>

Passo 11 – Arquivo Index.cshtml.cs

Esta é uma classe de fica no folder Areas/Identity/Pages/Account/Manage

Adicione novamente as propriedade first e last name na classe InputModel.

definindo propriedades nome e sobrenome para arquivo index
[Required, DataType(DataType.Text), Display(Name = "First Name")]
public string FirstName { get; set; }
 
[Required, DataType(DataType.Text), Display(Name = "Last Name")]
public string LastName { get; set; }

Atualize a variável Input e OnGetAsync para popular essas propriedades.

atualiza a variavel Input em OnGetAsync
Input = new InputModel
{
    Email = email,
    PhoneNumber = phoneNumber,
    FirstName = user.FirstName,
    LastName = user.LastName
};

Salve as propriedades em OnPostAsync que fica próxima ao final do método.

Mudando OnPostAsync
if (Input.FirstName != user.FirstName) user.FirstName = Input.FirstName;
if (Input.LastName != user.LastName) user.LastName = Input.LastName;
await _userManager.UpdateAsync(user);
 
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Your profile has been updated";
return RedirectToPage();

Passo 12 – Arquivo Index.cshtml

Adicione os campos de entrada para nome e sobrenome abaixo da entrada de e-mail e acima do número de telefone.

Colocando nome e sobrenome no arquivo index
<div class="form-group">
    <label asp-for="Input.FirstName"></label>
    <input asp-for="Input.FirstName" class="form-control" />
    <span asp-validation-for="Input.FirstName" class="text-danger"></span>
</div>
<div class="form-group">
    <label asp-for="Input.LastName"></label>
    <input asp-for="Input.LastName" class="form-control" />
    <span asp-validation-for="Input.LastName" class="text-danger"></span>
</div>

Passo 13 – Atualizando Migrações (Update Migrations)

O próximo passo lógico é atualizar migrações, para criar no banco de dados todas as tabelas de autorização e de identidade (ji usando números inteiros para o ID de pessoas (Users) e regras (Roles). 

Se o seu banco de dados está criado mas está vazio (sem tabelas)

Neste caso minha primeira recomendação é criar um template do seu projeto, para que você possa iniciar novas aplicações já a partir do estágio atingido no passo 12. Para criar o template:

  1. No menu superior dê um BUILD na solução para ter certeza de que não há nenhum erro ou file,
  2. Salve os arquivos todos.
  3. No menu superior, em Project, escolha Export Template. Deixe marcado Export Template e dê um NEXT. e um nome e uma descriçao para o seu temlate, que vai aparecer cada vez que você iniciar um novo projeto

Se eu banco de dados está criado, mas não está vazio (tem tabelas)

Isso significa que você já fez pelo menos alguma migração neste projeto. Se você tentar adicionar uma nova migração, ela falhará devido à alteração da chave primária.

Neste caso, o caminho mais fácil é:

  1. Excluir sua pasta de migração
  2. Excluir seu banco de dados ou recriá-lo (não esqueça de mudar a string de conexão em APPSETTINGS.
  3. Se você não quiser recriar o banco de dados, mas somente limpá-lo completamente, deixando-o sem tabelas, views e procedures, rode no sue MSSQL a rotina proposta abaixo. Abra uma janela de NEW QUERY, copie a rotina e execute.
  4. Depois de recriar ou limpar o banco de dados, você poderá adicionar sua “primeira migração”.
  5. No Console do Gerenciador de Pacotes PMC rode estes dois comandos, um após o outro:
    1. Add-Migration Initial -o Data\Migrations
    2. Update-Database

Agora execute seu aplicativo e você poderá registrar uma conta com nome e sobrenome, ver o nome do usuário na barra de navegação e atualizar o primeiro e último nome. Agora você pode usar a propriedade AppUser.Id em seus próprios modelos para referenciar o registro do usuário como faria com qualquer outro modelo.

 

Rotina para limpar completamente o banco de dados MSSQL
/* Drop all non-system stored procs */
DECLARE @name VARCHAR(128)
DECLARE @SQL VARCHAR(254)

SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'P' AND category = 0 ORDER BY [name])

WHILE @name is not null
BEGIN
    SELECT @SQL = 'DROP PROCEDURE [dbo].[' + RTRIM(@name) +']'
    EXEC (@SQL)
    PRINT 'Dropped Procedure: ' + @name
    SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'P' AND category = 0 AND [name] > @name ORDER BY [name])
END
GO

/* Drop all views */
DECLARE @name VARCHAR(128)
DECLARE @SQL VARCHAR(254)

SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'V' AND category = 0 ORDER BY [name])

WHILE @name IS NOT NULL
BEGIN
    SELECT @SQL = 'DROP VIEW [dbo].[' + RTRIM(@name) +']'
    EXEC (@SQL)
    PRINT 'Dropped View: ' + @name
    SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'V' AND category = 0 AND [name] > @name ORDER BY [name])
END
GO

/* Drop all functions */
DECLARE @name VARCHAR(128)
DECLARE @SQL VARCHAR(254)

SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] IN (N'FN', N'IF', N'TF', N'FS', N'FT') AND category = 0 ORDER BY [name])

WHILE @name IS NOT NULL
BEGIN
    SELECT @SQL = 'DROP FUNCTION [dbo].[' + RTRIM(@name) +']'
    EXEC (@SQL)
    PRINT 'Dropped Function: ' + @name
    SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] IN (N'FN', N'IF', N'TF', N'FS', N'FT') AND category = 0 AND [name] > @name ORDER BY [name])
END
GO

/* Drop all Foreign Key constraints */
DECLARE @name VARCHAR(128)
DECLARE @constraint VARCHAR(254)
DECLARE @SQL VARCHAR(254)

SELECT @name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' ORDER BY TABLE_NAME)

WHILE @name is not null
BEGIN
    SELECT @constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' AND TABLE_NAME = @name ORDER BY CONSTRAINT_NAME)
    WHILE @constraint IS NOT NULL
    BEGIN
        SELECT @SQL = 'ALTER TABLE [dbo].[' + RTRIM(@name) +'] DROP CONSTRAINT [' + RTRIM(@constraint) +']'
        EXEC (@SQL)
        PRINT 'Dropped FK Constraint: ' + @constraint + ' on ' + @name
        SELECT @constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' AND CONSTRAINT_NAME <> @constraint AND TABLE_NAME = @name ORDER BY CONSTRAINT_NAME)
    END
SELECT @name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' ORDER BY TABLE_NAME)
END
GO

/* Drop all Primary Key constraints */
DECLARE @name VARCHAR(128)
DECLARE @constraint VARCHAR(254)
DECLARE @SQL VARCHAR(254)

SELECT @name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'PRIMARY KEY' ORDER BY TABLE_NAME)

WHILE @name IS NOT NULL
BEGIN
    SELECT @constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'PRIMARY KEY' AND TABLE_NAME = @name ORDER BY CONSTRAINT_NAME)
    WHILE @constraint is not null
    BEGIN
        SELECT @SQL = 'ALTER TABLE [dbo].[' + RTRIM(@name) +'] DROP CONSTRAINT [' + RTRIM(@constraint)+']'
        EXEC (@SQL)
        PRINT 'Dropped PK Constraint: ' + @constraint + ' on ' + @name
        SELECT @constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'PRIMARY KEY' AND CONSTRAINT_NAME <> @constraint AND TABLE_NAME = @name ORDER BY CONSTRAINT_NAME)
    END
SELECT @name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'PRIMARY KEY' ORDER BY TABLE_NAME)
END
GO

/* Drop all tables */
DECLARE @name VARCHAR(128)
DECLARE @SQL VARCHAR(254)

SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'U' AND category = 0 ORDER BY [name])

WHILE @name IS NOT NULL
BEGIN
    SELECT @SQL = 'DROP TABLE [dbo].[' + RTRIM(@name) +']'
    EXEC (@SQL)
    PRINT 'Dropped Table: ' + @name
    SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'U' AND category = 0 AND [name] > @name ORDER BY [name])
END
GO

Rodando o script acima, ele vai mostrar que apagou os índices e as tabelas:

Dropped FK Constraint: FK_AspNetRoleClaims_AspNetRoles_RoleId on AspNetRoleClaims
Dropped FK Constraint: FK_AspNetUserClaims_AspNetUsers_UserId on AspNetUserClaims
Dropped FK Constraint: FK_AspNetUserLogins_AspNetUsers_UserId on AspNetUserLogins
Dropped FK Constraint: FK_AspNetUserRoles_AspNetRoles_RoleId on AspNetUserRoles
Dropped FK Constraint: FK_AspNetUserRoles_AspNetUsers_UserId on AspNetUserRoles
Dropped FK Constraint: FK_AspNetUserTokens_AspNetUsers_UserId on AspNetUserTokens
Dropped PK Constraint: PK___EFMigrationsHistory on __EFMigrationsHistory
Dropped PK Constraint: PK_AspNetRoleClaims on AspNetRoleClaims
Dropped PK Constraint: PK_AspNetRoles on AspNetRoles
Dropped PK Constraint: PK_AspNetUserClaims on AspNetUserClaims
Dropped PK Constraint: PK_AspNetUserLogins on AspNetUserLogins
Dropped PK Constraint: PK_AspNetUserRoles on AspNetUserRoles
Dropped PK Constraint: PK_AspNetUsers on AspNetUsers
Dropped PK Constraint: PK_AspNetUserTokens on AspNetUserTokens
Dropped Table: __EFMigrationsHistory
Dropped Table: AspNetRoleClaims
Dropped Table: AspNetRoles
Dropped Table: AspNetUserClaims
Dropped Table: AspNetUserLogins
Dropped Table: AspNetUserRoles
Dropped Table: AspNetUsers
Dropped Table: AspNetUserTokens

Note, abaixo, que as tabelas foram geradas dom IDs com números inteiros!!!

Rodando a aplicação, a tela de entrada contendo à direita os campos de Login e Registro

Tela de Registro, com Nome e Sobrenome:

Tela Inicial, mostrando o nome no lugar do e-mail. E aparece o campo de Logout, no lugar de login.

Instalando o Syncfusion

Instalando os pacotes via NuGet Packages

O NuGet Package Manager pode ser usado para pesquisar e instalar pacotes do NuGet na solução ou no projeto do Visual Studio.

Passo 1 : Clique com o botão direito do mouse no projeto / solução na guia Solution Explorer e escolha Manage NuGet Packages…

 

Alternativamente, clique Tools, menu, NuGet Package Manager | Manage NuGet Packages for Solution…

Passo 2 : Selecione o NuGet.org na lista suspensa Fonte do pacote (Package source drop-down).

Passo 3 : Todos os Pacotes do Syncfusion NuGet estão listados e disponíveis. Pesquise e instale os pacotes necessários em seu aplicativo clicando no botão Instalar..

Os pacotes Syncfusion NuGet estão disponíveis publicamente em NuGet.org desde a versão v16.2.0.46 . Para instalar versões anteriores à 16.2.0.46 , configure Syncfusion URL fees privado.

Em nosso exemplo vamos instalar a versão CORE J2 17.2.0.34.

Os controles do ASP.NET Core UI são criados do zero para serem leves, responsivos, modulares e sensíveis ao toque. Inclui mais de 50 componentes, incluindo grade, gráfico, agendador e muito mais. Saiba mais: https://www.syncfusion.com/aspnet-core-ui-controls Características principais:

  • Suporta o TagHelper e o construtor fluente para inicialização de controle.
  • Suporte de anotação de dados.
  • Temas internos impressionantes, incluindo material, bootstrap, tecido, etc.
  • Conformidade com os padrões de acessibilidade – WCAG 2.0 e Seção 508.

Componentes e controles disponíveis:

GRIDS
DataGrid
Pivot Table
Tree Grid (Preview)
DATA VISUALIZATION
Charts
Stock Chart (Preview)
Circular Gauge
Diagram
Heatmap chart
Linear Gauge
Maps
Range Selector
Smith Chart
Sparkline Charts
TreeMap
EDITORS
Rich Text Editor
Word Processor
Chips (Preview)
CALENDARS
Scheduler
Calendar
DatePicker
DateRangePicker
DateTime Picker
TimePicker
BUTTONS
Button
ButtonGroup
Dropdown Menu
Progress Button
Split Button
DROPDOWNS
AutoComplete
ComboBox
Dropdown List
MultiSelect Dropdown
NAVIGATION
Accordion
Context Menu
Menu Bar
Sidebar
Tabs
Toolbar
TreeView
INPUTS
Form Validator
TextBox
Input Mask
Numeric Textbox
Radio Button
Checkbox
Color Picker
File Upload
Range Slider
Toggle Switch Button
LAYOUT
Avatar
Card
Dialog
ListView
Tooltip
Splitter (Preview)
NOTIFICATIONS
Badge
Toast
FORMS
In-place Editor (Preview)
Viewer
PdfViewer (Preview)

Passo 4: Ajustes Finais

Visual Studio 2017/2019

Abra o arquivo ~/Views/_ViewImports.cshtml e importe o pacote Syncfusion.EJ2. Acrescente:

@addTagHelper *, Syncfusion.EJ2

Adicione os recursos do lado do cliente por meio do CDN ou do pacote npm local no elemento <head> da página de layout ~/Views/Shared/_Layout.cshtml. Acrescente:

acrescentar na ~/Views/Shared/_Layout.cshtml layout page
<head>
    ....
    ....

    <!-- 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>
</head>

Adicione o Essential JS 2 Script Manager no final do elemento <body> na página de layout ~/Views/Shared/_Layout.cshtml.

Acrescentar em body
<body>
    ....
    ....
    <!-- Syncfusion Essential JS 2 ScriptManager -->
    <ejs-scripts></ejs-scripts>
</body>

Agora, adicione os componentes do Syncfusion Essential JS 2 em qualquer página da web (cshtml) na pasta Views.

Por exemplo, o componente de calendário é adicionado na página ~/Views /Home/Index.cshtml.

teste de calendario na view index
<div>
    <ejs-calendar id="calendar"></ejs-calendar>
</div>

Execute o aplicativo. O componente de calendário do Essential JS 2 será renderizado no navegador da web.

Conclusão

Agora você pode salvar o template do projeto considerando o uso dos componentes e controles do Syncfusion. Basta ir in Projec->Export Template. Ao iniciar um novo projeto, poderá desfrutar de inicio de todas as mudanças. Eventualmente, terá de mudar a conexão ao banco de dados (em appsettings), apagar o diretórios migrations e fazer a primeira migração novamente. Mas isso é coisa que tomará ,initos – e não horas. Esperamos que tenha curtido este artigo e – se tiver sugestões ou dúvidas – é só nos enviar,

Syncfusion Core: como instalar Identidade e Autorização da Microsoft

Ajustando os parâmetros de senha no CORE MVC

Ajustando os parâmetros de senha no CORE MVC

Se você define usar o módulo de Autenticação / Identidade, saiba que pode definir com clareza todos os parâmetros para sua aplicação

Você pode definir todos os padrões no arquivo STARTUP.CS. Por padrão, Identity requer que as senhas contenham um caractere maiúsculo, caractere minúsculo, um dígito e um caractere não alfanumérico. As senhas devem ter pelo menos seis caracteres. PasswordOptions pode ser definido em Startup.ConfigureServices.

ARQUIVO STARTUP.CS - NO METODO public void ConfigureServices(IServiceCollection services)//

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            var cultureInfo = new CultureInfo("pt-BR");
            cultureInfo.NumberFormat.CurrencySymbol = "R$";

            CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
            CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;



            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));


            //services.AddDefaultIdentity<IdentityUser>()
            //    .AddDefaultUI(UIFramework.Bootstrap4)
            //    .AddEntityFrameworkStores<ApplicationDbContext>();
            // https://medium.com/@scottkuhl/extending-asp-net-core-2-2-identity-management-c3cc657cc448
            services.AddIdentity<AppUser, AppRole>()
             .AddDefaultUI(UIFramework.Bootstrap4)
             .AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();

//- comece aqui 

            //https://www.c-sharpcorner.com/article/asp-net-core-2-0-user-role-base-menu-management-using-dependency-injection/
            //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(300);
                options.Lockout.MaxFailedAccessAttempts = 10;
                options.Lockout.AllowedForNewUsers = true;

                // User settings  
                options.User.RequireUniqueEmail = true;
            });

//- termine aqui 

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddViewLocalization();
        }

IdentityOptions.Password especifica o PasswordOptions com as propriedades mostradas na tabela. 

Propriedade Descrição Padrão
RequireDigit Requer um número entre 0-9 na senha true
RequiredLength O comprimento mínimo da senha 6
RequireLowercase Requer um caractere minúsculo na senha true
RequireNonAlphanumeric Requer um caractere não alfanumérico na senha. true
RequiredUniqueChars Aplica-se somente ao ASP.NET Core 2.0 ou posterior. Requer o número de caracteres distintos na senha 1
RequireUppercase Requer um caractere maiúsculo na senha true

 

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 ao final da página.

Não consegue executar o SQL Server Database localmente (em debug)?

Não consegue executar o SQL Server Database localmente (em debug)?

As vezes o Visual Studio nos prega umas peças…

Hoje uma aplicação que sempre rodou no localhost, chamando uma base de dados MS SQL no Azure, parou de rodar.

Isso aconteceu assim que migrei a base de dados para um outro servidor SQL, fora do Azure.

Redefini a string de conexão.

No core, a string de conexão não fica no web.config, por motivos de segurança.

Ela é definida no arquivo appsettings.json, onde a conexão “ganha” um nome (como uma variável de ambiente) e é chamada no “start” da aplicação, o arquivo startup.cs.

Chamando o serviço de SQL no startup.cs

using Microsoft.EntityFrameworkCore;
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ContextoDaSuaBaseDeDados>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("SeuApelidoDaConexao")));
}

Guardando string da conexão no Appsettings.json

Para aquivos locais:

{
"ConnectionStrings": {
"SeuApelidoDaConexao": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"
},
}

Para arquivos em servidor SQL  externo:

{
 "ConnectionStrings": {
    //https://www.connectionstrings.com/sql-server/
    //http://stackoverflow.com/questions/1642483/when-using-trusted-connection-true-and-sql-server-authentication-will-this-effe
    "SeuApelidoDaConexao": "Server=NomeDOSeuServidor_sql_ou_IP;Database=NomeDoSeuBancoDeDados;User ID=usuarioDoBancoDeDados;Password=senhaDoUsuario;"
  }
}

Os links acime tem artigos que podem ajudá-lo a tirar dúvidas. São ótimos.

Algumas Dicas adicionais:

  1. Certifique-se que seu usuário tem poder de leitura e escrita no servidor SQL.
  2. O Visual Studio 2015, de alguma forma, memoriza esses dados e, mesmo trocando os dados da conexão, ele continua usando os dados da conexão anterior qwuando você está fazendo debug na máquina local. Se o banco de dados definido anteriormente não existe mais, a aplicação vai dar erro e “abrindo” os dados do erro você vai descobrir que ele está tentando fazer a conexão no endereço antigo. Varri a internet e não descobri onde “limpar” esse “cache”. A única forma que funcionou: troquei o apelido da conexão (no startup.cs e no appsettings.json). Funcionou de primeira. Então a regra é, se mudar a conexão, mude também o apelido e não fique maluco procurando erros.

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 ao final da página.

Gerenciando e tratando Erros (Errors) em Core MVC

Gerenciando e tratando Erros (Errors) em Core MVC

ASP.NET CORE e MVC são diferentes…

O ASP.NET Core continua a ser a principal plataforma subjacente para a construção de aplicativos da Web no .NET Core. Já o MVC ainda é uma estrutura web opcional, que pode ser conectada ao pipeline ASP.NET Core.

É basicamente uma biblioteca NuGet que fica no topo do ASP.NET Core e oferece alguns recursos adicionais para o padrão de design Model-View-Controller.

O que isso significa em termos de tratamento de erros é que qualquer capacidade de gerenciamento de exceção oferecida pela MVC ficará limitada ao MVC. Isso se tornará muito mais evidente quando analisarmos a arquitetura ASP.NET Core.

O middleware do ASP.NET CORE

O ASP.NET Core é completamente modular e o pipeline de solicitações é definida principalmente pelo “middleware” instalado em uma aplicação.

Para entender melhor vamos criar uma nova aplicação MVC e analisar o método

"void Configure (...)"

que fica sempre dentro do arquivo Startup.cs.

public void Configure(
    IApplicationBuilder app,
    IHostingEnvironment env,
    ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseIdentity();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Porque esse é um código muito extenso para um aplicativo web simples, vou cortá-lo para os principais pontos de interesse:

app.UseExceptionHandler("/Home/Error");
app.UseStaticFiles();
app.UseIdentity();
app.UseMvc(routes => ...);

O que você pode ver aqui é essencialmente auto-explicativo, mas há algumas coisas importantes para entender nesse código. As chamadas de método app.Use … () (extensão-) estão ativando vários middlewares registrando-os com o objeto IApplicationBuilder. Cada middleware será responsável por chamar o próximo middleware no pipeline de solicitações. É por isso que a ordem de chamada dos métodos do app.Use … () é importante!

Por exemplo, este é um esqueleto do Static File Middleware:

public StaticFileMiddleware(RequestDelegate next, ...)
{
    // Alguma coisa antes

    _next = next;

    // Alguma coisa deposi}

public Task Invoke(HttpContext context)
{
    // Um monte de código para ver se esse middleware pode
    // servir um arquivo estático que corresponde a uma solicitação HTTP ...

    // Caso contrário, o código chegará a esta linha:/
    return _next(context);
}

Foi cortado algum ruído para destacar o uso da variável RequestDelegate.

Como você pode ver, cada middleware deve aceitar um objeto RequestDelegate no construtor. E cada middleware deve implementar um método de tipo Task Invoke (no contexto HttpContext).

O RequestDelegate é, como seu nome sugere, um delegador que representa o próximo middleware no ciclo de vida da aplicação. ASP.NET Core tem a responsabilidade de invocá-lo, a partir do próprio middleware atual. Por exemplo, se o StaticFileMiddleware não conseguir encontrar um arquivo estático que corresponda à solicitação HTTP recebida, então ele invocará o próximo middleware chamando return _next (context); no fim. Por outro lado, se conseguiu encontrar o arquivo estático solicitado, ele o devolverá ao cliente e nunca mais invocará o próximo ou qualquer middleware subseqüente.

pipeline request - delegate core mvc

É por isso que a ordem do método app.Use … () é importante. Quando você pensa sobre isso, o padrão subjacente de qualquer requisição HTTP pode ser visto em camadas, como se fosse  uma “cebola”:

cebola core mvc de uma requisição HTTP

Uma requisição HTTP viajará do middleware de nível superior até o último middleware, a menos que um middleware no meio possa satisfazer a solicitação e retornar uma resposta HTTP mais cedo para o cliente.

Em contraste, uma exceção não tratada viajaria de baixo para cima. Começando no middleware onde ele foi parado, ela voltaria até o topo do middleware esperando por algo que possa pegá-lo. Em teoria, um middleware também poderia tentar fazer alterações na resposta depois de ter invocado o próximo middleware, mas isso normalmente não é o caso. o que não é aconselhável. Porque isso poderia resultar em uma exceção se o outro middleware já estivesse trabalhando na resposta à requisição HTTP.

O tratamento de erros deve estar no primeiro middleware

Com isso em mente, fica claro que para capturar qualquer exceção não tratada, um erro,  seu gerenciamento  deve ser o primeiro no pipeline do middleware.  Aí, então, se pode garantir uma captura final – se nada mais captar a exceção antes.

Como o MVC normalmente é registrado no final do pipeline do middleware, também é claro que os recursos de gerenciamento de exceção (como os infames ExceptionFilters) dentro do MVC não poderão capturar todas as exceções.

Para mais informações sobre middleware, verifique a documentação oficial sobre middleware.

Manipuladores de Exceção Personalizados (Custom Exception Handlers)

Entendendo melhor o funcionamento do middleware e a necessidade de tratar as exceções. é possível que você crie o seu próprio manipulador de exceção global no ASP.NET Core.

Embora já existam alguns manipuladores de exceção úteis no pacote NuGet Microsoft.AspNetCore.Diagnostics disponível, ainda pode fazer sentido criar o seu próprio. Por exemplo, você pode querer ter um manipulador de exceção que registre exceções críticas no Sentry (que ajuda\ a saber quando os aplicativos brecam, enviando notificações por e-mail, sms, etc.)usando o Raven Client for .NET do Sentry ou pode querer implementar uma integração com uma ferramenta de rastreamento de erros e registrar um novo ticket para cada NullReferenceException que ocorra. Outra opção seria uma integração com o elmah.io (outro monitorador de sites).

Bons motivos para você ter seus próprios Manipuladores de Exceção

Há razões muitas boas pelas quais você  pode querer criar manipuladores de exceção adicionais. Pode ser muito útil ter vários manipuladores de exceções, registrados de uma só vez. Por exemplo, o primeiro manipulador de exceção registra um “ticket” em um sistema de rastreamento de “bugs” (falhas) e relança a exceção original. Em seguida, o próximo manipulador de exceção poderá registrar o erro no ELMAH e reiniciar novamente a exceção original. O manipulador de exceção final pode capturar a exceção e retornar uma página de erro amigável para o cliente.

Ao ter cada manipulador de exceção concentrando-se em uma única atividade, no seu conjunto eles se tornam automaticamente mais reutilizáveis em vários projetos. também é possível usar diferentes combinações de manipuladores em diferentes ambientes (por exemplo, em dev / staging / production).

Um bom exemplo de escrever seu próprio middleware de gerenciamento de exceção é o padrão ExceptionHandlerMiddleware no ASP.NET Core.

Um “gabarito” de um manipulador de exceção “padrão” seria mais ou menos assim:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace SuaAplicacao
{
    public sealed class CustomExceptionHandlerMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        public CustomExceptionHandlerMiddleware(
            RequestDelegate next,
            ILoggerFactory loggerFactory)
        {
            _next = next;
            _logger = loggerFactory.
                    CreateLogger<CustomExceptionHandlerMiddleware>();
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {
                try
                {
                    // Faz alguma coisa
                    // Pode ser algo tão simples como uma chamada para logger.LogError

                    // se você não quer retomar a exceção original
                    // então chame o return:
                    // return;
                }
                catch (Exception ex2)
                {
                    _logger.LogError(
                        0, ex2, 
                        "Uma exeção ocorreu tentando " + 
                        "ao utilizar o manipulador de exceções.");
                }

                // Caso contrário o manipulador irá
                // retentar a exceção original
                throw;
            }
        }
    }
}

Adicionalmente ao RequestDelegate, o construtor também aceita um ILoggerFactory – que pode ser usado para instanciar um novo objeto do ILogger.

No método Task Invoke (HttpContext context), o manipulador de erros basicamente não faz nada além de chamar imediatamente o próximo middleware. Somente se uma exceção for lançada, ela entrará em ação, capturando-a no bloco de captura. O que você colocou no bloco de captura depende de você, mas seria uma boa prática para envolver qualquer código não trivial em um segundo bloco try-catch e padrão de volta ao log básico se todo o resto estiver desmoronando.

Se você tiver dúvidas ou contribuições, deixe-me um comentário abaixo.

Referências:  

  1. Error Handling in ASP.NET Core (Dusted Codes) e 
  2. Error Handling in ASP.NET Core (Microsoft)

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 ao final da página.

Tudo para Marketing Digital numa única Plataforma!

turbine seus negócios online com mais de 20 aplicativos Builderall

Armazenamento seguro de segredos de aplicativos durante o desenvolvimento em Core MVC (app secrets)

Armazenamento seguro de segredos de aplicativos durante o desenvolvimento em Core MVC (app secrets)

Usando a ferramenta “Secret Manager” no Visual Studio 2017

Você pode usar a ferramenta Secret Manager no desenvolvimento de seus aplicativos e com ele manter seus segredos fora do seu código. Assim, você nunca deve armazenar senhas ou outros dados confidenciais no código-fonte. Além do que, você não deve e precisa usar segredos de produção em modo de desenvolvimento e teste (principalmente se usar repositórios públicos).

Você pode configurar o aplicativo para ler esses valores de variáveis de ambiente ou de valores armazenados usando a ferramenta Secret Manager. Com a ferramenta Secret Manager você evita que dados confidenciais sejam verificados nos programas fonte.

Usando Variáveis de Ambiente

Para evitar armazenar segredos de aplicativos em código ou em arquivos de configuração local, você pode armazenar segredos em variáveis de ambiente. Você pode configurar a estrutura de configuração para ler valores de variáveis de ambiente chamando AddEnvironmentVariables. Você pode então usar variáveis de ambiente para substituir valores de configuração para todas as fontes de configuração previamente especificadas.

Exemplo: se você criar um novo aplicativo da Web ASP.NET Core com contas de usuário individuais, ele adicionará uma seqüência de conexão padrão ao arquivo appsettings.json no projeto com a chave DefaultConnection. A cadeia de conexão padrão é inicialmente configurada para usar o LocalDB, que é executado no modo usuário e não requer uma senha. Mas quando você implanta seu aplicativo em um servidor de teste ou produção, você terá que substituir o valor da chave DefaultConnection com uma configuração de variável de ambiente que conterá toda a seqüência de conexão (potencialmente com credenciais sensíveis, como usuário e senha), para que seja acessado o banco de dados no servidor de teste ou de produção.

Cuidado

As variáveis de ambiente geralmente são armazenadas em texto simples e não são criptografadas. Se a máquina ou o processo estiverem comprometidos, então as partes não confiáveis podem acessar as variáveis de ambiente. Medidas adicionais para evitar a divulgação de segredos de usuários ainda podem ser necessárias…

Usando a ferramenta Secret Manager

A ferramenta Secret Manager armazena dados confidenciais para o trabalho de desenvolvimento fora da árvore de arquivos do seu projeto. Que pode ser usada para armazenar segredos no seu  projeto do .NET Core durante o desenvolvimento. Com a ferramenta Secret Manager, você pode associar segredos de aplicativos a um projeto específico e compartilhá-los em vários projetos.

Cuidado

A ferramenta Secret Manager não criptografa os segredos armazenados e não deve ser tratada como uma “depósito” confiável. Serve apenas para fins de desenvolvimento. As chaves e os valores são armazenados em um arquivo de configuração JSON no diretório do perfil do usuário….

Visual Studio 2017: Instalando a ferramenta Secret Manager

Clique com o botão direito do mouse no projeto, dentro da janela Solution Explorer. Selecione Editar <bome_do_projeto> .csproj no menu de contexto. Adicione a linha destacada ao arquivo .csproj e salve para restaurar o pacote NuGet associado (em XML):

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>

  <PropertyGroup>
    <UserSecretsId>My-USER-SECRET-ID-HERE-c23d27a4-eb88</UserSecretsId>
  </PropertyGroup>

  <ItemGroup>
    <Folder Include="wwwroot\" />
  </ItemGroup>
  
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.1" />
  </ItemGroup>

<!-- ACRESCENTAR -->
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="1.0.1" />
  </ItemGroup>
<!-- FIM ACRESCENTAR -->

</Project>

Agora clique com o botão direito do mouse no projeto, na janela do Solution Explorer e selecione Gerenciar Segredos de Usuário (Manage User Secrets),  no menu de contexto. Esta ação adicionará um novo nó UserSecretsId dentro de um PropertyGroup do arquivo .csproj. Ele também abre um arquivo secrets.json no editor de texto. Adicione o seguinte ao secrets.json:

{
    "MeuSegredo": "ValorDoMeuSegredo"
}

Se precisar colocar mais de um “segredo”, separe com vígulas:

{
    "MeuSegredo1": "ValorDoMeuSegredo1",
    "MeuSegredo2": "ValorDoMeuSegredo2",
    "MeuSegredo3": "ValorDoMeuSegredo3"
}

Teste a ferramenta Secret Manager executando o seguinte comando (na CONSOLE):

dotnet user-secrets -h

Você deve estar no mesmo diretório que o arquivo .csproj para executar as ferramentas definidas nos nós DotNetCliToolReference do arquivo .csproj.

A ferramenta Secret Manager exibirá uso, opções e comandos de ajuda:

dotnet user-secrets -h

Usage: dotnet-user-secrets [options] [command]
Options:
  -?|-h|--help  Show help information
  -v|--verbose  Verbose output
Commands:
  clear   Deletes all the application secrets
  list    Lists all the application secrets
  remove  Removes the specified user secret
  set     Sets the user secret to the specified value

A ferramenta Secret Manager opera com parâmetros de configuração específicas de projeto, que ficam armazenados no diretório do seu perfil de usuário. Para usar seus segredos de “usuário”, o projeto deve especificar um valor UserSecretsId dentro do seu arquivo .csproj. O valor de UserSecretsId é arbitrário, mas geralmente é algo exclusivo de um projeto seu. Os desenvolvedores tipicamente geram um GUID para o UserSecretsId.

GUID (ou UUID) é um acrônimo para ‘Globally Unique Identifier’ (ou ‘Universal Unique Identifier’). É um número inteiro de 128 bits usado para identificar recursos. O termo GUID é geralmente usado por desenvolvedores que trabalham com tecnologias da Microsoft. Exemplo de GUID:

4f750817-77be-48ec-90e3-dc7bf92671b2

Você pode gerar on-line um GUID se quiser: Online GUID Generator

Depois disso adicione o UserSecretsId no seu projeto, no arquivo .csproj (em XML):

<PropertyGroup>
  <UserSecretsId>MINHA-ID-DE-USUARIO-SECRETA-AQUI-c23d27a4-eb88</UserSecretsId>
</PropertyGroup>

Se fosse a GUID do nosso exemplo, ficaria:

<PropertyGroup>
  <UserSecretsId>4f750817-77be-48ec-90e3-dc7bf92671b2</UserSecretsId>
</PropertyGroup>

Você pode usar a ferramenta Secret Manager para estabelecer um segredo por linha de comando. Por exemplo, em uma janela de comando no diretório do projeto, digite o seguinte:

dotnet user-secrets set MySecret ValueOfMySecret

Você pode executar a ferramenta Secret Manager de outros diretórios, mas você deve usar a opção –project para passar no caminho para o arquivo .csproj:

dotnet user-secrets set MySecret ValueOfMySecret --project c:\work\WebApp1\src\webapp1

Você também pode usar a ferramenta Secret Manager para listar, remover e limpar segredos de aplicativos.

Acessando seus segredos de usuário por meio do arquivo de configuração do projeto

Você acessa os segredos do Secret Manager através do aruquivo de configuração do sistema (método Startup.cs). Adicione o pacote Microsoft.Extensions.Configuration.UserSecrets e execute, na console,  dotnet restore.

dotnet restore

 

Adicione a fonte de configuração dos segredos de usuário ao método Startup:

Adicione a configuração dos segredos no arquivo Startup.cs, como no exemplo:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace UserSecrets
{
    public class Startup
    {
        string _testSecret = null;
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder();

            if (env.IsDevelopment())
            {
                   //VAI USAR NO AMBIENTE DE DESENVOLVIMENTO
                  builder.AddUserSecrets<Startup>();
            }

            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            //A VARIAVEL PODE SER CHAMADA DE OUTROS FONTES C#
            var _testaSegredo = Configuration["NomeDoMeuSegredo"];
        }

        public void Configure(IApplicationBuilder app)
        {
            var result = string.IsNullOrEmpty(_testaSegredo) ? "Null" : "Not Null";
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync($"Meu segredo é {result}");
            });
        }
    }
}

Note que as variáveis não são usadas em ambiente de produção, etc. Nestes ambientes você pode usar Variáveis de Ambiente ou configurações do Azure que sobrescrevem os arquivos de configuração (web.config ou appsettings.json).

Quando quisermos utilizar os segredos no nosso código, fazemos algo do tipo:

var emailConfig = new EmailConfiguration(Configuration["ContaDeEmail"], Configuration["SenhaDeEmail"]);

ContaDeEmail e SenhaDeEmail são informações que você poder guardar no UserSecrets, no ambiente de desenvolvimento. No ambiente de produção podem estar nas configurações do Azure. Não será necessário mudar seu código pois o ASP.NET Core abstrai isso para você.

Um jeito mais elegante (e  poderoso) de usar as variáveis geradas na Ferramenta Secret Manager

Já vimos que os segredos de usuário são armazenados nos diretórios de aplicação (APPDATA) de cada usuário. Você deve atribuir um userSecretsId exclusivo para cada aplicativo que usa segredos.  Isso é usado para criar um diretório de aplicativos exclusivo no diretório de perfil do usuário que armazenará o arquivo secrets.json contendo segredos de usuário.

local de arquivos secret manager

Esse local varia conforme o sistema operacional:

Windows: %APPDATA%\microsoft\UserSecrets\<usersecretsid>\secrets.json
Linux: ~/.microsoft/usersecrets/<usersecretsid>/secrets.json
Mac: ~/.microsoft/usersecrets/<usersecretsid>/secrets.json

Para criar um arquivo com os dados:

{
  "DataService.PublicKey": "abcd",
  "DataService.PrivateKey": "efgh"
}

Você digita no diretório da aplicação o comando dotnet user-secrets set . que é executado e gera uma linha de informação dando feedback  você se tudo deu certo:

dotnet user-secrets set DataService.PublicKey abcd
info: Successfully saved DataService.PublicKey = abcd to the secret store.

dotnet user-secrets set DataService.PrivateKey efgh
info: Successfully saved DataService.PrivateKey = efgh to the secret store.

Configuração ASP.NET Core e User Secrets

A dependência Microsoft.Extensions.Configuration.UserSecrets adicionada anteriormente adiciona um método de extensão que nos permite chamar AddUserSecrets ao criar a Configuração do núcleo do ASP.NET. Os segredos de dados DataService.PublicKey e DataService.PrivateKey podem ser lidos a partir da configuração e usados para ajudar a configurar o DataService. Você pode fazer tudo isso na classe de Inicialização, no arquivo Startup.cs.

using NomeDaSuaAplicacao.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace NomeDaSuaAplicacao {
    public class Startup {
        public Startup(IHostingEnvironment env) {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath);

            builder.AddUserSecrets();

            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        public void ConfigureServices(IServiceCollection services) {
            services.AddMvc();

            var publicKey = Configuration["DataService.PublicKey"];
            var privateKey = Configuration["DataService.PrivateKey"];

            services.AddTransient<IDataService>(_ => new DataService(publicKey, privateKey));
        }

        public void Configure(IApplicationBuilder app) {
            app.UseMvcWithDefaultRoute();
        }
    }
}

E definir uma classe para esses serviços:

namespace NomeDaSuaAPlicacao.Services {
    public interface IDataService {
        string PublicKey { get; }
    }

    public class DataService : IDataService {
        private readonly string _publicKey;
        private readonly string _privateKey;

        public DataService(string publicKey, string privateKey) {
            _publicKey = publicKey;
            _privateKey = privateKey;
        }

        public string PublicKey { get { return _publicKey; } }
    }
}

Controlador (controller) e Visualizador (view) do ASP.NET Core

Certifique-se de que isso esteja funcionando como esperado, criaando um ASP.NET Core MVC Controller e uma View para exibir a chave pública armazenada no arquivo secrets.json.

Controlador (controller)

using Microsoft.AspNetCore.Mvc;

namespace NomeDaSuaAplicacao.Controllers {
    public class HomeController : Controller {
        public IActionResult Index() {
            return View();
        }
    }
}

Visualizador (view)

@using NomeDaSuaAplicaco.Services
@inject IDataService dataService
<!DOCTYPE html>
<html>
<head>
    <title>Serviço de Leitura de Chave da Aplicação</title>
</head>
<body>
    <h1>Nome da Sua Aplicação - Leitura de Chave Pública</h1>
    <p>Chave Pública: @dataService.PublicKey</p>
</body>
</html>

ASP.NET Core View Injection

Uma outra forma de visualizar os dados: ao invés de injetar o DataService no controlador, injete DataService diretamente no visualizador (view) usando @inject, logo no cabeçalho do arquivo.

@inject IDataService dataService

Uma vez que o serviço é injetado no visualizador, é possível exibir o segredo do usuário em qualquer parte do Razor de qualquer página de Visualização.

<p>Chave Pública: @dataService.PublicKey</p>

Você pode usar esta técnica para mostrar dados que se repetem em vários locais ou mesmo dados em views compartilhadas (shared), como a página _Layout.cshtml, que em geral é o pano de fundo para as demais (ou várias) páginas do sistema. Outro uso para o segredos são as chaves de API (autenticação) de serviços do Google e provedores de e-mail como SendGrid.

Algumas outras formas de implementar são apresentadas nas referências 5 a 8, abaixo. São ótimos artigos com boas dicas de implementação prática.

Se você tiver dúvidas ou contribuições, deixe-me um comentário abaixo.

Referências:  

  1. Safe storage of app secrets during development (MICROSOFT)
  2. Configuration in ASP.NET Core
  3. ASP.NET Core – usando UserSecrets para armazenar informações sensíveis
  4. USER SECRETS IN ASP.NET CORE MVC WEB APPLICATION
  5. User Secrets in ASP.NET Core
  6. User Secrets – Storing sensitive data in ASP.NET Core projects
  7. User Secret management in ASP.NET Core
  8. Working with user secrets in ASP.​NET Core applications

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 ao final da página.

Tudo para Marketing Digital numa única Plataforma!

turbine seus negócios online com mais de 20 aplicativos Builderall