ASP .NET Core 3.1 - Usando o Identity de cabo a rabo - XXIII


Hoje vamos continuar a série de artigos mostrando como usar o ASP .NET Core Identiy na versão 3.1 da ASP .NET .Core e do EF Core.

Continuando a vigésima segunda parte do artigo vamos criar uma página de erros customizada para a nossa aplicação.

Criando uma página de erros customizada

No artigo anterior vimos que ao tentar excluir uma role que possui usuários relacionados teremos um erro com a exibição de uma página mostrada abaixo:

Esta é uma página exibida no ambiente de desenvolvimento pelo middleware app.UseDeveloperExceptionPage() conforme definição do método Configure do arquivo Startup:

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
 {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
      ...
}

O método de extensão UseDeveloperExceptionPage adiciona o middleware ao pipeline de solicitação, que exibe a página de detalhes da exceção amigável ao desenvolvedor. Isso ajuda os desenvolvedores a rastrear erros que ocorrem durante a fase de desenvolvimento.

Como esse middleware exibe informações confidenciais, é recomendável adicioná-las apenas no ambiente de desenvolvimento.

Dessa forma vamos criar outro controlador para lidar com todas as exceções que ocorram em nossa aplicação e vamos exibir mensagens de erro amigáveis ao usuário.

Para isso vamos usar o método de extensão UseExceptionHandler() que nos permite configurar a rota de tratamento de erros personalizada. Isso é útil quando um aplicativo é executado no ambiente de produção.

Vamos então alterar o nosso arquivo Startup conforme abaixo:

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

Definimos no método UseExceptionHandler() a rota para o controlador Error quando estivermos no ambiente de produção.

Assim, se houver uma exceção não tratada, e se o aplicativo estiver sendo executado no ambiente de produção, o usuário será redirecionado para o controlador ErrorController, que exibe a view de erro personalizada.

Vamos então criar na pasta Controllers o controlador ErrorController que vai recuperar os detalhes da exceção e retorna a view de erro personalizada. Em um aplicativo de produção, não exibimos os detalhes da exceção na exibição de erro.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;
namespace FuncionariosWeb.Controllers
{
    public class ErrorController : Controller
    {
        [AllowAnonymous]
        [Route("Error")]
        public IActionResult Error()
        {
            // Retorna detalhes da exceção
            var exceptionHandlerPathFeature =
                    HttpContext.Features.Get<IExceptionHandlerPathFeature>();
            //ViewBag.ExceptionPath = exceptionHandlerPathFeature.Path;
            //ViewBag.ExceptionMessage = exceptionHandlerPathFeature.Error.Message;
            //ViewBag.StackTrace = exceptionHandlerPathFeature.Error.StackTrace;
            return View("Error");
        }
    }
}

No código acima comentamos a exibição de detalhes do erro para o ambiente de produção.

Se desejar exibir esses detalhes o código da nossa view Error deveria ser o seguinte:

<h4>
  Ocorreu um erro durante o processamento da operação
</h4>
<h5>Contate o suporte : suporte@email.com</h5>
<hr />
<h3>Detalhes do erro:</h3>
<div class="alert alert-danger">
    <h5>Caminho</h5>
    <hr />
    <p>@ViewBag.ExceptionPath</p>
</div>
<div class="alert alert-danger">
    <h5>Mensagem</h5>
    <hr />
    <p>@ViewBag.ExceptionMessage</p>
</div>
<div class="alert alert-danger">
    <h5>Stack Trace</h5>
    <hr />
    <p>@ViewBag.StackTrace</p>
</div>

Mas como vamos atuar no ambiente de produção não queremos exibir detalhes do erro e a nossa view vai ter o seguinte código:

<div class="card">
   <div class="card-body">
        <p class="card-text">
            <h2> Ocorreu um erro durante o processamento da operação
                 <br /><br />
                  O time de suporte já esta trabalhando para resolver
              </h2>
         </p>
       <div>
           <br />
            <h3>Para esclarecimentos contacte-nos em :</h3>
         </div>
         <a href="suporte@email.com" class="card-link">suporte@email.com</a>   
    </div>
</div>

Assim agora ao executar o projeto em ambiente de produção iremos obter a seguinte página de erro:

Nota:  Para alterar o ambiente acesse as propriedades do projeto e altere o valor de ASPNETCORE_ENVIRONMENT para Production.

Podemos personalizar mais ainda a nossa página de erro alterando o método Action DeleteRole e inserindo um bloco try/catch e tratando a exceção DbUpdateException e passando o título e a mensagem de erro para a view usando um ViewBag.

        [HttpPost]
        public async Task<IActionResult> DeleteRole(string id)

        {
            var role = await roleManager.FindByIdAsync(id);

            if (role == null)
            {
                ViewBag.ErrorMessage = $"Role com Id = {id} não foi encontrada";
                return View("NotFound");
            }
            else
            {
                try
                {
                    var result = await roleManager.DeleteAsync(role);
                    if (result.Succeeded)
                    {
                        return RedirectToAction("ListRoles");
                    }

                    foreach (var error in result.Errors)
                    {
                        ModelState.AddModelError("", error.Description);
                    }

                    return View("ListRoles");
                }
                // Se a exceção é um DbUpdateException, não podemos deletar
                // a role pois ela contém usuários

                catch (DbUpdateException)
                {
                    // Passamos o ErrorTitle e ErrorMessage para exibir
                    // usando ViewBag.

                    ViewBag.ErrorTitle = $"A role {role.Name} esta em uso";
                    ViewBag.ErrorMessage = $"A role {role.Name} não pode ser deletada pois contém usuários.
Delete os usuários antes de deletar a role";
                    return View("Error");

                }
            }
        }

Aqui vamos definir uma nova view Error.cshtml que vai atuar tanto no ambiente de produção como no ambiente de desenvolvimento.

Para isso vamos criar na pasta Shared a view Error.cshtml com o código abaixo:

@if (ViewBag.ErrorTitle == null)
{
    <h3>
     Ocorreu um erro durante o processamento da sua operação
    </h3>
    <h5>Contate o suporte em : suporte@email.com</h5>
}
else
{
    <h1 class="text-danger">@ViewBag.ErrorTitle</h1>
    <h6 class="text-danger">@ViewBag.ErrorMessage</h6>
}

Agora ao tentar deletar uma role com usuários relacionados iremos obter a seguinte página de erro:

No próximo artigo veremos como gerenciar as roles dos usuários na ASP .NET Core Identity.

"Foge também das paixões da mocidade; e segue a justiça, a fé, o amor, e a paz com os que, com um coração puro, invocam o Senhor."
2 Timóteo 2:22

Referências:


José Carlos Macoratti