ASP.NET Core - Tratando exceções usando Middlewares


  Hoje vamos recordar como realizar o tratamento de exceções usando Middlewares na ASP.NET Core.

O tratamento de exceções constitui a base de aplicações web robustas. Hoje vamos focar na construção de um entendimento básico, estabelecendo as bases para estratégias mais complexas de tratamento de exceções.



Começaremos do zero, criando um projeto limpo de API Web ASP.NET Core. Com a simplicidade em mente, orientarei você na configuração de um ExceptionMiddleware personalizado.

Esse middleware fundamental detectará erros globalmente, fornecendo uma base estável sobre a qual você poderá construir mecanismos de tratamento de exceções mais complexos no futuro.

Seja você um iniciante em busca de um ponto de partida sólido ou um desenvolvedor que deseja reforçar seus conceitos básicos, junte-se a mim enquanto estabelecemos as bases simples para lidar com exceções no ASP.NET Core. Vamos começar.

Criando o Projeto

Para iniciar nosso projeto, começamos criando um projeto de API Web vazio. Usando o terminal, navegue até uma pasta vazia e execute os seguintes comandos:

dotnet new webapi -o ApiExceptionHandling --use-controllers
cd ApiExceptionHandling



Abaixo vemos a estrutura de pastas do nosso projeto criado. Localize e remova os arquivos WeatherForecast.cs e WeatherForecastController.cs. Eles são desnecessários para nossa implementação atual.

Criando o Model

Vamos criar uma pasta Models no projeto e nesta pasta vamos criar a classe ErrorModel que vai atuar como um template para as mensagens de erro.

namespace ApiExceptionHandling.Models
{
    public class ErrorModel
    {
        public int StatusCode { get; set; }
        public string? Message { get; set; }
        public string? Details { get; set; }

        public ErrorModel(int statusCode, string? message, string? details = null)
        {
            StatusCode = statusCode;
            Message = message;
            Details = details;
        }
    }
}

Criando um Middleware customizado

Agora, vamos nos aprofundar no middleware personalizado responsável por lidar com exceções globalmente.

Crie uma pasta Middlewares no projeto e nesta pasta crie um arquivo chamado ExceptionMiddleware.cs. Este middleware intercepta exceções, registra-as e envia uma resposta de erro no formato JSON ao cliente.

using System.Net;
using System.Net.Mime;
using System.Text.Json;using ApiExceptionHandling.Models;

namespace ApiExceptionHandling.Middlewares
{

   public class ExceptionMiddleware   
   {       
      private readonly ILogger<ExceptionMiddleware> _logger;
      private readonly RequestDelegate _next;

   public ExceptionMiddleware(ILogger<ExceptionMiddleware> logger,
                                   RequestDelegate next)       
   {           
       _logger = logger;
      _next = next;       
   }

   public async Task InvokeAsync(HttpContext context)       
   { 
     try       
     { 
         await _next(context);   
     }     
     catch (Exception ex) 
     {     
       _logger.LogError(ex, ex.Message);
       await HandleCustomExceptionResponseAsync(context, ex);     
    } 
  }

  private async Task HandleCustomExceptionResponseAsync(HttpContext context,
                                                       Exception ex)
  { 
    context.Response.ContentType = MediaTypeNames.Application.Json; 
    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;          
    var response = new ErrorModel(context.Response.StatusCode,
                 ex.Message, ex.StackTrace?.ToString());   

    var options = new JsonSerializerOptions
    {
       PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; 

       var json = JsonSerializer.Serialize(response, options);   
        await context.Response.WriteAsync(json);       
    }
}

Vamos entender o código:

A classe ExceptionMiddleware é definida, e usa ILogger<ExceptionMiddleware> e RequestDelegate como parâmetros do construtor. ILogger é usado para registrar exceções e RequestDelegate representa o próximo middleware no pipeline.

O método InvokeAsync é o ponto de entrada do middleware. Ele captura exceções que ocorrem durante a execução de middlewares subsequentes ou do pipeline de tratamento de solicitações.

No bloco try, ele tenta executar o próximo middleware invocando _next(context). Se uma exceção for lançada, ela será capturada no bloco catch. A exceção é registrada usando o criador de logs fornecido (_logger) e o método HandleCustomExceptionResponseAsync é invocado para gerenciar a exceção e enviar uma resposta de erro personalizada.

Este método é responsável por criar e transmitir a resposta de erro personalizada:

Testando a implementação

Agora, vamos implementar um controlador para observar nosso middleware personalizado em ação. Vamos criar um novo arquivo chamado HomeController.cs na pasta Controllers com o seguinte código:

using Microsoft.AspNetCore.Mvc;

namespace ApiExceptionHandling.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class HomeController : ControllerBase     
   {         
      [HttpGet]
       public IActionResult Get() 
      { 
        throw new Exception("Exception em HomeController.");       
      } 
   }
}

Neste HomeController, definimos um método Get() simples que lança intencionalmente uma exceção quando acessado.

Para integrar nosso middleware personalizado ao pipeline do aplicativo, abra o arquivo Program.cs e adicione a seguinte linha:

  app.UseMiddleware<ExceptionMiddleware>();
...

Com esta linha, nosso middleware customizado torna-se parte integrante do fluxo de tratamento de solicitações.

A configuração do nosso projeto está concluída. Para observar o middleware customizado em ação, execute o projeto e teste-o usando o Swagger.

Ao executar o método Get() na UI do Swagger, você pode ver a funcionalidade do middleware em ação.

Após a execução, você receberá uma resposta 500: Internal Server Error contendo o corpo da resposta personalizada criada por nosso ErrorModel.

A resposta inclui o código de status, a mensagem de exceção e os detalhes do rastreamento de pilha, fornecendo informações detalhadas para depuração e diagnóstico. O corpo da resposta pode ser personalizado de acordo com suas necessidades.

Em resumo, exploramos técnicas essenciais de tratamento de exceções do ASP.NET Core, enfatizando a simplicidade e a legibilidade.

Ao implementar um Exception Middleware básico, estabelecemos as bases para um gerenciamento robusto de erros. Esse novo conhecimento garante uma lógica de tratamento de erros mais limpa, reutilizável e sofisticada em todos os seus projetos.

E  estamos conversados...

"E Jesus, respondendo, disse-lhes: Ide, e anunciai a João as coisas que ouvis e vedes:  Os cegos veem, e os coxos andam; os leprosos são limpos, e os surdos ouvem; os mortos são ressuscitados, e aos pobres é anunciado o evangelho. "
Mateus 11:4,5

Referências:


José Carlos Macoratti