ASP.NET Core MVC - Tratamento de erros - I


 Neste artigo vamos rever os conceitos envolvidos no tratamento de erros em aplicações ASP.NET Core MVC.

A ASP.NET Core MVC é um framework usado para desenvolver aplicativos da web com interfaces de usuário interativas. Nesse tipo de aplicação, o tratamento de erros geralmente envolve a exibição de mensagens de erro na interface do usuário para informar o usuário final sobre o problema

As principais diferenças no tratamento de erros em aplicações ASP.NET Core MVC incluem:

  1. Manipulação de erros em controladores: Na ASP.NET Core MVC, você pode usar filtros de ação, como [HandleError], para lidar com exceções lançadas dentro dos controladores. Esses filtros podem redirecionar para uma página de erro personalizada ou exibir uma visualização de erro adequada.
     
  2. Páginas de erro personalizadas: No ASP.NET Core MVC, você pode personalizar páginas de erro para diferentes códigos de status HTTP ou exceções específicas. Isso permite que você exiba uma página de erro personalizada com informações relevantes para os usuários.
     
  3. Tratamento de erros globais: É possível configurar um manipulador de erros global no ASP.NET Core MVC para capturar exceções não tratadas em toda a aplicação. Isso permite que você registre informações detalhadas sobre o erro e forneça uma resposta adequada ao usuário.

A seguir temos o roteiro que vamos seguir para discutir o tratamento de erros em aplicações MVC:

1-  Tratamento de exceções no ambiente de desenvolvimento 
           Abordagem :  UseDeveloperExceptionPage

2 - Tratamento de Exceções em Ambiente de Produção
      Abordagem 1: UseExceptionHandler
            1: Página do Manipulador de Exceções(Exception Handler Page)
            2: Manipulador de Exceções Lambda(Exception Handler Lambda)

       Abordagem 2: UseStatusCodePages
             1: UseStatusCodePages, e com string de formato e com Lambda
             2: UseStatusCodePagesWithRedirects
             3: UseStatusCodePagesWithReExecute

       Abordagem 3: Filtro de exceção
             a- Local
             b- Global

1-  Tratamento de exceções no ambiente de desenvolvimento 

Vamos criar uma aplicação ASP.NET usando o template ASP.NET Core Web App(Model-View-Controller) chamada MvcTrataErros onde teremos a seguinte estrutura após a criação do projeto:

Ao analisar o projeto criado veremos na classe Program o código abaixo:

...
if
(!app.Environment.IsDevelopment())
{
   app.UseExceptionHandler(
"/Home/Error");
   app.UseHsts();
}
...

Este trecho de código registra um manipulador de erros que é chamado quando um erro ocorre na aplicação se o ambiente da aplicação não for o ambiente de desenvolvimento. Neste caso o manipulador de erros redireciona aciona o método Action Error do controlador Home que vai invocar a view Error.cshtml existente na pasta Shared.

O método Action Error do controller possui este código:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
	return View(new ErrorViewModel 
	{
		RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier 
	});
}

Note que estamo usando a anotação [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]:

Essa é uma anotação (atributo) que configura o cache da resposta HTTP para a Action Error(). Nesse caso, Duration = 0 indica que a resposta não deve ser armazenada em cache, Location = ResponseCacheLocation.None especifica que não deve haver um cache intermediário entre o servidor e o cliente, e NoStore = true indica que o cache do navegador também deve ser desabilitado para essa resposta.

O segundo bloco do código, app.UseHsts(), habilita o Strict Transport Security (HSTS) para a aplicação. O HSTS é um recurso de segurança que força os navegadores a se comunicarem com a aplicação apenas usando o protocolo HTTPS.

Durante o desenvolvimento, a ASP.NET Core configura automaticamente a classe DeveloperExceptionPageMiddleware como um dos primeiros middlewares na pipeline de processamento de request. Esse middleware captura exceções não tratadas e exibe informações detalhadas sobre o erro, incluindo uma página de exceção com rastreamento de pilha e outras informações úteis para depuração.

O uso do DeveloperExceptionPageMiddleware no ambiente de desenvolvimento ajuda os desenvolvedores a identificar e corrigir problemas rapidamente, fornecendo uma visão clara dos erros que ocorrem durante a execução da aplicação.

É importante observar que o DeveloperExceptionPageMiddleware não deve ser usado em um ambiente de produção, pois pode expor informações sensíveis sobre a aplicação e apresentar possíveis riscos de segurança. No ambiente de produção, é recomendado configurar um tratamento de erros apropriado, como um middleware de tratamento de erros personalizado ou uma página de erro personalizada, que forneça uma resposta mais controlada e amigável aos usuários finais.

A página de exceção do desenvolvedor mostra rastreamentos de pilha detalhados para erros do servidor e  usa a classe DeveloperExceptionPageMiddleware para capturar exceções síncronas e assíncronas do pipeline HTTP e gerar respostas de erro.

Para mostrar como esta classe atua vamos alterar o código do método Action Index controlador HomeController conforme abaixo:

public IActionResult Index(int? id = null)
{
 
if (id.HasValue)
  {
   
if (id == 1)
    {
      
throw new FileNotFoundException("Arquivo não encontrado,
                     lançou a exceção na view Index.cshtml"
);
    }
   
else if (id == 2)
    {
      
return StatusCode(500);
    }
   }
  
return View();
}

A seguir vamos alterar a view Index.cshtml da pasta Views usando o seguinte código:

@{
  ViewData[
"Title"] = "Home Page";
}


<
br />

<
div class="text-left">
<
p>
<
a href="/PaginaNaoExiste">
  Request para um endpoint que não existe. Dispara um Error 404
</a>.

</p>
  <
p><a href="/home/index/1">Dispara uma exceção</a>.
</
p>
<
p>
  <
a href="/home/index/2">Retorna um erro internod do servidor : Error 500</a>.
</
p>

</
div>

Executando o projeto teremos a seguinte pagina inicial:

Ao clicar no primeiro link teremos o erro 404 pois a pagina /PaginaNaoExiste não existe mesmo:

O código de status 404 pode ser causado por uma variedade de fatores, incluindo:

Se você receber o código de status 404, a primeira coisa que você deve fazer é verificar o URL do recurso. Se a URL estiver correta, você pode tentar novamente mais tarde. Se a URL estiver incorreta, você pode tentar corrigi-lo. Se você ainda não conseguir acessar o recurso, você pode entrar em contato com o suporte técnico para obter ajuda.

Ao clicar no terceiro link teremos o retorno do código de status (StatusCode) 500 indicando um erro interno de servidor:

Ele é retornado quando o servidor não consegue processar uma solicitação devido a um erro inesperado.

O código de status 500 pode ser causado por uma variedade de fatores, incluindo:

Se você receber o código de status 500, a primeira coisa que você deve fazer é verificar os logs do servidor. Os logs podem fornecer informações sobre a causa do erro. Se você não conseguir identificar a causa do erro nos logs, você pode entrar em contato com o suporte do servidor para obter ajuda.

Aqui estão algumas dicas para ajudar a evitar o código de status 500:

Agora clicando no link do meio teremos a exibição da seguinte página:

Esta é a pagina de exceção do desenvolvedor emitida pela classe DeveloperExceptionPageMiddleware que inclui as seguintes informações sobre a exceção e a solicitação :

Podemos assim exibir todas essas informações relacionadas com o request e a exceção gerada. Como exemplo a seguir temos os Headers:

Por este motivo esta abordagem deve ser usada apenas no ambiente de desenvolvimento.

2 - Tratamento de Exceções em Ambiente de Produção

Ao executar a sua aplicação no ambiente de produção já vimos que a classe UseExceptionHandler será usada para realizar o tratamento de erros conforme configuração feita na classe Program:

...
if
(!app.Environment.IsDevelopment())
{
   app.UseExceptionHandler(
"/Home/Error");
   app.UseHsts();
}
...

Agora como saber em qual embiente estamos executando a nossa aplicação MVC ?

O ASP.NET Core configura o comportamento do aplicativo com base no ambiente de tempo de execução determinado no arquivo launchSettings.json presente na pasta Properties do projeto.

Para o nosso projeto ele possui o seguinte código:

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:56239",
      "sslPort": 44359
    }
  },
  "profiles": {
    "http": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "applicationUrl": "http://localhost:5167",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "https": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "applicationUrl": "https://localhost:7230;http://localhost:5167",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

O objetivo principal do launchSettings.json é fornecer uma maneira conveniente de configurar e gerenciar as diferentes configurações de ambiente e opções de execução para a aplicação. Ele é usado principalmente pelo Visual Studio e pelo Visual Studio Code durante o desenvolvimento, mas também pode ser usado em outros ambientes de desenvolvimento integrados (IDEs) ou ao executar a aplicação manualmente por linha de comando.

Através do launchSettings.json, você pode definir perfis de execução, cada um representando uma configuração específica para a execução da aplicação. Cada perfil pode conter várias propriedades, como o ambiente de execução, a porta em que a aplicação será executada, a URL base, configurações de depuração, entre outras.

Alguns perfis comuns definidos no launchSettings.json são:

O arquivo launchSettings.json também contém as seguintes propriedades:

Observe que em environmentVariables temos a definição da variável de ambiente ASPNETCORE_ENVIRONMENT que é usada para especificar o ambiente de execução em uma aplicação ASP.NET Core. Essa variável permite que você defina o ambiente em que a aplicação deve ser executada, como :

Ao definir a variável de ambiente ASPNETCORE_ENVIRONMENT, a aplicação ASP.NET Core usa essa configuração para determinar qual conjunto de configurações aplicar. Por padrão, se a variável não for definida, o ambiente é considerado "Production".

Vamos alterar o valor desta variável para 'Production' no arquivo launchSettings.json :

"ASPNETCORE_ENVIRONMENT": "Production"

Agora o tratamento de erros será feito pela classe UseExceptionHandler e vai acionar o método Action Error do controlador Home que vai invocar a view Error.cshtml existente na pasta Shared.

Executando novamente o projeto e clicando no link do meio para gerar uma exceção teremos o seguinte resultado:

Assim vemos que este middleware de manipulação de exceção :

Observe que não temos aqui a exibição de detalhes dos erros no ambiente de produção. Cabe a você definir como vai notificar os usuários quando da ocorrência de uma exceção.

Para ilustrar podemos redefinir o código definido na classe Program para :

...
if
(!app.Environment.IsDevelopment())
{
  app.UseExceptionHandler(
"/error");
}
}
...

A seguir vamos configurar uma Action no controlador HomeController para responder a rota /error:


[Route(
"/error")]
public IActionResult HandleError() => Problem();
 

Desta forma a Action HandleError envia um payload compatível com a RFC 7807 para o cliente.

O payload Problem contém as seguintes propriedades:

Este recurso é uma maneira útil de fornecer informações adicionais sobre erros que são retornados em respostas HTTP. Ele pode ajudar os usuários a entender e solucionar o problema.

Lembrando que você não deve usar no método Action do manipulador de erro atributos HTTP, como HttpGet pois os verbos explícitos impedem que alguns requests cheguem ao método Action.

E estamos conversados.

"E houve também entre eles contenda, sobre qual deles parecia ser o maior.
E ele lhes disse: Os reis dos gentios dominam sobre eles, e os que têm autoridade sobre eles são chamados benfeitores. Mas não sereis vós assim; antes o maior entre vós seja como o menor; e quem governa como quem serve."
Lucas 22:24-26

Referências:


José Carlos Macoratti