Tratar erros no ASP.NET Core

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.

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.

Por Tom Dykstra

Este artigo apresenta abordagens comuns para o tratamento de erros em aplicativos Web ASP.NET Core. Confira também Tratar erros em APIs da Web do ASP.NET Core e Tratar erros em aplicativos de API Mínima.

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. Os aplicativos ASP.NET Core habilitam a página de exceção do desenvolvedor por padrão quando ambos:

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.

As informações detalhadas da exceção não devem ser exibidas publicamente quando o aplicativo é executado no ambiente de 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
  • Cabeçalhos
  • Metadados de ponto de extremidade, se houver

A imagem a seguir mostra uma página de exceção de desenvolvedor de amostra com Roteamento selecionado e metadados de ponto de extremidade exibidos:

Página de exceção do desenvolvedor com Roteamento selecionado e metadados de ponto de extremidade exibidos

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.

Página do Manipulador de exceção

Para configurar uma página personalizada de tratamento de erros para o Ambiente de produção, chame UseExceptionHandler. Este middleware de tratamento de exceções:

  • Captura e registra exceções sem tratamento.
  • Executa novamente a solicitação em um pipeline alternativo usando o caminho indicado. A solicitação não será executada novamente se a resposta tiver sido iniciada. O código gerado pelo modelo executa novamente a solicitação usando o caminho /Error.

Aviso

Se o pipeline alternativo gerar uma exceção própria, o Middleware de Tratamento de Exceções aumentará a exceção original.

Como esse middleware pode executar novamente o pipeline de solicitação:

  • Os middlewares precisam lidar com a reinserção da mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar _next ou armazenar em cache seu processamento no HttpContext para evitar retrabalho. Ao lidar com o corpo da solicitação, isso significa armazenar em buffer ou em cache os resultados do tipo do leitor de formulário.
  • Para a sobrecarga UseExceptionHandler(IApplicationBuilder, String) usada em modelos, somente o caminho da solicitação é modificado, e os dados de rota são limpos. Os dados de solicitação, como cabeçalhos, método e itens, são todos reutilizados no estado em que se encontram.
  • Os serviços com escopo permanecem os mesmos.

O exemplo abaixo, UseExceptionHandler inclui o middleware de manipulação de exceções em ambientes que não são de desenvolvimento:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

O modelo de aplicativo Razor Pages fornece uma Página de erro (.cshtml) e uma classe PageModel (ErrorModel) na pasta Pages. Para um aplicativo MVC, o modelo de projeto inclui um método de ação Error e uma Exibição de erro para o controlador Home.

O middleware de tratamento de exceção executa novamente a solicitação usando o método HTTP original. Se um ponto de extremidade do manipulador de erros estiver restrito a um conjunto específico de métodos HTTP, ele será executado somente para esses métodos HTTP. Por exemplo, uma ação do controlador MVC que usa o atributo [HttpGet] é executada apenas para solicitações GET. Para garantir que todas as solicitações cheguem à página de tratamento de erros personalizada, não as restrinja a um conjunto específico de métodos HTTP.

Para lidar com exceções de forma diferente com base no método HTTP original:

  • Para Razor Pages, crie vários métodos de manipulador. Por exemplo, use OnGet para lidar com exceções GET e use OnPost para lidar com exceções POST.
  • Para MVC, aplique atributos de verbo HTTP a várias ações. Por exemplo, use [HttpGet] para lidar com exceções GET e use [HttpPost] para lidar com exceções POST.

Para permitir que usuários não autenticados exibam a página de tratamento de erros personalizada, verifique se ela dá suporte ao acesso anônimo.

Acessar a exceção

Use IExceptionHandlerPathFeature para acessar a exceção e o caminho de solicitação original em um manipulador de erros. O exemplo a seguir usa IExceptionHandlerPathFeature para obter mais informações sobre a exceção gerada:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
    public string? RequestId { get; set; }

    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    public string? ExceptionMessage { get; set; }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
            HttpContext.Features.Get<IExceptionHandlerPathFeature>();

        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "The file was not found.";
        }

        if (exceptionHandlerPathFeature?.Path == "/")
        {
            ExceptionMessage ??= string.Empty;
            ExceptionMessage += " Page: Home.";
        }
    }
}

Aviso

Não forneça informações de erro confidenciais aos clientes. Fornecer erros é um risco à segurança.

Lambda do Manipulador de exceção

Uma alternativa a uma página personalizada de manipulador de exceção é fornecer um lambda a UseExceptionHandler. Usar um lambda permite acessar o erro antes de retornar a resposta.

O código abaixo usa um lambda para tratamento de exceções:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            // using static System.Net.Mime.MediaTypeNames;
            context.Response.ContentType = Text.Plain;

            await context.Response.WriteAsync("An exception was thrown.");

            var exceptionHandlerPathFeature =
                context.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
            {
                await context.Response.WriteAsync(" The file was not found.");
            }

            if (exceptionHandlerPathFeature?.Path == "/")
            {
                await context.Response.WriteAsync(" Page: Home.");
            }
        });
    });

    app.UseHsts();
}

Aviso

Não forneça informações de erro confidenciais aos clientes. Fornecer erros é um risco à segurança.

IExceptionHandler

IExceptionHandler é uma interface que dá ao desenvolvedor um retorno de chamada para tratar exceções conhecidas em um local central.

As implementações IExceptionHandler são registradas ao chamar IServiceCollection.AddExceptionHandler<T>. O tempo de vida de uma instância IExceptionHandler é singleton. Várias implementações podem ser adicionadas e são chamadas na ordem registrada.

Se um manipulador de exceção tratar uma solicitação, ele poderá retornar true para interromper o processamento. Se uma exceção não for tratada por nenhum manipulador de exceção, o controle retornará ao comportamento padrão e às opções do middleware. Diferentes métricas e logs são emitidos para exceções tratadas versus exceções sem tratamento.

O exemplo a seguir mostra uma implementação de IExceptionHandler:

using Microsoft.AspNetCore.Diagnostics;

namespace ErrorHandlingSample
{
    public class CustomExceptionHandler : IExceptionHandler
    {
        private readonly ILogger<CustomExceptionHandler> logger;
        public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
        {
            this.logger = logger;
        }
        public ValueTask<bool> TryHandleAsync(
            HttpContext httpContext,
            Exception exception,
            CancellationToken cancellationToken)
        {
            var exceptionMessage = exception.Message;
            logger.LogError(
                "Error Message: {exceptionMessage}, Time of occurrence {time}",
                exceptionMessage, DateTime.UtcNow);
            // Return false to continue with the default behavior
            // - or - return true to signal that this exception is handled
            return ValueTask.FromResult(false);
        }
    }
}

O exemplo a seguir mostra como registrar uma implementação de IExceptionHandler para injeção de dependência:

using ErrorHandlingSample;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
builder.Services.AddExceptionHandler<CustomExceptionHandler>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// Remaining Program.cs code omitted for brevity

Quando o código anterior é executado no ambiente de desenvolvimento:

  • O CustomExceptionHandler é chamado primeiro para tratar uma exceção.
  • Depois de registrar em log uma exceção, o método TryHandleException retorna false, portanto, a página de exceção do desenvolvedor é mostrada.

Em outros ambientes:

  • O CustomExceptionHandler é chamado primeiro para tratar uma exceção.
  • Depois de registrar em log uma exceção, o método TryHandleException retorna false, portanto, a /Errorpágina é mostrada.

UseStatusCodePages

Por padrão, o aplicativo ASP.NET Core não fornece uma página de código de status para códigos de status de erro HTTP, como 404 – Não Encontrado. Quando o aplicativo define um código de status de erro HTTP 400-599 sem um corpo, ele retorna o código de status e um corpo de resposta vazio. Para habilitar os manipuladores padrão somente texto para os códigos de status de erros comuns, chame UseStatusCodePages em Program.cs:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages();

Chame UseStatusCodePages antes da solicitação de tratamento de middleware. Por exemplo, chame UseStatusCodePages antes do Middleware de Arquivo Estático e do Middleware de Pontos de Extremidade.

Quando UseStatusCodePages não for usado, a navegação até uma URL sem um ponto de extremidade retornará uma mensagem de erro dependente do navegador indicando que o ponto de extremidade não pode ser encontrado. Quando UseStatusCodePages é chamado, o navegador retorna a seguinte resposta:

Status Code: 404; Not Found

UseStatusCodePages normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

Observação

O middleware de páginas de código de status não captura exceções. Para fornecer uma página personalizada de tratamento de erros, use a página do manipulador de exceção.

UseStatusCodePages com cadeia de caracteres de formato

Para personalizar o texto e o tipo de conteúdo de resposta, use uma sobrecarga de UseStatusCodePages que leva um tipo de conteúdo e uma cadeia de caracteres de formato:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// using static System.Net.Mime.MediaTypeNames;
app.UseStatusCodePages(Text.Plain, "Status Code Page: {0}");

No código anterior, {0} é um espaço reservado para o código de erro.

UseStatusCodePages com uma cadeia de caracteres de formato normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

UseStatusCodePages com lambda

Para especificar a manipulação de erro personalizada e o código de gravação de resposta, use a sobrecarga de UseStatusCodePages que leva uma expressão lambda:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages(async statusCodeContext =>
{
    // using static System.Net.Mime.MediaTypeNames;
    statusCodeContext.HttpContext.Response.ContentType = Text.Plain;

    await statusCodeContext.HttpContext.Response.WriteAsync(
        $"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});

UseStatusCodePages com um lambda normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

UseStatusCodePagesWithRedirects

O método de extensão UseStatusCodePagesWithRedirects:

  • Envia um código de status 302 – Encontrado ao cliente.
  • Redireciona o cliente para o ponto de extremidade de tratamento de erros fornecido no modelo de URL. O ponto de extremidade de tratamento de erro normalmente exibe informações de erro e retorna HTTP 200.
var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");

O modelo de URL pode incluir um espaço reservado {0} para o código de status, conforme mostrado no código anterior. Se o modelo de URL começar com um til (~), o ~ será substituído pelo PathBase do aplicativo. Ao especificar um ponto de extremidade no aplicativo, crie uma exibição de MVC ou Razor Page para o ponto de extremidade.

Este método normalmente é usado quando o aplicativo:

  • Deveria redirecionar o cliente para um terminal diferente, geralmente em situações nas quais um aplicativo diferente processa o erro. Para aplicativos Web, a barra de endereços do navegador do cliente reflete o ponto de extremidade redirecionado.
  • Não deveria preservar e retornar o código de status original com a resposta de redirecionamento inicial.

UseStatusCodePagesWithReExecute

O método de extensão UseStatusCodePagesWithReExecute:

  • Gera o corpo da resposta ao executar novamente o pipeline de solicitação por meio de um caminho alternativo.
  • Não altera o código de status antes ou depois de executar novamente o pipeline.

A nova execução do pipeline pode alterar o código de status da resposta, pois o novo pipeline tem controle total sobre o código de status. Se o novo pipeline não alterar o código de status, o código de status original será enviado ao cliente.

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");

Se um ponto de extremidade dentro do aplicativo for especificado, crie uma exibição de MVC ou Razor Page para o ponto de extremidade.

Este método normalmente é usado quando o aplicativo tem que:

  • Processar a solicitação sem redirecionar para um ponto de extremidade diferente. Para aplicativos Web, a barra de endereços do navegador do cliente reflete o ponto de extremidade originalmente solicitado.
  • Preservar e retornar o código de status original com a resposta.

O modelo de URL precisa começar com / e pode incluir um espaço reservado {0} para o código de status. Para transmitir o código de status como um parâmetro de cadeia de caracteres de consulta, transmita um segundo argumento a UseStatusCodePagesWithReExecute. Por exemplo:

var app = builder.Build();  
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");

O ponto de extremidade que processa o erro pode obter a URL original que gerou o erro, conforme mostrado no exemplo a seguir:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
    public int OriginalStatusCode { get; set; }

    public string? OriginalPathAndQuery { get; set; }

    public void OnGet(int statusCode)
    {
        OriginalStatusCode = statusCode;

        var statusCodeReExecuteFeature =
            HttpContext.Features.Get<IStatusCodeReExecuteFeature>();

        if (statusCodeReExecuteFeature is not null)
        {
            OriginalPathAndQuery = string.Join(
                statusCodeReExecuteFeature.OriginalPathBase,
                statusCodeReExecuteFeature.OriginalPath,
                statusCodeReExecuteFeature.OriginalQueryString);
        }
    }
}

Como esse middleware pode executar novamente o pipeline de solicitação:

  • Os middlewares precisam lidar com a reinserção da mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar _next ou armazenar em cache seu processamento no HttpContext para evitar retrabalho. Ao lidar com o corpo da solicitação, isso significa armazenar em buffer ou em cache os resultados do tipo do leitor de formulário.
  • Os serviços com escopo permanecem os mesmos.

Desabilitar páginas de código de status

Para desabilitar as páginas de código de status de um método de ação ou controlador MVC, use o atributo [SkipStatusCodePages].

Para desabilitar as páginas de código de status de solicitações específicas em um método manipulador Razor Pages ou em um controlador MVC, use IStatusCodePagesFeature:

public void OnGet()
{
    var statusCodePagesFeature =
        HttpContext.Features.Get<IStatusCodePagesFeature>();

    if (statusCodePagesFeature is not null)
    {
        statusCodePagesFeature.Enabled = false;
    }
}

Código de tratamento de exceção

Código em páginas de tratamento de exceção também pode gerar exceções. As páginas de erro de produção devem ser testadas minuciosamente, e tome cuidado extra para evitar gerar exceções próprias.

Cabeçalhos de resposta

Depois que os cabeçalhos de resposta são enviados:

  • O aplicativo já não consegue alterar o código de status da resposta.
  • As páginas de exceção ou manipuladores não podem ser executados. A resposta deve ser concluída ou a conexão será anulada.

Tratamento de exceções do servidor

Além da lógica de tratamento de exceção no aplicativo, a implementação do servidor HTTP pode lidar com algumas exceções. Se o servidor capturar uma exceção antes que os cabeçalhos de resposta sejam enviados, o servidor enviará uma resposta 500 - Internal Server Error sem um corpo de resposta. Se o servidor capturar uma exceção depois que os cabeçalhos de resposta forem enviados, o servidor fechará a conexão. As solicitações que não são manipuladas pelo aplicativo são manipuladas pelo servidor. Qualquer exceção que ocorrer quando o servidor estiver tratando a solicitação será tratada pelo tratamento de exceção do servidor. As páginas de erro personalizadas do aplicativo, o middleware de tratamento de exceção e os filtros configurados não afetam esse comportamento.

Tratamento de exceção na inicialização

Apenas a camada de hospedagem pode tratar exceções que ocorrem durante a inicialização do aplicativo. O host pode ser configurado para capturar erros de inicialização e capturar erros detalhados.

A camada de hospedagem só poderá mostrar uma página de erro para um erro de inicialização capturado se o erro ocorrer após a associação de endereço do host/porta. Se a associação falhar:

  • A camada de hospedagem registrará uma exceção crítica.
  • O processo dotnet falhará.
  • Nenhuma página de erro é exibida quando o servidor HTTP é Kestrel.

Quando executado no IIS (ou no Serviço de Aplicativo do Azure) ou no IIS Express, um erro 502.5 Falha no Processo será retornado pelo Módulo do ASP.NET Core se o processo não puder ser iniciado. Para obter mais informações, consulte Solução de problemas do ASP.NET Core no Serviço de Aplicativo do Azure e no IIS.

Página de erro do banco de dados

O filtro de exceção de página do desenvolvedor de banco de dados AddDatabaseDeveloperPageExceptionFilter captura exceções relacionadas ao banco de dados que podem ser resolvidas usando migrações do Entity Framework Core. Quando estas exceções ocorrem, é gerada uma resposta HTML com detalhes das ações possíveis para resolver o problema. Esta página só fica habilitada no Ambiente de desenvolvimento. O código abaixo adiciona o filtro de exceção de página do desenvolvedor de banco de dados:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();

Filtros de exceção

Em aplicativos MVC, os filtros de exceção podem ser configurados globalmente, por controlador ou por ação. Em aplicativos do Razor Pages, eles podem ser configurados globalmente ou por modelo de página. Esses filtros tratam qualquer exceção sem tratamento ocorrida durante a execução de uma ação do controlador ou de outro filtro. Para obter mais informações, confira Filtros no ASP.NET Core.

Os filtros de exceção são úteis para interceptar exceções que ocorrem em ações do MVC, mas não são tão flexíveis quanto o middleware de tratamento de exceção, UseExceptionHandler. Recomendamos usar UseExceptionHandler, a não ser que você precise fazer o tratamento de erro de forma diferente com base na ação de MVC escolhida.

Erros de estado do modelo

Confira informações sobre como lidar com erros de estado de modelo em model binding e Validação de modelos.

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 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):

O código a seguir configura o aplicativo para gerar uma resposta de detalhes do problema para todas as respostas de erro de servidor e cliente HTTP que ainda não têm um conteúdo do corpo:

builder.Services.AddProblemDetails();

var app = builder.Build();        

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
    app.UseHsts();
}

app.UseStatusCodePages();

A próxima seção mostra como personalizar o corpo da resposta de detalhes do problema.

Personalizar detalhes do problema

A criação automática de ProblemDetails pode ser personalizada usando qualquer uma das seguintes opções:

  1. Usar ProblemDetailsOptions.CustomizeProblemDetails
  2. Usar um IProblemDetailsWriter personalizado
  3. Chamar o IProblemDetailsService em um middleware

Operação CustomizeProblemDetails

Os detalhes do problema gerados podem ser personalizados com o uso de CustomizeProblemDetails e as personalizações são aplicadas a todos os detalhes do problema gerados automaticamente.

O código abaixo usa ProblemDetailsOptions para definir CustomizeProblemDetails:

builder.Services.AddProblemDetails(options =>
    options.CustomizeProblemDetails = ctx =>
            ctx.ProblemDetails.Extensions.Add("nodeId", Environment.MachineName));

var app = builder.Build();        

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
    app.UseHsts();
}

app.UseStatusCodePages();

Por exemplo, um resultado de ponto de extremidade HTTP Status 400 Bad Request produz o seguinte corpo da resposta de detalhes do problema:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "Bad Request",
  "status": 400,
  "nodeId": "my-machine-name"
}

IProblemDetailsWriter personalizado

Uma implementação IProblemDetailsWriter pode ser criada para personalizações avançadas.

public class SampleProblemDetailsWriter : IProblemDetailsWriter
{
    // Indicates that only responses with StatusCode == 400
    // are handled by this writer. All others are
    // handled by different registered writers if available.
    public bool CanWrite(ProblemDetailsContext context)
        => context.HttpContext.Response.StatusCode == 400;

    public ValueTask WriteAsync(ProblemDetailsContext context)
    {
        // Additional customizations.

        // Write to the response.
        var response = context.HttpContext.Response;
        return new ValueTask(response.WriteAsJsonAsync(context.ProblemDetails));
    }
}

Observação: ao usar um IProblemDetailsWriter personalizado, o IProblemDetailsWriter personalizado deve ser registrado antes de chamar AddRazorPages, AddControllers, AddControllersWithViews ou AddMvc:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<IProblemDetailsWriter, SampleProblemDetailsWriter>();

var app = builder.Build();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsWriter>() is
            { } problemDetailsService)
        {

            if (problemDetailsService.CanWrite(new ProblemDetailsContext() { HttpContext = context }))
            {
                (string Detail, string Type) details = mathErrorFeature.MathError switch
                {
                    MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                        "https://en.wikipedia.org/wiki/Division_by_zero"),
                    _ => ("Negative or complex numbers are not valid input.",
                        "https://en.wikipedia.org/wiki/Square_root")
                };

                await problemDetailsService.WriteAsync(new ProblemDetailsContext
                {
                    HttpContext = context,
                    ProblemDetails =
                    {
                        Title = "Bad Input",
                        Detail = details.Detail,
                        Type = details.Type
                    }
                });
            }
        }
    }
});

// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
    if (denominator == 0)
    {
        var errorType = new MathErrorFeature
        {
            MathError = MathErrorType.DivisionByZeroError
        };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(numerator / denominator);
});

// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
    if (radicand < 0)
    {
        var errorType = new MathErrorFeature
        {
            MathError = MathErrorType.NegativeRadicandError
        };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(Math.Sqrt(radicand));
});

app.Run();

Detalhes do problema do Middleware

Uma abordagem alternativa ao uso de ProblemDetailsOptions com CustomizeProblemDetails é definir o ProblemDetails no middleware. Uma resposta de detalhes do problema pode ser gravada chamando IProblemDetailsService.WriteAsync:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStatusCodePages();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsService>() is
                                                           { } problemDetailsService)
        {
            (string Detail, string Type) details = mathErrorFeature.MathError switch
            {
                MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                "https://en.wikipedia.org/wiki/Division_by_zero"),
                _ => ("Negative or complex numbers are not valid input.", 
                "https://en.wikipedia.org/wiki/Square_root")
            };

            await problemDetailsService.WriteAsync(new ProblemDetailsContext
            {
                HttpContext = context,
                ProblemDetails =
                {
                    Title = "Bad Input",
                    Detail = details.Detail,
                    Type = details.Type
                }
            });
        }
    }
});

// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
    if (denominator == 0)
    {
        var errorType = new MathErrorFeature { MathError =
                                               MathErrorType.DivisionByZeroError };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(numerator / denominator);
});

// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
    if (radicand < 0)
    {
        var errorType = new MathErrorFeature { MathError =
                                               MathErrorType.NegativeRadicandError };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(Math.Sqrt(radicand));
});

app.MapControllers();

app.Run();

No código anterior, os pontos de extremidade da API mínima /divide e /squareroot retornam a resposta do problema personalizada esperada na entrada do erro.

Os pontos de extremidade do controlador de API retornam a resposta do problema padrão na entrada do erro, não a resposta do problema personalizada. A resposta do problema padrão é retornada porque o controlador de API gravou no fluxo de resposta, Detalhes do problema de códigos de status de erro, antes de IProblemDetailsService.WriteAsync ser chamado, e a resposta não é gravada novamente.

O ValuesController a seguir retorna BadRequestResult, que grava no fluxo de resposta, ou seja, impede que a resposta do problema personalizada seja retornada.

[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // /api/values/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Math.Sqrt(radicand));
    }

}

O Values3Controller abaixo retorna ControllerBase.Problem para que o resultado do problema personalizado esperado seja retornado:

[Route("api/[controller]/[action]")]
[ApiController]
public class Values3Controller : ControllerBase
{
    // /api/values3/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Divison by zero is not defined.",
                type: "https://en.wikipedia.org/wiki/Division_by_zero",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values3/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Negative or complex numbers are not valid input.",
                type: "https://en.wikipedia.org/wiki/Square_root",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

        return Ok(Math.Sqrt(radicand));
    }

}

Produzir um conteúdo ProblemDetails para exceções

Considere o seguinte aplicativo:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

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

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.MapControllers();
app.Run();

Em ambientes que não são de desenvolvimento, quando ocorre uma exceção, o que vemos abaixo é uma resposta ProblemDetails padrão que é retornada ao cliente:

{
"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title":"An error occurred while processing your request.",
"status":500,"traceId":"00-b644<snip>-00"
}

Para a maioria dos aplicativos, o código anterior é tudo o que é necessário para exceções. No entanto, a seção a seguir mostra como obter respostas de problema mais detalhadas.

Uma alternativa a uma página personalizada de manipulador de exceção é fornecer um lambda a UseExceptionHandler. O uso de um lambda permite o acesso ao erro e à gravação de uma resposta de detalhes do problema com IProblemDetailsService.WriteAsync:

using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

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

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            context.Response.ContentType = Text.Plain;

            var title = "Bad Input";
            var detail = "Invalid input";
            var type = "https://errors.example.com/badInput";

            if (context.RequestServices.GetService<IProblemDetailsService>() is
                { } problemDetailsService)
            {
                var exceptionHandlerFeature =
               context.Features.Get<IExceptionHandlerFeature>();

                var exceptionType = exceptionHandlerFeature?.Error;
                if (exceptionType != null &&
                   exceptionType.Message.Contains("infinity"))
                {
                    title = "Argument exception";
                    detail = "Invalid input";
                    type = "https://errors.example.com/argumentException";
                }

                await problemDetailsService.WriteAsync(new ProblemDetailsContext
                {
                    HttpContext = context,
                    ProblemDetails =
                {
                    Title = title,
                    Detail = detail,
                    Type = type
                }
                });
            }
        });
    });
}

app.MapControllers();
app.Run();

Aviso

Não forneça informações de erro confidenciais aos clientes. Fornecer erros é um risco à segurança.

Uma abordagem alternativa para gerar detalhes do problema é o uso do pacote NuGet de terceiros Hellang.Middleware.ProblemDetails que pode ser usado a fim de mapear exceções e erros do cliente para detalhes do problema.

Recursos adicionais

Por Tom Dykstra

Este artigo apresenta abordagens comuns para o tratamento de erros em aplicativos Web ASP.NET Core. Confira também Tratar erros em APIs da Web do ASP.NET Core e Tratar erros em aplicativos de API Mínima.

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. Os aplicativos ASP.NET Core habilitam a página de exceção do desenvolvedor por padrão quando ambos:

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.

As informações detalhadas da exceção não devem ser exibidas publicamente quando o aplicativo é executado no ambiente de 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
  • Cabeçalhos

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.

Página do Manipulador de exceção

Para configurar uma página personalizada de tratamento de erros para o Ambiente de produção, chame UseExceptionHandler. Este middleware de tratamento de exceções:

  • Captura e registra exceções sem tratamento.
  • Executa novamente a solicitação em um pipeline alternativo usando o caminho indicado. A solicitação não será executada novamente se a resposta tiver sido iniciada. O código gerado pelo modelo executa novamente a solicitação usando o caminho /Error.

Aviso

Se o pipeline alternativo gerar uma exceção própria, o Middleware de Tratamento de Exceções aumentará a exceção original.

Como esse middleware pode executar novamente o pipeline de solicitação:

  • Os middlewares precisam lidar com a reinserção da mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar _next ou armazenar em cache seu processamento no HttpContext para evitar retrabalho. Ao lidar com o corpo da solicitação, isso significa armazenar em buffer ou em cache os resultados do tipo do leitor de formulário.
  • Para a sobrecarga UseExceptionHandler(IApplicationBuilder, String) usada em modelos, somente o caminho da solicitação é modificado, e os dados de rota são limpos. Os dados de solicitação, como cabeçalhos, método e itens, são todos reutilizados no estado em que se encontram.
  • Os serviços com escopo permanecem os mesmos.

O exemplo abaixo, UseExceptionHandler inclui o middleware de manipulação de exceções em ambientes que não são de desenvolvimento:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

O modelo de aplicativo Razor Pages fornece uma Página de erro (.cshtml) e uma classe PageModel (ErrorModel) na pasta Pages. Para um aplicativo MVC, o modelo de projeto inclui um método de ação Error e uma Exibição de erro para o controlador Home.

O middleware de tratamento de exceção executa novamente a solicitação usando o método HTTP original. Se um ponto de extremidade do manipulador de erros estiver restrito a um conjunto específico de métodos HTTP, ele será executado somente para esses métodos HTTP. Por exemplo, uma ação do controlador MVC que usa o atributo [HttpGet] é executada apenas para solicitações GET. Para garantir que todas as solicitações cheguem à página de tratamento de erros personalizada, não as restrinja a um conjunto específico de métodos HTTP.

Para lidar com exceções de forma diferente com base no método HTTP original:

  • Para Razor Pages, crie vários métodos de manipulador. Por exemplo, use OnGet para lidar com exceções GET e use OnPost para lidar com exceções POST.
  • Para MVC, aplique atributos de verbo HTTP a várias ações. Por exemplo, use [HttpGet] para lidar com exceções GET e use [HttpPost] para lidar com exceções POST.

Para permitir que usuários não autenticados exibam a página de tratamento de erros personalizada, verifique se ela dá suporte ao acesso anônimo.

Acessar a exceção

Use IExceptionHandlerPathFeature para acessar a exceção e o caminho de solicitação original em um manipulador de erros. O exemplo a seguir usa IExceptionHandlerPathFeature para obter mais informações sobre a exceção gerada:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
    public string? RequestId { get; set; }

    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    public string? ExceptionMessage { get; set; }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
            HttpContext.Features.Get<IExceptionHandlerPathFeature>();

        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "The file was not found.";
        }

        if (exceptionHandlerPathFeature?.Path == "/")
        {
            ExceptionMessage ??= string.Empty;
            ExceptionMessage += " Page: Home.";
        }
    }
}

Aviso

Não forneça informações de erro confidenciais aos clientes. Fornecer erros é um risco à segurança.

Lambda do Manipulador de exceção

Uma alternativa a uma página personalizada de manipulador de exceção é fornecer um lambda a UseExceptionHandler. Usar um lambda permite acessar o erro antes de retornar a resposta.

O código abaixo usa um lambda para tratamento de exceções:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            // using static System.Net.Mime.MediaTypeNames;
            context.Response.ContentType = Text.Plain;

            await context.Response.WriteAsync("An exception was thrown.");

            var exceptionHandlerPathFeature =
                context.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
            {
                await context.Response.WriteAsync(" The file was not found.");
            }

            if (exceptionHandlerPathFeature?.Path == "/")
            {
                await context.Response.WriteAsync(" Page: Home.");
            }
        });
    });

    app.UseHsts();
}

Aviso

Não forneça informações de erro confidenciais aos clientes. Fornecer erros é um risco à segurança.

IExceptionHandler

IExceptionHandler é uma interface que dá ao desenvolvedor um retorno de chamada para tratar exceções conhecidas em um local central.

As implementações IExceptionHandler são registradas ao chamar IServiceCollection.AddExceptionHandler<T>. O tempo de vida de uma instância IExceptionHandler é singleton. Várias implementações podem ser adicionadas e são chamadas na ordem registrada.

Se um manipulador de exceção tratar uma solicitação, ele poderá retornar true para interromper o processamento. Se uma exceção não for tratada por nenhum manipulador de exceção, o controle retornará ao comportamento padrão e às opções do middleware. Diferentes métricas e logs são emitidos para exceções tratadas versus exceções sem tratamento.

O exemplo a seguir mostra uma implementação de IExceptionHandler:

using Microsoft.AspNetCore.Diagnostics;

namespace ErrorHandlingSample
{
    public class CustomExceptionHandler : IExceptionHandler
    {
        private readonly ILogger<CustomExceptionHandler> logger;
        public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
        {
            this.logger = logger;
        }
        public ValueTask<bool> TryHandleAsync(
            HttpContext httpContext,
            Exception exception,
            CancellationToken cancellationToken)
        {
            var exceptionMessage = exception.Message;
            logger.LogError(
                "Error Message: {exceptionMessage}, Time of occurrence {time}",
                exceptionMessage, DateTime.UtcNow);
            // Return false to continue with the default behavior
            // - or - return true to signal that this exception is handled
            return ValueTask.FromResult(false);
        }
    }
}

O exemplo a seguir mostra como registrar uma implementação de IExceptionHandler para injeção de dependência:

using ErrorHandlingSample;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
builder.Services.AddExceptionHandler<CustomExceptionHandler>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// Remaining Program.cs code omitted for brevity

Quando o código anterior é executado no ambiente de desenvolvimento:

  • O CustomExceptionHandler é chamado primeiro para tratar uma exceção.
  • Depois de registrar em log uma exceção, o método TryHandleException retorna false, portanto, a página de exceção do desenvolvedor é mostrada.

Em outros ambientes:

  • O CustomExceptionHandler é chamado primeiro para tratar uma exceção.
  • Depois de registrar em log uma exceção, o método TryHandleException retorna false, portanto, a /Errorpágina é mostrada.

UseStatusCodePages

Por padrão, o aplicativo ASP.NET Core não fornece uma página de código de status para códigos de status de erro HTTP, como 404 – Não Encontrado. Quando o aplicativo define um código de status de erro HTTP 400-599 sem um corpo, ele retorna o código de status e um corpo de resposta vazio. Para habilitar os manipuladores padrão somente texto para os códigos de status de erros comuns, chame UseStatusCodePages em Program.cs:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages();

Chame UseStatusCodePages antes da solicitação de tratamento de middleware. Por exemplo, chame UseStatusCodePages antes do Middleware de Arquivo Estático e do Middleware de Pontos de Extremidade.

Quando UseStatusCodePages não for usado, a navegação até uma URL sem um ponto de extremidade retornará uma mensagem de erro dependente do navegador indicando que o ponto de extremidade não pode ser encontrado. Quando UseStatusCodePages é chamado, o navegador retorna a seguinte resposta:

Status Code: 404; Not Found

UseStatusCodePages normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

Observação

O middleware de páginas de código de status não captura exceções. Para fornecer uma página personalizada de tratamento de erros, use a página do manipulador de exceção.

UseStatusCodePages com cadeia de caracteres de formato

Para personalizar o texto e o tipo de conteúdo de resposta, use uma sobrecarga de UseStatusCodePages que leva um tipo de conteúdo e uma cadeia de caracteres de formato:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// using static System.Net.Mime.MediaTypeNames;
app.UseStatusCodePages(Text.Plain, "Status Code Page: {0}");

No código anterior, {0} é um espaço reservado para o código de erro.

UseStatusCodePages com uma cadeia de caracteres de formato normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

UseStatusCodePages com lambda

Para especificar a manipulação de erro personalizada e o código de gravação de resposta, use a sobrecarga de UseStatusCodePages que leva uma expressão lambda:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages(async statusCodeContext =>
{
    // using static System.Net.Mime.MediaTypeNames;
    statusCodeContext.HttpContext.Response.ContentType = Text.Plain;

    await statusCodeContext.HttpContext.Response.WriteAsync(
        $"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});

UseStatusCodePages com um lambda normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

UseStatusCodePagesWithRedirects

O método de extensão UseStatusCodePagesWithRedirects:

  • Envia um código de status 302 – Encontrado ao cliente.
  • Redireciona o cliente para o ponto de extremidade de tratamento de erros fornecido no modelo de URL. O ponto de extremidade de tratamento de erro normalmente exibe informações de erro e retorna HTTP 200.
var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");

O modelo de URL pode incluir um espaço reservado {0} para o código de status, conforme mostrado no código anterior. Se o modelo de URL começar com um til (~), o ~ será substituído pelo PathBase do aplicativo. Ao especificar um ponto de extremidade no aplicativo, crie uma exibição de MVC ou Razor Page para o ponto de extremidade.

Este método normalmente é usado quando o aplicativo:

  • Deveria redirecionar o cliente para um terminal diferente, geralmente em situações nas quais um aplicativo diferente processa o erro. Para aplicativos Web, a barra de endereços do navegador do cliente reflete o ponto de extremidade redirecionado.
  • Não deveria preservar e retornar o código de status original com a resposta de redirecionamento inicial.

UseStatusCodePagesWithReExecute

O método de extensão UseStatusCodePagesWithReExecute:

  • Gera o corpo da resposta ao executar novamente o pipeline de solicitação por meio de um caminho alternativo.
  • Não altera o código de status antes ou depois de executar novamente o pipeline.

A nova execução do pipeline pode alterar o código de status da resposta, pois o novo pipeline tem controle total sobre o código de status. Se o novo pipeline não alterar o código de status, o código de status original será enviado ao cliente.

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");

Se um ponto de extremidade dentro do aplicativo for especificado, crie uma exibição de MVC ou Razor Page para o ponto de extremidade.

Este método normalmente é usado quando o aplicativo tem que:

  • Processar a solicitação sem redirecionar para um ponto de extremidade diferente. Para aplicativos Web, a barra de endereços do navegador do cliente reflete o ponto de extremidade originalmente solicitado.
  • Preservar e retornar o código de status original com a resposta.

O modelo de URL precisa começar com / e pode incluir um espaço reservado {0} para o código de status. Para transmitir o código de status como um parâmetro de cadeia de caracteres de consulta, transmita um segundo argumento a UseStatusCodePagesWithReExecute. Por exemplo:

var app = builder.Build();  
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");

O ponto de extremidade que processa o erro pode obter a URL original que gerou o erro, conforme mostrado no exemplo a seguir:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
    public int OriginalStatusCode { get; set; }

    public string? OriginalPathAndQuery { get; set; }

    public void OnGet(int statusCode)
    {
        OriginalStatusCode = statusCode;

        var statusCodeReExecuteFeature =
            HttpContext.Features.Get<IStatusCodeReExecuteFeature>();

        if (statusCodeReExecuteFeature is not null)
        {
            OriginalPathAndQuery = string.Join(
                statusCodeReExecuteFeature.OriginalPathBase,
                statusCodeReExecuteFeature.OriginalPath,
                statusCodeReExecuteFeature.OriginalQueryString);
        }
    }
}

Como esse middleware pode executar novamente o pipeline de solicitação:

  • Os middlewares precisam lidar com a reinserção da mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar _next ou armazenar em cache seu processamento no HttpContext para evitar retrabalho. Ao lidar com o corpo da solicitação, isso significa armazenar em buffer ou em cache os resultados do tipo do leitor de formulário.
  • Os serviços com escopo permanecem os mesmos.

Desabilitar páginas de código de status

Para desabilitar as páginas de código de status de um método de ação ou controlador MVC, use o atributo [SkipStatusCodePages].

Para desabilitar as páginas de código de status de solicitações específicas em um método manipulador Razor Pages ou em um controlador MVC, use IStatusCodePagesFeature:

public void OnGet()
{
    var statusCodePagesFeature =
        HttpContext.Features.Get<IStatusCodePagesFeature>();

    if (statusCodePagesFeature is not null)
    {
        statusCodePagesFeature.Enabled = false;
    }
}

Código de tratamento de exceção

Código em páginas de tratamento de exceção também pode gerar exceções. As páginas de erro de produção devem ser testadas minuciosamente, e tome cuidado extra para evitar gerar exceções próprias.

Cabeçalhos de resposta

Depois que os cabeçalhos de resposta são enviados:

  • O aplicativo já não consegue alterar o código de status da resposta.
  • As páginas de exceção ou manipuladores não podem ser executados. A resposta deve ser concluída ou a conexão será anulada.

Tratamento de exceções do servidor

Além da lógica de tratamento de exceção no aplicativo, a implementação do servidor HTTP pode lidar com algumas exceções. Se o servidor capturar uma exceção antes que os cabeçalhos de resposta sejam enviados, o servidor enviará uma resposta 500 - Internal Server Error sem um corpo de resposta. Se o servidor capturar uma exceção depois que os cabeçalhos de resposta forem enviados, o servidor fechará a conexão. As solicitações que não são manipuladas pelo aplicativo são manipuladas pelo servidor. Qualquer exceção que ocorrer quando o servidor estiver tratando a solicitação será tratada pelo tratamento de exceção do servidor. As páginas de erro personalizadas do aplicativo, o middleware de tratamento de exceção e os filtros configurados não afetam esse comportamento.

Tratamento de exceção na inicialização

Apenas a camada de hospedagem pode tratar exceções que ocorrem durante a inicialização do aplicativo. O host pode ser configurado para capturar erros de inicialização e capturar erros detalhados.

A camada de hospedagem só poderá mostrar uma página de erro para um erro de inicialização capturado se o erro ocorrer após a associação de endereço do host/porta. Se a associação falhar:

  • A camada de hospedagem registrará uma exceção crítica.
  • O processo dotnet falhará.
  • Nenhuma página de erro é exibida quando o servidor HTTP é Kestrel.

Quando executado no IIS (ou no Serviço de Aplicativo do Azure) ou no IIS Express, um erro 502.5 Falha no Processo será retornado pelo Módulo do ASP.NET Core se o processo não puder ser iniciado. Para obter mais informações, consulte Solução de problemas do ASP.NET Core no Serviço de Aplicativo do Azure e no IIS.

Página de erro do banco de dados

O filtro de exceção de página do desenvolvedor de banco de dados AddDatabaseDeveloperPageExceptionFilter captura exceções relacionadas ao banco de dados que podem ser resolvidas usando migrações do Entity Framework Core. Quando estas exceções ocorrem, é gerada uma resposta HTML com detalhes das ações possíveis para resolver o problema. Esta página só fica habilitada no Ambiente de desenvolvimento. O código abaixo adiciona o filtro de exceção de página do desenvolvedor de banco de dados:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();

Filtros de exceção

Em aplicativos MVC, os filtros de exceção podem ser configurados globalmente, por controlador ou por ação. Em aplicativos do Razor Pages, eles podem ser configurados globalmente ou por modelo de página. Esses filtros tratam qualquer exceção sem tratamento ocorrida durante a execução de uma ação do controlador ou de outro filtro. Para obter mais informações, confira Filtros no ASP.NET Core.

Os filtros de exceção são úteis para interceptar exceções que ocorrem em ações do MVC, mas não são tão flexíveis quanto o middleware de tratamento de exceção, UseExceptionHandler. Recomendamos usar UseExceptionHandler, a não ser que você precise fazer o tratamento de erro de forma diferente com base na ação de MVC escolhida.

Erros de estado do modelo

Confira informações sobre como lidar com erros de estado de modelo em model binding e Validação de modelos.

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 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):

O código a seguir configura o aplicativo para gerar uma resposta de detalhes do problema para todas as respostas de erro de servidor e cliente HTTP que ainda não têm um conteúdo do corpo:

builder.Services.AddProblemDetails();

var app = builder.Build();        

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
    app.UseHsts();
}

app.UseStatusCodePages();

A próxima seção mostra como personalizar o corpo da resposta de detalhes do problema.

Personalizar detalhes do problema

A criação automática de ProblemDetails pode ser personalizada usando qualquer uma das seguintes opções:

  1. Usar ProblemDetailsOptions.CustomizeProblemDetails
  2. Usar um IProblemDetailsWriter personalizado
  3. Chamar o IProblemDetailsService em um middleware

Operação CustomizeProblemDetails

Os detalhes do problema gerados podem ser personalizados com o uso de CustomizeProblemDetails e as personalizações são aplicadas a todos os detalhes do problema gerados automaticamente.

O código abaixo usa ProblemDetailsOptions para definir CustomizeProblemDetails:

builder.Services.AddProblemDetails(options =>
    options.CustomizeProblemDetails = ctx =>
            ctx.ProblemDetails.Extensions.Add("nodeId", Environment.MachineName));

var app = builder.Build();        

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
    app.UseHsts();
}

app.UseStatusCodePages();

Por exemplo, um resultado de ponto de extremidade HTTP Status 400 Bad Request produz o seguinte corpo da resposta de detalhes do problema:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "Bad Request",
  "status": 400,
  "nodeId": "my-machine-name"
}

IProblemDetailsWriter personalizado

Uma implementação IProblemDetailsWriter pode ser criada para personalizações avançadas.

public class SampleProblemDetailsWriter : IProblemDetailsWriter
{
    // Indicates that only responses with StatusCode == 400
    // are handled by this writer. All others are
    // handled by different registered writers if available.
    public bool CanWrite(ProblemDetailsContext context)
        => context.HttpContext.Response.StatusCode == 400;

    public ValueTask WriteAsync(ProblemDetailsContext context)
    {
        // Additional customizations.

        // Write to the response.
        var response = context.HttpContext.Response;
        return new ValueTask(response.WriteAsJsonAsync(context.ProblemDetails));
    }
}

Observação: ao usar um IProblemDetailsWriter personalizado, o IProblemDetailsWriter personalizado deve ser registrado antes de chamar AddRazorPages, AddControllers, AddControllersWithViews ou AddMvc:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<IProblemDetailsWriter, SampleProblemDetailsWriter>();

var app = builder.Build();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsWriter>() is
            { } problemDetailsService)
        {

            if (problemDetailsService.CanWrite(new ProblemDetailsContext() { HttpContext = context }))
            {
                (string Detail, string Type) details = mathErrorFeature.MathError switch
                {
                    MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                        "https://en.wikipedia.org/wiki/Division_by_zero"),
                    _ => ("Negative or complex numbers are not valid input.",
                        "https://en.wikipedia.org/wiki/Square_root")
                };

                await problemDetailsService.WriteAsync(new ProblemDetailsContext
                {
                    HttpContext = context,
                    ProblemDetails =
                    {
                        Title = "Bad Input",
                        Detail = details.Detail,
                        Type = details.Type
                    }
                });
            }
        }
    }
});

// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
    if (denominator == 0)
    {
        var errorType = new MathErrorFeature
        {
            MathError = MathErrorType.DivisionByZeroError
        };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(numerator / denominator);
});

// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
    if (radicand < 0)
    {
        var errorType = new MathErrorFeature
        {
            MathError = MathErrorType.NegativeRadicandError
        };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(Math.Sqrt(radicand));
});

app.Run();

Detalhes do problema do Middleware

Uma abordagem alternativa ao uso de ProblemDetailsOptions com CustomizeProblemDetails é definir o ProblemDetails no middleware. Uma resposta de detalhes do problema pode ser gravada chamando IProblemDetailsService.WriteAsync:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStatusCodePages();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsService>() is
                                                           { } problemDetailsService)
        {
            (string Detail, string Type) details = mathErrorFeature.MathError switch
            {
                MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                "https://en.wikipedia.org/wiki/Division_by_zero"),
                _ => ("Negative or complex numbers are not valid input.", 
                "https://en.wikipedia.org/wiki/Square_root")
            };

            await problemDetailsService.WriteAsync(new ProblemDetailsContext
            {
                HttpContext = context,
                ProblemDetails =
                {
                    Title = "Bad Input",
                    Detail = details.Detail,
                    Type = details.Type
                }
            });
        }
    }
});

// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
    if (denominator == 0)
    {
        var errorType = new MathErrorFeature { MathError =
                                               MathErrorType.DivisionByZeroError };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(numerator / denominator);
});

// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
    if (radicand < 0)
    {
        var errorType = new MathErrorFeature { MathError =
                                               MathErrorType.NegativeRadicandError };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(Math.Sqrt(radicand));
});

app.MapControllers();

app.Run();

No código anterior, os pontos de extremidade da API mínima /divide e /squareroot retornam a resposta do problema personalizada esperada na entrada do erro.

Os pontos de extremidade do controlador de API retornam a resposta do problema padrão na entrada do erro, não a resposta do problema personalizada. A resposta do problema padrão é retornada porque o controlador de API gravou no fluxo de resposta, Detalhes do problema de códigos de status de erro, antes de IProblemDetailsService.WriteAsync ser chamado, e a resposta não é gravada novamente.

O ValuesController a seguir retorna BadRequestResult, que grava no fluxo de resposta, ou seja, impede que a resposta do problema personalizada seja retornada.

[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // /api/values/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Math.Sqrt(radicand));
    }

}

O Values3Controller abaixo retorna ControllerBase.Problem para que o resultado do problema personalizado esperado seja retornado:

[Route("api/[controller]/[action]")]
[ApiController]
public class Values3Controller : ControllerBase
{
    // /api/values3/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Divison by zero is not defined.",
                type: "https://en.wikipedia.org/wiki/Division_by_zero",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values3/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Negative or complex numbers are not valid input.",
                type: "https://en.wikipedia.org/wiki/Square_root",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

        return Ok(Math.Sqrt(radicand));
    }

}

Produzir um conteúdo ProblemDetails para exceções

Considere o seguinte aplicativo:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

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

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.MapControllers();
app.Run();

Em ambientes que não são de desenvolvimento, quando ocorre uma exceção, o que vemos abaixo é uma resposta ProblemDetails padrão que é retornada ao cliente:

{
"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title":"An error occurred while processing your request.",
"status":500,"traceId":"00-b644<snip>-00"
}

Para a maioria dos aplicativos, o código anterior é tudo o que é necessário para exceções. No entanto, a seção a seguir mostra como obter respostas de problema mais detalhadas.

Uma alternativa a uma página personalizada de manipulador de exceção é fornecer um lambda a UseExceptionHandler. O uso de um lambda permite o acesso ao erro e à gravação de uma resposta de detalhes do problema com IProblemDetailsService.WriteAsync:

using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

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

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            context.Response.ContentType = Text.Plain;

            var title = "Bad Input";
            var detail = "Invalid input";
            var type = "https://errors.example.com/badInput";

            if (context.RequestServices.GetService<IProblemDetailsService>() is
                { } problemDetailsService)
            {
                var exceptionHandlerFeature =
               context.Features.Get<IExceptionHandlerFeature>();

                var exceptionType = exceptionHandlerFeature?.Error;
                if (exceptionType != null &&
                   exceptionType.Message.Contains("infinity"))
                {
                    title = "Argument exception";
                    detail = "Invalid input";
                    type = "https://errors.example.com/argumentException";
                }

                await problemDetailsService.WriteAsync(new ProblemDetailsContext
                {
                    HttpContext = context,
                    ProblemDetails =
                {
                    Title = title,
                    Detail = detail,
                    Type = type
                }
                });
            }
        });
    });
}

app.MapControllers();
app.Run();

Aviso

Não forneça informações de erro confidenciais aos clientes. Fornecer erros é um risco à segurança.

Uma abordagem alternativa para gerar detalhes do problema é o uso do pacote NuGet de terceiros Hellang.Middleware.ProblemDetails que pode ser usado a fim de mapear exceções e erros do cliente para detalhes do problema.

Recursos adicionais

Por Tom Dykstra

Este artigo apresenta abordagens comuns para o tratamento de erros em aplicativos Web ASP.NET Core. Confira também Tratar erros em APIs da Web do ASP.NET Core e Tratar erros em aplicativos de API Mínima.

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. Os aplicativos ASP.NET Core habilitam a página de exceção do desenvolvedor por padrão quando ambos:

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.

As informações detalhadas da exceção não devem ser exibidas publicamente quando o aplicativo é executado no ambiente de 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
  • Cabeçalhos

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.

Página do Manipulador de exceção

Para configurar uma página personalizada de tratamento de erros para o Ambiente de produção, chame UseExceptionHandler. Este middleware de tratamento de exceções:

  • Captura e registra exceções sem tratamento.
  • Executa novamente a solicitação em um pipeline alternativo usando o caminho indicado. A solicitação não será executada novamente se a resposta tiver sido iniciada. O código gerado pelo modelo executa novamente a solicitação usando o caminho /Error.

Aviso

Se o pipeline alternativo gerar uma exceção própria, o Middleware de Tratamento de Exceções aumentará a exceção original.

Como esse middleware pode executar novamente o pipeline de solicitação:

  • Os middlewares precisam lidar com a reinserção da mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar _next ou armazenar em cache seu processamento no HttpContext para evitar retrabalho. Ao lidar com o corpo da solicitação, isso significa armazenar em buffer ou em cache os resultados do tipo do leitor de formulário.
  • Para a sobrecarga UseExceptionHandler(IApplicationBuilder, String) usada em modelos, somente o caminho da solicitação é modificado, e os dados de rota são limpos. Os dados de solicitação, como cabeçalhos, método e itens, são todos reutilizados no estado em que se encontram.
  • Os serviços com escopo permanecem os mesmos.

O exemplo abaixo, UseExceptionHandler inclui o middleware de manipulação de exceções em ambientes que não são de desenvolvimento:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

O modelo de aplicativo Razor Pages fornece uma Página de erro (.cshtml) e uma classe PageModel (ErrorModel) na pasta Pages. Para um aplicativo MVC, o modelo de projeto inclui um método de ação Error e uma Exibição de erro para o controlador Home.

O middleware de tratamento de exceção executa novamente a solicitação usando o método HTTP original. Se um ponto de extremidade do manipulador de erros estiver restrito a um conjunto específico de métodos HTTP, ele será executado somente para esses métodos HTTP. Por exemplo, uma ação do controlador MVC que usa o atributo [HttpGet] é executada apenas para solicitações GET. Para garantir que todas as solicitações cheguem à página de tratamento de erros personalizada, não as restrinja a um conjunto específico de métodos HTTP.

Para lidar com exceções de forma diferente com base no método HTTP original:

  • Para Razor Pages, crie vários métodos de manipulador. Por exemplo, use OnGet para lidar com exceções GET e use OnPost para lidar com exceções POST.
  • Para MVC, aplique atributos de verbo HTTP a várias ações. Por exemplo, use [HttpGet] para lidar com exceções GET e use [HttpPost] para lidar com exceções POST.

Para permitir que usuários não autenticados exibam a página de tratamento de erros personalizada, verifique se ela dá suporte ao acesso anônimo.

Acessar a exceção

Use IExceptionHandlerPathFeature para acessar a exceção e o caminho de solicitação original em um manipulador de erros. O exemplo a seguir usa IExceptionHandlerPathFeature para obter mais informações sobre a exceção gerada:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
    public string? RequestId { get; set; }

    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    public string? ExceptionMessage { get; set; }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
            HttpContext.Features.Get<IExceptionHandlerPathFeature>();

        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "The file was not found.";
        }

        if (exceptionHandlerPathFeature?.Path == "/")
        {
            ExceptionMessage ??= string.Empty;
            ExceptionMessage += " Page: Home.";
        }
    }
}

Aviso

Não forneça informações de erro confidenciais aos clientes. Fornecer erros é um risco à segurança.

Lambda do Manipulador de exceção

Uma alternativa a uma página personalizada de manipulador de exceção é fornecer um lambda a UseExceptionHandler. Usar um lambda permite acessar o erro antes de retornar a resposta.

O código abaixo usa um lambda para tratamento de exceções:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            // using static System.Net.Mime.MediaTypeNames;
            context.Response.ContentType = Text.Plain;

            await context.Response.WriteAsync("An exception was thrown.");

            var exceptionHandlerPathFeature =
                context.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
            {
                await context.Response.WriteAsync(" The file was not found.");
            }

            if (exceptionHandlerPathFeature?.Path == "/")
            {
                await context.Response.WriteAsync(" Page: Home.");
            }
        });
    });

    app.UseHsts();
}

Aviso

Não forneça informações de erro confidenciais aos clientes. Fornecer erros é um risco à segurança.

UseStatusCodePages

Por padrão, o aplicativo ASP.NET Core não fornece uma página de código de status para códigos de status de erro HTTP, como 404 – Não Encontrado. Quando o aplicativo define um código de status de erro HTTP 400-599 sem um corpo, ele retorna o código de status e um corpo de resposta vazio. Para habilitar os manipuladores padrão somente texto para os códigos de status de erros comuns, chame UseStatusCodePages em Program.cs:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages();

Chame UseStatusCodePages antes da solicitação de tratamento de middleware. Por exemplo, chame UseStatusCodePages antes do Middleware de Arquivo Estático e do Middleware de Pontos de Extremidade.

Quando UseStatusCodePages não for usado, a navegação até uma URL sem um ponto de extremidade retornará uma mensagem de erro dependente do navegador indicando que o ponto de extremidade não pode ser encontrado. Quando UseStatusCodePages é chamado, o navegador retorna a seguinte resposta:

Status Code: 404; Not Found

UseStatusCodePages normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

Observação

O middleware de páginas de código de status não captura exceções. Para fornecer uma página personalizada de tratamento de erros, use a página do manipulador de exceção.

UseStatusCodePages com cadeia de caracteres de formato

Para personalizar o texto e o tipo de conteúdo de resposta, use uma sobrecarga de UseStatusCodePages que leva um tipo de conteúdo e uma cadeia de caracteres de formato:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// using static System.Net.Mime.MediaTypeNames;
app.UseStatusCodePages(Text.Plain, "Status Code Page: {0}");

No código anterior, {0} é um espaço reservado para o código de erro.

UseStatusCodePages com uma cadeia de caracteres de formato normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

UseStatusCodePages com lambda

Para especificar a manipulação de erro personalizada e o código de gravação de resposta, use a sobrecarga de UseStatusCodePages que leva uma expressão lambda:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages(async statusCodeContext =>
{
    // using static System.Net.Mime.MediaTypeNames;
    statusCodeContext.HttpContext.Response.ContentType = Text.Plain;

    await statusCodeContext.HttpContext.Response.WriteAsync(
        $"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});

UseStatusCodePages com um lambda normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

UseStatusCodePagesWithRedirects

O método de extensão UseStatusCodePagesWithRedirects:

  • Envia um código de status 302 – Encontrado ao cliente.
  • Redireciona o cliente para o ponto de extremidade de tratamento de erros fornecido no modelo de URL. O ponto de extremidade de tratamento de erro normalmente exibe informações de erro e retorna HTTP 200.
var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");

O modelo de URL pode incluir um espaço reservado {0} para o código de status, conforme mostrado no código anterior. Se o modelo de URL começar com um til (~), o ~ será substituído pelo PathBase do aplicativo. Ao especificar um ponto de extremidade no aplicativo, crie uma exibição de MVC ou Razor Page para o ponto de extremidade.

Este método normalmente é usado quando o aplicativo:

  • Deveria redirecionar o cliente para um terminal diferente, geralmente em situações nas quais um aplicativo diferente processa o erro. Para aplicativos Web, a barra de endereços do navegador do cliente reflete o ponto de extremidade redirecionado.
  • Não deveria preservar e retornar o código de status original com a resposta de redirecionamento inicial.

UseStatusCodePagesWithReExecute

O método de extensão UseStatusCodePagesWithReExecute:

  • Gera o corpo da resposta ao executar novamente o pipeline de solicitação por meio de um caminho alternativo.
  • Não altera o código de status antes ou depois de executar novamente o pipeline.

A nova execução do pipeline pode alterar o código de status da resposta, pois o novo pipeline tem controle total sobre o código de status. Se o novo pipeline não alterar o código de status, o código de status original será enviado ao cliente.

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");

Se um ponto de extremidade dentro do aplicativo for especificado, crie uma exibição de MVC ou Razor Page para o ponto de extremidade.

Este método normalmente é usado quando o aplicativo tem que:

  • Processar a solicitação sem redirecionar para um ponto de extremidade diferente. Para aplicativos Web, a barra de endereços do navegador do cliente reflete o ponto de extremidade originalmente solicitado.
  • Preservar e retornar o código de status original com a resposta.

O modelo de URL precisa começar com / e pode incluir um espaço reservado {0} para o código de status. Para transmitir o código de status como um parâmetro de cadeia de caracteres de consulta, transmita um segundo argumento a UseStatusCodePagesWithReExecute. Por exemplo:

var app = builder.Build();  
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");

O ponto de extremidade que processa o erro pode obter a URL original que gerou o erro, conforme mostrado no exemplo a seguir:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
    public int OriginalStatusCode { get; set; }

    public string? OriginalPathAndQuery { get; set; }

    public void OnGet(int statusCode)
    {
        OriginalStatusCode = statusCode;

        var statusCodeReExecuteFeature =
            HttpContext.Features.Get<IStatusCodeReExecuteFeature>();

        if (statusCodeReExecuteFeature is not null)
        {
            OriginalPathAndQuery = string.Join(
                statusCodeReExecuteFeature.OriginalPathBase,
                statusCodeReExecuteFeature.OriginalPath,
                statusCodeReExecuteFeature.OriginalQueryString);
        }
    }
}

Como esse middleware pode executar novamente o pipeline de solicitação:

  • Os middlewares precisam lidar com a reinserção da mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar _next ou armazenar em cache seu processamento no HttpContext para evitar retrabalho. Ao lidar com o corpo da solicitação, isso significa armazenar em buffer ou em cache os resultados do tipo do leitor de formulário.
  • Os serviços com escopo permanecem os mesmos.

Desabilitar páginas de código de status

Para desabilitar as páginas de código de status de um método de ação ou controlador MVC, use o atributo [SkipStatusCodePages].

Para desabilitar as páginas de código de status de solicitações específicas em um método manipulador Razor Pages ou em um controlador MVC, use IStatusCodePagesFeature:

public void OnGet()
{
    var statusCodePagesFeature =
        HttpContext.Features.Get<IStatusCodePagesFeature>();

    if (statusCodePagesFeature is not null)
    {
        statusCodePagesFeature.Enabled = false;
    }
}

Código de tratamento de exceção

Código em páginas de tratamento de exceção também pode gerar exceções. As páginas de erro de produção devem ser testadas minuciosamente, e tome cuidado extra para evitar gerar exceções próprias.

Cabeçalhos de resposta

Depois que os cabeçalhos de resposta são enviados:

  • O aplicativo já não consegue alterar o código de status da resposta.
  • As páginas de exceção ou manipuladores não podem ser executados. A resposta deve ser concluída ou a conexão será anulada.

Tratamento de exceções do servidor

Além da lógica de tratamento de exceção no aplicativo, a implementação do servidor HTTP pode lidar com algumas exceções. Se o servidor capturar uma exceção antes que os cabeçalhos de resposta sejam enviados, o servidor enviará uma resposta 500 - Internal Server Error sem um corpo de resposta. Se o servidor capturar uma exceção depois que os cabeçalhos de resposta forem enviados, o servidor fechará a conexão. As solicitações que não são manipuladas pelo aplicativo são manipuladas pelo servidor. Qualquer exceção que ocorrer quando o servidor estiver tratando a solicitação será tratada pelo tratamento de exceção do servidor. As páginas de erro personalizadas do aplicativo, o middleware de tratamento de exceção e os filtros configurados não afetam esse comportamento.

Tratamento de exceção na inicialização

Apenas a camada de hospedagem pode tratar exceções que ocorrem durante a inicialização do aplicativo. O host pode ser configurado para capturar erros de inicialização e capturar erros detalhados.

A camada de hospedagem só poderá mostrar uma página de erro para um erro de inicialização capturado se o erro ocorrer após a associação de endereço do host/porta. Se a associação falhar:

  • A camada de hospedagem registrará uma exceção crítica.
  • O processo dotnet falhará.
  • Nenhuma página de erro é exibida quando o servidor HTTP é Kestrel.

Quando executado no IIS (ou no Serviço de Aplicativo do Azure) ou no IIS Express, um erro 502.5 Falha no Processo será retornado pelo Módulo do ASP.NET Core se o processo não puder ser iniciado. Para obter mais informações, consulte Solução de problemas do ASP.NET Core no Serviço de Aplicativo do Azure e no IIS.

Página de erro do banco de dados

O filtro de exceção de página do desenvolvedor de banco de dados AddDatabaseDeveloperPageExceptionFilter captura exceções relacionadas ao banco de dados que podem ser resolvidas usando migrações do Entity Framework Core. Quando estas exceções ocorrem, é gerada uma resposta HTML com detalhes das ações possíveis para resolver o problema. Esta página só fica habilitada no Ambiente de desenvolvimento. O código abaixo adiciona o filtro de exceção de página do desenvolvedor de banco de dados:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();

Filtros de exceção

Em aplicativos MVC, os filtros de exceção podem ser configurados globalmente, por controlador ou por ação. Em aplicativos do Razor Pages, eles podem ser configurados globalmente ou por modelo de página. Esses filtros tratam qualquer exceção sem tratamento ocorrida durante a execução de uma ação do controlador ou de outro filtro. Para obter mais informações, confira Filtros no ASP.NET Core.

Os filtros de exceção são úteis para interceptar exceções que ocorrem em ações do MVC, mas não são tão flexíveis quanto o middleware de tratamento de exceção, UseExceptionHandler. Recomendamos usar UseExceptionHandler, a não ser que você precise fazer o tratamento de erro de forma diferente com base na ação de MVC escolhida.

Erros de estado do modelo

Confira informações sobre como lidar com erros de estado de modelo em model binding e Validação de modelos.

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 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):

O código a seguir configura o aplicativo para gerar uma resposta de detalhes do problema para todas as respostas de erro de servidor e cliente HTTP que ainda não têm um conteúdo do corpo:

builder.Services.AddProblemDetails();

var app = builder.Build();        

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
    app.UseHsts();
}

app.UseStatusCodePages();

A próxima seção mostra como personalizar o corpo da resposta de detalhes do problema.

Personalizar detalhes do problema

A criação automática de ProblemDetails pode ser personalizada usando qualquer uma das seguintes opções:

  1. Usar ProblemDetailsOptions.CustomizeProblemDetails
  2. Usar um IProblemDetailsWriter personalizado
  3. Chamar o IProblemDetailsService em um middleware

Operação CustomizeProblemDetails

Os detalhes do problema gerados podem ser personalizados com o uso de CustomizeProblemDetails e as personalizações são aplicadas a todos os detalhes do problema gerados automaticamente.

O código abaixo usa ProblemDetailsOptions para definir CustomizeProblemDetails:

builder.Services.AddProblemDetails(options =>
    options.CustomizeProblemDetails = ctx =>
            ctx.ProblemDetails.Extensions.Add("nodeId", Environment.MachineName));

var app = builder.Build();        

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
    app.UseHsts();
}

app.UseStatusCodePages();

Por exemplo, um resultado de ponto de extremidade HTTP Status 400 Bad Request produz o seguinte corpo da resposta de detalhes do problema:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "Bad Request",
  "status": 400,
  "nodeId": "my-machine-name"
}

IProblemDetailsWriter personalizado

Uma implementação IProblemDetailsWriter pode ser criada para personalizações avançadas.

public class SampleProblemDetailsWriter : IProblemDetailsWriter
{
    // Indicates that only responses with StatusCode == 400
    // are handled by this writer. All others are
    // handled by different registered writers if available.
    public bool CanWrite(ProblemDetailsContext context)
        => context.HttpContext.Response.StatusCode == 400;

    public ValueTask WriteAsync(ProblemDetailsContext context)
    {
        // Additional customizations.

        // Write to the response.
        var response = context.HttpContext.Response;
        return new ValueTask(response.WriteAsJsonAsync(context.ProblemDetails));
    }
}

Observação: ao usar um IProblemDetailsWriter personalizado, o IProblemDetailsWriter personalizado deve ser registrado antes de chamar AddRazorPages, AddControllers, AddControllersWithViews ou AddMvc:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<IProblemDetailsWriter, SampleProblemDetailsWriter>();

var app = builder.Build();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsWriter>() is
            { } problemDetailsService)
        {

            if (problemDetailsService.CanWrite(new ProblemDetailsContext() { HttpContext = context }))
            {
                (string Detail, string Type) details = mathErrorFeature.MathError switch
                {
                    MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                        "https://en.wikipedia.org/wiki/Division_by_zero"),
                    _ => ("Negative or complex numbers are not valid input.",
                        "https://en.wikipedia.org/wiki/Square_root")
                };

                await problemDetailsService.WriteAsync(new ProblemDetailsContext
                {
                    HttpContext = context,
                    ProblemDetails =
                    {
                        Title = "Bad Input",
                        Detail = details.Detail,
                        Type = details.Type
                    }
                });
            }
        }
    }
});

// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
    if (denominator == 0)
    {
        var errorType = new MathErrorFeature
        {
            MathError = MathErrorType.DivisionByZeroError
        };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(numerator / denominator);
});

// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
    if (radicand < 0)
    {
        var errorType = new MathErrorFeature
        {
            MathError = MathErrorType.NegativeRadicandError
        };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(Math.Sqrt(radicand));
});

app.Run();

Detalhes do problema do Middleware

Uma abordagem alternativa ao uso de ProblemDetailsOptions com CustomizeProblemDetails é definir o ProblemDetails no middleware. Uma resposta de detalhes do problema pode ser gravada chamando IProblemDetailsService.WriteAsync:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStatusCodePages();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsService>() is
                                                           { } problemDetailsService)
        {
            (string Detail, string Type) details = mathErrorFeature.MathError switch
            {
                MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                "https://en.wikipedia.org/wiki/Division_by_zero"),
                _ => ("Negative or complex numbers are not valid input.", 
                "https://en.wikipedia.org/wiki/Square_root")
            };

            await problemDetailsService.WriteAsync(new ProblemDetailsContext
            {
                HttpContext = context,
                ProblemDetails =
                {
                    Title = "Bad Input",
                    Detail = details.Detail,
                    Type = details.Type
                }
            });
        }
    }
});

// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
    if (denominator == 0)
    {
        var errorType = new MathErrorFeature { MathError =
                                               MathErrorType.DivisionByZeroError };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(numerator / denominator);
});

// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
    if (radicand < 0)
    {
        var errorType = new MathErrorFeature { MathError =
                                               MathErrorType.NegativeRadicandError };
        context.Features.Set(errorType);
        return Results.BadRequest();
    }

    return Results.Ok(Math.Sqrt(radicand));
});

app.MapControllers();

app.Run();

No código anterior, os pontos de extremidade da API mínima /divide e /squareroot retornam a resposta do problema personalizada esperada na entrada do erro.

Os pontos de extremidade do controlador de API retornam a resposta do problema padrão na entrada do erro, não a resposta do problema personalizada. A resposta do problema padrão é retornada porque o controlador de API gravou no fluxo de resposta, Detalhes do problema de códigos de status de erro, antes de IProblemDetailsService.WriteAsync ser chamado, e a resposta não é gravada novamente.

O ValuesController a seguir retorna BadRequestResult, que grava no fluxo de resposta, ou seja, impede que a resposta do problema personalizada seja retornada.

[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // /api/values/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Math.Sqrt(radicand));
    }

}

O Values3Controller abaixo retorna ControllerBase.Problem para que o resultado do problema personalizado esperado seja retornado:

[Route("api/[controller]/[action]")]
[ApiController]
public class Values3Controller : ControllerBase
{
    // /api/values3/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Divison by zero is not defined.",
                type: "https://en.wikipedia.org/wiki/Division_by_zero",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values3/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Negative or complex numbers are not valid input.",
                type: "https://en.wikipedia.org/wiki/Square_root",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

        return Ok(Math.Sqrt(radicand));
    }

}

Produzir um conteúdo ProblemDetails para exceções

Considere o seguinte aplicativo:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

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

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.MapControllers();
app.Run();

Em ambientes que não são de desenvolvimento, quando ocorre uma exceção, o que vemos abaixo é uma resposta ProblemDetails padrão que é retornada ao cliente:

{
"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title":"An error occurred while processing your request.",
"status":500,"traceId":"00-b644<snip>-00"
}

Para a maioria dos aplicativos, o código anterior é tudo o que é necessário para exceções. No entanto, a seção a seguir mostra como obter respostas de problema mais detalhadas.

Uma alternativa a uma página personalizada de manipulador de exceção é fornecer um lambda a UseExceptionHandler. O uso de um lambda permite o acesso ao erro e à gravação de uma resposta de detalhes do problema com IProblemDetailsService.WriteAsync:

using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

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

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            context.Response.ContentType = Text.Plain;

            var title = "Bad Input";
            var detail = "Invalid input";
            var type = "https://errors.example.com/badInput";

            if (context.RequestServices.GetService<IProblemDetailsService>() is
                { } problemDetailsService)
            {
                var exceptionHandlerFeature =
               context.Features.Get<IExceptionHandlerFeature>();

                var exceptionType = exceptionHandlerFeature?.Error;
                if (exceptionType != null &&
                   exceptionType.Message.Contains("infinity"))
                {
                    title = "Argument exception";
                    detail = "Invalid input";
                    type = "https://errors.example.com/argumentException";
                }

                await problemDetailsService.WriteAsync(new ProblemDetailsContext
                {
                    HttpContext = context,
                    ProblemDetails =
                {
                    Title = title,
                    Detail = detail,
                    Type = type
                }
                });
            }
        });
    });
}

app.MapControllers();
app.Run();

Aviso

Não forneça informações de erro confidenciais aos clientes. Fornecer erros é um risco à segurança.

Uma abordagem alternativa para gerar detalhes do problema é o uso do pacote NuGet de terceiros Hellang.Middleware.ProblemDetails que pode ser usado a fim de mapear exceções e erros do cliente para detalhes do problema.

Recursos adicionais

Por Tom Dykstra

Este artigo apresenta abordagens comuns para o tratamento de erros em aplicativos Web ASP.NET Core. Confira APIs Web em Erros de identificador nas APIs Web do ASP.NET Core.

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. Os aplicativos ASP.NET Core habilitam a página de exceção do desenvolvedor por padrão quando ambos:

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.

As informações detalhadas da exceção não devem ser exibidas publicamente quando o aplicativo é executado no ambiente de 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
  • Cabeçalhos

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.

Página do Manipulador de exceção

Para configurar uma página personalizada de tratamento de erros para o Ambiente de produção, chame UseExceptionHandler. Este middleware de tratamento de exceções:

  • Captura e registra exceções sem tratamento.
  • Executa novamente a solicitação em um pipeline alternativo usando o caminho indicado. A solicitação não será executada novamente se a resposta tiver sido iniciada. O código gerado pelo modelo executa novamente a solicitação usando o caminho /Error.

Aviso

Se o pipeline alternativo gerar uma exceção própria, o Middleware de Tratamento de Exceções aumentará a exceção original.

O exemplo abaixo, UseExceptionHandler inclui o middleware de manipulação de exceções em ambientes que não são de desenvolvimento:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

O modelo de aplicativo Razor Pages fornece uma Página de erro (.cshtml) e uma classe PageModel (ErrorModel) na pasta Pages. Para um aplicativo MVC, o modelo de projeto inclui um método de ação Error e uma Exibição de erro para o controlador Home.

O middleware de tratamento de exceção executa novamente a solicitação usando o método HTTP original. Se um ponto de extremidade do manipulador de erros estiver restrito a um conjunto específico de métodos HTTP, ele será executado somente para esses métodos HTTP. Por exemplo, uma ação do controlador MVC que usa o atributo [HttpGet] é executada apenas para solicitações GET. Para garantir que todas as solicitações cheguem à página de tratamento de erros personalizada, não as restrinja a um conjunto específico de métodos HTTP.

Para lidar com exceções de forma diferente com base no método HTTP original:

  • Para Razor Pages, crie vários métodos de manipulador. Por exemplo, use OnGet para lidar com exceções GET e use OnPost para lidar com exceções POST.
  • Para MVC, aplique atributos de verbo HTTP a várias ações. Por exemplo, use [HttpGet] para lidar com exceções GET e use [HttpPost] para lidar com exceções POST.

Para permitir que usuários não autenticados exibam a página de tratamento de erros personalizada, verifique se ela dá suporte ao acesso anônimo.

Acessar a exceção

Use IExceptionHandlerPathFeature para acessar a exceção e o caminho de solicitação original em um manipulador de erros. O exemplo a seguir usa IExceptionHandlerPathFeature para obter mais informações sobre a exceção gerada:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
    public string? RequestId { get; set; }

    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    public string? ExceptionMessage { get; set; }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
            HttpContext.Features.Get<IExceptionHandlerPathFeature>();

        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "The file was not found.";
        }

        if (exceptionHandlerPathFeature?.Path == "/")
        {
            ExceptionMessage ??= string.Empty;
            ExceptionMessage += " Page: Home.";
        }
    }
}

Aviso

Não forneça informações de erro confidenciais aos clientes. Fornecer erros é um risco à segurança.

Lambda do Manipulador de exceção

Uma alternativa a uma página personalizada de manipulador de exceção é fornecer um lambda a UseExceptionHandler. Usar um lambda permite acessar o erro antes de retornar a resposta.

O código abaixo usa um lambda para tratamento de exceções:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            // using static System.Net.Mime.MediaTypeNames;
            context.Response.ContentType = Text.Plain;

            await context.Response.WriteAsync("An exception was thrown.");

            var exceptionHandlerPathFeature =
                context.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
            {
                await context.Response.WriteAsync(" The file was not found.");
            }

            if (exceptionHandlerPathFeature?.Path == "/")
            {
                await context.Response.WriteAsync(" Page: Home.");
            }
        });
    });

    app.UseHsts();
}

Aviso

Não forneça informações de erro confidenciais aos clientes. Fornecer erros é um risco à segurança.

UseStatusCodePages

Por padrão, o aplicativo ASP.NET Core não fornece uma página de código de status para códigos de status de erro HTTP, como 404 – Não Encontrado. Quando o aplicativo define um código de status de erro HTTP 400-599 sem um corpo, ele retorna o código de status e um corpo de resposta vazio. Para habilitar os manipuladores padrão somente texto para os códigos de status de erros comuns, chame UseStatusCodePages em Program.cs:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages();

Chame UseStatusCodePages antes da solicitação de tratamento de middleware. Por exemplo, chame UseStatusCodePages antes do Middleware de Arquivo Estático e do Middleware de Pontos de Extremidade.

Quando UseStatusCodePages não for usado, a navegação até uma URL sem um ponto de extremidade retornará uma mensagem de erro dependente do navegador indicando que o ponto de extremidade não pode ser encontrado. Quando UseStatusCodePages é chamado, o navegador retorna a seguinte resposta:

Status Code: 404; Not Found

UseStatusCodePages normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

Observação

O middleware de páginas de código de status não captura exceções. Para fornecer uma página personalizada de tratamento de erros, use a página do manipulador de exceção.

UseStatusCodePages com cadeia de caracteres de formato

Para personalizar o texto e o tipo de conteúdo de resposta, use uma sobrecarga de UseStatusCodePages que leva um tipo de conteúdo e uma cadeia de caracteres de formato:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// using static System.Net.Mime.MediaTypeNames;
app.UseStatusCodePages(Text.Plain, "Status Code Page: {0}");

No código anterior, {0} é um espaço reservado para o código de erro.

UseStatusCodePages com uma cadeia de caracteres de formato normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

UseStatusCodePages com lambda

Para especificar a manipulação de erro personalizada e o código de gravação de resposta, use a sobrecarga de UseStatusCodePages que leva uma expressão lambda:

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePages(async statusCodeContext =>
{
    // using static System.Net.Mime.MediaTypeNames;
    statusCodeContext.HttpContext.Response.ContentType = Text.Plain;

    await statusCodeContext.HttpContext.Response.WriteAsync(
        $"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});

UseStatusCodePages com um lambda normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

UseStatusCodePagesWithRedirects

O método de extensão UseStatusCodePagesWithRedirects:

  • Envia um código de status 302 – Encontrado ao cliente.
  • Redireciona o cliente para o ponto de extremidade de tratamento de erros fornecido no modelo de URL. O ponto de extremidade de tratamento de erro normalmente exibe informações de erro e retorna HTTP 200.
var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");

O modelo de URL pode incluir um espaço reservado {0} para o código de status, conforme mostrado no código anterior. Se o modelo de URL começar com um til (~), o ~ será substituído pelo PathBase do aplicativo. Ao especificar um ponto de extremidade no aplicativo, crie uma exibição de MVC ou Razor Page para o ponto de extremidade.

Este método normalmente é usado quando o aplicativo:

  • Deveria redirecionar o cliente para um terminal diferente, geralmente em situações nas quais um aplicativo diferente processa o erro. Para aplicativos Web, a barra de endereços do navegador do cliente reflete o ponto de extremidade redirecionado.
  • Não deveria preservar e retornar o código de status original com a resposta de redirecionamento inicial.

UseStatusCodePagesWithReExecute

O método de extensão UseStatusCodePagesWithReExecute:

  • Retorna o código de status original ao cliente.
  • Gera o corpo da resposta ao executar novamente o pipeline de solicitação por meio de um caminho alternativo.
var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");

Se um ponto de extremidade dentro do aplicativo for especificado, crie uma exibição de MVC ou Razor Page para o ponto de extremidade.

Este método normalmente é usado quando o aplicativo tem que:

  • Processar a solicitação sem redirecionar para um ponto de extremidade diferente. Para aplicativos Web, a barra de endereços do navegador do cliente reflete o ponto de extremidade originalmente solicitado.
  • Preservar e retornar o código de status original com a resposta.

O modelo de URL precisa começar com / e pode incluir um espaço reservado {0} para o código de status. Para transmitir o código de status como um parâmetro de cadeia de caracteres de consulta, transmita um segundo argumento a UseStatusCodePagesWithReExecute. Por exemplo:

app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");

O ponto de extremidade que processa o erro pode obter a URL original que gerou o erro, conforme mostrado no exemplo a seguir:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
    public int OriginalStatusCode { get; set; }

    public string? OriginalPathAndQuery { get; set; }

    public void OnGet(int statusCode)
    {
        OriginalStatusCode = statusCode;

        var statusCodeReExecuteFeature =
            HttpContext.Features.Get<IStatusCodeReExecuteFeature>();

        if (statusCodeReExecuteFeature is not null)
        {
            OriginalPathAndQuery = string.Join(
                statusCodeReExecuteFeature.OriginalPathBase,
                statusCodeReExecuteFeature.OriginalPath,
                statusCodeReExecuteFeature.OriginalQueryString);
        }
    }
}

Desabilitar páginas de código de status

Para desabilitar as páginas de código de status de um método de ação ou controlador MVC, use o atributo [SkipStatusCodePages].

Para desabilitar as páginas de código de status de solicitações específicas em um método manipulador Razor Pages ou em um controlador MVC, use IStatusCodePagesFeature:

public void OnGet()
{
    var statusCodePagesFeature =
        HttpContext.Features.Get<IStatusCodePagesFeature>();

    if (statusCodePagesFeature is not null)
    {
        statusCodePagesFeature.Enabled = false;
    }
}

Código de tratamento de exceção

Código em páginas de tratamento de exceção também pode gerar exceções. As páginas de erro de produção devem ser testadas minuciosamente, e tome cuidado extra para evitar gerar exceções próprias.

Cabeçalhos de resposta

Depois que os cabeçalhos de resposta são enviados:

  • O aplicativo já não consegue alterar o código de status da resposta.
  • As páginas de exceção ou manipuladores não podem ser executados. A resposta deve ser concluída ou a conexão será anulada.

Tratamento de exceções do servidor

Além da lógica de tratamento de exceção no aplicativo, a implementação do servidor HTTP pode lidar com algumas exceções. Se o servidor capturar uma exceção antes que os cabeçalhos de resposta sejam enviados, o servidor enviará uma resposta 500 - Internal Server Error sem um corpo de resposta. Se o servidor capturar uma exceção depois que os cabeçalhos de resposta forem enviados, o servidor fechará a conexão. As solicitações que não são manipuladas pelo aplicativo são manipuladas pelo servidor. Qualquer exceção que ocorrer quando o servidor estiver tratando a solicitação será tratada pelo tratamento de exceção do servidor. As páginas de erro personalizadas do aplicativo, o middleware de tratamento de exceção e os filtros configurados não afetam esse comportamento.

Tratamento de exceção na inicialização

Apenas a camada de hospedagem pode tratar exceções que ocorrem durante a inicialização do aplicativo. O host pode ser configurado para capturar erros de inicialização e capturar erros detalhados.

A camada de hospedagem só poderá mostrar uma página de erro para um erro de inicialização capturado se o erro ocorrer após a associação de endereço do host/porta. Se a associação falhar:

  • A camada de hospedagem registrará uma exceção crítica.
  • O processo dotnet falhará.
  • Nenhuma página de erro é exibida quando o servidor HTTP é Kestrel.

Quando executado no IIS (ou no Serviço de Aplicativo do Azure) ou no IIS Express, um erro 502.5 Falha no Processo será retornado pelo Módulo do ASP.NET Core se o processo não puder ser iniciado. Para obter mais informações, consulte Solução de problemas do ASP.NET Core no Serviço de Aplicativo do Azure e no IIS.

Página de erro do banco de dados

O filtro de exceção de página do desenvolvedor de banco de dados AddDatabaseDeveloperPageExceptionFilter captura exceções relacionadas ao banco de dados que podem ser resolvidas usando migrações do Entity Framework Core. Quando estas exceções ocorrem, é gerada uma resposta HTML com detalhes das ações possíveis para resolver o problema. Esta página só fica habilitada no Ambiente de desenvolvimento. O código abaixo adiciona o filtro de exceção de página do desenvolvedor de banco de dados:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();

Filtros de exceção

Em aplicativos MVC, os filtros de exceção podem ser configurados globalmente, por controlador ou por ação. Em aplicativos do Razor Pages, eles podem ser configurados globalmente ou por modelo de página. Esses filtros tratam qualquer exceção sem tratamento ocorrida durante a execução de uma ação do controlador ou de outro filtro. Para obter mais informações, confira Filtros no ASP.NET Core.

Os filtros de exceção são úteis para interceptar exceções que ocorrem em ações do MVC, mas não são tão flexíveis quanto o middleware de tratamento de exceção, UseExceptionHandler. Recomendamos usar UseExceptionHandler, a não ser que você precise fazer o tratamento de erro de forma diferente com base na ação de MVC escolhida.

Erros de estado do modelo

Confira informações sobre como lidar com erros de estado de modelo em model binding e Validação de modelos.

Recursos adicionais

Por Kirk Larkin, Tom Dykstra e Steve Smith

Este artigo apresenta abordagens comuns para o tratamento de erros em aplicativos Web ASP.NET Core. Confira APIs Web em Erros de identificador nas APIs Web do ASP.NET Core.

Exibir ou baixar o código de exemplo. (Como baixar.) A guia de rede nas ferramentas de desenvolvedor do navegador F12 é útil durante o teste do aplicativo de exemplo.

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. Os modelos do ASP.NET Core geram o seguinte código:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

O código anterior realçado habilita a página de exceção do desenvolvedor quando o aplicativo está em execução no ambiente de desenvolvimento.

Os modelos colocam UseDeveloperExceptionPage no início do pipeline do middleware para que ele possa capturar exceções sem tratamento geradas no middleware a seguir.

O código anterior habilita a Página de exceção do desenvolvedor somente quando o aplicativo está em execução no ambiente de desenvolvimento. As informações detalhadas da exceção não devem ser exibidas publicamente quando o aplicativo é executado no ambiente de 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
  • Cabeçalhos

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.

Página do Manipulador de exceção

Para configurar uma página personalizada de tratamento de erros para o Ambiente de produção, chame UseExceptionHandler. Este middleware de tratamento de exceções:

  • Captura e registra exceções sem tratamento.
  • Executa novamente a solicitação em um pipeline alternativo usando o caminho indicado. A solicitação não será executada novamente se a resposta tiver sido iniciada. O código gerado pelo modelo executa novamente a solicitação usando o caminho /Error.

Aviso

Se o pipeline alternativo gerar uma exceção própria, o Middleware de Tratamento de Exceções aumentará a exceção original.

O exemplo abaixo, UseExceptionHandler inclui o middleware de manipulação de exceções em ambientes que não são de desenvolvimento:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

O modelo de aplicativo Razor Pages fornece uma Página de erro (.cshtml) e uma classe PageModel (ErrorModel) na pasta Pages. Para um aplicativo MVC, o modelo de projeto inclui um método de ação Error e uma Exibição de erro para o controlador Home.

O middleware de tratamento de exceção executa novamente a solicitação usando o método HTTP original. Se um ponto de extremidade do manipulador de erros estiver restrito a um conjunto específico de métodos HTTP, ele será executado somente para esses métodos HTTP. Por exemplo, uma ação do controlador MVC que usa o atributo [HttpGet] é executada apenas para solicitações GET. Para garantir que todas as solicitações cheguem à página de tratamento de erros personalizada, não as restrinja a um conjunto específico de métodos HTTP.

Para lidar com exceções de forma diferente com base no método HTTP original:

  • Para Razor Pages, crie vários métodos de manipulador. Por exemplo, use OnGet para lidar com exceções GET e use OnPost para lidar com exceções POST.
  • Para MVC, aplique atributos de verbo HTTP a várias ações. Por exemplo, use [HttpGet] para lidar com exceções GET e use [HttpPost] para lidar com exceções POST.

Para permitir que usuários não autenticados exibam a página de tratamento de erros personalizada, verifique se ela dá suporte ao acesso anônimo.

Acessar a exceção

Use IExceptionHandlerPathFeature para acessar a exceção e o caminho de solicitação original em um manipulador de erros. O código a seguir adiciona ExceptionMessage ao Pages/Error.cshtml.cs padrão gerado pelos modelos do ASP.NET Core:

[ResponseCache(Duration=0, Location=ResponseCacheLocation.None, NoStore=true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
    public string RequestId { get; set; }
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
    public string ExceptionMessage { get; set; }
    private readonly ILogger<ErrorModel> _logger;

    public ErrorModel(ILogger<ErrorModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
        HttpContext.Features.Get<IExceptionHandlerPathFeature>();
        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "File error thrown";
            _logger.LogError(ExceptionMessage);
        }
        if (exceptionHandlerPathFeature?.Path == "/index")
        {
            ExceptionMessage += " from home page";
        }
    }
}

Aviso

Não forneça informações de erro confidenciais aos clientes. Fornecer erros é um risco à segurança.

Para testar a exceção no aplicativo de amostra:

  • Defina o ambiente como produção.
  • Remova os comentários de webBuilder.UseStartup<Startup>(); em Program.cs.
  • Selecione Disparar uma exceção na home page.

Lambda do Manipulador de exceção

Uma alternativa a uma página personalizada de manipulador de exceção é fornecer um lambda a UseExceptionHandler. Usar um lambda permite acessar o erro antes de retornar a resposta.

O código abaixo usa um lambda para tratamento de exceções:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler(errorApp =>
        {
            errorApp.Run(async context =>
            {
                context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;;
                context.Response.ContentType = "text/html";

                await context.Response.WriteAsync("<html lang=\"en\"><body>\r\n");
                await context.Response.WriteAsync("ERROR!<br><br>\r\n");

                var exceptionHandlerPathFeature =
                    context.Features.Get<IExceptionHandlerPathFeature>();

                if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
                {
                    await context.Response.WriteAsync(
                                              "File error thrown!<br><br>\r\n");
                }

                await context.Response.WriteAsync(
                                              "<a href=\"/\">Home</a><br>\r\n");
                await context.Response.WriteAsync("</body></html>\r\n");
                await context.Response.WriteAsync(new string(' ', 512)); 
            });
        });
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Aviso

Não forneça informações de erro confidenciais de IExceptionHandlerFeature ou IExceptionHandlerPathFeature para clientes. Fornecer erros é um risco à segurança.

Para testar o lambda de tratamento de exceção no aplicativo de amostra:

  • Defina o ambiente como produção.
  • Remova os comentários de webBuilder.UseStartup<StartupLambda>(); em Program.cs.
  • Selecione Disparar uma exceção na home page.

UseStatusCodePages

Por padrão, o aplicativo ASP.NET Core não fornece uma página de código de status para códigos de status de erro HTTP, como 404 – Não Encontrado. Quando o aplicativo define um código de status de erro HTTP 400-599 sem um corpo, ele retorna o código de status e um corpo de resposta vazio. Use o middleware das Páginas de código de status para fornecer páginas de código de status. Para habilitar os manipuladores padrão somente texto para os códigos de status de erros comuns, chame UseStatusCodePages no método Startup.Configure:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePages();

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Chame UseStatusCodePages antes da solicitação de tratamento de middleware. Por exemplo, chame UseStatusCodePages antes do Middleware de Arquivo Estático e do Middleware de Pontos de Extremidade.

Quando UseStatusCodePages não for usado, a navegação até uma URL sem um ponto de extremidade retornará uma mensagem de erro dependente do navegador indicando que o ponto de extremidade não pode ser encontrado. Por exemplo, navegando até Home/Privacy2. Quando UseStatusCodePages é chamado, o navegador retorna:

Status Code: 404; Not Found

UseStatusCodePages normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

Para testar UseStatusCodePages no aplicativo de amostra:

  • Defina o ambiente como produção.
  • Remova os comentários de webBuilder.UseStartup<StartupUseStatusCodePages>(); em Program.cs.
  • Selecione os links na home page.

Observação

O middleware de páginas de código de status não captura exceções. Para fornecer uma página personalizada de tratamento de erros, use a página do manipulador de exceção.

UseStatusCodePages com cadeia de caracteres de formato

Para personalizar o texto e o tipo de conteúdo de resposta, use uma sobrecarga de UseStatusCodePages que leva um tipo de conteúdo e uma cadeia de caracteres de formato:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePages(
        "text/plain", "Status code page, status code: {0}");

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

No código anterior, {0} é um espaço reservado para o código de erro.

UseStatusCodePages com uma cadeia de caracteres de formato normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

Para testar UseStatusCodePages no aplicativo de amostra, remova os comentários de webBuilder.UseStartup<StartupFormat>(); em Program.cs.

UseStatusCodePages com lambda

Para especificar a manipulação de erro personalizada e o código de gravação de resposta, use a sobrecarga de UseStatusCodePages que leva uma expressão lambda:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePages(async context =>
    {
        context.HttpContext.Response.ContentType = "text/plain";

        await context.HttpContext.Response.WriteAsync(
            "Status code page, status code: " +
            context.HttpContext.Response.StatusCode);
    });

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

UseStatusCodePages com um lambda normalmente não é usado na produção porque retorna uma mensagem que não é útil para os usuários.

Para testar UseStatusCodePages no aplicativo de amostra, remova os comentários de webBuilder.UseStartup<StartupStatusLambda>(); em Program.cs.

UseStatusCodePagesWithRedirects

O método de extensão UseStatusCodePagesWithRedirects:

  • Envia um código de status 302 – Encontrado ao cliente.
  • Redireciona o cliente para o ponto de extremidade de tratamento de erros fornecido no modelo de URL. O ponto de extremidade de tratamento de erro normalmente exibe informações de erro e retorna HTTP 200.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePagesWithRedirects("/MyStatusCode?code={0}");

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

O modelo de URL pode incluir um espaço reservado {0} para o código de status, conforme mostrado no código anterior. Se o modelo de URL começar com um til (~), o ~ será substituído pelo PathBase do aplicativo. Ao especificar um ponto de extremidade no aplicativo, crie uma exibição de MVC ou Razor Page para o ponto de extremidade. Confira um exemplo de Razor Pages em Pages/MyStatusCode.cshtml no aplicativo de amostra.

Este método normalmente é usado quando o aplicativo:

  • Deveria redirecionar o cliente para um terminal diferente, geralmente em situações nas quais um aplicativo diferente processa o erro. Para aplicativos Web, a barra de endereços do navegador do cliente reflete o ponto de extremidade redirecionado.
  • Não deveria preservar e retornar o código de status original com a resposta de redirecionamento inicial.

Para testar UseStatusCodePages no aplicativo de amostra, remova os comentários de webBuilder.UseStartup<StartupSCredirect>(); em Program.cs.

UseStatusCodePagesWithReExecute

O método de extensão UseStatusCodePagesWithReExecute:

  • Retorna o código de status original ao cliente.
  • Gera o corpo da resposta ao executar novamente o pipeline de solicitação por meio de um caminho alternativo.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePagesWithReExecute("/MyStatusCode2", "?code={0}");

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Se um ponto de extremidade dentro do aplicativo for especificado, crie uma exibição de MVC ou Razor Page para o ponto de extremidade. Verifique se UseStatusCodePagesWithReExecute ele foi colocado antes de UseRouting para que a solicitação possa ser redirecionada para a página de status. Para obter um exemplo de Razor Pages, confira Pages/MyStatusCode2.cshtml no aplicativo de amostra.

Este método normalmente é usado quando o aplicativo tem que:

  • Processar a solicitação sem redirecionar para um ponto de extremidade diferente. Para aplicativos Web, a barra de endereços do navegador do cliente reflete o ponto de extremidade originalmente solicitado.
  • Preservar e retornar o código de status original com a resposta.

Os modelos de URL e cadeia de caracteres de consulta podem incluir um espaço reservado {0} para o código de status. O modelo de URL precisa começar com /.

O ponto de extremidade que processa o erro pode obter a URL original que gerou o erro, conforme mostrado no exemplo a seguir:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class MyStatusCode2Model : PageModel
{
    public string RequestId { get; set; }
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    public string ErrorStatusCode { get; set; }

    public string OriginalURL { get; set; }
    public bool ShowOriginalURL => !string.IsNullOrEmpty(OriginalURL);

    public void OnGet(string code)
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
        ErrorStatusCode = code;

        var statusCodeReExecuteFeature = HttpContext.Features.Get<
                                               IStatusCodeReExecuteFeature>();
        if (statusCodeReExecuteFeature != null)
        {
            OriginalURL =
                statusCodeReExecuteFeature.OriginalPathBase
                + statusCodeReExecuteFeature.OriginalPath
                + statusCodeReExecuteFeature.OriginalQueryString;
        }
    }
}

Para obter um exemplo de Razor Pages, confira Pages/MyStatusCode2.cshtml no aplicativo de amostra.

Para testar UseStatusCodePages no aplicativo de amostra, remova os comentários de webBuilder.UseStartup<StartupSCreX>(); em Program.cs.

Desabilitar páginas de código de status

Para desabilitar as páginas de código de status de um método de ação ou controlador MVC, use o atributo [SkipStatusCodePages].

Para desabilitar as páginas de código de status de solicitações específicas em um método manipulador Razor Pages ou em um controlador MVC, use IStatusCodePagesFeature:

public void OnGet()
{
    // using Microsoft.AspNetCore.Diagnostics;
    var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();

    if (statusCodePagesFeature != null)
    {
        statusCodePagesFeature.Enabled = false;
    }
}

Código de tratamento de exceção

Código em páginas de tratamento de exceção também pode gerar exceções. As páginas de erro de produção devem ser testadas minuciosamente, e tome cuidado extra para evitar gerar exceções próprias.

Cabeçalhos de resposta

Depois que os cabeçalhos de resposta são enviados:

  • O aplicativo já não consegue alterar o código de status da resposta.
  • As páginas de exceção ou manipuladores não podem ser executados. A resposta deve ser concluída ou a conexão será anulada.

Tratamento de exceções do servidor

Além da lógica de tratamento de exceção no aplicativo, a implementação do servidor HTTP pode lidar com algumas exceções. Se o servidor capturar uma exceção antes que os cabeçalhos de resposta sejam enviados, o servidor enviará uma resposta 500 - Internal Server Error sem um corpo de resposta. Se o servidor capturar uma exceção depois que os cabeçalhos de resposta forem enviados, o servidor fechará a conexão. As solicitações que não são manipuladas pelo aplicativo são manipuladas pelo servidor. Qualquer exceção que ocorrer quando o servidor estiver tratando a solicitação será tratada pelo tratamento de exceção do servidor. As páginas de erro personalizadas do aplicativo, o middleware de tratamento de exceção e os filtros configurados não afetam esse comportamento.

Tratamento de exceção na inicialização

Apenas a camada de hospedagem pode tratar exceções que ocorrem durante a inicialização do aplicativo. O host pode ser configurado para capturar erros de inicialização e capturar erros detalhados.

A camada de hospedagem só poderá mostrar uma página de erro para um erro de inicialização capturado se o erro ocorrer após a associação de endereço do host/porta. Se a associação falhar:

  • A camada de hospedagem registrará uma exceção crítica.
  • O processo dotnet falhará.
  • Nenhuma página de erro é exibida quando o servidor HTTP é Kestrel.

Quando executado no IIS (ou no Serviço de Aplicativo do Azure) ou no IIS Express, um erro 502.5 Falha no Processo será retornado pelo Módulo do ASP.NET Core se o processo não puder ser iniciado. Para obter mais informações, consulte Solução de problemas do ASP.NET Core no Serviço de Aplicativo do Azure e no IIS.

Página de erro do banco de dados

O filtro de exceção de página do desenvolvedor de banco de dados AddDatabaseDeveloperPageExceptionFilter captura exceções relacionadas ao banco de dados que podem ser resolvidas usando migrações do Entity Framework Core. Quando estas exceções ocorrem, é gerada uma resposta HTML com detalhes das ações possíveis para resolver o problema. Esta página só fica habilitada no Ambiente de desenvolvimento. O código a seguir foi gerado pelos modelos Razor Pages do ASP.NET Core quando contas de usuário individuais foram especificadas:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDatabaseDeveloperPageExceptionFilter();
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();
}

Filtros de exceção

Em aplicativos MVC, os filtros de exceção podem ser configurados globalmente, por controlador ou por ação. Em aplicativos do Razor Pages, eles podem ser configurados globalmente ou por modelo de página. Esses filtros tratam qualquer exceção sem tratamento ocorrida durante a execução de uma ação do controlador ou de outro filtro. Para obter mais informações, confira Filtros no ASP.NET Core.

Os filtros de exceção são úteis para interceptar exceções que ocorrem em ações do MVC, mas não são tão flexíveis quanto o middleware de tratamento de exceção, UseExceptionHandler. Recomendamos usar UseExceptionHandler, a não ser que você precise fazer o tratamento de erro de forma diferente com base na ação de MVC escolhida.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Erros de estado do modelo

Confira informações sobre como lidar com erros de estado de modelo em model binding e Validação de modelos.

Recursos adicionais

Por Tom Dykstra e Steve Smith

Este artigo apresenta abordagens comuns para o tratamento de erros em aplicativos Web ASP.NET Core. Confira APIs Web em Erros de identificador nas APIs Web do ASP.NET Core.

Exibir ou baixar o código de exemplo. (Como baixar.)

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. Os modelos do ASP.NET Core geram o seguinte código:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

O código anterior habilita a página de exceção do desenvolvedor quando o aplicativo está em execução no ambiente de desenvolvimento.

Os modelos colocam UseDeveloperExceptionPage antes de um middleware para que as exceções sejam capturadas no middleware a seguir.

O código anterior habilita a Página de exceção do desenvolvedor somente quando o aplicativo está em execução no ambiente de desenvolvimento. As informações detalhadas de exceção não devem ser exibidas publicamente quando o aplicativo é executado na 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 inclui 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
  • Cabeçalhos

Página do Manipulador de exceção

Use o Middleware de tratamento de exceção para configurar uma página personalizada de tratamento de erro para o ambiente de produção. O middleware:

  • Captura e registra em log as exceções.
  • Executa novamente a solicitação em um pipeline alternativo para a página ou controlador indicado. A solicitação não será executada novamente se a resposta tiver sido iniciada. O código gerado pelo modelo executa novamente a solicitação para /Error.

No exemplo a seguir, UseExceptionHandler inclui o Middleware de Manipulação de Exceções em ambientes que não são de desenvolvimento:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

O modelo de aplicativo Razor Pages fornece uma Página de erro (.cshtml) e uma classe PageModel (ErrorModel) na pasta Pages. Para um aplicativo MVC, o modelo de projeto inclui um Método de ação de erro e uma Exibição de erro no controlador Home.

Não marque o método de ação do manipulador de erro com atributos de método HTTP, como HttpGet. Verbos explícitos impedem algumas solicitações de chegar ao método. Permita o acesso anônimo ao método se os usuários não autenticados tiverem que ver a exibição de erro.

Acessar a exceção

Use IExceptionHandlerPathFeature para acessar a exceção e o caminho de solicitação original em uma página ou controlador de manipulador de erro:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
    public string RequestId { get; set; }
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
    public string ExceptionMessage { get; set; }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
            HttpContext.Features.Get<IExceptionHandlerPathFeature>();
        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "File error thrown";
        }
        if (exceptionHandlerPathFeature?.Path == "/index")
        {
            ExceptionMessage += " from home page";
        }
    }
}

Aviso

Não forneça informações de erro confidenciais aos clientes. Fornecer erros é um risco à segurança.

Para disparar a página de tratamento de exceção anterior, defina o ambiente como produções e force uma exceção.

Lambda do Manipulador de exceção

Uma alternativa a uma página personalizada de manipulador de exceção é fornecer um lambda a UseExceptionHandler. Usar um lambda permite acessar o erro antes de retornar a resposta.

Este é um exemplo de como usar um lambda para a manipulação de exceção:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
   app.UseExceptionHandler(errorApp =>
   {
        errorApp.Run(async context =>
        {
            context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
            context.Response.ContentType = "text/html";

            await context.Response.WriteAsync("<html lang=\"en\"><body>\r\n");
            await context.Response.WriteAsync("ERROR!<br><br>\r\n");

            var exceptionHandlerPathFeature = 
                context.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
            {
                await context.Response.WriteAsync("File error thrown!<br><br>\r\n");
            }

            await context.Response.WriteAsync("<a href=\"/\">Home</a><br>\r\n");
            await context.Response.WriteAsync("</body></html>\r\n");
            await context.Response.WriteAsync(new string(' ', 512)); // IE padding
        });
    });
    app.UseHsts();
}

No código anterior, await context.Response.WriteAsync(new string(' ', 512)); é adicionado para que o navegador Internet Explorer exiba a mensagem de erro em vez de uma mensagem de erro do IE. Para obter mais informações, confira este problema do GitHub.

Aviso

Não forneça informações de erro confidenciais de IExceptionHandlerFeature ou IExceptionHandlerPathFeature para clientes. Fornecer erros é um risco à segurança.

Para ver o resultado do lambda de tratamento de exceções no aplicativo de exemplo, use as diretivas de pré-processador ProdEnvironment e ErrorHandlerLambda e selecione Disparar uma exceção na página inicial.

UseStatusCodePages

Por padrão, o aplicativo ASP.NET Core não fornece uma página de código de status para códigos de status HTTP, como 404 - Não Encontrado. O aplicativo retornar um código de status e um corpo de resposta vazio. Use o middleware das Páginas de código de status para fornecer páginas de código de status.

O middleware é disponibilizado pelo pacote Microsoft.AspNetCore.Diagnostics.

Para habilitar os manipuladores padrão somente texto para os códigos de status de erros comuns, chame UseStatusCodePages no método Startup.Configure:

app.UseStatusCodePages();

Chame UseStatusCodePages antes do middleware de tratamento da solicitação (por exemplo, o middleware de arquivos estáticos e o middleware MVC).

Quando UseStatusCodePages não for usado, a navegação até uma URL sem um ponto de extremidade retornará uma mensagem de erro dependente do navegador indicando que o ponto de extremidade não pode ser encontrado. Por exemplo, navegando até Home/Privacy2. Quando UseStatusCodePages é chamado, o navegador retorna:

Status Code: 404; Not Found

UseStatusCodePages com cadeia de caracteres de formato

Para personalizar o texto e o tipo de conteúdo de resposta, use uma sobrecarga de UseStatusCodePages que leva um tipo de conteúdo e uma cadeia de caracteres de formato:

app.UseStatusCodePages(
    "text/plain", "Status code page, status code: {0}");

UseStatusCodePages com lambda

Para especificar a manipulação de erro personalizada e o código de gravação de resposta, use a sobrecarga de UseStatusCodePages que leva uma expressão lambda:

app.UseStatusCodePages(async context =>
{
    context.HttpContext.Response.ContentType = "text/plain";

    await context.HttpContext.Response.WriteAsync(
        "Status code page, status code: " + 
        context.HttpContext.Response.StatusCode);
});

UseStatusCodePagesWithRedirects

O método de extensão UseStatusCodePagesWithRedirects:

  • Envia um código de status 302 – Encontrado ao cliente.
  • Redireciona o cliente para o local fornecido no modelo de URL.
app.UseStatusCodePagesWithRedirects("/StatusCode?code={0}");

O modelo de URL pode incluir um espaço reservado {0} para o código de status, conforme mostrado no exemplo. Se o modelo de URL começar com um til (~), o ~ será substituído pelo PathBase do aplicativo. Se você apontar para um ponto de extremidade dentro do aplicativo, crie um modo de exibição do MVC ou Razor Page para o ponto de extremidade. Para obter um exemplo de Razor Pages, confira Pages/StatusCode.cshtml no aplicativo de amostra.

Este método normalmente é usado quando o aplicativo:

  • Deveria redirecionar o cliente para um terminal diferente, geralmente em situações nas quais um aplicativo diferente processa o erro. Para aplicativos Web, a barra de endereços do navegador do cliente reflete o ponto de extremidade redirecionado.
  • Não deveria preservar e retornar o código de status original com a resposta de redirecionamento inicial.

UseStatusCodePagesWithReExecute

O método de extensão UseStatusCodePagesWithReExecute:

  • Retorna o código de status original ao cliente.
  • Gera o corpo da resposta ao executar novamente o pipeline de solicitação por meio de um caminho alternativo.
app.UseStatusCodePagesWithReExecute("/StatusCode","?code={0}");

Se você apontar para um ponto de extremidade dentro do aplicativo, crie um modo de exibição do MVC ou Razor Page para o ponto de extremidade. Verifique se UseStatusCodePagesWithReExecute ele foi colocado antes de UseRouting para que a solicitação possa ser redirecionada para a página de status. Para obter um exemplo de Razor Pages, confira Pages/StatusCode.cshtml no aplicativo de amostra.

Este método normalmente é usado quando o aplicativo tem que:

  • Processar a solicitação sem redirecionar para um ponto de extremidade diferente. Para aplicativos Web, a barra de endereços do navegador do cliente reflete o ponto de extremidade originalmente solicitado.
  • Preservar e retornar o código de status original com a resposta.

Os modelos de URL e cadeia de caracteres de consulta podem incluir um espaço reservado ({0}) para o código de status. O modelo de URL deve começar com uma barra (/). Ao usar um espaço reservado no caminho, verifique se o ponto de extremidade (página ou controlador) pode processar o segmento de linha. Por exemplo, uma Razor Page para erros deve aceitar o valor de segmento de linha opcional com a diretiva @page:

@page "{code?}"

O ponto de extremidade que processa o erro pode obter a URL original que gerou o erro, conforme mostrado no exemplo a seguir:

var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature != null)
{
    OriginalURL =
        statusCodeReExecuteFeature.OriginalPathBase
        + statusCodeReExecuteFeature.OriginalPath
        + statusCodeReExecuteFeature.OriginalQueryString;
}

Não marque o método de ação do manipulador de erro com atributos de método HTTP, como HttpGet. Verbos explícitos impedem algumas solicitações de chegar ao método. Permita o acesso anônimo ao método se os usuários não autenticados tiverem que ver a exibição de erro.

Desabilitar páginas de código de status

Para desabilitar as páginas de código de status de um método de ação ou controlador MVC, use o atributo [SkipStatusCodePages].

Para desabilitar as páginas de código de status de solicitações específicas em um método manipulador Razor Pages ou em um controlador MVC, use IStatusCodePagesFeature:

var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();

if (statusCodePagesFeature != null)
{
    statusCodePagesFeature.Enabled = false;
}

Código de tratamento de exceção

Código em páginas de tratamento de exceção pode gerar exceções. Geralmente, é uma boa ideia que páginas de erro de produção sejam compostas por conteúdo puramente estático.

Cabeçalhos de resposta

Depois que os cabeçalhos de resposta são enviados:

  • O aplicativo já não consegue alterar o código de status da resposta.
  • As páginas de exceção ou manipuladores não podem ser executados. A resposta deve ser concluída ou a conexão será anulada.

Tratamento de exceções do servidor

Além da lógica de tratamento de exceção no aplicativo, a implementação do servidor HTTP pode lidar com algumas exceções. Se o servidor capturar uma exceção antes que os cabeçalhos de resposta sejam enviados, o servidor enviará uma resposta 500 Erro Interno do Servidor sem um corpo de resposta. Se o servidor capturar uma exceção depois que os cabeçalhos de resposta forem enviados, o servidor fechará a conexão. As solicitações que não são manipuladas pelo aplicativo são manipuladas pelo servidor. Qualquer exceção que ocorrer quando o servidor estiver tratando a solicitação será tratada pelo tratamento de exceção do servidor. As páginas de erro personalizadas do aplicativo, o middleware de tratamento de exceção e os filtros configurados não afetam esse comportamento.

Tratamento de exceção na inicialização

Apenas a camada de hospedagem pode tratar exceções que ocorrem durante a inicialização do aplicativo. O host pode ser configurado para capturar erros de inicialização e capturar erros detalhados.

A camada de hospedagem só poderá mostrar uma página de erro para um erro de inicialização capturado se o erro ocorrer após a associação de endereço do host/porta. Se a associação falhar:

  • A camada de hospedagem registrará uma exceção crítica.
  • O processo dotnet falhará.
  • Nenhuma página de erro é exibida quando o servidor HTTP é Kestrel.

Quando executado no IIS (ou no Serviço de Aplicativo do Azure) ou no IIS Express, um erro 502.5 Falha no Processo será retornado pelo Módulo do ASP.NET Core se o processo não puder ser iniciado. Para obter mais informações, consulte Solução de problemas do ASP.NET Core no Serviço de Aplicativo do Azure e no IIS.

Página de erro do banco de dados

O Middleware da Página de erro de banco de dados captura as exceções relacionadas a banco de dados que podem ser resolvidas com o uso de migrações do Entity Framework. Quando estas exceções ocorrem, é gerada uma resposta HTML com detalhes das ações possíveis para resolver o problema. Esta página só deve ser habilitada no Ambiente de desenvolvimento. Habilite a página adicionando código a Startup.Configure:

if (env.IsDevelopment())
{
    app.UseDatabaseErrorPage();
}

UseDatabaseErrorPage requer o pacote NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.

Filtros de exceção

Em aplicativos MVC, os filtros de exceção podem ser configurados globalmente, por controlador ou por ação. Em aplicativos do Razor Pages, eles podem ser configurados globalmente ou por modelo de página. Esses filtros tratam qualquer exceção sem tratamento ocorrida durante a execução de uma ação do controlador ou de outro filtro. Para obter mais informações, confira Filtros no ASP.NET Core.

Dica

Os filtros de exceção são úteis para interceptar exceções que ocorrem em ações do MVC, mas não são tão flexíveis quanto o middleware de tratamento de exceção. É recomendável usar o middleware. Use filtros somente onde você precisar fazer o tratamento de erro de forma diferente com base na ação de MVC escolhida.

Erros de estado do modelo

Confira informações sobre como lidar com erros de estado de modelo em model binding e Validação de modelos.

Recursos adicionais