Partilhar via


Como tratar erros em aplicativos de API mínima

Observação

Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Aviso

Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, confira .NET e a Política de Suporte do .NET Core. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Com contribuições de David Acker

Este artigo descreve como tratar erros em aplicativos de API mínima. Para obter informações sobre o tratamento de erros em APIs baseadas em controlador, consulte Manipular erros no ASP.NET Core e Manipular erros em APIs Web baseadas em controlador ASP.NET Core.

Exceções

Em um aplicativo de API mínima, há dois mecanismos centralizados internos diferentes para lidar com exceções sem tratamento:

Esta seção se refere ao aplicativo de amostra a seguir para demonstrar formas de tratar exceções em uma API Mínima. Ele gera uma exceção quando o ponto de extremidade /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();

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 do desenvolvedor é executada no início do pipeline do middleware, para que ele possa capturar exceções sem tratamento geradas no middleware a seguir.

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

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 Ambiente de desenvolvimento. Não compartilhe informações de exceção detalhadas publicamente quando o aplicativo é executado em produção. Para obter mais informações sobre como configurar ambientes, confira Use vários ambientes no ASP.NET Core.

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 algum
  • Cabeçalhos
  • Metadados de ponto de extremidade, se houver

Não existe garantia de que a Página de Exceção do Desenvolvedor fornecerá alguma informação. Use Registro em log para obter informações de erro completas.

A imagem a seguir mostra uma página de exceção do desenvolvedor de exemplo 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:

Manipulador de exceção

Em ambientes de não desenvolvimento, use o Middleware do manipulador de exceções para produzir um conteúdo de erro. Para configurar o Exception Handler Middleware, chame UseExceptionHandler.

Por exemplo, o código a seguir altera o aplicativo para responder com um conteúdo 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

Cogite o aplicativo de API mínima 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 ponto de extremidade /users produz 200 OK com uma representação json do User quando id for maior que 0, caso contrário, produzirá um código de status 400 BAD REQUEST sem um corpo de resposta. Para obter mais informações sobre como criar uma resposta, confira Criar respostas em aplicativos de API mínimo.

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

O exemplo a seguir altera o aplicativo para responder com uma carga compatível com RFC 7807 para o cliente para todas as respostas de cliente e servidor, incluindo erros de roteamento (por exemplo, 404 NOT FOUND). Para obter mais informações, confira 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 abaixo 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 com suporte pelo IProblemDetailsWriter registrado (padrão: application/json):

Aplicativos de API mínima podem ser configurados para gerar uma resposta de detalhes do problema para todas as respostas de erro de cliente e servidor HTTP que ainda não tenham conteúdo de 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, confira Detalhes do problema

Fallback de IProblemDetailsService

No código a seguir, httpContext.Response.WriteAsync("Fallback: An error occurred.") retornará um erro se a implementação IProblemDetailsService não conseguir 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:

O exemplo a seguir é semelhante ao anterior, exceto que ele chama 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);

Este artigo descreve como tratar erros em aplicativos de API mínima.

Exceções

Em um aplicativo de API mínima, há dois mecanismos centralizados internos diferentes para lidar com exceções sem tratamento:

Esta seção se refere ao aplicativo de API mínima a seguir para demonstrar formas de tratar exceções. Ele gera uma exceção quando o ponto de extremidade /exception é solicitado:

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

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

app.Run();

Página de exceção do desenvolvedor

A Página de exceção do desenvolvedor mostra rastreamentos de pilha detalhados para erros de servidor. Ele usa DeveloperExceptionPageMiddleware para capturar exceções síncronas e assíncronas do pipeline HTTP e para gerar respostas de erro.

Os aplicativos ASP.NET Core habilitam a página de exceção do desenvolvedor por padrão quando ocorrerem estes dois pontos:

Para obter mais informações sobre como configurar o middleware, confira Middleware em aplicativos de API mínima.

Ao usar o aplicativo de API mínima anterior, quando a Developer Exception Page detectar uma exceção sem tratamento, será gerada uma resposta de texto sem formatação padrão semelhante ao exemplo a seguir:

HTTP/1.1 500 Internal Server Error
Content-Type: text/plain; charset=utf-8
Date: Thu, 27 Oct 2022 18:00:59 GMT
Server: Kestrel
Transfer-Encoding: chunked

    System.InvalidOperationException: Sample Exception
    at Program.<>c.<<Main>$>b__0_1() in ....:line 17
    at lambda_method2(Closure, Object, HttpContext)
    at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
    --- End of stack trace from previous location ---
    at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
HEADERS
=======
Accept: */*
Connection: keep-alive
Host: localhost:5239
Accept-Encoding: gzip, deflate, br

Aviso

Não habilite a Página de exceção do desenvolvedor a menos que o aplicativo esteja em execução no Ambiente de desenvolvimento. Não compartilhe informações de exceção detalhadas publicamente quando o aplicativo é executado em produção. Para obter mais informações sobre como configurar ambientes, confira Use vários ambientes no ASP.NET Core.

Manipulador de exceção

Em ambientes de não desenvolvimento, use o Middleware do manipulador de exceções para produzir um conteúdo de erro. Para configurar o Exception Handler Middleware, chame UseExceptionHandler.

Por exemplo, o código a seguir altera o aplicativo para responder com um conteúdo compatível com RFC 7807 para o cliente. Para obter mais informações, confira Detalhes do problema.

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

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

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

app.Run();

Respostas de erro de cliente e servidor

Cogite o aplicativo de API mínima a seguir.

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

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

app.Run();

public record User(int Id);

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

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

O exemplo a seguir altera o aplicativo para responder com uma carga compatível com RFC 7807 para o cliente para todas as respostas de cliente e servidor, incluindo erros de roteamento (por exemplo, 404 NOT FOUND). Para obter mais informações, confira 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.Map("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );

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 abaixo 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 com suporte pelo IProblemDetailsWriter registrado (padrão: application/json):

Aplicativos de API mínima podem ser configurados para gerar uma resposta de detalhes do problema para todas as respostas de erro de cliente e servidor HTTP que ainda não tenham um conteúdo de 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.Map("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );

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

app.Run();

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