Manipular erros em APIs de ASP.NET Core

Observação

Esta não é a versão mais recente deste artigo. Para ver a versão atual, consulte a versão .NET 10 deste artigo.

Aviso

Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte o .NET e .NET Core Support Policy. Para ver a versão atual, consulte a versão .NET 10 deste artigo.

Este artigo descreve como lidar com erros em APIs de ASP.NET Core. A documentação para APIs Mínimas está selecionada. Para ver a documentação das APIs baseadas em controlador, selecione a guia Controllers. Para diretrizes de tratamento de erros, consulte Blazor.

Página de Exceção do Desenvolvedor

A Página de exceção do desenvolvedor exibe informações detalhadas sobre as exceções de solicitação não tratadas. Ele usa DeveloperExceptionPageMiddleware para capturar exceções síncronas e assíncronas do pipeline HTTP e para gerar respostas de erro. A página de exceção de desenvolvedor é executada no início do pipeline do middleware, para que ela possa capturar exceções sem tratamento lançadas no middleware subsequente.

Os aplicativos ASP.NET Core habilitam a página de exceção do desenvolvedor por padrão quando ambos estão presentes:

Os aplicativos criados usando modelos anteriores, ou seja, usando WebHost.CreateDefaultBuilder, podem ativar a página de exceção do desenvolvedor chamando app.UseDeveloperExceptionPage.

Aviso

Não habilite a Página de Exceção do Desenvolvedor, a menos que o aplicativo esteja em execução no Development ambiente. Não compartilhe informações detalhadas de exceção publicamente quando o aplicativo for executado em produção. Para obter mais informações sobre como configurar ambientes, consulte ASP.NET Core ambientes de runtime.

A Página de Exceção do Desenvolvedor pode incluir as seguintes informações sobre a exceção e a solicitação:

  • Rastreamento de pilha
  • Parâmetros de cadeia de caracteres de consulta, se houver
  • Cookies, se houver
  • Headers
  • Metadados de ponto de extremidade, se houver

A Página de Exceção do Desenvolvedor não tem garantia de fornecer nenhuma informação. Use Registro em log para obter informações de erro completas.

A imagem a seguir mostra um exemplo de página de exceção de desenvolvedor, com animação para mostrar as guias e as informações exibidas.

Página de exceção do desenvolvedor animada para mostrar cada guia selecionada.

Em resposta a uma solicitação com um cabeçalho Accept: text/plain, a Página de Exceção do Desenvolvedor retorna texto sem formatação em vez de HTML. Por exemplo:

Status: 500 Internal Server Error
Time: 9.39 msSize: 480 bytes
FormattedRawHeadersRequest
Body
text/plain; charset=utf-8, 480 bytes
System.InvalidOperationException: Sample Exception
   at WebApplicationMinimal.Program.<>c.<Main>b__0_0() in C:\Source\WebApplicationMinimal\Program.cs:line 12
   at lambda_method1(Closure, Object, HttpContext)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

HEADERS
=======
Accept: text/plain
Host: localhost:7267
traceparent: 00-0eab195ea19d07b90a46cd7d6bf2f

Para ver a página de exceção do desenvolvedor em uma API mínima:

Esta seção refere-se ao aplicativo de exemplo a seguir para demonstrar maneiras de lidar com exceções em uma API Mínima. Ele lança uma exceção quando o endpoint /exception é solicitado:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/exception", () => 
{
    throw new InvalidOperationException("Sample Exception");
});

app.MapGet("/", () => "Test by calling /exception");

app.Run();

Manipulador de exceção

Em ambientes que não são de desenvolvimento, use o middleware de tratamento de exceções para gerar uma carga de erro.

Para configurar a exception handler middleware, chame UseExceptionHandler. Por exemplo, o código a seguir altera o aplicativo para responder com uma carga compatível com RFC 7807 para o cliente. Para obter mais informações, consulte a seção Detalhes do Problema mais adiante neste artigo.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseExceptionHandler(exceptionHandlerApp 
    => exceptionHandlerApp.Run(async context 
        => await Results.Problem()
                     .ExecuteAsync(context)));

app.MapGet("/exception", () => 
{
    throw new InvalidOperationException("Sample Exception");
});

app.MapGet("/", () => "Test by calling /exception");

app.Run();

Respostas de erro de cliente e servidor

Considere o aplicativo de API Mínimo a seguir.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)));

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

O /users endpoint produz 200 OK com uma representação json de User quando id é maior que 0; caso contrário, 400 BAD REQUEST código de status sem corpo de resposta. Para obter mais informações sobre como criar uma resposta, consulte Criar respostas em aplicativos de API mínimos.

O Status Code Pages middleware pode ser configurado para produzir um conteúdo comum para o corpo, quando vazio, para todas as respostas de cliente HTTP (400-499) ou de servidor (500 -599). O middleware é configurado chamando o método de extensão UseStatusCodePages .

Por exemplo, o exemplo a seguir altera o aplicativo para responder ao cliente com uma carga útil compatível com RFC 7807 para todas as respostas de cliente e de servidor, incluindo erros de roteamento (por exemplo, 404 NOT FOUND). Para obter mais informações, consulte a seção Detalhes do Problema .

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseStatusCodePages(async statusCodeContext 
    => await Results.Problem(statusCode: statusCodeContext.HttpContext.Response.StatusCode)
                 .ExecuteAsync(statusCodeContext.HttpContext));

app.MapGet("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

Detalhes do problema

Os Detalhes do problema não são o único formato de resposta a descrever um erro de API HTTP; no entanto, eles geralmente são usados para relatar erros para APIs HTTP.

O serviço de detalhes do problema implementa a interface IProblemDetailsService, que dá suporte à criação de detalhes do problema no ASP.NET Core. O método de extensão AddProblemDetails(IServiceCollection) em IServiceCollection regista a implementação IProblemDetailsService padrão.

Em aplicativos ASP.NET Core, o middleware a seguir gera respostas HTTP de detalhes do problema quando AddProblemDetails é chamado, exceto quando o cabeçalho HTTP da solicitação Accept não inclui um dos tipos de conteúdo compatíveis com o IProblemDetailsWriter registrado (padrão: application/json):

Aplicativos de API mínima podem ser configurados para gerar respostas com detalhes do problema para todas as respostas de erro HTTP do cliente e do servidor que ainda não têm corpo, usando o método de extensão AddProblemDetails.

O código a seguir configura o aplicativo para gerar detalhes do problema:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

app.MapGet("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)));

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

Para obter mais informações sobre como usar AddProblemDetails, consulte Detalhes do problema

Fallback IProblemDetailsService

No código a seguir, httpContext.Response.WriteAsync("Fallback: An error occurred.") retornará um erro se a IProblemDetailsService implementação não for capaz de gerar um ProblemDetails:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler(exceptionHandlerApp =>
{
    exceptionHandlerApp.Run(async httpContext =>
    {
        var pds = httpContext.RequestServices.GetService<IProblemDetailsService>();
        if (pds == null
            || !await pds.TryWriteAsync(new() { HttpContext = httpContext }))
        {
            // Fallback behavior
            await httpContext.Response.WriteAsync("Fallback: An error occurred.");
        }
    });
});

app.MapGet("/exception", () =>
{
    throw new InvalidOperationException("Sample Exception");
});

app.MapGet("/", () => "Test by calling /exception");

app.Run();

O código anterior:

  • Grava uma mensagem de erro com o código de fallback se o problemDetailsService não conseguir gravar um ProblemDetails. Por exemplo, um ponto de extremidade em que o cabeçalho de solicitação Accept especifica um tipo de mídia ao qual o DefaultProblemDetailsWriter não oferece suporte.
  • Usa o middleware de tratamento de exceções.

Observação

O DefaultProblemDetailsWriter suporta os seguintes tipos de mídia no cabeçalho de solicitação Accept:

  • application/json
  • application/problem+json
  • Tipos curinga, como */* e application/*

Tipos de mídia não JSON, como application/xml ou text/html, não têm suporte e disparam o comportamento de fallback.

O exemplo a seguir é semelhante ao anterior, exceto pelo fato de chamar o Status Code Pages middleware.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseStatusCodePages(statusCodeHandlerApp =>
{
    statusCodeHandlerApp.Run(async httpContext =>
    {
        var pds = httpContext.RequestServices.GetService<IProblemDetailsService>();
        if (pds == null
            || !await pds.TryWriteAsync(new() { HttpContext = httpContext }))
        {
            // Fallback behavior
            await httpContext.Response.WriteAsync("Fallback: An error occurred.");
        }
    });
});

app.MapGet("/users/{id:int}", (int id) =>
{
    return id <= 0 ? Results.BadRequest() : Results.Ok(new User(id));
});

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

Recursos adicionais de tratamento de erros

Migração de controladores para APIs mínimas

Se você estiver migrando de APIs baseadas em controlador para APIs mínimas:

  1. Substitua filtros de ação por filtros de endpoint ou middleware
  2. Substituir validação de modelo por validação manual ou associação personalizada
  3. Substituir filtros de exceção por middleware de tratamento de exceções
  4. Configurar detalhes do problema usando AddProblemDetails() para respostas de erro consistentes

Quando usar o tratamento de erros baseado em controlador

Considere APIs baseadas em controlador se precisar.

  • Cenários complexos de validação de modelo
  • Tratamento centralizado de exceções entre vários controladores
  • Controle refinado sobre a formatação de resposta de erro
  • Integração com recursos do MVC, como filtros e convenções

Para obter informações detalhadas sobre o tratamento de erros baseados em controlador, incluindo erros de validação, personalização de detalhes do problema e filtros de exceção, consulte as seções da guia Controladores .

Recursos adicionais