Middleware global de exceções
ResumoEm vez de
try/catchespalhados por cada ação, centralizamos o tratamento de erros num único ponto usandoUseExceptionHandlere retornamos respostas padronizadas comProblemDetails(RFC 7807/9457).
1. Objetivos de Aprendizagem#
- Configurar tratamento global de exceções.
- Retornar respostas de erro padronizadas com
ProblemDetails. - Diferenciar detalhes de erro entre Development e Production.
2. Pré-requisitos#
- Serviço de logging (Módulo 16) e endpoints GET (Módulo 18).
3. Conceito#
Tratamento global significa que qualquer exceção não capturada em qualquer camada é interceptada por um único middleware. O porquê: consistência (todo erro sai no mesmo formato), zero repetição e um único lugar para logar. O padrão ProblemDetails define um corpo JSON padronizado para erros HTTP.
O ASP.NET Core oferece duas abordagens modernas:
UseExceptionHandler(...)com um handler inline, ouIExceptionHandler(a partir do .NET 8) registrado comAddExceptionHandler.
4. Mão na Massa#
4.1. Setup#
Nenhum pacote extra.
4.2. Implementação Passo a Passo#
- Habilite
ProblemDetailse umIExceptionHandler:
builder.Services.AddProblemDetails();
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
- Implemente o handler:
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
public sealed class GlobalExceptionHandler : IExceptionHandler
{
private readonly ILoggerManager _logger;
public GlobalExceptionHandler(ILoggerManager logger) => _logger = logger;
public async ValueTask<bool> TryHandleAsync(
HttpContext context, Exception exception, CancellationToken ct)
{
_logger.LogError($"Erro não tratado: {exception.Message}");
var (status, title) = exception switch
{
NotFoundException => (StatusCodes.Status404NotFound, "Recurso não encontrado"),
_ => (StatusCodes.Status500InternalServerError, "Erro interno do servidor")
};
context.Response.StatusCode = status;
await context.Response.WriteAsJsonAsync(new ProblemDetails
{
Status = status,
Title = title,
Detail = "Ocorreu um erro ao processar a requisição.",
Instance = context.Request.Path
}, ct);
return true;
}
}
- Registre no pipeline (bem no início):
app.UseExceptionHandler();
4.3. Executando#
Lance uma exceção proposital numa ação e observe a resposta JSON 500 padronizada, com o erro registrado no log.
5. Exemplo Completo#
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
builder.Services.ConfigureLoggerService();
builder.Services.AddControllers();
var app = builder.Build();
app.UseExceptionHandler();
app.MapControllers();
app.Run();
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
Centralizar o tratamento em um IExceptionHandler | try/catch repetido em cada ação |
| Mapear exceções de negócio para status codes específicos | Retornar sempre 500 para tudo |
| Registrar o middleware de erro como o primeiro do pipeline | Colocá-lo depois do roteamento |
Atençãonunca devolva
exception.ToString()ou a stack trace ao cliente em produção — isso vaza detalhes internos e caminhos do servidor.
7. Segurança e Produção#
- Em Development,
UseDeveloperExceptionPage()pode mostrar detalhes; em Production, apenas mensagens genéricas. - Logue a exceção completa internamente, mas exponha ao cliente somente
title/detailseguros. - Correlacione erros com um
traceId(oProblemDetailsdo ASP.NET Core já inclui um) para diagnóstico sem vazar dados.
8. Exercícios#
- Fácil: adicione o
traceIdao corpo doProblemDetails. - Médio: crie uma
NotFoundExceptionde domínio e mapeie-a para404. - Desafio: implemente um segundo
IExceptionHandlerpara exceções de validação e ordene a cadeia de handlers.
9. Resumo#
Centralizamos o tratamento de erros com IExceptionHandler + ProblemDetails, mapeando exceções de domínio para status codes e protegendo detalhes internos em produção.
10. Próximos Passos#
Módulo 20: recurso único, requisições inválidas e relacionamentos pai/filho.
11. Referências#
- Microsoft Learn — Tratar erros em APIs web
- Microsoft Learn — IExceptionHandler
- Microsoft Learn — ProblemDetails e RFC 9457