O lançamento do .NET 8 pela Microsoft em novembro trouxe todos os tipos de novos recursos excelentes. Uma das melhorias interessantes introduzidas no ASP.NET Core 8 é IExceptionHandler, uma interface que facilita o tratamento de exceções normalmente em aplicativos Web ASP.NET Core.

O tratamento de erros tem uma longa história em linguagens e estruturas de programação. IExceptionHandler simplifica o tratamento de erros, fornecendo um retorno de chamada e um local central para tratar exceções conhecidas. Neste artigo, discutiremos como você pode usar IExceptionHandler em seus aplicativos ASP.NET Core 8 e apresentar respostas de erro significativas ao usuário.

Para usar os exemplos de código fornecidos neste artigo, você deve ter o Visual Studio 2022 instalado em seu sistema. Se ainda não tiver uma cópia, você pode baixar o Visual Studio 2022 aqui.

Crie um projeto de API Web ASP.NET Core no Visual Studio 2022

Para criar um projeto de API Web ASP.NET Core 8 no Visual Studio 2022, siga as etapas descritas abaixo.

  1. Inicie o IDE do Visual Studio 2022.
  2. Clique em “Criar novo projeto”.
  3. Na janela “Criar novo projeto”, selecione “API Web ASP.NET Core” na lista de modelos exibida.
  4. Clique em Avançar.
  5. Na janela “Configure seu novo projeto”, especifique o nome e o local do novo projeto.
  6. Opcionalmente, marque a caixa de seleção “Colocar solução e projeto no mesmo diretório”, dependendo de suas preferências.
  7. Clique em Avançar.
  8. Na janela “Informações adicionais” mostrada a seguir, selecione “.NET 8.0 (Long Term Support)” como a versão da estrutura e certifique-se de que a caixa “Usar controladores” esteja desmarcada. Usaremos APIs mínimas neste projeto.
  9. Em outra parte da janela “Informações adicionais”, deixe o “Tipo de autenticação” definido como “Nenhum” (o padrão) e certifique-se de que as caixas de seleção “Ativar suporte à API aberta”, “Configurar para HTTPS” e “Ativar Docker” permaneçam desmarcadas . Não usaremos nenhum desses recursos aqui.
  10. Clique em Criar.

Usaremos este projeto de API Web do ASP.NET Core para trabalhar com a interface IExceptionHandler nas seções abaixo.

Por que precisamos de um manipulador de exceções?

No ASP.NET Core, um manipulador de exceções é um componente que pode manipular exceções globalmente em um aplicativo. Ele pode capturar todas as exceções não tratadas e gerar respostas de erro apropriadas.

Um manipulador de exceções pode ajudar a implementar um mecanismo centralizado de tratamento de erros, permitindo que seus aplicativos falhem normalmente. Isso permitirá que você garanta que todas as exceções sejam tratadas, que os erros sejam registrados e processados ​​corretamente e que respostas de erro significativas sejam geradas e apresentadas ao usuário.

Vamos entender isso com um exemplo. Considere o seguinte código.

using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using System.Net;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseExceptionHandler(opt => { });
app.MapGet("/GenerateError", () =>
{
    throw new NotImplementedException();
});
app.Run();

Quando você executa o aplicativo e atinge o endpoint /GenerateError, a resposta exibida em seu navegador aparecerá conforme mostrado abaixo.

Figura 1: Resposta gerada sem usar manipuladores de exceção.

Observe que a resposta de erro não está formatada e é bastante difícil ler e compreender os metadados de erro desta resposta.

Apresentando a interface IExceptionHandler

O tratamento de exceções tem sido usado em linguagens de programação há décadas para lidar com erros de tempo de execução em aplicativos. O ASP.NET Core 8 melhora significativamente o tratamento de exceções com a introdução da interface IExceptionHandler. Você pode criar uma classe central para tratamento de exceções no ASP.NET Core 8 implementando a interface IExceptionHandler.

A interface IExceptionHandler se parece com isto:

public interface IExceptionHandler
{
    ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken);
}

A interface IExceptionHandler contém a declaração do método TryHandleAsync. Este método aceita três parâmetros: uma instância do tipo HttpContext, uma Exception e um CancellationToken. Retorna ValueTask para indicar se a exceção foi tratada com sucesso ou não.

Ao implementar o método TryHandleAsync em sua classe que estende a interface IExceptionHandler, você deve retornar um valor booleano desse método. Você deve retornar verdadeiro se a exceção foi tratada ou falso caso contrário.

Crie um manipulador de exceções personalizado no ASP.NET Core

Para implementar a interface IExceptionHandler, crie uma nova classe chamada GlobalExceptionHandler em um arquivo de mesmo nome com extensão .cs. E digite o seguinte código lá.

public class GlobalExceptionHandler(IHostEnvironment hostEnvironment, ILogger<GlobalExceptionHandler> logger)
    : IExceptionHandler
{
    public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
    {
        return true;
    }
}

Sua lógica personalizada de tratamento de erros deve residir no método TryHandleAsync. O trecho de código a seguir mostra como você pode lidar com exceções que ocorrem em seu aplicativo de forma assíncrona.

private const string ExceptionMessage = "An unhandled exception has occurred while executing the request.";

public async ValueTask TryHandleAsync(HttpContext httpContext, exceção de exceção, CancellationToken cancelamentoToken)

{

logger.LogError (exceção, exceção é Exception? exceção.Message: ExceptionMessage);

var problemDetails = CreateProblemDetails(httpContext, exceção);

aguarde httpContext.Response.WriteAsJsonAsync(problemDetails, cancelamentoToken);

retornar verdadeiro;

}

Agora aproveitaremos o middleware ProblemDetails de código aberto para gerar mensagens de erro consistentes, estruturadas e legíveis por máquina. O método CreateProblemDetails retorna uma instância de ProblemDetails contendo os metadados do erro, conforme mostrado no trecho de código fornecido a seguir.

private ProblemDetails CreateProblemDetails(in HttpContext httpContext, in Exception exception)

{

httpContext.Response.ContentType = “aplicativo/json”;

interruptor (exceção)

{

caso NotImplementedException notImplementedException:

httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;

quebrar;

padrão:

httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

quebrar;

}

retornar novos detalhes do problema

{

Status = (int)httpContext.Response.StatusCode,

Tipo = exceção.GetType().Name,

Title = “Ocorreu um erro inesperado”,

Detalhe = exceção.Mensagem,

Instância = $”{httpContext.Request.Method} {httpContext.Request.Path}”

};

}

Código-fonte completo do nosso manipulador de exceções personalizado

A listagem de código a seguir compreende o código-fonte completo da classe GlobalExceptionHandler.

public class GlobalExceptionHandler(IHostEnvironment hostEnvironment, ILogger<GlobalExceptionHandler> logger)

: IExceptionHandler

{

private const string ExceptionMessage = “Ocorreu uma exceção não tratada durante a execução da solicitação.”;

public async ValueTask TryHandleAsync(HttpContext httpContext, exceção de exceção, CancellationToken cancelamentoToken)

{

logger.LogError (exceção, exceção é Exception? exceção.Message: ExceptionMessage);

var problemDetails = CreateProblemDetails(httpContext, exceção);

aguarde httpContext.Response.WriteAsJsonAsync(problemDetails, cancelamentoToken);

retornar verdadeiro;

}

private ProblemDetails CreateProblemDetails (em HttpContext httpContext, em exceção de exceção)

{

httpContext.Response.ContentType = “aplicativo/json”;

interruptor (exceção)

{

caso NotImplementedException notImplementedException:

httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;

quebrar;

padrão:

httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

quebrar;

}

retornar novos detalhes do problema

{

Status = (int)httpContext.Response.StatusCode,

Tipo = exceção.GetType().Name,

Title = “Ocorreu um erro inesperado”,

Detalhe = exceção.Mensagem,

Instância = $”{httpContext.Request.Method} {httpContext.Request.Path}”

};

}

}

Registre o manipulador de exceções com o pipeline de processamento de solicitações

Agora que nosso manipulador de exceção personalizado está pronto, precisamos registrá-lo no pipeline de processamento de solicitações para começar a usá-lo em nosso aplicativo. Você pode registrar o manipulador de exceções incluindo a seguinte linha de código no arquivo Program.cs.

builder.Services.AddExceptionHandler<GlobalExceptionHandler>();

Vamos agora criar um endpoint de tratamento de erros em Program.cs. Quando invocado, esse endpoint exibirá uma resposta de erro amigável, conforme especificado no manipulador de erros configurado. Para criar um endpoint de tratamento de erros, insira o código a seguir no arquivo Program.cs.

app.MapGet("/GenerateError", () =>

{

lançar nova ValidationException();

});

A chamada ao método UseExceptionHandler() configura o pipeline de processamento de solicitações no ASP.NET Core para lidar com exceções usando nosso middleware.

app.UseExceptionHandler(opt => { });

Agora, quando você executa o aplicativo e atinge o endpoint /GenerateError, a resposta exibida no navegador da web aparecerá conforme abaixo. Muito melhor!

iexceptionhandler 02

Figura 2: Resposta de erro gerada usando um manipulador de erros.

É sempre aconselhável implementar seu próprio tratamento de exceções personalizado em seu aplicativo. Isso garante não apenas que a lógica de tratamento de exceções atenda aos seus requisitos, mas também que as respostas de erro sejam geradas no formato desejado. Naturalmente, lançar seu próprio manipulador de exceções também oferece um controle muito mais granular sobre o mecanismo de tratamento de exceções.