IRONSOFTWAREHOME

Tratamento Global de Erros em APIs Mínimas em C#

Global Error Handling in C# Minimal APIs

Tim Corey

13m 30s

Uma API web que lança uma exceção não tratada, por padrão, retornará o tipo de página de erro que ajuda um desenvolvedor a depurar localmente e ajuda um estranho a mapear sua pilha de chamadas. Números de linha, nomes de tipos, e o caminho para o arquivo fonte fluem de volta para quem fez a solicitação. Capturar cada erro no ponto onde ele pode acontecer é a abordagem correta, mas isso só funciona até o próximo try/catch esquecido. Um manipulador global é a rede de segurança que captura o que o ponto de extremidade perdeu.

Em seu vídeo "Tratamento Global de Erros em APIs Mínimas C#", Tim Corey constrói uma pequena API mínima com um ponto de extremidade deliberadamente quebrado, demonstra a página de erro do desenvolvedor que é retornada sem proteção e, em seguida, conecta app.UseExceptionHandler para interceptar qualquer exceção não capturada e responder com um genérico 500. Ele também reforça por que o tratamento no nível do ponto de extremidade continua sendo o caminho preferido: o manipulador global é o recurso, não a estratégia. Qualquer pessoa que esteja enviando uma API mínima e queira ter certeza de que nenhum rastreamento de pilha jamais saia do servidor, encontrará abaixo a configuração do middleware e a lógica de design em torno dela.

Construindo uma API Mínima com um Endpoint Quebrado

[1:08 - 3:01] Tim começa com um novo projeto .NET 8 ASP.NET Core Web API chamado ErrorDemoApp. As opções do modelo de projeto permanecem próximas aos padrões: HTTPS ativado, OpenAPI ativado, sem autenticação, instruções de nível superior deixadas habilitadas, e a caixa de seleção de controladores desmarcada, já que esta é uma API mínima. O Program.cs gerado mantém o Swagger, mas o endpoint de exemplo de previsão do tempo e seu registro são excluídos para que o arquivo mostre apenas o básico.

No lugar do exemplo, ele adiciona um único endpoint em /demo cujo único propósito é falhar:

app.MapGet("/demo", () =>
{
    throw new Exception("This is a demo exception");
});
C#

Executar o projeto com Ctrl+F5 (iniciar sem depuração) impede que o depurador do Visual Studio intercepte o lançamento, então a falha se manifesta da maneira que ocorreria para um chamador HTTP real. O Swagger é aberto, o endpoint /demo é o único disponível, e executá-lo retorna uma resposta 500. O corpo da resposta contém o tipo de exceção, a mensagem e uma referência à linha 18 do Program.cs.

Por que a Página de Erro Padrão Vaza Detalhes de Implementação

[3:01 - 5:00] Acessar /demo diretamente no navegador (sem o wrapper ?message= do Swagger) mostra a página de exceção do desenvolvedor em vez da resposta JSON. A página renderiza o nome da exceção, a mensagem, o caminho do arquivo e o número da linha onde o lançamento ocorreu, os detalhes brutos da exceção e os quadros de pilha acima do lançamento. Para um desenvolvedor trabalhando localmente, isso é ouro. Para qualquer outra pessoa, é um mapa gratuito da base de código.

O ponto de Tim é apresentado sem rodeios: essa página existe para ajudar os desenvolvedores e nunca deve chegar aos usuários finais. O fato de que às vezes chega é o motivo pelo qual um manipulador global é importante. Mesmo as equipes que diligentemente envolvem cada endpoint em tratamento de exceções eventualmente esquecem um, e o custo de esquecer um é o rastreamento completo da pilha indo para quem perguntou.

Capturando Erros Primeiramente no Endpoint

[5:00 - 6:30] Antes de instalar o manipulador global, Tim envolve o endpoint de demonstração em um try/catch para ilustrar o caminho preferido. O manipulador retorna Results.BadRequest(ex.Message) para qualquer exceção lançada:

app.MapGet("/demo", () =>
{
    try
    {
        throw new Exception("This is a demo exception");
    }
    catch (Exception ex)
    {
        return Results.BadRequest(ex.Message);
    }
});
C#

O resultado é um 400 carregando apenas a string de mensagem. Nenhum rastreamento de pilha, nenhum caminho de arquivo, nenhum número de linha. Se a própria mensagem deve ser exposta depende da aplicação; para uma API pública, mesmo a mensagem pode vazar mais do que a equipe deseja, caso em que o manipulador substitui por uma string genérica. O catch local dá ao endpoint controle total sobre o que o chamador vê, incluindo a escolha de retornar um código de status mais específico do que 500 quando o modo de falha é realmente conhecido.

O que este padrão não pode fazer é capturar o que o ponto de extremidade se esqueceu de envolver. Qualquer novo caminho de código, qualquer relançamento de uma camada mais profunda, qualquer Task que lance em um thread separado, todos ignoram o try/catch do ponto de extremidade. Essa é a lacuna que o middleware preenche.

Conectando o Middleware UseExceptionHandler

[6:30 - 10:00] Logo abaixo da linha app.UseHttpsRedirection(), o manipulador é registrado com app.UseExceptionHandler. A sobrecarga que aceita uma ação de construtor expõe o pipeline subjacente, o que permite que o manipulador defina explicitamente a forma da resposta:

app.UseExceptionHandler(appError =>
{
    appError.Run(async context =>
    {
        context.Response.StatusCode = StatusCodes.Status500InternalServerError;
        context.Response.ContentType = "application/json";

        var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
        if (contextFeature is not null)
        {
            Console.WriteLine($"Error: {contextFeature.Error}");
        }

        await context.Response.WriteAsJsonAsync(new
        {
            StatusCode = context.Response.StatusCode,
            Message = "Internal Server Error"
        });
    });
});
C#

Algumas escolhas nesse bloco importam. Forçar o código de status para 500 significa que o chamador não pode inferir nada do número de resposta; seja qual for o tipo de exceção interna, a superfície parece a mesma. Forçar o tipo de conteúdo para application/json combina com o resto das respostas da API, o que mantém os clientes em um único analisador. O IExceptionHandlerFeature expõe a exceção original para que um verdadeiro manipulador possa registrá-la; Tim usa Console.WriteLine aqui como um substituto para qualquer logger que o projeto realmente traria.

A chamada final WriteAsJsonAsync retorna um objeto anônimo com o código de status e a mensagem genérica. O corpo não diz nada sobre o que falhou além do fato de que algo falhou, que é o ponto. Diagnósticos internos pertencem ao log, não à resposta.

Testando os Caminhos Tratados e Não Tratados

[10:00 - 13:14] Com o try/catch ainda no lugar, o endpoint executa o caminho local: Swagger mostra um 400 carregando "Esta é uma exceção de demonstração". O middleware nunca vê o lançamento porque o bloco de catch o resolve primeiro. Este é o design que Tim quer por padrão: manipuladores locais fazem seu trabalho, e o manipulador global está dormente.

Removendo o try/catch e executando novamente exercita o fallback. A mesma solicitação agora retorna um 500 com o corpo JSON { "statusCode": 500, "message": "Internal Server Error" }. Nada na resposta revela onde a exceção foi lançada ou qual tipo era. A janela do console do Visual Studio, no entanto, mostra o texto da exceção original registrado através do placeholder Console.WriteLine, incluindo o caminho do arquivo e número da linha. O diagnóstico completo fica onde os desenvolvedores podem lê-lo; a resposta fica onde não pode vazar.

Este padrão se estende para uma API mínima com middleware de validação, autenticação personalizada ou qualquer outro componente de pipeline. O manipulador de exceções se senta cedo no pipeline e captura o que quer que se propague a partir de um estágio posterior.

Concluindo: Defesa em Profundidade

[13:14 - 13:30] O tratamento local e o tratamento global não são alternativas; são camadas. O manipulador local dá ao endpoint a chance de responder de maneira significativa quando o modo de falha é conhecido. O manipulador global garante que qualquer coisa que a camada local perdeu produza uma resposta que seja consistente, genérica e segura. APIs baseadas em controladores usam a mesma ideia com pequenos ajustes de sintaxe, mas a forma de API mínima é a que vale a pena se acostumar primeiro porque a área de superfície é pequena o suficiente para ver o quadro completo em um único arquivo.

Conclusão

[13:14 - 13:30] Configurar um manipulador de erro global em uma API mínima é feito em três etapas: registrar UseExceptionHandler cedo no pipeline, definir o status da resposta e o tipo de conteúdo dentro do manipulador, e escrever um corpo deliberadamente genérico para que nenhum detalhe de implementação escape. Combine isso com blocos try/catch locais em torno dos caminhos de código mais propensos a falhar, e você tem um modelo de defesa em profundidade onde o manipulador global é a rede de segurança em vez da estratégia.

Dica de Exemplo: Quando o manipulador chama um logger real, passe todo o objeto de exceção (não apenas a mensagem) para que o pipeline de registro estruturado capture o tipo, a pilha e quaisquer exceções internas. Um logger como o Serilog preservará tudo isso como propriedades consultáveis, o que significa que o alerta que dispara para um 500 em produção carrega contexto suficiente para reproduzir localmente sem ninguém executar novamente a solicitação.

Assista ao vídeo completo no canal do YouTube dele e obtenha mais insights sobre como construir APIs mínimas prontas para produção na série 10-Minute Training.

Earn More by Sharing What You Love

Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Let's Stay in Touch!

Join our newsletter, you’ll get exclusive access on article updates. We value your privacy

Key in blue circle

Obtenha sua chave de avaliação gratuita de 30 dias instantaneamente.

Your trial license will be sent to your email address

Sem limitações. 100% desbloqueado. Sem cartão de crédito.

bullet_checkedNão é necessário cartão de crédito nem criação de conta.Sem limitações. 100% desbloqueado. Sem cartão de crédito.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
Agende sua consulta sem compromisso.
Preencha o formulário abaixo ou envie um e-mail para sales@ironsoftware.com
Os seus dados serão sempre mantidos em sigilo.
Aprovado por milhões de engenheiros em todo o mundo.
Logotipos dos clientes da Iron Software
Obtenha sua chave de avaliação gratuita de 30 dias instantaneamente.
Não é necessário cartão de crédito nem criação de conta.