Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Note
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.
Warning
Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.
Este artigo aborda abordagens comuns para lidar com erros em aplicativos Web ASP.NET Core. Consulte também Manipular erros em ASP.NET APIs principais.
Para Blazor obter orientações sobre tratamento de erros, que adicionam ou substituem as orientações deste artigo, consulte Manipular erros em aplicativos ASP.NET CoreBlazor.
Página de exceção do desenvolvedor
A página de exceção do desenvolvedor exibe informações detalhadas sobre exceções de solicitação não tratadas. Ele usa DeveloperExceptionPageMiddleware para capturar exceções síncronas e assíncronas do pipeline HTTP e para gerar respostas de erro. A página de exceção do desenvolvedor é executada no início do pipeline de middleware, para que possa capturar exceções não tratadas lançadas no middleware subsequente.
ASP.NET aplicativos principais habilitam a página de exceção do desenvolvedor por padrão quando:
- Execução no ambiente de Desenvolvimento.
- O aplicativo foi criado com os modelos atuais, ou seja, usando WebApplication.CreateBuilder.
Os aplicativos criados usando modelos anteriores, ou seja, usando WebHost.CreateDefaultBuilder, podem habilitar a página de exceção do desenvolvedor chamando app.UseDeveloperExceptionPage.
Warning
Não habilite a Página de Exceção do Desenvolvedor , a menos que o aplicativo esteja sendo executado no ambiente de Desenvolvimento. Não compartilhe informações detalhadas de exceção publicamente quando o aplicativo for executado em produção. Para obter mais informações sobre como configurar ambientes, consulte ASP.NET Core runtime environments.
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 da cadeia de caracteres de consulta, se houver
- Cookies, se existirem
- Headers
- Metadados do ponto final, se houver
Não é garantido que a Página de Exceção do Desenvolvedor forneça nenhuma informação. Utilize o Registo para obter informações completas sobre erros.
A imagem a seguir mostra uma página de exceção de desenvolvedor de exemplo com animação para mostrar as guias e as informações exibidas:
Em resposta a uma solicitação com um Accept: text/plain cabeçalho, a Página de Exceção do Desenvolvedor retorna texto sem formatação em vez de HTML. Por exemplo:
Status: 500 Internal Server Error
Time: 9.39 msSize: 480 bytes
FormattedRawHeadersRequest
Body
text/plain; charset=utf-8, 480 bytes
System.InvalidOperationException: Sample Exception
at WebApplicationMinimal.Program.<>c.<Main>b__0_0() in C:\Source\WebApplicationMinimal\Program.cs:line 12
at lambda_method1(Closure, Object, HttpContext)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
HEADERS
=======
Accept: text/plain
Host: localhost:7267
traceparent: 00-0eab195ea19d07b90a46cd7d6bf2f
Página do manipulador de exceções
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 não tratadas.
- 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 reexecuta a solicitação usando o
/Errorcaminho.
Warning
Caso o pipeline alternativo lance uma exceção própria, o Middleware de Manipulação de Exceções relançará a exceção original.
Como esse middleware pode executar novamente o pipeline de solicitação:
- Os middlewares precisam manejar a reentrância com a mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar
_nextou armazenar em cache seu processamento noHttpContextpara evitar refazê-lo. Ao lidar com o corpo da solicitação, isto pode significar armazenar os resultados em buffer ou em cache, como faz o leitor de formulários. - Para a UseExceptionHandler(IApplicationBuilder, String) sobrecarga usada em modelos, somente o caminho da solicitação é modificado e os dados da rota são limpos. Os dados de solicitação, como cabeçalhos, método e itens, são todos reutilizados tal como estão.
- Os serviços com escopo permanecem os mesmos.
No exemplo a seguir, UseExceptionHandler adiciona o middleware de tratamento de exceção em ambientes que não são de desenvolvimento:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
O Razor modelo de aplicativo Páginas fornece uma página de erro (.cshtml) e PageModel uma classe (ErrorModel) na pasta Páginas . Para um aplicativo MVC, o modelo de projeto inclui um Error método de ação e uma visualização de erro para o Home controlador.
O middleware de tratamento de exceção reexecuta 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 apenas para esses métodos HTTP. Por exemplo, uma ação do controlador MVC que usa o atributo [HttpGet] é executada somente para solicitações GET. Para garantir que todas as solicitações cheguem à página personalizada de tratamento de erros, 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
OnGetpara manipular exceções GET e useOnPostpara manipular exceções POST. - Para MVC, aplique atributos verbais HTTP a várias ações. Por exemplo, use
[HttpGet]para manipular exceções GET e use[HttpPost]para manipular exceções POST.
Para permitir que usuários não autenticados visualizem a página personalizada de tratamento de erros, verifique se ela oferece suporte ao acesso anônimo.
Aceda à exceção
Use IExceptionHandlerPathFeature para acessar a exceção e o caminho da solicitação original em um manipulador de erros. O exemplo a seguir usa IExceptionHandlerPathFeature para obter mais informações sobre a exceção que foi lançada:
[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.";
}
}
}
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
Manipulador de exceções lambda
Uma alternativa a uma página de manipulador de exceção personalizada é fornecer um lambda para UseExceptionHandler. O uso de um lambda permite o acesso ao erro antes de retornar a resposta.
O código a seguir 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();
}
Outra maneira de usar um lambda é definir o código de status com base no tipo de exceção, como no exemplo a seguir:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError
});
}
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
IExceptionHandler
IExceptionHandler é uma interface que dá ao desenvolvedor um callback para lidar com exceções conhecidas em um local central. A interface contém um único método, TryHandleAsync, que recebe um HttpContext e um Exception parâmetro.
IExceptionHandler implementações são registradas chamando IServiceCollection.AddExceptionHandler<T>. O tempo de vida de uma instância de IExceptionHandler é único. Várias implementações podem ser adicionadas e são chamadas na ordem registrada.
O middleware de tratamento de exceções percorre os manipuladores de exceções registados em ordem até que um devolva true de TryHandleAsync, indicando que a exceção foi tratada. Se um manipulador de exceção manipular uma exceção, ele poderá retornar true para interromper o processamento. Se uma exceção não for manipulada por nenhum manipulador de exceções, o controle retornará ao comportamento padrão e às opções do middleware.
A partir do .NET 10, o comportamento padrão é suprimir a emissão de diagnósticos, como logs e métricas, em exceções manipuladas (quando TryHandleAsync retorna true). Isso difere das versões anteriores (.NET 8 e 9), onde o diagnóstico sempre foi emitido, independentemente de a exceção ter sido tratada. O comportamento padrão pode ser alterado definindo SuppressDiagnosticsCallback.
O exemplo a seguir mostra uma IExceptionHandler implementação:
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 IExceptionHandler implementação 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 lidar com uma exceção. - Depois de registrar a exceção, o
TryHandleAsyncmétodo retornafalse, para que a página de exceção do desenvolvedor seja mostrada.
Em outros ambientes:
- O
CustomExceptionHandleré chamado primeiro para lidar com uma exceção. - Depois de registrar a exceção, o
TryHandleAsyncmétodo retornafalse, para que a/Errorpágina seja mostrada.
SuppressDiagnosticsCallback
A partir do .NET 10, você pode controlar se o middleware de manipulação de exceções grava diagnósticos para exceções manipuladas configurando a SuppressDiagnosticsCallback propriedade em ExceptionHandlerOptions. Este callback recebe o contexto da exceção e permite determinar se o diagnóstico deve ser suprimido com base na exceção ou no pedido específico.
Para restaurar o comportamento do .NET 8 e 9, em que os diagnósticos são sempre emitidos para exceções tratadas, defina o callback para sempre retornar false:
app.UseExceptionHandler(new ExceptionHandlerOptions
{
SuppressDiagnosticsCallback = context => false
});
Você também pode suprimir condicionalmente diagnósticos com base no tipo de exceção ou outro contexto:
app.UseExceptionHandler(new ExceptionHandlerOptions
{
SuppressDiagnosticsCallback = context => context.Exception is ArgumentException
});
Quando uma exceção não é tratada por nenhuma IExceptionHandler implementação (todos os manipuladores retornam false do TryHandleAsync), o controle retorna ao comportamento padrão e às opções do middleware, e os diagnósticos são emitidos de acordo com o comportamento padrão do middleware.
UseStatusCodePages
Por padrão, um 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 que não tem um corpo, ele retorna o código de status e um corpo de resposta vazio. Para habilitar manipuladores padrão de apenas texto para códigos de status de erro comuns, chame UseStatusCodePages em Program.cs.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Ligue UseStatusCodePages antes de solicitar middleware de manipulação. Por exemplo, chame UseStatusCodePages antes do Middleware de Ficheiros Estáticos e do Middleware de Endpoints.
Quando UseStatusCodePages não é usado, navegar para uma URL sem um ponto de extremidade retorna 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.
Note
O middleware de páginas de códigos de estado não captura exceções. Para fornecer uma página personalizada de tratamento de erros, use a página do manipulador de exceções.
UseStatusCodePages com cadeia de caracteres de formato
Para personalizar o tipo de conteúdo de resposta e o texto, use a sobrecarga de UseStatusCodePages que aceita um tipo de conteúdo e uma string de formatação:
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 é usada na produção porque retorna uma mensagem que não é útil para os usuários.
UseStatusCodePages com lambda
Para especificar o código personalizado de manipulação de erros e gravação de resposta, use a sobrecarga de UseStatusCodePages que usa 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 em produção porque retorna uma mensagem que não é útil para os utilizadores.
UseStatusCodePagesWithRedirects
O UseStatusCodePagesWithRedirects método de extensão:
- Envia um código de status 302 - Encontrado para o 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 erros 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 {0} marcador de posição para o código de estado, tal como mostrado no código anterior. Se o modelo de URL começar com ~ (tilde), o ~ é substituído pelo PathBase do aplicativo. Ao especificar um ponto de extremidade na aplicação, crie uma vista ou Razor página MVC para o ponto de extremidade.
Esse método é comumente usado quando o aplicativo:
- Deve redirecionar o cliente para um ponto de extremidade diferente, geralmente nos casos em que um aplicativo diferente processa o erro. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint redirecionado.
- Não deve preservar e retornar o código de status original com a resposta de redirecionamento inicial.
UseStatusCodePagesWithReExecute
O UseStatusCodePagesWithReExecute método de extensão:
- Gera o corpo de resposta executando novamente o pipeline de solicitação usando um caminho alternativo.
- Não altera o código de status antes ou depois de executar novamente o pipeline.
A execução do novo pipeline pode alterar o código de status da resposta, pois o novo pipeline tem controle total do 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 da aplicação for especificado, crie uma vista MVC ou Razor página para o ponto de extremidade.
Esse método é comumente usado quando o aplicativo deve:
- Processe a solicitação sem redirecionar para um ponto de extremidade diferente. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint originalmente solicitado.
- Preserve e devolva o código de status original com a resposta.
O modelo de URL deve começar com / e pode incluir um espaço reservado {0} para o código de status. Para passar o código de status como um parâmetro de cadeia de caracteres de consulta, passe um segundo argumento para UseStatusCodePagesWithReExecute. Por exemplo:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
O endpoint 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 = $"{statusCodeReExecuteFeature.OriginalPathBase}"
+ $"{statusCodeReExecuteFeature.OriginalPath}"
+ $"{statusCodeReExecuteFeature.OriginalQueryString}";
}
}
}
Como esse middleware pode executar novamente o pipeline de solicitação:
- Os middlewares precisam manejar a reentrância com a mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar
_nextou armazenar em cache seu processamento noHttpContextpara evitar refazê-lo. Ao lidar com o corpo da solicitação, isto pode significar armazenar os resultados em buffer ou em cache, como faz o leitor de formulários. - Os serviços com escopo permanecem os mesmos.
Desativar páginas de código de estado
Para desabilitar páginas de código de status para um controlador MVC ou método de ação, use o atributo [SkipStatusCodePages].
Para desabilitar páginas de código de status para solicitações específicas em um Razor método manipulador 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ções
O código em páginas de tratamento de exceções também pode gerar exceções. As páginas de erro de produção devem ser testadas minuciosamente e tomar cuidado extra para evitar lançar exceções próprias.
Cabeçalhos de resposta
Uma vez que os cabeçalhos de uma resposta são enviados:
- O aplicativo não pode alterar o código de status da resposta.
- Quaisquer páginas de exceção ou manipuladores não podem ser executados. A resposta deve ser concluída ou a conexão anulada.
Tratamento de exceções do servidor
Além da lógica de tratamento de exceções em um aplicativo, a implementação do servidor HTTP pode lidar com algumas exceções. Se o servidor detetar uma exceção antes que os cabeçalhos de resposta sejam enviados, o servidor enviará uma 500 - Internal Server Error resposta sem um corpo de resposta. Se o servidor detetar 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 tratadas pelo aplicativo são tratadas pelo servidor. Qualquer exceção que ocorra quando o servidor está manipulando a solicitação é tratada pelo tratamento de exceções do servidor. As páginas de erro personalizadas, o middleware de tratamento de exceções e os filtros do aplicativo não afetam esse comportamento.
Manejo de exceções durante a inicialização
Somente a camada de hospedagem pode lidar com 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 pode mostrar uma página de erro para um erro de inicialização capturado somente se o erro ocorrer após a vinculação de endereço/porta do host. Se a vinculação falhar:
- A camada de hospedagem registra uma exceção crítica.
- O processo dotnet falha.
- Nenhuma página de erro é exibida quando o servidor HTTP é Kestrel.
Ao ser executado no IIS (ou no Serviço de Aplicativo do Azure) ou no IIS Express, uma Falha de Processo 502.5 - é retornada pelo ASP.NET Módulo Principal se o processo não puder ser iniciado. Para obter mais informações, consulte Solucionar 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 AddDatabaseDeveloperPageExceptionFilter de exceção da página do desenvolvedor do banco de dados captura exceções relacionadas ao banco de dados que podem ser resolvidas usando migrações do Entity Framework Core. Quando essas exceções ocorrem, uma resposta HTML é gerada com detalhes de possíveis ações para resolver o problema. Esta página está habilitada somente no ambiente de desenvolvimento. O código a seguir adiciona o filtro de exceção da página do desenvolvedor do 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 ou por controlador ou por ação. Nos Razor aplicativos do Pages, eles podem ser configurados globalmente ou por modelo de página. Esses filtros lidam com quaisquer exceções não tratadas que ocorram durante a execução de uma ação do controlador ou outro filtro. Para obter mais informações, consulte Filtros no ASP.NET Core.
Os filtros de exceção são úteis para intercetar exceções que ocorrem em ações MVC, mas não são tão flexíveis quanto o middleware interno de tratamento de exceções, UseExceptionHandler. Recomendamos o uso do UseExceptionHandler, a menos que você precise executar o tratamento de erros de forma diferente com base na ação MVC escolhida.
Erros de estado do modelo
Para obter informações sobre como lidar com erros de estado do modelo, consulte Vinculação de modelo e validação de modelo.
Detalhes do problema
Detalhes do problema não são o único formato de resposta para descrever um erro de API HTTP, no entanto, eles são comumente usados para relatar erros para APIs HTTP.
O serviço de detalhes do problema implementa a interface IProblemDetailsService, que suporta a criação de detalhes do problema no ASP.NET Core. O AddProblemDetails(IServiceCollection) método de extensão em IServiceCollection registra a implementação padrão IProblemDetailsService .
Nos aplicações ASP.NET Core, o middleware seguinte gera respostas HTTP com detalhes de problema quando
- ExceptionHandlerMiddleware: Gera uma resposta de detalhes do problema quando um manipulador personalizado não está definido.
- StatusCodePagesMiddleware: Gera uma resposta de detalhes do problema por padrão.
-
DeveloperExceptionPageMiddleware: Gera uma resposta de detalhes do problema durante o desenvolvimento quando o cabeçalho de solicitação HTTP
Acceptnão incluitext/html.
O código a seguir configura a aplicação para gerar uma resposta com detalhes do problema para todas as respostas de erro do cliente e do servidor HTTP que ainda não têm corpo de conteúdo:
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 dos detalhes do problema.
Personalizar detalhes do problema
A criação automática de um ProblemDetails pode ser personalizada usando qualquer uma das seguintes opções:
- Utilize
ProblemDetailsOptions.CustomizeProblemDetails - Utilize um
IProblemDetailsWriterpersonalizado - Chamar o
IProblemDetailsServiceem um middleware
CustomizeProblemDetails Funcionamento
Os detalhes do problema gerado podem ser personalizados usando CustomizeProblemDetails, e as personalizações são aplicadas a todos os detalhes gerados automaticamente do problema.
O código a seguir 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 HTTP Status 400 Bad Request endpoint produz o seguinte corpo da resposta com detalhes do problema:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
Personalizado IProblemDetailsWriter
Uma IProblemDetailsWriter implementação 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 utilizar um IProblemDetailsWriter personalizado, o IProblemDetailsWriter personalizado deve ser registado 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 para usar ProblemDetailsOptions com CustomizeProblemDetails é definir o ProblemDetails no middleware. Uma resposta de detalhes do problema pode ser escrita ligando para 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 de API mínimos /divide e /squareroot retornam a resposta de problema personalizada esperada sobre uma entrada com erro.
Os pontos de extremidade do controlador de API retornam a resposta de problema padrão quando há erro na entrada, não a resposta de problema personalizada. A resposta padrão do problema é retornada porque o controlador de API gravou no fluxo de resposta, Detalhes do problema para códigos de status de erro, antes IProblemDetailsService.WriteAsync é chamado e a resposta não é gravada novamente.
O seguinte ValuesController retorna BadRequestResult, que grava no fluxo de resposta e, portanto, impede que a resposta de 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 seguinte Values3Controller retorna ControllerBase.Problem para que o resultado esperado do problema personalizado 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 uma carga útil de 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, a seguinte é 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 problemas mais detalhadas.
Uma alternativa a uma página de manipulador de exceção personalizada é fornecer um lambda para UseExceptionHandler. Usar um lambda permite acessar o erro e escrever 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();
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
Uma abordagem alternativa para gerar detalhes do problema é usar o pacote NuGet de terceiros Hellang.Middleware.ProblemDetails que pode ser usado para mapear exceções e erros do cliente para detalhes do problema.
Recursos adicionais
- Visualizar ou descarregar amostra de código (como descarregar)
- Solucionar problemas do ASP.NET Core no Serviço de Aplicativo do Azure e no IIS
- Solução de problemas de erro comum para o Serviço de Aplicativo do Azure e o IIS com ASP.NET Core
- Manipular erros em APIs do ASP.NET Core
-
Alteração de quebra: o diagnóstico de exceção é suprimido quando
IExceptionHandler.TryHandleAsyncretorna true
Este artigo aborda abordagens comuns para lidar com erros em aplicativos Web ASP.NET Core. Consulte também Manipular erros em ASP.NET APIs principais.
Para Blazor obter orientações sobre tratamento de erros, que adicionam ou substituem as orientações deste artigo, consulte Manipular erros em aplicativos ASP.NET CoreBlazor.
Página de exceção do desenvolvedor
A página de exceção do desenvolvedor exibe informações detalhadas sobre exceções de solicitação não tratadas. Ele usa DeveloperExceptionPageMiddleware para capturar exceções síncronas e assíncronas do pipeline HTTP e para gerar respostas de erro. A página de exceção do desenvolvedor é executada no início do pipeline de middleware, para que possa capturar exceções não tratadas lançadas no middleware subsequente.
ASP.NET aplicativos principais habilitam a página de exceção do desenvolvedor por padrão quando:
- Execução no ambiente de Desenvolvimento.
- O aplicativo foi criado com os modelos atuais, ou seja, usando WebApplication.CreateBuilder.
Os aplicativos criados usando modelos anteriores, ou seja, usando WebHost.CreateDefaultBuilder, podem habilitar a página de exceção do desenvolvedor chamando app.UseDeveloperExceptionPage.
Warning
Não habilite a Página de Exceção do Desenvolvedor , a menos que o aplicativo esteja sendo executado no ambiente de Desenvolvimento. Não compartilhe informações detalhadas de exceção publicamente quando o aplicativo for executado em produção. Para obter mais informações sobre como configurar ambientes, consulte ASP.NET Core runtime environments.
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 da cadeia de caracteres de consulta, se houver
- Cookies, se existirem
- Headers
- Metadados do ponto final, se houver
Não é garantido que a Página de Exceção do Desenvolvedor forneça nenhuma informação. Utilize o Registo para obter informações completas sobre erros.
A imagem a seguir mostra uma página de exceção de desenvolvedor de exemplo com animação para mostrar as guias e as informações exibidas:
Em resposta a uma solicitação com um Accept: text/plain cabeçalho, a Página de Exceção do Desenvolvedor retorna texto sem formatação em vez de HTML. Por exemplo:
Status: 500 Internal Server Error
Time: 9.39 msSize: 480 bytes
FormattedRawHeadersRequest
Body
text/plain; charset=utf-8, 480 bytes
System.InvalidOperationException: Sample Exception
at WebApplicationMinimal.Program.<>c.<Main>b__0_0() in C:\Source\WebApplicationMinimal\Program.cs:line 12
at lambda_method1(Closure, Object, HttpContext)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
HEADERS
=======
Accept: text/plain
Host: localhost:7267
traceparent: 00-0eab195ea19d07b90a46cd7d6bf2f
Página do manipulador de exceções
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 não tratadas.
- 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 reexecuta a solicitação usando o
/Errorcaminho.
Warning
Caso o pipeline alternativo lance uma exceção própria, o Middleware de Manipulação de Exceções relançará a exceção original.
Como esse middleware pode executar novamente o pipeline de solicitação:
- Os middlewares precisam manejar a reentrância com a mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar
_nextou armazenar em cache seu processamento noHttpContextpara evitar refazê-lo. Ao lidar com o corpo da solicitação, isto pode significar armazenar os resultados em buffer ou em cache, como faz o leitor de formulários. - Para a UseExceptionHandler(IApplicationBuilder, String) sobrecarga usada em modelos, somente o caminho da solicitação é modificado e os dados da rota são limpos. Os dados de solicitação, como cabeçalhos, método e itens, são todos reutilizados tal como estão.
- Os serviços com escopo permanecem os mesmos.
No exemplo a seguir, UseExceptionHandler adiciona o middleware de tratamento de exceção em ambientes que não são de desenvolvimento:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
O Razor modelo de aplicativo Páginas fornece uma página de erro (.cshtml) e PageModel uma classe (ErrorModel) na pasta Páginas . Para um aplicativo MVC, o modelo de projeto inclui um Error método de ação e uma visualização de erro para o Home controlador.
O middleware de tratamento de exceção reexecuta 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 apenas para esses métodos HTTP. Por exemplo, uma ação do controlador MVC que usa o atributo [HttpGet] é executada somente para solicitações GET. Para garantir que todas as solicitações cheguem à página personalizada de tratamento de erros, 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
OnGetpara manipular exceções GET e useOnPostpara manipular exceções POST. - Para MVC, aplique atributos verbais HTTP a várias ações. Por exemplo, use
[HttpGet]para manipular exceções GET e use[HttpPost]para manipular exceções POST.
Para permitir que usuários não autenticados visualizem a página personalizada de tratamento de erros, verifique se ela oferece suporte ao acesso anônimo.
Aceda à exceção
Use IExceptionHandlerPathFeature para acessar a exceção e o caminho da solicitação original em um manipulador de erros. O exemplo a seguir usa IExceptionHandlerPathFeature para obter mais informações sobre a exceção que foi lançada:
[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.";
}
}
}
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
Manipulador de exceções lambda
Uma alternativa a uma página de manipulador de exceção personalizada é fornecer um lambda para UseExceptionHandler. O uso de um lambda permite o acesso ao erro antes de retornar a resposta.
O código a seguir 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();
}
Outra maneira de usar um lambda é definir o código de status com base no tipo de exceção, como no exemplo a seguir:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError
});
}
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
IExceptionHandler
IExceptionHandler é uma interface que dá ao desenvolvedor um callback para lidar com exceções conhecidas em um local central.
IExceptionHandler implementações são registradas chamando IServiceCollection.AddExceptionHandler<T>. O tempo de vida de uma instância de IExceptionHandler é único. Várias implementações podem ser adicionadas e são chamadas na ordem registrada.
Se um manipulador de exceção lida com uma solicitação, ele pode retornar true para interromper o processamento. Se uma exceção não for manipulada por nenhum manipulador de exceções, o controle retornará ao comportamento padrão e às opções do middleware. Métricas e logs diferentes são emitidos para exceções manipuladas versus não tratadas.
O exemplo a seguir mostra uma IExceptionHandler implementação:
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 IExceptionHandler implementação 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 lidar com uma exceção. - Depois de registrar a exceção, o
TryHandleAsyncmétodo retornafalse, para que a página de exceção do desenvolvedor seja mostrada.
Em outros ambientes:
- O
CustomExceptionHandleré chamado primeiro para lidar com uma exceção. - Depois de registrar a exceção, o
TryHandleAsyncmétodo retornafalse, para que a/Errorpágina seja mostrada.
UseStatusCodePages
Por padrão, um 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 que não tem um corpo, ele retorna o código de status e um corpo de resposta vazio. Para habilitar manipuladores padrão de apenas texto para códigos de status de erro comuns, chame UseStatusCodePages em Program.cs.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Ligue UseStatusCodePages antes de solicitar middleware de manipulação. Por exemplo, chame UseStatusCodePages antes do Middleware de Ficheiros Estáticos e do Middleware de Endpoints.
Quando UseStatusCodePages não é usado, navegar para uma URL sem um ponto de extremidade retorna 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.
Note
O middleware de páginas de códigos de estado não captura exceções. Para fornecer uma página personalizada de tratamento de erros, use a página do manipulador de exceções.
UseStatusCodePages com cadeia de caracteres de formato
Para personalizar o tipo de conteúdo de resposta e o texto, use a sobrecarga de UseStatusCodePages que aceita um tipo de conteúdo e uma string de formatação:
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 é usada na produção porque retorna uma mensagem que não é útil para os usuários.
UseStatusCodePages com lambda
Para especificar o código personalizado de manipulação de erros e gravação de resposta, use a sobrecarga de UseStatusCodePages que usa 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 em produção porque retorna uma mensagem que não é útil para os utilizadores.
UseStatusCodePagesWithRedirects
O UseStatusCodePagesWithRedirects método de extensão:
- Envia um código de status 302 - Encontrado para o 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 erros 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 {0} marcador de posição para o código de estado, tal como mostrado no código anterior. Se o modelo de URL começar com ~ (tilde), o ~ é substituído pelo PathBase do aplicativo. Ao especificar um ponto de extremidade na aplicação, crie uma vista ou Razor página MVC para o ponto de extremidade.
Esse método é comumente usado quando o aplicativo:
- Deve redirecionar o cliente para um ponto de extremidade diferente, geralmente nos casos em que um aplicativo diferente processa o erro. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint redirecionado.
- Não deve preservar e retornar o código de status original com a resposta de redirecionamento inicial.
UseStatusCodePagesWithReExecute
O UseStatusCodePagesWithReExecute método de extensão:
- Gera o corpo de resposta executando novamente o pipeline de solicitação usando um caminho alternativo.
- Não altera o código de status antes ou depois de executar novamente o pipeline.
A execução do novo pipeline pode alterar o código de status da resposta, pois o novo pipeline tem controle total do 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 da aplicação for especificado, crie uma vista MVC ou Razor página para o ponto de extremidade.
Esse método é comumente usado quando o aplicativo deve:
- Processe a solicitação sem redirecionar para um ponto de extremidade diferente. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint originalmente solicitado.
- Preserve e devolva o código de status original com a resposta.
O modelo de URL deve começar com / e pode incluir um espaço reservado {0} para o código de status. Para passar o código de status como um parâmetro de cadeia de caracteres de consulta, passe um segundo argumento para UseStatusCodePagesWithReExecute. Por exemplo:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
O endpoint 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 = $"{statusCodeReExecuteFeature.OriginalPathBase}"
+ $"{statusCodeReExecuteFeature.OriginalPath}"
+ $"{statusCodeReExecuteFeature.OriginalQueryString}";
}
}
}
Como esse middleware pode executar novamente o pipeline de solicitação:
- Os middlewares precisam manejar a reentrância com a mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar
_nextou armazenar em cache seu processamento noHttpContextpara evitar refazê-lo. Ao lidar com o corpo da solicitação, isto pode significar armazenar os resultados em buffer ou em cache, como faz o leitor de formulários. - Os serviços com escopo permanecem os mesmos.
Desativar páginas de código de estado
Para desabilitar páginas de código de status para um controlador MVC ou método de ação, use o atributo [SkipStatusCodePages].
Para desabilitar páginas de código de status para solicitações específicas em um Razor método manipulador 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ções
O código em páginas de tratamento de exceções também pode gerar exceções. As páginas de erro de produção devem ser testadas minuciosamente e tomar cuidado extra para evitar lançar exceções próprias.
Cabeçalhos de resposta
Uma vez que os cabeçalhos de uma resposta são enviados:
- O aplicativo não pode alterar o código de status da resposta.
- Quaisquer páginas de exceção ou manipuladores não podem ser executados. A resposta deve ser concluída ou a conexão anulada.
Tratamento de exceções do servidor
Além da lógica de tratamento de exceções em um aplicativo, a implementação do servidor HTTP pode lidar com algumas exceções. Se o servidor detetar uma exceção antes que os cabeçalhos de resposta sejam enviados, o servidor enviará uma 500 - Internal Server Error resposta sem um corpo de resposta. Se o servidor detetar 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 tratadas pelo aplicativo são tratadas pelo servidor. Qualquer exceção que ocorra quando o servidor está manipulando a solicitação é tratada pelo tratamento de exceções do servidor. As páginas de erro personalizadas, o middleware de tratamento de exceções e os filtros do aplicativo não afetam esse comportamento.
Manejo de exceções durante a inicialização
Somente a camada de hospedagem pode lidar com 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 pode mostrar uma página de erro para um erro de inicialização capturado somente se o erro ocorrer após a vinculação de endereço/porta do host. Se a vinculação falhar:
- A camada de hospedagem registra uma exceção crítica.
- O processo dotnet falha.
- Nenhuma página de erro é exibida quando o servidor HTTP é Kestrel.
Ao ser executado no IIS (ou no Serviço de Aplicativo do Azure) ou no IIS Express, uma Falha de Processo 502.5 - é retornada pelo ASP.NET Módulo Principal se o processo não puder ser iniciado. Para obter mais informações, consulte Solucionar 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 AddDatabaseDeveloperPageExceptionFilter de exceção da página do desenvolvedor do banco de dados captura exceções relacionadas ao banco de dados que podem ser resolvidas usando migrações do Entity Framework Core. Quando essas exceções ocorrem, uma resposta HTML é gerada com detalhes de possíveis ações para resolver o problema. Esta página está habilitada somente no ambiente de desenvolvimento. O código a seguir adiciona o filtro de exceção da página do desenvolvedor do 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 ou por controlador ou por ação. Nos Razor aplicativos do Pages, eles podem ser configurados globalmente ou por modelo de página. Esses filtros lidam com quaisquer exceções não tratadas que ocorram durante a execução de uma ação do controlador ou outro filtro. Para obter mais informações, consulte Filtros no ASP.NET Core.
Os filtros de exceção são úteis para intercetar exceções que ocorrem em ações MVC, mas não são tão flexíveis quanto o middleware interno de tratamento de exceções, UseExceptionHandler. Recomendamos o uso do UseExceptionHandler, a menos que você precise executar o tratamento de erros de forma diferente com base na ação MVC escolhida.
Erros de estado do modelo
Para obter informações sobre como lidar com erros de estado do modelo, consulte Vinculação de modelo e validação de modelo.
Detalhes do problema
Detalhes do problema não são o único formato de resposta para descrever um erro de API HTTP, no entanto, eles são comumente usados para relatar erros para APIs HTTP.
O serviço de detalhes do problema implementa a interface IProblemDetailsService, que suporta a criação de detalhes do problema no ASP.NET Core. O AddProblemDetails(IServiceCollection) método de extensão em IServiceCollection registra a implementação padrão IProblemDetailsService .
Nos aplicações ASP.NET Core, o middleware seguinte gera respostas HTTP com detalhes de problema quando
- ExceptionHandlerMiddleware: Gera uma resposta de detalhes do problema quando um manipulador personalizado não está definido.
- StatusCodePagesMiddleware: Gera uma resposta de detalhes do problema por padrão.
-
DeveloperExceptionPageMiddleware: Gera uma resposta de detalhes do problema durante o desenvolvimento quando o cabeçalho de solicitação HTTP
Acceptnão incluitext/html.
O código a seguir configura a aplicação para gerar uma resposta com detalhes do problema para todas as respostas de erro do cliente e do servidor HTTP que ainda não têm corpo de conteúdo:
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 dos detalhes do problema.
Personalizar detalhes do problema
A criação automática de um ProblemDetails pode ser personalizada usando qualquer uma das seguintes opções:
- Utilize
ProblemDetailsOptions.CustomizeProblemDetails - Utilize um
IProblemDetailsWriterpersonalizado - Chamar o
IProblemDetailsServiceem um middleware
CustomizeProblemDetails Funcionamento
Os detalhes do problema gerado podem ser personalizados usando CustomizeProblemDetails, e as personalizações são aplicadas a todos os detalhes gerados automaticamente do problema.
O código a seguir 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 HTTP Status 400 Bad Request endpoint produz o seguinte corpo da resposta com detalhes do problema:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
Personalizado IProblemDetailsWriter
Uma IProblemDetailsWriter implementação 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 utilizar um IProblemDetailsWriter personalizado, o IProblemDetailsWriter personalizado deve ser registado 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 para usar ProblemDetailsOptions com CustomizeProblemDetails é definir o ProblemDetails no middleware. Uma resposta de detalhes do problema pode ser escrita ligando para 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 de API mínimos /divide e /squareroot retornam a resposta de problema personalizada esperada sobre uma entrada com erro.
Os pontos de extremidade do controlador de API retornam a resposta de problema padrão quando há erro na entrada, não a resposta de problema personalizada. A resposta padrão do problema é retornada porque o controlador de API gravou no fluxo de resposta, Detalhes do problema para códigos de status de erro, antes IProblemDetailsService.WriteAsync é chamado e a resposta não é gravada novamente.
O seguinte ValuesController retorna BadRequestResult, que grava no fluxo de resposta e, portanto, impede que a resposta de 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 seguinte Values3Controller retorna ControllerBase.Problem para que o resultado esperado do problema personalizado 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 uma carga útil de 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, a seguinte é 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 problemas mais detalhadas.
Uma alternativa a uma página de manipulador de exceção personalizada é fornecer um lambda para UseExceptionHandler. Usar um lambda permite acessar o erro e escrever 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();
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
Uma abordagem alternativa para gerar detalhes do problema é usar o pacote NuGet de terceiros Hellang.Middleware.ProblemDetails que pode ser usado para mapear exceções e erros do cliente para detalhes do problema.
Recursos adicionais
Por Tom Dykstra
Este artigo aborda abordagens comuns para lidar com erros em aplicativos Web ASP.NET Core. Consulte também Manipular erros em ASP.NET APIs principais.
Página de exceção do desenvolvedor
A página de exceção do desenvolvedor exibe informações detalhadas sobre exceções de solicitação não tratadas. ASP.NET aplicativos principais habilitam a página de exceção do desenvolvedor por padrão quando:
- Execução no ambiente de Desenvolvimento.
- Aplicativo criado com os modelos atuais, ou seja, usando WebApplication.CreateBuilder. Os aplicativos criados usando o
WebHost.CreateDefaultBuilderdevem habilitar a página de exceção do desenvolvedor chamandoapp.UseDeveloperExceptionPageConfigure.
A página de exceção do desenvolvedor é executada no início do pipeline de middleware, para que possa capturar exceções não tratadas lançadas no middleware subsequente.
Informações detalhadas de 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, consulte ASP.NET Core runtime environments.
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 da cadeia de caracteres de consulta, se houver
- Cookies, se existirem
- Headers
Não é garantido que a Página de Exceção do Desenvolvedor forneça nenhuma informação. Utilize o Registo para obter informações completas sobre erros.
Página do manipulador de exceções
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 não tratadas.
- 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 reexecuta a solicitação usando o
/Errorcaminho.
Warning
Caso o pipeline alternativo lance uma exceção própria, o Middleware de Manipulação de Exceções relançará a exceção original.
Como esse middleware pode executar novamente o pipeline de solicitação:
- Os middlewares precisam manejar a reentrância com a mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar
_nextou armazenar em cache seu processamento noHttpContextpara evitar refazê-lo. Ao lidar com o corpo da solicitação, isto pode significar armazenar os resultados em buffer ou em cache, como faz o leitor de formulários. - Para a UseExceptionHandler(IApplicationBuilder, String) sobrecarga usada em modelos, somente o caminho da solicitação é modificado e os dados da rota são limpos. Os dados de solicitação, como cabeçalhos, método e itens, são todos reutilizados tal como estão.
- Os serviços com escopo permanecem os mesmos.
No exemplo a seguir, UseExceptionHandler adiciona o middleware de tratamento de exceção em ambientes que não são de desenvolvimento:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
O Razor modelo de aplicativo Páginas fornece uma página de erro (.cshtml) e PageModel uma classe (ErrorModel) na pasta Páginas . Para um aplicativo MVC, o modelo de projeto inclui um Error método de ação e uma visualização de erro para o Home controlador.
O middleware de tratamento de exceção reexecuta 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 apenas para esses métodos HTTP. Por exemplo, uma ação do controlador MVC que usa o atributo [HttpGet] é executada somente para solicitações GET. Para garantir que todas as solicitações cheguem à página personalizada de tratamento de erros, 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
OnGetpara manipular exceções GET e useOnPostpara manipular exceções POST. - Para MVC, aplique atributos verbais HTTP a várias ações. Por exemplo, use
[HttpGet]para manipular exceções GET e use[HttpPost]para manipular exceções POST.
Para permitir que usuários não autenticados visualizem a página personalizada de tratamento de erros, verifique se ela oferece suporte ao acesso anônimo.
Aceda à exceção
Use IExceptionHandlerPathFeature para acessar a exceção e o caminho da solicitação original em um manipulador de erros. O exemplo a seguir usa IExceptionHandlerPathFeature para obter mais informações sobre a exceção que foi lançada:
[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.";
}
}
}
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
Manipulador de exceções lambda
Uma alternativa a uma página de manipulador de exceção personalizada é fornecer um lambda para UseExceptionHandler. O uso de um lambda permite o acesso ao erro antes de retornar a resposta.
O código a seguir 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();
}
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
IExceptionHandler
IExceptionHandler é uma interface que dá ao desenvolvedor um callback para manipular exceções conhecidas num ponto central.
IExceptionHandler As implementações são registradas chamando IServiceCollection.AddExceptionHandler<T> [IServiceCollection.AddExceptionHandler<T>]. O tempo de vida de uma instância de IExceptionHandler é único. Várias implementações podem ser adicionadas e são chamadas na ordem registrada.
Se um manipulador de exceção lida com uma solicitação, ele pode retornar true para interromper o processamento. Se uma exceção não for manipulada por nenhum manipulador de exceções, o controle retornará ao comportamento padrão e às opções do middleware. Métricas e logs diferentes são emitidos para exceções manipuladas versus não tratadas.
O exemplo a seguir mostra uma IExceptionHandler implementação:
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 IExceptionHandler implementação 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 lidar com uma exceção. - Depois de registrar a exceção, o
TryHandleExceptionmétodo retornafalse, para que a página de exceção do desenvolvedor seja mostrada.
Em outros ambientes:
- O
CustomExceptionHandleré chamado primeiro para lidar com uma exceção. - Depois de registrar a exceção, o
TryHandleExceptionmétodo retornafalse, para que a/Errorpágina seja mostrada.
UseStatusCodePages
Por padrão, um 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 que não tem um corpo, ele retorna o código de status e um corpo de resposta vazio. Para habilitar manipuladores padrão de apenas texto para códigos de status de erro comuns, chame UseStatusCodePages em Program.cs.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Ligue UseStatusCodePages antes de solicitar middleware de manipulação. Por exemplo, chame UseStatusCodePages antes do Middleware de Ficheiros Estáticos e do Middleware de Endpoints.
Quando UseStatusCodePages não é usado, navegar para uma URL sem um ponto de extremidade retorna 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.
Note
O middleware de páginas de códigos de estado não captura exceções. Para fornecer uma página personalizada de tratamento de erros, use a página do manipulador de exceções.
UseStatusCodePages com cadeia de caracteres de formato
Para personalizar o tipo de conteúdo de resposta e o texto, use a sobrecarga de UseStatusCodePages que aceita um tipo de conteúdo e uma string de formatação:
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 é usada na produção porque retorna uma mensagem que não é útil para os usuários.
UseStatusCodePages com lambda
Para especificar o código personalizado de manipulação de erros e gravação de resposta, use a sobrecarga de UseStatusCodePages que usa 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 em produção porque retorna uma mensagem que não é útil para os utilizadores.
UseStatusCodePagesWithRedirects
O UseStatusCodePagesWithRedirects método de extensão:
- Envia um código de status 302 - Encontrado para o 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 erros 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 {0} marcador de posição para o código de estado, tal como mostrado no código anterior. Se o modelo de URL começar com ~ (tilde), o ~ é substituído pelo PathBase do aplicativo. Ao especificar um ponto de extremidade na aplicação, crie uma vista ou Razor página MVC para o ponto de extremidade.
Esse método é comumente usado quando o aplicativo:
- Deve redirecionar o cliente para um ponto de extremidade diferente, geralmente nos casos em que um aplicativo diferente processa o erro. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint redirecionado.
- Não deve preservar e retornar o código de status original com a resposta de redirecionamento inicial.
UseStatusCodePagesWithReExecute
O UseStatusCodePagesWithReExecute método de extensão:
- Gera o corpo de resposta executando novamente o pipeline de solicitação usando um caminho alternativo.
- Não altera o código de status antes ou depois de executar novamente o pipeline.
A execução do novo pipeline pode alterar o código de status da resposta, pois o novo pipeline tem controle total do 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 da aplicação for especificado, crie uma vista MVC ou Razor página para o ponto de extremidade.
Esse método é comumente usado quando o aplicativo deve:
- Processe a solicitação sem redirecionar para um ponto de extremidade diferente. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint originalmente solicitado.
- Preserve e devolva o código de status original com a resposta.
O modelo de URL deve começar com / e pode incluir um espaço reservado {0} para o código de status. Para passar o código de status como um parâmetro de cadeia de caracteres de consulta, passe um segundo argumento para UseStatusCodePagesWithReExecute. Por exemplo:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
O endpoint 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 = $"{statusCodeReExecuteFeature.OriginalPathBase}"
+ $"{statusCodeReExecuteFeature.OriginalPath}"
+ $"{statusCodeReExecuteFeature.OriginalQueryString}";
}
}
}
Como esse middleware pode executar novamente o pipeline de solicitação:
- Os middlewares precisam manejar a reentrância com a mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar
_nextou armazenar em cache seu processamento noHttpContextpara evitar refazê-lo. Ao lidar com o corpo da solicitação, isto pode significar armazenar os resultados em buffer ou em cache, como faz o leitor de formulários. - Os serviços com escopo permanecem os mesmos.
Desativar páginas de código de estado
Para desabilitar páginas de código de status para um controlador MVC ou método de ação, use o atributo [SkipStatusCodePages].
Para desabilitar páginas de código de status para solicitações específicas em um Razor método manipulador 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ções
O código em páginas de tratamento de exceções também pode gerar exceções. As páginas de erro de produção devem ser testadas minuciosamente e tomar cuidado extra para evitar lançar exceções próprias.
Cabeçalhos de resposta
Uma vez que os cabeçalhos de uma resposta são enviados:
- O aplicativo não pode alterar o código de status da resposta.
- Quaisquer páginas de exceção ou manipuladores não podem ser executados. A resposta deve ser concluída ou a conexão anulada.
Tratamento de exceções do servidor
Além da lógica de tratamento de exceções em um aplicativo, a implementação do servidor HTTP pode lidar com algumas exceções. Se o servidor detetar uma exceção antes que os cabeçalhos de resposta sejam enviados, o servidor enviará uma 500 - Internal Server Error resposta sem um corpo de resposta. Se o servidor detetar 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 tratadas pelo aplicativo são tratadas pelo servidor. Qualquer exceção que ocorra quando o servidor está manipulando a solicitação é tratada pelo tratamento de exceções do servidor. As páginas de erro personalizadas, o middleware de tratamento de exceções e os filtros do aplicativo não afetam esse comportamento.
Manejo de exceções durante a inicialização
Somente a camada de hospedagem pode lidar com 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 pode mostrar uma página de erro para um erro de inicialização capturado somente se o erro ocorrer após a vinculação de endereço/porta do host. Se a vinculação falhar:
- A camada de hospedagem registra uma exceção crítica.
- O processo dotnet falha.
- Nenhuma página de erro é exibida quando o servidor HTTP é Kestrel.
Ao ser executado no IIS (ou no Serviço de Aplicativo do Azure) ou no IIS Express, uma Falha de Processo 502.5 - é retornada pelo ASP.NET Módulo Principal se o processo não puder ser iniciado. Para obter mais informações, consulte Solucionar 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 AddDatabaseDeveloperPageExceptionFilter de exceção da página do desenvolvedor do banco de dados captura exceções relacionadas ao banco de dados que podem ser resolvidas usando migrações do Entity Framework Core. Quando essas exceções ocorrem, uma resposta HTML é gerada com detalhes de possíveis ações para resolver o problema. Esta página está habilitada somente no ambiente de desenvolvimento. O código a seguir adiciona o filtro de exceção da página do desenvolvedor do 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 ou por controlador ou por ação. Nos Razor aplicativos do Pages, eles podem ser configurados globalmente ou por modelo de página. Esses filtros lidam com quaisquer exceções não tratadas que ocorram durante a execução de uma ação do controlador ou outro filtro. Para obter mais informações, consulte Filtros no ASP.NET Core.
Os filtros de exceção são úteis para intercetar exceções que ocorrem em ações MVC, mas não são tão flexíveis quanto o middleware interno de tratamento de exceções, UseExceptionHandler. Recomendamos o uso do UseExceptionHandler, a menos que você precise executar o tratamento de erros de forma diferente com base na ação MVC escolhida.
Erros de estado do modelo
Para obter informações sobre como lidar com erros de estado do modelo, consulte Vinculação de modelo e validação de modelo.
Detalhes do problema
Detalhes do problema não são o único formato de resposta para descrever um erro de API HTTP, no entanto, eles são comumente usados para relatar erros para APIs HTTP.
O serviço de detalhes do problema implementa a interface IProblemDetailsService, que suporta a criação de detalhes do problema no ASP.NET Core. O AddProblemDetails(IServiceCollection) método de extensão em IServiceCollection registra a implementação padrão IProblemDetailsService .
Nos aplicações ASP.NET Core, o middleware seguinte gera respostas HTTP com detalhes de problema quando
- ExceptionHandlerMiddleware: Gera uma resposta de detalhes do problema quando um manipulador personalizado não está definido.
- StatusCodePagesMiddleware: Gera uma resposta de detalhes do problema por padrão.
-
DeveloperExceptionPageMiddleware: Gera uma resposta de detalhes do problema durante o desenvolvimento quando o cabeçalho de solicitação HTTP
Acceptnão incluitext/html.
O código a seguir configura o aplicativo para gerar uma resposta de detalhes do problema para todas as respostas de erro do cliente HTTP e do servidor que ainda não têm um conteúdo de 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 dos detalhes do problema.
Personalizar detalhes do problema
A criação automática de um ProblemDetails pode ser personalizada usando qualquer uma das seguintes opções:
- Utilize
ProblemDetailsOptions.CustomizeProblemDetails - Utilize um
IProblemDetailsWriterpersonalizado - Chamar o
IProblemDetailsServiceem um middleware
CustomizeProblemDetails Funcionamento
Os detalhes do problema gerado podem ser personalizados usando CustomizeProblemDetails, e as personalizações são aplicadas a todos os detalhes gerados automaticamente do problema.
O código a seguir 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 HTTP Status 400 Bad Request endpoint produz o seguinte corpo da resposta com detalhes do problema:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
Personalizado IProblemDetailsWriter
Uma IProblemDetailsWriter implementação 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 utilizar um IProblemDetailsWriter personalizado, o IProblemDetailsWriter personalizado deve ser registado 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 para usar ProblemDetailsOptions com CustomizeProblemDetails é definir o ProblemDetails no middleware. Uma resposta de detalhes do problema pode ser escrita ligando para 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 de API mínimos /divide e /squareroot retornam a resposta de problema personalizada esperada sobre uma entrada com erro.
Os pontos de extremidade do controlador de API retornam a resposta de problema padrão quando há erro na entrada, não a resposta de problema personalizada. A resposta padrão do problema é retornada porque o controlador de API gravou no fluxo de resposta, Detalhes do problema para códigos de status de erro, antes IProblemDetailsService.WriteAsync é chamado e a resposta não é gravada novamente.
O seguinte ValuesController retorna BadRequestResult, que grava no fluxo de resposta e, portanto, impede que a resposta de 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 seguinte Values3Controller retorna ControllerBase.Problem para que o resultado esperado do problema personalizado 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 uma carga útil de 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, a seguinte é 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 problemas mais detalhadas.
Uma alternativa a uma página de manipulador de exceção personalizada é fornecer um lambda para UseExceptionHandler. Usar um lambda permite acessar o erro e escrever 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();
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
Uma abordagem alternativa para gerar detalhes do problema é usar o pacote NuGet de terceiros Hellang.Middleware.ProblemDetails que pode ser usado para mapear exceções e erros do cliente para detalhes do problema.
Recursos adicionais
- Visualizar ou descarregar amostra de código (como descarregar)
- Solucionar problemas do ASP.NET Core no Serviço de Aplicativo do Azure e no IIS
- Solução de problemas de erro comum para o Serviço de Aplicativo do Azure e o IIS com ASP.NET Core
- Manipular erros em APIs do ASP.NET Core
- Manipule erros em ASP.NET APIs principais.
Por Tom Dykstra
Este artigo aborda abordagens comuns para lidar com erros em aplicativos Web ASP.NET Core. Consulte também Manipular erros em ASP.NET APIs principais.
Página de exceção do desenvolvedor
A página de exceção do desenvolvedor exibe informações detalhadas sobre exceções de solicitação não tratadas. ASP.NET aplicativos principais habilitam a página de exceção do desenvolvedor por padrão quando:
- Execução no ambiente de Desenvolvimento.
- Aplicativo criado com os modelos atuais, ou seja, usando WebApplication.CreateBuilder. Os aplicativos criados usando o
WebHost.CreateDefaultBuilderdevem habilitar a página de exceção do desenvolvedor chamandoapp.UseDeveloperExceptionPageConfigure.
A página de exceção do desenvolvedor é executada no início do pipeline de middleware, para que possa capturar exceções não tratadas lançadas no middleware subsequente.
Informações detalhadas de 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, consulte ASP.NET Core runtime environments.
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 da cadeia de caracteres de consulta, se houver
- Cookies, se existirem
- Headers
Não é garantido que a Página de Exceção do Desenvolvedor forneça nenhuma informação. Utilize o Registo para obter informações completas sobre erros.
Página do manipulador de exceções
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 não tratadas.
- 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 reexecuta a solicitação usando o
/Errorcaminho.
Warning
Caso o pipeline alternativo lance uma exceção própria, o Middleware de Manipulação de Exceções relançará a exceção original.
Como esse middleware pode executar novamente o pipeline de solicitação:
- Os middlewares precisam manejar a reentrância com a mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar
_nextou armazenar em cache seu processamento noHttpContextpara evitar refazê-lo. Ao lidar com o corpo da solicitação, isto pode significar armazenar os resultados em buffer ou em cache, como faz o leitor de formulários. - Para a UseExceptionHandler(IApplicationBuilder, String) sobrecarga usada em modelos, somente o caminho da solicitação é modificado e os dados da rota são limpos. Os dados de solicitação, como cabeçalhos, método e itens, são todos reutilizados tal como estão.
- Os serviços com escopo permanecem os mesmos.
No exemplo a seguir, UseExceptionHandler adiciona o middleware de tratamento de exceção em ambientes que não são de desenvolvimento:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
O Razor modelo de aplicativo Páginas fornece uma página de erro (.cshtml) e PageModel uma classe (ErrorModel) na pasta Páginas . Para um aplicativo MVC, o modelo de projeto inclui um Error método de ação e uma visualização de erro para o Home controlador.
O middleware de tratamento de exceção reexecuta 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 apenas para esses métodos HTTP. Por exemplo, uma ação do controlador MVC que usa o atributo [HttpGet] é executada somente para solicitações GET. Para garantir que todas as solicitações cheguem à página personalizada de tratamento de erros, 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
OnGetpara manipular exceções GET e useOnPostpara manipular exceções POST. - Para MVC, aplique atributos verbais HTTP a várias ações. Por exemplo, use
[HttpGet]para manipular exceções GET e use[HttpPost]para manipular exceções POST.
Para permitir que usuários não autenticados visualizem a página personalizada de tratamento de erros, verifique se ela oferece suporte ao acesso anônimo.
Aceda à exceção
Use IExceptionHandlerPathFeature para acessar a exceção e o caminho da solicitação original em um manipulador de erros. O exemplo a seguir usa IExceptionHandlerPathFeature para obter mais informações sobre a exceção que foi lançada:
[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.";
}
}
}
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
Manipulador de exceções lambda
Uma alternativa a uma página de manipulador de exceção personalizada é fornecer um lambda para UseExceptionHandler. O uso de um lambda permite o acesso ao erro antes de retornar a resposta.
O código a seguir 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();
}
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
UseStatusCodePages
Por padrão, um 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 que não tem um corpo, ele retorna o código de status e um corpo de resposta vazio. Para habilitar manipuladores padrão de apenas texto para códigos de status de erro comuns, chame UseStatusCodePages em Program.cs.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Ligue UseStatusCodePages antes de solicitar middleware de manipulação. Por exemplo, chame UseStatusCodePages antes do Middleware de Ficheiros Estáticos e do Middleware de Endpoints.
Quando UseStatusCodePages não é usado, navegar para uma URL sem um ponto de extremidade retorna 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.
Note
O middleware de páginas de códigos de estado não captura exceções. Para fornecer uma página personalizada de tratamento de erros, use a página do manipulador de exceções.
UseStatusCodePages com cadeia de caracteres de formato
Para personalizar o tipo de conteúdo de resposta e o texto, use a sobrecarga de UseStatusCodePages que aceita um tipo de conteúdo e uma string de formatação:
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 é usada na produção porque retorna uma mensagem que não é útil para os usuários.
UseStatusCodePages com lambda
Para especificar o código personalizado de manipulação de erros e gravação de resposta, use a sobrecarga de UseStatusCodePages que usa 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 em produção porque retorna uma mensagem que não é útil para os utilizadores.
UseStatusCodePagesWithRedirects
O UseStatusCodePagesWithRedirects método de extensão:
- Envia um código de status 302 - Encontrado para o 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 erros 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 {0} marcador de posição para o código de estado, tal como mostrado no código anterior. Se o modelo de URL começar com ~ (tilde), o ~ é substituído pelo PathBase do aplicativo. Ao especificar um ponto de extremidade na aplicação, crie uma vista ou Razor página MVC para o ponto de extremidade.
Esse método é comumente usado quando o aplicativo:
- Deve redirecionar o cliente para um ponto de extremidade diferente, geralmente nos casos em que um aplicativo diferente processa o erro. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint redirecionado.
- Não deve preservar e retornar o código de status original com a resposta de redirecionamento inicial.
UseStatusCodePagesWithReExecute
O UseStatusCodePagesWithReExecute método de extensão:
- Gera o corpo de resposta executando novamente o pipeline de solicitação usando um caminho alternativo.
- Não altera o código de status antes ou depois de executar novamente o pipeline.
A execução do novo pipeline pode alterar o código de status da resposta, pois o novo pipeline tem controle total do 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 da aplicação for especificado, crie uma vista MVC ou Razor página para o ponto de extremidade.
Esse método é comumente usado quando o aplicativo deve:
- Processe a solicitação sem redirecionar para um ponto de extremidade diferente. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint originalmente solicitado.
- Preserve e devolva o código de status original com a resposta.
O modelo de URL deve começar com / e pode incluir um espaço reservado {0} para o código de status. Para passar o código de status como um parâmetro de cadeia de caracteres de consulta, passe um segundo argumento para UseStatusCodePagesWithReExecute. Por exemplo:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
O endpoint 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 = $"{statusCodeReExecuteFeature.OriginalPathBase}"
+ $"{statusCodeReExecuteFeature.OriginalPath}"
+ $"{statusCodeReExecuteFeature.OriginalQueryString}";
}
}
}
Como esse middleware pode executar novamente o pipeline de solicitação:
- Os middlewares precisam manejar a reentrância com a mesma solicitação. Isso normalmente significa limpar seu estado depois de chamar
_nextou armazenar em cache seu processamento noHttpContextpara evitar refazê-lo. Ao lidar com o corpo da solicitação, isto pode significar armazenar os resultados em buffer ou em cache, como faz o leitor de formulários. - Os serviços com escopo permanecem os mesmos.
Desativar páginas de código de estado
Para desabilitar páginas de código de status para um controlador MVC ou método de ação, use o atributo [SkipStatusCodePages].
Para desabilitar páginas de código de status para solicitações específicas em um Razor método manipulador 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ções
O código em páginas de tratamento de exceções também pode gerar exceções. As páginas de erro de produção devem ser testadas minuciosamente e tomar cuidado extra para evitar lançar exceções próprias.
Cabeçalhos de resposta
Uma vez que os cabeçalhos de uma resposta são enviados:
- O aplicativo não pode alterar o código de status da resposta.
- Quaisquer páginas de exceção ou manipuladores não podem ser executados. A resposta deve ser concluída ou a conexão anulada.
Tratamento de exceções do servidor
Além da lógica de tratamento de exceções em um aplicativo, a implementação do servidor HTTP pode lidar com algumas exceções. Se o servidor detetar uma exceção antes que os cabeçalhos de resposta sejam enviados, o servidor enviará uma 500 - Internal Server Error resposta sem um corpo de resposta. Se o servidor detetar 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 tratadas pelo aplicativo são tratadas pelo servidor. Qualquer exceção que ocorra quando o servidor está manipulando a solicitação é tratada pelo tratamento de exceções do servidor. As páginas de erro personalizadas, o middleware de tratamento de exceções e os filtros do aplicativo não afetam esse comportamento.
Manejo de exceções durante a inicialização
Somente a camada de hospedagem pode lidar com 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 pode mostrar uma página de erro para um erro de inicialização capturado somente se o erro ocorrer após a vinculação de endereço/porta do host. Se a vinculação falhar:
- A camada de hospedagem registra uma exceção crítica.
- O processo dotnet falha.
- Nenhuma página de erro é exibida quando o servidor HTTP é Kestrel.
Ao ser executado no IIS (ou no Serviço de Aplicativo do Azure) ou no IIS Express, uma Falha de Processo 502.5 - é retornada pelo ASP.NET Módulo Principal se o processo não puder ser iniciado. Para obter mais informações, consulte Solucionar 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 AddDatabaseDeveloperPageExceptionFilter de exceção da página do desenvolvedor do banco de dados captura exceções relacionadas ao banco de dados que podem ser resolvidas usando migrações do Entity Framework Core. Quando essas exceções ocorrem, uma resposta HTML é gerada com detalhes de possíveis ações para resolver o problema. Esta página está habilitada somente no ambiente de desenvolvimento. O código a seguir adiciona o filtro de exceção da página do desenvolvedor do 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 ou por controlador ou por ação. Nos Razor aplicativos do Pages, eles podem ser configurados globalmente ou por modelo de página. Esses filtros lidam com quaisquer exceções não tratadas que ocorram durante a execução de uma ação do controlador ou outro filtro. Para obter mais informações, consulte Filtros no ASP.NET Core.
Os filtros de exceção são úteis para intercetar exceções que ocorrem em ações MVC, mas não são tão flexíveis quanto o middleware interno de tratamento de exceções, UseExceptionHandler. Recomendamos o uso do UseExceptionHandler, a menos que você precise executar o tratamento de erros de forma diferente com base na ação MVC escolhida.
Erros de estado do modelo
Para obter informações sobre como lidar com erros de estado do modelo, consulte Vinculação de modelo e validação de modelo.
Detalhes do problema
Detalhes do problema não são o único formato de resposta para descrever um erro de API HTTP, no entanto, eles são comumente usados para relatar erros para APIs HTTP.
O serviço de detalhes do problema implementa a interface IProblemDetailsService, que suporta a criação de detalhes do problema no ASP.NET Core. O AddProblemDetails(IServiceCollection) método de extensão em IServiceCollection registra a implementação padrão IProblemDetailsService .
Nos aplicações ASP.NET Core, o middleware seguinte gera respostas HTTP com detalhes de problema quando
- ExceptionHandlerMiddleware: Gera uma resposta de detalhes do problema quando um manipulador personalizado não está definido.
- StatusCodePagesMiddleware: Gera uma resposta de detalhes do problema por padrão.
-
DeveloperExceptionPageMiddleware: Gera uma resposta de detalhes do problema durante o desenvolvimento quando o cabeçalho de solicitação HTTP
Acceptnão incluitext/html.
O código a seguir configura o aplicativo para gerar uma resposta de detalhes do problema para todas as respostas de erro do cliente HTTP e do servidor que ainda não têm um conteúdo de 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 dos detalhes do problema.
Personalizar detalhes do problema
A criação automática de um ProblemDetails pode ser personalizada usando qualquer uma das seguintes opções:
- Utilize
ProblemDetailsOptions.CustomizeProblemDetails - Utilize um
IProblemDetailsWriterpersonalizado - Chamar o
IProblemDetailsServiceem um middleware
CustomizeProblemDetails Funcionamento
Os detalhes do problema gerado podem ser personalizados usando CustomizeProblemDetails, e as personalizações são aplicadas a todos os detalhes gerados automaticamente do problema.
O código a seguir 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 HTTP Status 400 Bad Request endpoint produz o seguinte corpo da resposta com detalhes do problema:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
Personalizado IProblemDetailsWriter
Uma IProblemDetailsWriter implementação 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 utilizar um IProblemDetailsWriter personalizado, o IProblemDetailsWriter personalizado deve ser registado 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 para usar ProblemDetailsOptions com CustomizeProblemDetails é definir o ProblemDetails no middleware. Uma resposta de detalhes do problema pode ser escrita ligando para 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 de API mínimos /divide e /squareroot retornam a resposta de problema personalizada esperada sobre uma entrada com erro.
Os pontos de extremidade do controlador de API retornam a resposta de problema padrão quando há erro na entrada, não a resposta de problema personalizada. A resposta padrão do problema é retornada porque o controlador de API gravou no fluxo de resposta, Detalhes do problema para códigos de status de erro, antes IProblemDetailsService.WriteAsync é chamado e a resposta não é gravada novamente.
O seguinte ValuesController retorna BadRequestResult, que grava no fluxo de resposta e, portanto, impede que a resposta de 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 seguinte Values3Controller retorna ControllerBase.Problem para que o resultado esperado do problema personalizado 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 uma carga útil de 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, a seguinte é 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 problemas mais detalhadas.
Uma alternativa a uma página de manipulador de exceção personalizada é fornecer um lambda para UseExceptionHandler. Usar um lambda permite acessar o erro e escrever 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();
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
Uma abordagem alternativa para gerar detalhes do problema é usar o pacote NuGet de terceiros Hellang.Middleware.ProblemDetails que pode ser usado para mapear exceções e erros do cliente para detalhes do problema.
Recursos adicionais
Por Tom Dykstra
Este artigo aborda abordagens comuns para lidar com erros em aplicativos Web ASP.NET Core. Consulte Manipular erros em APIs ASP.NET Core para APIs da Web.
Página de exceção do desenvolvedor
A página de exceção do desenvolvedor exibe informações detalhadas sobre exceções de solicitação não tratadas. ASP.NET aplicativos principais habilitam a página de exceção do desenvolvedor por padrão quando:
- Execução no ambiente de Desenvolvimento.
- Aplicativo criado com os modelos atuais, ou seja, usando WebApplication.CreateBuilder. Os aplicativos criados usando o
WebHost.CreateDefaultBuilderdevem habilitar a página de exceção do desenvolvedor chamandoapp.UseDeveloperExceptionPageConfigure.
A página de exceção do desenvolvedor é executada no início do pipeline de middleware, para que possa capturar exceções não tratadas lançadas no middleware subsequente.
Informações detalhadas de 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, consulte ASP.NET Core runtime environments.
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 da cadeia de caracteres de consulta, se houver
- Cookies, se existirem
- Headers
Não é garantido que a Página de Exceção do Desenvolvedor forneça nenhuma informação. Utilize o Registo para obter informações completas sobre erros.
Página do manipulador de exceções
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 não tratadas.
- 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 reexecuta a solicitação usando o
/Errorcaminho.
Warning
Caso o pipeline alternativo lance uma exceção própria, o Middleware de Manipulação de Exceções relançará a exceção original.
No exemplo a seguir, UseExceptionHandler adiciona o middleware de tratamento de exceção em ambientes que não são de desenvolvimento:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
O Razor modelo de aplicativo Páginas fornece uma página de erro (.cshtml) e PageModel uma classe (ErrorModel) na pasta Páginas . Para um aplicativo MVC, o modelo de projeto inclui um Error método de ação e uma visualização de erro para o Home controlador.
O middleware de tratamento de exceção reexecuta 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 apenas para esses métodos HTTP. Por exemplo, uma ação do controlador MVC que usa o atributo [HttpGet] é executada somente para solicitações GET. Para garantir que todas as solicitações cheguem à página personalizada de tratamento de erros, 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
OnGetpara manipular exceções GET e useOnPostpara manipular exceções POST. - Para MVC, aplique atributos verbais HTTP a várias ações. Por exemplo, use
[HttpGet]para manipular exceções GET e use[HttpPost]para manipular exceções POST.
Para permitir que usuários não autenticados visualizem a página personalizada de tratamento de erros, verifique se ela oferece suporte ao acesso anônimo.
Aceda à exceção
Use IExceptionHandlerPathFeature para acessar a exceção e o caminho da solicitação original em um manipulador de erros. O exemplo a seguir usa IExceptionHandlerPathFeature para obter mais informações sobre a exceção que foi lançada:
[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.";
}
}
}
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
Manipulador de exceções lambda
Uma alternativa a uma página de manipulador de exceção personalizada é fornecer um lambda para UseExceptionHandler. O uso de um lambda permite o acesso ao erro antes de retornar a resposta.
O código a seguir 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();
}
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
UseStatusCodePages
Por padrão, um 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 que não tem um corpo, ele retorna o código de status e um corpo de resposta vazio. Para habilitar manipuladores padrão de apenas texto para códigos de status de erro comuns, chame UseStatusCodePages em Program.cs.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Ligue UseStatusCodePages antes de solicitar middleware de manipulação. Por exemplo, chame UseStatusCodePages antes do Middleware de Ficheiros Estáticos e do Middleware de Endpoints.
Quando UseStatusCodePages não é usado, navegar para uma URL sem um ponto de extremidade retorna 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.
Note
O middleware de páginas de códigos de estado não captura exceções. Para fornecer uma página personalizada de tratamento de erros, use a página do manipulador de exceções.
UseStatusCodePages com cadeia de caracteres de formato
Para personalizar o tipo de conteúdo de resposta e o texto, use a sobrecarga de UseStatusCodePages que aceita um tipo de conteúdo e uma string de formatação:
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 é usada na produção porque retorna uma mensagem que não é útil para os usuários.
UseStatusCodePages com lambda
Para especificar o código personalizado de manipulação de erros e gravação de resposta, use a sobrecarga de UseStatusCodePages que usa 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 em produção porque retorna uma mensagem que não é útil para os utilizadores.
UseStatusCodePagesWithRedirects
O UseStatusCodePagesWithRedirects método de extensão:
- Envia um código de status 302 - Encontrado para o 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 erros 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 {0} marcador de posição para o código de estado, tal como mostrado no código anterior. Se o modelo de URL começar com ~ (tilde), o ~ é substituído pelo PathBase do aplicativo. Ao especificar um ponto de extremidade na aplicação, crie uma vista ou Razor página MVC para o ponto de extremidade.
Esse método é comumente usado quando o aplicativo:
- Deve redirecionar o cliente para um ponto de extremidade diferente, geralmente nos casos em que um aplicativo diferente processa o erro. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint redirecionado.
- Não deve preservar e retornar o código de status original com a resposta de redirecionamento inicial.
UseStatusCodePagesWithReExecute
O UseStatusCodePagesWithReExecute método de extensão:
- Retorna o código de status original para o cliente.
- Gera o corpo de resposta executando novamente o pipeline de solicitação usando 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 da aplicação for especificado, crie uma vista MVC ou Razor página para o ponto de extremidade.
Esse método é comumente usado quando o aplicativo deve:
- Processe a solicitação sem redirecionar para um ponto de extremidade diferente. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint originalmente solicitado.
- Preserve e devolva o código de status original com a resposta.
O modelo de URL deve começar com / e pode incluir um espaço reservado {0} para o código de status. Para passar o código de status como um parâmetro de cadeia de caracteres de consulta, passe um segundo argumento para UseStatusCodePagesWithReExecute. Por exemplo:
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
O endpoint 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);
}
}
}
Desativar páginas de código de estado
Para desabilitar páginas de código de status para um controlador MVC ou método de ação, use o atributo [SkipStatusCodePages].
Para desabilitar páginas de código de status para solicitações específicas em um Razor método manipulador 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ções
O código em páginas de tratamento de exceções também pode gerar exceções. As páginas de erro de produção devem ser testadas minuciosamente e tomar cuidado extra para evitar lançar exceções próprias.
Cabeçalhos de resposta
Uma vez que os cabeçalhos de uma resposta são enviados:
- O aplicativo não pode alterar o código de status da resposta.
- Quaisquer páginas de exceção ou manipuladores não podem ser executados. A resposta deve ser concluída ou a conexão anulada.
Tratamento de exceções do servidor
Além da lógica de tratamento de exceções em um aplicativo, a implementação do servidor HTTP pode lidar com algumas exceções. Se o servidor detetar uma exceção antes que os cabeçalhos de resposta sejam enviados, o servidor enviará uma 500 - Internal Server Error resposta sem um corpo de resposta. Se o servidor detetar 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 tratadas pelo aplicativo são tratadas pelo servidor. Qualquer exceção que ocorra quando o servidor está manipulando a solicitação é tratada pelo tratamento de exceções do servidor. As páginas de erro personalizadas, o middleware de tratamento de exceções e os filtros do aplicativo não afetam esse comportamento.
Manejo de exceções durante a inicialização
Somente a camada de hospedagem pode lidar com 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 pode mostrar uma página de erro para um erro de inicialização capturado somente se o erro ocorrer após a vinculação de endereço/porta do host. Se a vinculação falhar:
- A camada de hospedagem registra uma exceção crítica.
- O processo dotnet falha.
- Nenhuma página de erro é exibida quando o servidor HTTP é Kestrel.
Ao ser executado no IIS (ou no Serviço de Aplicativo do Azure) ou no IIS Express, uma Falha de Processo 502.5 - é retornada pelo ASP.NET Módulo Principal se o processo não puder ser iniciado. Para obter mais informações, consulte Solucionar 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 AddDatabaseDeveloperPageExceptionFilter de exceção da página do desenvolvedor do banco de dados captura exceções relacionadas ao banco de dados que podem ser resolvidas usando migrações do Entity Framework Core. Quando essas exceções ocorrem, uma resposta HTML é gerada com detalhes de possíveis ações para resolver o problema. Esta página está habilitada somente no ambiente de desenvolvimento. O código a seguir adiciona o filtro de exceção da página do desenvolvedor do 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 ou por controlador ou por ação. Nos Razor aplicativos do Pages, eles podem ser configurados globalmente ou por modelo de página. Esses filtros lidam com quaisquer exceções não tratadas que ocorram durante a execução de uma ação do controlador ou outro filtro. Para obter mais informações, consulte Filtros no ASP.NET Core.
Os filtros de exceção são úteis para intercetar exceções que ocorrem em ações MVC, mas não são tão flexíveis quanto o middleware interno de tratamento de exceções, UseExceptionHandler. Recomendamos o uso do UseExceptionHandler, a menos que você precise executar o tratamento de erros de forma diferente com base na ação MVC escolhida.
Erros de estado do modelo
Para obter informações sobre como lidar com erros de estado do modelo, consulte Vinculação de modelo e validação de modelo.
Recursos adicionais
Por Kirk Larkin, Tom Dykstra e Steve Smith
Este artigo aborda abordagens comuns para lidar com erros em aplicativos Web ASP.NET Core. Consulte Manipular erros em APIs ASP.NET Core para APIs da Web.
Ver e descarregar código de exemplo. (Como fazer o download.) A guia rede nas ferramentas de desenvolvedor do navegador F12 é útil ao testar o aplicativo de exemplo.
Página de Exceção do Desenvolvedor
A página de exceção do desenvolvedor exibe informações detalhadas sobre exceções de solicitação não tratadas. Os modelos 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 realçado anterior habilita a página de exceção do desenvolvedor quando o aplicativo está sendo executado no ambiente de desenvolvimento.
Os modelos são colocados UseDeveloperExceptionPage no início do pipeline de middleware para que ele possa capturar exceções não tratadas lançadas no middleware que se segue.
O código anterior habilita a página de exceção do desenvolvedor somente quando o aplicativo é executado no ambiente de desenvolvimento. Informações detalhadas de 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, consulte ASP.NET Core runtime environments.
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 consulta da cadeia de caracteres, se existirem
- Cookies, se existirem
- Headers
Não é garantido que a Página de Exceção do Desenvolvedor forneça nenhuma informação. Utilize o Registo para obter informações completas sobre erros.
Página do manipulador de exceções
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 não tratadas.
- 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 reexecuta a solicitação usando o
/Errorcaminho.
Warning
Caso o pipeline alternativo lance uma exceção própria, o Middleware de Manipulação de Exceções relançará a exceção original.
No exemplo a seguir, UseExceptionHandler adiciona o middleware de tratamento de exceção em ambientes que não são de desenvolvimento:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
O Razor modelo de aplicativo Páginas fornece uma página de erro (.cshtml) e PageModel uma classe (ErrorModel) na pasta Páginas . Para um aplicativo MVC, o modelo de projeto inclui um Error método de ação e uma visualização de erro para o Home controlador.
O middleware de tratamento de exceção reexecuta 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 apenas para esses métodos HTTP. Por exemplo, uma ação do controlador MVC que usa o atributo [HttpGet] é executada somente para solicitações GET. Para garantir que todas as solicitações cheguem à página personalizada de tratamento de erros, 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
OnGetpara manipular exceções GET e useOnPostpara manipular exceções POST. - Para MVC, aplique atributos verbais HTTP a várias ações. Por exemplo, use
[HttpGet]para manipular exceções GET e use[HttpPost]para manipular exceções POST.
Para permitir que usuários não autenticados visualizem a página personalizada de tratamento de erros, verifique se ela oferece suporte ao acesso anônimo.
Aceda à exceção
Use IExceptionHandlerPathFeature para acessar a exceção e o caminho da solicitação original em um manipulador de erros. O código a seguir adiciona ExceptionMessage ao padrão Pages/Error.cshtml.cs gerado pelos modelos 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";
}
}
}
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
Para testar a exceção no aplicativo de exemplo:
- Defina o ambiente para produção.
- Remova os comentários do
webBuilder.UseStartup<Startup>();noProgram.cs. - Selecione Acionar uma exceção na página inicial.
Manipulador de exceções lambda
Uma alternativa a uma página de manipulador de exceção personalizada é fornecer um lambda para UseExceptionHandler. O uso de um lambda permite o acesso ao erro antes de retornar a resposta.
O código a seguir 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();
});
}
Warning
Não forneça informações de erro confidenciais de IExceptionHandlerFeature ou IExceptionHandlerPathFeature para clientes. A exibição de mensagens de erro é um risco de segurança.
Para testar o lambda de manipulação de exceção no aplicativo de exemplo:
- Defina o ambiente para produção.
- Remova os comentários do
webBuilder.UseStartup<StartupLambda>();noProgram.cs. - Selecione Acionar uma exceção na página inicial.
UseStatusCodePages
Por padrão, um 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 que não tem um corpo, ele retorna o código de status e um corpo de resposta vazio. Para fornecer páginas de código de status, use o middleware de páginas de código de status. Para habilitar manipuladores padrão somente de texto para códigos de status de erro 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();
});
}
Ligue UseStatusCodePages antes de solicitar middleware de manipulação. Por exemplo, chame UseStatusCodePages antes do Middleware de Ficheiros Estáticos e do Middleware de Endpoints.
Quando UseStatusCodePages não é usado, navegar para uma URL sem um ponto de extremidade retorna uma mensagem de erro dependente do navegador indicando que o ponto de extremidade não pode ser encontrado. Por exemplo, navegando para 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 exemplo:
- Defina o ambiente para produção.
- Remova os comentários do
webBuilder.UseStartup<StartupUseStatusCodePages>();noProgram.cs. - Selecione os links na página inicial na página inicial.
Note
O middleware de páginas de códigos de estado não captura exceções. Para fornecer uma página personalizada de tratamento de erros, use a página do manipulador de exceções.
UseStatusCodePages com cadeia de caracteres de formato
Para personalizar o tipo de conteúdo de resposta e o texto, use a sobrecarga de UseStatusCodePages que aceita um tipo de conteúdo e uma string de formatação:
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 é usada na produção porque retorna uma mensagem que não é útil para os usuários.
Para testar UseStatusCodePages na aplicação de exemplo, remova os comentários de webBuilder.UseStartup<StartupFormat>(); em Program.cs.
UseStatusCodePages com lambda
Para especificar o código personalizado de manipulação de erros e gravação de resposta, use a sobrecarga de UseStatusCodePages que usa 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 em produção porque retorna uma mensagem que não é útil para os utilizadores.
Para testar UseStatusCodePages na aplicação de exemplo, remova os comentários de webBuilder.UseStartup<StartupStatusLambda>(); em Program.cs.
UseStatusCodePagesWithRedirects
O UseStatusCodePagesWithRedirects método de extensão:
- Envia um código de status 302 - Encontrado para o 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 erros 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 {0} marcador de posição para o código de estado, tal como mostrado no código anterior. Se o modelo de URL começar com ~ (tilde), o ~ é substituído pelo PathBase do aplicativo. Ao especificar um ponto de extremidade na aplicação, crie uma vista ou Razor página MVC para o ponto de extremidade. Para obter um exemplo de Razor Pages, consulte Pages/MyStatusCode.cshtml no aplicativo de exemplo.
Esse método é comumente usado quando o aplicativo:
- Deve redirecionar o cliente para um ponto de extremidade diferente, geralmente nos casos em que um aplicativo diferente processa o erro. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint redirecionado.
- Não deve preservar e retornar o código de status original com a resposta de redirecionamento inicial.
Para testar UseStatusCodePages na aplicação de exemplo, remova os comentários de webBuilder.UseStartup<StartupSCredirect>(); em Program.cs.
UseStatusCodePagesWithReExecute
O UseStatusCodePagesWithReExecute método de extensão:
- Retorna o código de status original para o cliente.
- Gera o corpo de resposta executando novamente o pipeline de solicitação usando 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 da aplicação for especificado, crie uma vista MVC ou Razor página para o ponto de extremidade. Certifique-se de que UseStatusCodePagesWithReExecute seja colocado antes de UseRouting para que a solicitação possa ser redirecionada para a página de estado. Para obter um exemplo de Razor Pages, consulte Pages/MyStatusCode2.cshtml no aplicativo de exemplo.
Esse método é comumente usado quando o aplicativo deve:
- Processe a solicitação sem redirecionar para um ponto de extremidade diferente. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint originalmente solicitado.
- Preserve e devolva 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 /.
O endpoint 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, consulte Pages/MyStatusCode2.cshtml no aplicativo de exemplo.
Para testar UseStatusCodePages na aplicação de exemplo, remova os comentários de webBuilder.UseStartup<StartupSCreX>(); em Program.cs.
Desativar páginas de código de estado
Para desabilitar páginas de código de status para um controlador MVC ou método de ação, use o atributo [SkipStatusCodePages].
Para desabilitar páginas de código de status para solicitações específicas em um Razor método manipulador 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ções
O código em páginas de tratamento de exceções também pode gerar exceções. As páginas de erro de produção devem ser testadas minuciosamente e tomar cuidado extra para evitar lançar exceções próprias.
Cabeçalhos de resposta
Uma vez que os cabeçalhos de uma resposta são enviados:
- O aplicativo não pode alterar o código de status da resposta.
- Quaisquer páginas de exceção ou manipuladores não podem ser executados. A resposta deve ser concluída ou a conexão anulada.
Tratamento de exceções do servidor
Além da lógica de tratamento de exceções em um aplicativo, a implementação do servidor HTTP pode lidar com algumas exceções. Se o servidor detetar uma exceção antes que os cabeçalhos de resposta sejam enviados, o servidor enviará uma 500 - Internal Server Error resposta sem um corpo de resposta. Se o servidor detetar 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 tratadas pelo aplicativo são tratadas pelo servidor. Qualquer exceção que ocorra quando o servidor está manipulando a solicitação é tratada pelo tratamento de exceções do servidor. As páginas de erro personalizadas, o middleware de tratamento de exceções e os filtros do aplicativo não afetam esse comportamento.
Manejo de exceções durante a inicialização
Somente a camada de hospedagem pode lidar com 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 pode mostrar uma página de erro para um erro de inicialização capturado somente se o erro ocorrer após a vinculação de endereço/porta do host. Se a vinculação falhar:
- A camada de hospedagem registra uma exceção crítica.
- O processo dotnet falha.
- Nenhuma página de erro é exibida quando o servidor HTTP é Kestrel.
Ao ser executado no IIS (ou no Serviço de Aplicativo do Azure) ou no IIS Express, uma Falha de Processo 502.5 - é retornada pelo ASP.NET Módulo Principal se o processo não puder ser iniciado. Para obter mais informações, consulte Solucionar 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 AddDatabaseDeveloperPageExceptionFilter de exceção da página do desenvolvedor do banco de dados captura exceções relacionadas ao banco de dados que podem ser resolvidas usando migrações do Entity Framework Core. Quando essas exceções ocorrem, uma resposta HTML é gerada com detalhes de possíveis ações para resolver o problema. Esta página está habilitada somente no ambiente de desenvolvimento. O código a seguir foi gerado pelos modelos ASP.NET Core Razor Pages 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 ou por controlador ou por ação. Nos Razor aplicativos do Pages, eles podem ser configurados globalmente ou por modelo de página. Esses filtros lidam com quaisquer exceções não tratadas que ocorram durante a execução de uma ação do controlador ou outro filtro. Para obter mais informações, consulte Filtros no ASP.NET Core.
Os filtros de exceção são úteis para intercetar exceções que ocorrem em ações MVC, mas não são tão flexíveis quanto o middleware interno de tratamento de exceções, UseExceptionHandler. Recomendamos o uso do UseExceptionHandler, a menos que você precise executar o tratamento de erros de forma diferente com base na ação 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
Para obter informações sobre como lidar com erros de estado do modelo, consulte Vinculação de modelo e validação de modelo.
Recursos adicionais
Por Tom Dykstra e Steve Smith
Este artigo aborda abordagens comuns para lidar com erros em aplicativos Web ASP.NET Core. Consulte Manipular erros em APIs ASP.NET Core para APIs da Web.
Ver e descarregar código de exemplo. (Como fazer o download.)
Página de Exceção do Desenvolvedor
A página de exceção do desenvolvedor exibe informações detalhadas sobre exceções de solicitação. Os modelos 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á sendo executado no ambiente de desenvolvimento.
Os templates colocam UseDeveloperExceptionPage antes de qualquer middleware para que as exceções sejam capturadas no middleware que vem a seguir.
O código anterior habilita a Página de Exceção do Desenvolvedor somente quando o aplicativo está sendo executado no ambiente de Desenvolvimento. Informações detalhadas de exceção não devem ser exibidas publicamente quando o aplicativo é executado em produção. Para obter mais informações sobre como configurar ambientes, consulte ASP.NET Core runtime environments.
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 consulta da cadeia de caracteres, se existirem
- Cookies, se existirem
- Headers
Página do manipulador de exceções
Para configurar uma página personalizada de tratamento de erros para o ambiente de produção, use o middleware de tratamento de exceções. O middleware:
- Captura e regista 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 reexecuta a solicitação para
/Error.
No exemplo a seguir, UseExceptionHandler adiciona o middleware de tratamento de exceções em ambientes que não são de desenvolvimento:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
O Razor modelo de aplicativo Páginas fornece uma página de erro (.cshtml) e PageModel uma classe (ErrorModel) na pasta Páginas . Para um aplicativo MVC, o modelo de projeto inclui um método de ação Error e uma visualização Error no Home controlador.
Não marque o método de ação do manipulador de erros com atributos de método HTTP, como HttpGet. Verbos explícitos impedem que algumas solicitações cheguem ao método. Permita acesso anônimo ao método se os usuários não autenticados devem ver a exibição de erro.
Aceda à exceção
Use IExceptionHandlerPathFeature para acessar a exceção e o caminho da solicitação original em um controlador ou página do manipulador de erros:
[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";
}
}
}
Warning
Não forneça informações de erro confidenciais aos clientes. A exibição de mensagens de erro é um risco de segurança.
Para ativar a página de tratamento de exceções anterior, defina o ambiente para produção e force uma exceção.
Manipulador de exceções lambda
Uma alternativa a uma página de manipulador de exceção personalizada é fornecer um lambda para UseExceptionHandler. O uso de um lambda permite o acesso ao erro antes de retornar a resposta.
Aqui está um exemplo de uso de um lambda para tratamento de exceções:
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 exibe a mensagem de erro em vez de uma mensagem de erro do IE. Para obter mais informações, consulte este problema do GitHub.
Warning
Não forneça informações de erro confidenciais de IExceptionHandlerFeature ou IExceptionHandlerPathFeature para clientes. A exibição de mensagens de erro é um risco de segurança.
Para ver o resultado do lambda de manipulação de exceção na aplicação de exemplo, use as diretivas de pré-processador ProdEnvironment e ErrorHandlerLambda e selecione Acionar uma exceção na página inicial.
UseStatusCodePages
Por padrão, um 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. A aplicação retorna um código de estado e um corpo de resposta vazio. Para fornecer páginas de código de status, use o middleware de Páginas de Código de Status.
O middleware é disponibilizado pelo pacote Microsoft.AspNetCore.Diagnostics .
Para habilitar manipuladores padrão somente de texto para códigos de status de erro comuns, chame UseStatusCodePages no método Startup.Configure.
app.UseStatusCodePages();
Ligue UseStatusCodePages antes de solicitar o middleware de tratamento (por exemplo, Static File Middleware e MVC Middleware).
Quando UseStatusCodePages não é usado, navegar para uma URL sem um ponto de extremidade retorna uma mensagem de erro dependente do navegador indicando que o ponto de extremidade não pode ser encontrado. Por exemplo, navegando para Home/Privacy2. Quando UseStatusCodePages é chamado, o navegador retorna:
Status Code: 404; Not Found
UseStatusCodePages com cadeia de caracteres de formato
Para personalizar o tipo de conteúdo de resposta e o texto, use a sobrecarga de UseStatusCodePages que aceita um tipo de conteúdo e uma string de formatação:
app.UseStatusCodePages(
"text/plain", "Status code page, status code: {0}");
UseStatusCodePages com lambda
Para especificar o código personalizado de manipulação de erros e gravação de resposta, use a sobrecarga de UseStatusCodePages que usa 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 UseStatusCodePagesWithRedirects método de extensão:
- Envia um código de status 302 - Encontrado para o cliente.
- Redireciona o cliente para o local fornecido no modelo de URL.
app.UseStatusCodePagesWithRedirects("/StatusCode?code={0}");
O modelo de URL pode incluir um {0} marcador de posição para o código de status, conforme mostrado no exemplo. Se o modelo de URL começar com ~ (tilde), o ~ é substituído pelo PathBase do aplicativo. Se você apontar para um ponto de extremidade dentro do aplicativo, crie uma exibição ou Razor página MVC para o ponto de extremidade. Para obter um Razor exemplo do Pages, consulte Pages/StatusCode.cshtml no aplicativo de exemplo.
Esse método é comumente usado quando o aplicativo:
- Deve redirecionar o cliente para um ponto de extremidade diferente, geralmente nos casos em que um aplicativo diferente processa o erro. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint redirecionado.
- Não deve preservar e retornar o código de status original com a resposta de redirecionamento inicial.
UseStatusCodePagesWithReExecute
O UseStatusCodePagesWithReExecute método de extensão:
- Retorna o código de status original para o cliente.
- Gera o corpo de resposta executando novamente o pipeline de solicitação usando um caminho alternativo.
app.UseStatusCodePagesWithReExecute("/StatusCode","?code={0}");
Se você apontar para um ponto de extremidade dentro do aplicativo, crie uma exibição ou Razor página MVC para o ponto de extremidade. Certifique-se de que UseStatusCodePagesWithReExecute seja colocado antes de UseRouting para que a solicitação possa ser redirecionada para a página de estado. Para obter um Razor exemplo do Pages, consulte Pages/StatusCode.cshtml no aplicativo de exemplo.
Esse método é comumente usado quando o aplicativo deve:
- Processe a solicitação sem redirecionar para um ponto de extremidade diferente. Para aplicações web, a barra de endereço do navegador do cliente reflete o endpoint originalmente solicitado.
- Preserve e devolva 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 placeholder no caminho, confira se o endpoint (página ou controlador) pode processar o segmento do caminho. Por exemplo, uma Razor página para erros deve aceitar o valor do segmento de caminho opcional com a @page diretiva:
@page "{code?}"
O endpoint 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 erros com atributos de método HTTP, como HttpGet. Verbos explícitos impedem que algumas solicitações cheguem ao método. Permita acesso anônimo ao método se os usuários não autenticados devem ver a exibição de erro.
Desativar páginas de código de estado
Para desabilitar páginas de código de status para um controlador MVC ou método de ação, use o [SkipStatusCodePages] atributo.
Para desabilitar páginas de código de status para solicitações específicas em um Razor método manipulador 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ções
O código em páginas de tratamento de exceções pode gerar exceções. Muitas vezes, é uma boa ideia que as páginas de erro de produção consistam em conteúdo puramente estático.
Cabeçalhos de resposta
Uma vez que os cabeçalhos de uma resposta são enviados:
- O aplicativo não pode alterar o código de status da resposta.
- Quaisquer páginas de exceção ou manipuladores não podem ser executados. A resposta deve ser concluída ou a conexão anulada.
Tratamento de exceções do servidor
Além da lógica de tratamento de exceções em seu aplicativo, a implementação do servidor HTTP pode lidar com algumas exceções. Se o servidor detetar 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 detetar 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 tratadas pelo seu aplicativo são tratadas pelo servidor. Qualquer exceção que ocorra quando o servidor está manipulando a solicitação é tratada pelo tratamento de exceções do servidor. As páginas de erro personalizadas, o middleware de tratamento de exceções e os filtros do aplicativo não afetam esse comportamento.
Manejo de exceções durante a inicialização
Somente a camada de hospedagem pode lidar com 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 pode mostrar uma página de erro para um erro de inicialização capturado somente se o erro ocorrer após a vinculação de endereço/porta do host. Se a vinculação falhar:
- A camada de hospedagem registra uma exceção crítica.
- O processo dotnet falha.
- Nenhuma página de erro é exibida quando o servidor HTTP é Kestrel.
Ao ser executado no IIS (ou no Serviço de Aplicativo do Azure) ou no IIS Express, uma Falha de Processo 502.5 - é retornada pelo ASP.NET Módulo Principal se o processo não puder ser iniciado. Para obter mais informações, consulte Solucionar 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 do banco de dados captura exceções relacionadas ao banco de dados que podem ser resolvidas usando migrações do Entity Framework. Quando essas exceções ocorrem, uma resposta HTML com detalhes de possíveis ações para resolver o problema é gerada. Esta página deve ser habilitada somente 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 ou por controlador ou por ação. Nos Razor aplicativos do Pages, eles podem ser configurados globalmente ou por modelo de página. Esses filtros lidam com qualquer exceção não tratada que ocorra durante a execução de uma ação do controlador ou outro filtro. Para obter mais informações, consulte Filtros no ASP.NET Core.
Tip
Os filtros de exceção são úteis para intercetar exceções que ocorrem em ações MVC, mas não são tão flexíveis quanto o Middleware de Tratamento de Exceções. Recomendamos o uso do middleware. Use filtros somente quando precisar executar o tratamento de erros de forma diferente com base na ação MVC escolhida.
Erros de estado do modelo
Para obter informações sobre como lidar com erros de estado do modelo, consulte Vinculação de modelo e validação de modelo.