Obsługa błędów w środowisku ASP.NET Core
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Autor: Tom Dykstra
W tym artykule opisano typowe podejścia do obsługi błędów w aplikacjach internetowych platformy ASP.NET Core. Zobacz również Obsługa błędów w internetowych interfejsach API opartych na kontrolerze ASP.NET Core i Obsługa błędów w minimalnych interfejsach API.
Aby uzyskać Blazor wskazówki dotyczące obsługi błędów, które dodaje lub zastępuje wskazówki zawarte w tym artykule, zobacz Obsługa błędów w aplikacjach ASP.NET CoreBlazor.
Strona wyjątku dla deweloperów
Na stronie wyjątku dewelopera są wyświetlane szczegółowe informacje o nieobsługiwanych wyjątkach żądań. Używa DeveloperExceptionPageMiddleware go do przechwytywania synchronicznych i asynchronicznych wyjątków z potoku HTTP i generowania odpowiedzi o błędach. Strona wyjątku dla deweloperów jest uruchamiana na wczesnym etapie potoku oprogramowania pośredniczącego, dzięki czemu może przechwytywać nieobsługiwane wyjątki zgłaszane w następujący sposób oprogramowania pośredniczącego.
aplikacje ASP.NET Core domyślnie włączają stronę wyjątku dla deweloperów, gdy obie te aplikacje:
- Uruchamianie w środowisku programistycznym.
- Aplikacja została utworzona przy użyciu bieżących szablonów, czyli przy użyciu polecenia WebApplication.CreateBuilder.
Aplikacje utworzone przy użyciu wcześniejszych szablonów, czyli przy użyciu metody WebHost.CreateDefaultBuilder, mogą włączyć stronę wyjątku dla deweloperów, wywołując metodę app.UseDeveloperExceptionPage
.
Ostrzeżenie
Nie włączaj strony wyjątku dla deweloperów, chyba że aplikacja jest uruchomiona w środowisku dewelopera. Nie udostępniaj publicznie szczegółowych informacji o wyjątkach, gdy aplikacja działa w środowisku produkcyjnym. Aby uzyskać więcej informacji na temat konfigurowania środowisk, zobacz Używanie wielu środowisk w programie ASP.NET Core.
Strona wyjątku dewelopera może zawierać następujące informacje o wyjątku i żądaniu:
- Ślad stosu
- Parametry ciągu zapytania, jeśli istnieją
- Pliki cookie, jeśli istnieją
- Nagłówki
- Metadane punktu końcowego, jeśli istnieją
Strona wyjątku dla deweloperów nie jest gwarantowana, aby podać żadne informacje. Użyj rejestrowania , aby uzyskać pełne informacje o błędzie.
Na poniższej ilustracji przedstawiono przykładową stronę wyjątku dla deweloperów z animacją, aby wyświetlić karty i wyświetlane informacje:
W odpowiedzi na żądanie z nagłówkiem Accept: text/plain
strona wyjątku dewelopera zwraca zwykły tekst zamiast HTML. Na przykład:
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
Strona obsługi wyjątków
Aby skonfigurować niestandardową stronę obsługi błędów dla środowiska produkcyjnego, wywołaj metodę .UseExceptionHandler Ta obsługa wyjątków oprogramowanie pośredniczące:
- Przechwytuje i rejestruje nieobsługiwane wyjątki.
- Ponownie wykonuje żądanie w alternatywnym potoku przy użyciu wskazanej ścieżki. Żądanie nie jest wykonywane ponownie, jeśli odpowiedź została uruchomiona. Kod wygenerowany przez szablon ponownie wykonuje żądanie przy użyciu ścieżki
/Error
.
Ostrzeżenie
Jeśli alternatywny potok zgłasza wyjątek samodzielnie, oprogramowanie pośredniczące obsługi wyjątków ponownie wywróci oryginalny wyjątek.
Ponieważ to oprogramowanie pośredniczące może ponownie wykonać potok żądania:
- Oprogramowanie pośredniczące musi obsługiwać ponowne wysyłanie za pomocą tego samego żądania. Zwykle oznacza to czyszczenie ich stanu po wywołaniu
_next
lub buforowaniu ich przetwarzania na obiekcie,HttpContext
aby uniknąć jego ponownego utworzenia. W przypadku obsługi treści żądania oznacza to buforowanie lub buforowanie wyników, takich jak czytnik formularzy. - W przypadku przeciążenia używanego UseExceptionHandler(IApplicationBuilder, String) w szablonach tylko ścieżka żądania jest modyfikowana, a dane trasy są czyszczone. Żądania danych, takich jak nagłówki, metoda i elementy, są ponownie używane w takim przypadku.
- Usługi o określonym zakresie pozostają takie same.
W poniższym przykładzie UseExceptionHandler dodano oprogramowanie pośredniczące obsługujące wyjątki w środowiskach nieprogramowania:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Szablon Razor aplikacji Pages zawiera stronę Błąd (.cshtml
) i PageModel klasę (ErrorModel
) w folderze Pages . W przypadku aplikacji MVC szablon projektu zawiera metodę Error
akcji i widok błędu Home dla kontrolera.
Obsługa wyjątków oprogramowanie pośredniczące ponownie wykonuje żądanie przy użyciu oryginalnej metody HTTP. Jeśli punkt końcowy procedury obsługi błędów jest ograniczony do określonego zestawu metod HTTP, jest uruchamiany tylko dla tych metod HTTP. Na przykład akcja kontrolera MVC korzystająca z atrybutu [HttpGet]
jest uruchamiana tylko dla żądań GET. Aby upewnić się, że wszystkie żądania docierają do niestandardowej strony obsługi błędów, nie ograniczaj ich do określonego zestawu metod HTTP.
Aby obsłużyć wyjątki inaczej na podstawie oryginalnej metody HTTP:
- W obszarze Razor Pages utwórz wiele metod obsługi. Na przykład użyj polecenia
OnGet
, aby obsługiwać wyjątki GET i używaćOnPost
ich do obsługi wyjątków POST. - W przypadku wzorca MVC zastosuj atrybuty czasownika HTTP do wielu akcji. Na przykład użyj polecenia
[HttpGet]
, aby obsługiwać wyjątki GET i używać[HttpPost]
ich do obsługi wyjątków POST.
Aby umożliwić nieuwierzytelnionym użytkownikom wyświetlanie niestandardowej strony obsługi błędów, upewnij się, że obsługuje dostęp anonimowy.
Uzyskiwanie dostępu do wyjątku
Użyj IExceptionHandlerPathFeature polecenia , aby uzyskać dostęp do wyjątku i oryginalnej ścieżki żądania w procedurze obsługi błędów. W poniższym przykładzie użyto IExceptionHandlerPathFeature
metody , aby uzyskać więcej informacji na temat wyjątku, który został zgłoszony:
[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.";
}
}
}
Ostrzeżenie
Nie należy udostępniać klientom poufnych informacji o błędach. Obsługa błędów jest zagrożeniem bezpieczeństwa.
Procedura obsługi wyjątków lambda
Alternatywą dla niestandardowej strony obsługi wyjątków jest podanie lambda do UseExceptionHandler. Użycie lambda umożliwia dostęp do błędu przed zwróceniem odpowiedzi.
Poniższy kod używa wyrażenia lambda do obsługi wyjątków:
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();
}
Innym sposobem użycia lambda jest ustawienie kodu stanu na podstawie typu wyjątku, jak w poniższym przykładzie:
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
});
}
Ostrzeżenie
Nie należy udostępniać klientom poufnych informacji o błędach. Obsługa błędów jest zagrożeniem bezpieczeństwa.
IExceptionHandler
IExceptionHandler to interfejs, który daje deweloperowi wywołanie zwrotne do obsługi znanych wyjątków w centralnej lokalizacji.
IExceptionHandler
implementacje są rejestrowane przez wywołanie metody IServiceCollection.AddExceptionHandler<T>
. Okres istnienia IExceptionHandler
wystąpienia jest pojedynczy. Można dodać wiele implementacji i są one wywoływane w kolejności zarejestrowanej.
Jeśli program obsługi wyjątków obsługuje żądanie, może wrócić true
do zatrzymania przetwarzania. Jeśli wyjątek nie jest obsługiwany przez żadną procedurę obsługi wyjątków, kontrolka powraca do domyślnego zachowania i opcji oprogramowania pośredniczącego. Różne metryki i dzienniki są emitowane dla obsługiwanych i nieobsługiwane wyjątki.
W poniższym przykładzie pokazano implementację IExceptionHandler
:
using Microsoft.AspNetCore.Diagnostics;
namespace ErrorHandlingSample
{
public class CustomExceptionHandler : IExceptionHandler
{
private readonly ILogger<CustomExceptionHandler> logger;
public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
{
this.logger = logger;
}
public ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{
var exceptionMessage = exception.Message;
logger.LogError(
"Error Message: {exceptionMessage}, Time of occurrence {time}",
exceptionMessage, DateTime.UtcNow);
// Return false to continue with the default behavior
// - or - return true to signal that this exception is handled
return ValueTask.FromResult(false);
}
}
}
W poniższym przykładzie pokazano, jak zarejestrować implementację IExceptionHandler
iniekcji zależności:
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
Po uruchomieniu poprzedniego kodu w środowisku programistycznym:
- Element jest wywoływany
CustomExceptionHandler
jako pierwszy w celu obsługi wyjątku. - Po rejestrowaniu wyjątku
TryHandleAsync
metoda zwracafalse
wartość , więc zostanie wyświetlona strona wyjątku dewelopera.
W innych środowiskach:
- Element jest wywoływany
CustomExceptionHandler
jako pierwszy w celu obsługi wyjątku. - Po rejestrowaniu wyjątku
TryHandleAsync
metoda zwracafalse
wartość , więc zostanie wyświetlona/Error
strona .
UseStatusCodePages
Domyślnie aplikacja ASP.NET Core nie udostępnia strony kodowej stanu kodów stanu http, takich jak 404 — Nie znaleziono. Gdy aplikacja ustawia kod stanu błędu HTTP 400-599, który nie ma treści, zwraca kod stanu i pustą treść odpowiedzi. Aby włączyć domyślne programy obsługi tylko do tekstu dla typowych kodów stanu błędów, wywołaj metodę UseStatusCodePages w pliku Program.cs
:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Przed obsługą żądań należy wywołać UseStatusCodePages
oprogramowanie pośredniczące. Na przykład wywołaj wywołanie UseStatusCodePages
przed oprogramowaniem pośredniczącym plików statycznych i oprogramowaniem pośredniczącym punktów końcowych.
Jeśli UseStatusCodePages
nie jest używany, przejście do adresu URL bez punktu końcowego zwraca komunikat o błędzie zależny od przeglądarki wskazujący, że nie można odnaleźć punktu końcowego. Po UseStatusCodePages
wywołaniu przeglądarka zwraca następującą odpowiedź:
Status Code: 404; Not Found
UseStatusCodePages
nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
Uwaga
Oprogramowanie pośredniczące stron kodu stanu nie przechwytuje wyjątków. Aby podać niestandardową stronę obsługi błędów, użyj strony obsługi wyjątków.
UseStatusCodePages z ciągiem formatu
Aby dostosować typ zawartości odpowiedzi i tekst, użyj przeciążenia UseStatusCodePages , które przyjmuje typ zawartości i ciąg formatu:
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}");
W poprzednim kodzie {0}
jest symbolem zastępczym kodu błędu.
UseStatusCodePages
ciąg formatu nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
UseStatusCodePages z lambda
Aby określić niestandardowy kod obsługi błędów i pisania odpowiedzi, użyj przeciążenia UseStatusCodePages , które przyjmuje wyrażenie 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
z lambda nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
UseStatusCodePagesWithRedirects
UseStatusCodePagesWithRedirects Metoda rozszerzenia:
- Wysyła do klienta kod stanu 302 — Znaleziono.
- Przekierowuje klienta do punktu końcowego obsługi błędów podanego w szablonie adresu URL. Punkt końcowy obsługi błędów zwykle wyświetla informacje o błędzie i zwraca błąd HTTP 200.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Szablon adresu URL może zawierać {0}
symbol zastępczy kodu stanu, jak pokazano w poprzednim kodzie. Jeśli szablon adresu URL zaczyna się od ~
(tylda), ~
element zostanie zastąpiony PathBase
przez aplikację . Podczas określania punktu końcowego w aplikacji utwórz widok MVC lub Razor stronę punktu końcowego.
Ta metoda jest często używana, gdy aplikacja:
- Powinien przekierować klienta do innego punktu końcowego, zwykle w przypadkach, gdy inna aplikacja przetwarza błąd. W przypadku aplikacji internetowych pasek adresu przeglądarki klienta odzwierciedla przekierowany punkt końcowy.
- Nie należy zachowywać i zwracać oryginalnego kodu stanu z początkową odpowiedzią przekierowania.
UseStatusCodePagesWithReExecute
UseStatusCodePagesWithReExecute Metoda rozszerzenia:
- Generuje treść odpowiedzi przez ponowne wykonanie potoku żądania przy użyciu alternatywnej ścieżki.
- Nie zmienia kodu stanu przed lub po ponownym wykonaniu potoku.
Nowe wykonanie potoku może zmienić kod stanu odpowiedzi, ponieważ nowy potok ma pełną kontrolę nad kodem stanu. Jeśli nowy potok nie zmieni kodu stanu, oryginalny kod stanu zostanie wysłany do klienta.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Jeśli określono punkt końcowy w aplikacji, utwórz widok MVC lub Razor stronę dla punktu końcowego.
Ta metoda jest często używana, gdy aplikacja powinna:
- Przetwarzanie żądania bez przekierowywania do innego punktu końcowego. W przypadku aplikacji internetowych pasek adresu przeglądarki klienta odzwierciedla pierwotnie żądany punkt końcowy.
- Zachowaj i zwróć oryginalny kod stanu z odpowiedzią.
Szablon adresu URL musi zaczynać się od /
i może zawierać symbol zastępczy {0}
kodu stanu. Aby przekazać kod stanu jako parametr ciągu zapytania, przekaż drugi argument do UseStatusCodePagesWithReExecute
elementu . Na przykład:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
Punkt końcowy, który przetwarza błąd, może uzyskać oryginalny adres URL, który wygenerował błąd, jak pokazano w poniższym przykładzie:
[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}";
}
}
}
Ponieważ to oprogramowanie pośredniczące może ponownie wykonać potok żądania:
- Oprogramowanie pośredniczące musi obsługiwać ponowne wysyłanie za pomocą tego samego żądania. Zwykle oznacza to czyszczenie ich stanu po wywołaniu
_next
lub buforowaniu ich przetwarzania na obiekcie,HttpContext
aby uniknąć jego ponownego utworzenia. W przypadku obsługi treści żądania oznacza to buforowanie lub buforowanie wyników, takich jak czytnik formularzy. - Usługi o określonym zakresie pozostają takie same.
Wyłączanie stron kodu stanu
Aby wyłączyć strony kodu stanu dla kontrolera MVC lub metody akcji, użyj atrybutu [SkipStatusCodePages].
Aby wyłączyć strony kodu stanu dla określonych żądań w Razor metodzie obsługi stron lub w kontrolerze MVC, użyj polecenia IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Kod obsługi wyjątków
Kod na stronach obsługi wyjątków może również zgłaszać wyjątki. Strony błędów produkcyjnych należy dokładnie przetestować i zachować szczególną ostrożność, aby uniknąć zgłaszania własnych wyjątków.
Nagłówki odpowiedzi
Po wysłaniu nagłówków odpowiedzi:
- Aplikacja nie może zmienić kodu stanu odpowiedzi.
- Nie można uruchomić żadnych stron wyjątków lub procedur obsługi. Odpowiedź musi zostać ukończona lub przerwane połączenie.
Obsługa wyjątków serwera
Oprócz logiki obsługi wyjątków w aplikacji implementacja serwera HTTP może obsługiwać niektóre wyjątki. Jeśli serwer przechwytuje wyjątek przed wysłaniem 500 - Internal Server Error
nagłówków odpowiedzi, serwer wysyła odpowiedź bez treści odpowiedzi. Jeśli serwer przechwytuje wyjątek po wysłaniu nagłówków odpowiedzi, serwer zamknie połączenie. Żądania, które nie są obsługiwane przez aplikację, są obsługiwane przez serwer. Każdy wyjątek, który występuje, gdy serwer obsługuje żądanie, jest obsługiwany przez obsługę wyjątków serwera. Niestandardowe strony błędów aplikacji, oprogramowanie pośredniczące obsługujące wyjątki i filtry nie mają wpływu na to zachowanie.
Obsługa wyjątków uruchamiania
Tylko warstwa hostingu może obsługiwać wyjątki, które mają miejsce podczas uruchamiania aplikacji. Host można skonfigurować do przechwytywania błędów uruchamiania i przechwytywania szczegółowych błędów.
Warstwa hostingu może wyświetlić stronę błędu przechwyconego błędu uruchamiania tylko wtedy, gdy błąd występuje po powiązaniu adresu hosta/portu. Jeśli powiązanie zakończy się niepowodzeniem:
- Warstwa hostingu rejestruje wyjątek krytyczny.
- Proces dotnet ulega awarii.
- Nie jest wyświetlana strona błędu, gdy serwer HTTP ma wartość Kestrel.
W przypadku uruchamiania w usługach IIS (lub aplikacja systemu Azure Service) lub IIS Express błąd procesu 502.5 jest zwracany przez moduł ASP.NET Core, jeśli nie można uruchomić procesu. Aby uzyskać więcej informacji, zobacz Rozwiązywanie problemów z programem ASP.NET Core w usłudze aplikacja systemu Azure i usługach IIS.
Strona błędu bazy danych
Filtr AddDatabaseDeveloperPageExceptionFilter wyjątku strony dewelopera bazy danych przechwytuje wyjątki związane z bazą danych, które można rozwiązać przy użyciu migracji programu Entity Framework Core. W przypadku wystąpienia tych wyjątków odpowiedź HTML jest generowana ze szczegółami możliwych akcji w celu rozwiązania problemu. Ta strona jest włączona tylko w środowisku deweloperów. Poniższy kod dodaje filtr wyjątku strony dewelopera bazy danych:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Filtry wyjątków
W aplikacjach MVC filtry wyjątków można skonfigurować globalnie lub na kontrolerze lub dla poszczególnych akcji. W Razor aplikacjach Pages można je skonfigurować globalnie lub na model strony. Te filtry obsługują wszelkie nieobsługiwane wyjątki występujące podczas wykonywania akcji kontrolera lub innego filtru. Aby uzyskać więcej informacji, zobacz Filtry w ASP.NET Core.
Filtry wyjątków są przydatne w przypadku podlewek wyjątków występujących w ramach akcji MVC, ale nie są tak elastyczne, jak wbudowane oprogramowanie pośredniczące obsługujące wyjątki. UseExceptionHandler Zalecamy użycie polecenia UseExceptionHandler
, chyba że musisz wykonać obsługę błędów inaczej w zależności od wybranej akcji MVC.
Błędy stanu modelu
Aby uzyskać informacje na temat obsługi błędów stanu modelu, zobacz Powiązanie modelu i Walidacja modelu.
Szczegóły problemu
Szczegóły problemu nie są jedynym formatem odpowiedzi opisujący błąd interfejsu API HTTP, jednak są one często używane do zgłaszania błędów dla interfejsów API HTTP.
Usługa szczegółów problemu IProblemDetailsService implementuje interfejs, który obsługuje tworzenie szczegółów problemu w ASP.NET Core. Metoda AddProblemDetails(IServiceCollection) rozszerzenia w systemie IServiceCollection rejestruje domyślną IProblemDetailsService
implementację.
W aplikacjach ASP.NET Core następujące oprogramowanie pośredniczące generuje szczegóły problemów odpowiedzi HTTP podczas AddProblemDetails
wywoływana, z wyjątkiem sytuacji, gdyAccept
nagłówek HTTP żądania nie zawiera jednego z typów zawartości obsługiwanych przez zarejestrowane IProblemDetailsWriter (domyślnie: application/json
):
- ExceptionHandlerMiddleware: generuje odpowiedź ze szczegółami problemu, gdy program obsługi niestandardowej nie jest zdefiniowany.
- StatusCodePagesMiddleware: domyślnie generuje odpowiedź ze szczegółami problemu.
- DeveloperExceptionPageMiddleware: generuje odpowiedź ze szczegółami problemu w programowania, gdy
Accept
nagłówek HTTP żądania nie zawieratext/html
elementu .
Poniższy kod konfiguruje aplikację tak, aby wygenerowała odpowiedź na szczegóły problemu dla wszystkich odpowiedzi na błędy klienta HTTP i serwera, które nie mają jeszcze zawartości treści:
builder.Services.AddProblemDetails();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
W następnej sekcji pokazano, jak dostosować treść odpowiedzi szczegółów problemu.
Dostosowywanie szczegółów problemu
Automatyczne tworzenie obiektu ProblemDetails
można dostosować przy użyciu dowolnej z następujących opcji:
- Korzystanie z polecenia
ProblemDetailsOptions.CustomizeProblemDetails
- Używanie niestandardowego
IProblemDetailsWriter
- Wywoływanie
IProblemDetailsService
oprogramowania pośredniczącego
CustomizeProblemDetails
operacja
Wygenerowane szczegóły problemu można dostosować przy użyciu polecenia CustomizeProblemDetails, a dostosowania są stosowane do wszystkich szczegółów problemu generowanego automatycznie.
Poniższy kod używa ProblemDetailsOptions metody do ustawienia :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();
Na przykład wynik punktu końcowego generuje następującą HTTP Status 400 Bad Request
treść odpowiedzi szczegółów problemu:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
Zwyczaj IProblemDetailsWriter
Implementację IProblemDetailsWriter można utworzyć na potrzeby zaawansowanych dostosowań.
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));
}
}
Uwaga: w przypadku korzystania z niestandardowego IProblemDetailsWriter
obiektu należy zarejestrować niestandardowy IProblemDetailsWriter
przed wywołaniem metody AddRazorPages, AddControllers, AddControllersWithViewslub 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();
Szczegóły problemu z oprogramowania pośredniczącego
Alternatywnym podejściem do używania z ProblemDetailsOptions CustomizeProblemDetails programem jest ustawienie oprogramowania pośredniczącego ProblemDetails w programie . Odpowiedź na szczegóły problemu może zostać napisana przez wywołanie metody 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();
W poprzednim kodzie minimalne punkty końcowe /divide
interfejsu API i /squareroot
zwracają oczekiwaną niestandardową odpowiedź problemu na dane wejściowe błędu.
Punkty końcowe kontrolera interfejsu API zwracają domyślną odpowiedź problemu na dane wejściowe błędu, a nie niestandardową odpowiedź na problem. Zwracana jest domyślna odpowiedź na problem, ponieważ kontroler interfejsu API zapisał się do strumienia odpowiedzi, Szczegóły problemu dla kodów stanu błędu, zanim IProblemDetailsService.WriteAsync
zostanie wywołana, a odpowiedź nie zostanie zapisana ponownie.
Poniższe ValuesController
polecenie zwraca wartość BadRequestResult, która zapisuje w strumieniu odpowiedzi i w związku z tym uniemożliwia zwracanie niestandardowej odpowiedzi na problem.
[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));
}
}
Następujące zwracany ControllerBase.Problem
jest następujący Values3Controller
wynik problemu, więc zwracany jest oczekiwany wynik problemu niestandardowego:
[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));
}
}
Tworzenie ładunku ProblemDetails dla wyjątków
Rozważmy następującą aplikację:
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();
W środowiskach nieprogramowania, gdy wystąpi wyjątek, poniżej znajduje się standardowa odpowiedź ProblemDetails zwrócona do klienta:
{
"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"
}
W przypadku większości aplikacji powyższy kod jest wymagany w przypadku wyjątków. Jednak w poniższej sekcji pokazano, jak uzyskać bardziej szczegółowe odpowiedzi na problemy.
Alternatywą dla niestandardowej strony obsługi wyjątków jest podanie lambda do UseExceptionHandler. Użycie lambda umożliwia dostęp do błędu i napisanie odpowiedzi ze szczegółami problemu za pomocą polecenia 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();
Ostrzeżenie
Nie należy udostępniać klientom poufnych informacji o błędach. Obsługa błędów jest zagrożeniem bezpieczeństwa.
Alternatywną metodą generowania szczegółów problemu jest użycie pakietu NuGet innej firmy Hellang.Middleware.ProblemDetails , który może służyć do mapowania wyjątków i błędów klienta na szczegóły problemu.
Dodatkowe zasoby
- Wyświetl lub pobierz przykładowy kod (jak pobrać)
- Rozwiązywanie problemów z typowymi błędami platformy ASP.NET Core w usłudze Azure App Service i usługach IIS
- Rozwiązywanie problemów z typowymi błędami platformy ASP.NET Core w usłudze Azure App Service i usługach IIS
- Obsługa błędów w internetowych interfejsach API opartych na kontrolerze ASP.NET Core
- Obsługa błędów w minimalnych interfejsach API.
Autor: Tom Dykstra
W tym artykule opisano typowe podejścia do obsługi błędów w aplikacjach internetowych platformy ASP.NET Core. Zobacz również Obsługa błędów w internetowych interfejsach API opartych na kontrolerze ASP.NET Core i Obsługa błędów w minimalnych interfejsach API.
Strona wyjątku dla deweloperów
Na stronie wyjątku dewelopera są wyświetlane szczegółowe informacje o nieobsługiwanych wyjątkach żądań. aplikacje ASP.NET Core domyślnie włączają stronę wyjątku dla deweloperów, gdy obie te aplikacje:
- Uruchamianie w środowisku programistycznym.
- Aplikacja utworzona przy użyciu bieżących szablonów, czyli przy użyciu aplikacji WebApplication.CreateBuilder. Aplikacje utworzone przy użyciu elementu
WebHost.CreateDefaultBuilder
muszą włączyć stronę wyjątku dla deweloperów, wywołując metodęapp.UseDeveloperExceptionPage
w plikuConfigure
.
Strona wyjątku dla deweloperów jest uruchamiana na wczesnym etapie potoku oprogramowania pośredniczącego, dzięki czemu może przechwytywać nieobsługiwane wyjątki zgłaszane w następujący sposób oprogramowania pośredniczącego.
Szczegółowe informacje o wyjątku nie powinny być wyświetlane publicznie, gdy aplikacja działa w środowisku produkcyjnym. Aby uzyskać więcej informacji na temat konfigurowania środowisk, zobacz Używanie wielu środowisk w programie ASP.NET Core.
Strona wyjątku dewelopera może zawierać następujące informacje o wyjątku i żądaniu:
- Ślad stosu
- Parametry ciągu zapytania, jeśli istnieją
- Pliki cookie, jeśli istnieją
- Nagłówki
Strona wyjątku dla deweloperów nie jest gwarantowana, aby podać żadne informacje. Użyj rejestrowania , aby uzyskać pełne informacje o błędzie.
Strona obsługi wyjątków
Aby skonfigurować niestandardową stronę obsługi błędów dla środowiska produkcyjnego, wywołaj metodę .UseExceptionHandler Ta obsługa wyjątków oprogramowanie pośredniczące:
- Przechwytuje i rejestruje nieobsługiwane wyjątki.
- Ponownie wykonuje żądanie w alternatywnym potoku przy użyciu wskazanej ścieżki. Żądanie nie jest wykonywane ponownie, jeśli odpowiedź została uruchomiona. Kod wygenerowany przez szablon ponownie wykonuje żądanie przy użyciu ścieżki
/Error
.
Ostrzeżenie
Jeśli alternatywny potok zgłasza wyjątek samodzielnie, oprogramowanie pośredniczące obsługi wyjątków ponownie wywróci oryginalny wyjątek.
Ponieważ to oprogramowanie pośredniczące może ponownie wykonać potok żądania:
- Oprogramowanie pośredniczące musi obsługiwać ponowne wysyłanie za pomocą tego samego żądania. Zwykle oznacza to czyszczenie ich stanu po wywołaniu
_next
lub buforowaniu ich przetwarzania na obiekcie,HttpContext
aby uniknąć jego ponownego utworzenia. W przypadku obsługi treści żądania oznacza to buforowanie lub buforowanie wyników, takich jak czytnik formularzy. - W przypadku przeciążenia używanego UseExceptionHandler(IApplicationBuilder, String) w szablonach tylko ścieżka żądania jest modyfikowana, a dane trasy są czyszczone. Żądania danych, takich jak nagłówki, metoda i elementy, są ponownie używane w takim przypadku.
- Usługi o określonym zakresie pozostają takie same.
W poniższym przykładzie UseExceptionHandler dodano oprogramowanie pośredniczące obsługujące wyjątki w środowiskach nieprogramowania:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Szablon Razor aplikacji Pages zawiera stronę Błąd (.cshtml
) i PageModel klasę (ErrorModel
) w folderze Pages . W przypadku aplikacji MVC szablon projektu zawiera metodę Error
akcji i widok błędu Home dla kontrolera.
Obsługa wyjątków oprogramowanie pośredniczące ponownie wykonuje żądanie przy użyciu oryginalnej metody HTTP. Jeśli punkt końcowy procedury obsługi błędów jest ograniczony do określonego zestawu metod HTTP, jest uruchamiany tylko dla tych metod HTTP. Na przykład akcja kontrolera MVC korzystająca z atrybutu [HttpGet]
jest uruchamiana tylko dla żądań GET. Aby upewnić się, że wszystkie żądania docierają do niestandardowej strony obsługi błędów, nie ograniczaj ich do określonego zestawu metod HTTP.
Aby obsłużyć wyjątki inaczej na podstawie oryginalnej metody HTTP:
- W obszarze Razor Pages utwórz wiele metod obsługi. Na przykład użyj polecenia
OnGet
, aby obsługiwać wyjątki GET i używaćOnPost
ich do obsługi wyjątków POST. - W przypadku wzorca MVC zastosuj atrybuty czasownika HTTP do wielu akcji. Na przykład użyj polecenia
[HttpGet]
, aby obsługiwać wyjątki GET i używać[HttpPost]
ich do obsługi wyjątków POST.
Aby umożliwić nieuwierzytelnionym użytkownikom wyświetlanie niestandardowej strony obsługi błędów, upewnij się, że obsługuje dostęp anonimowy.
Uzyskiwanie dostępu do wyjątku
Użyj IExceptionHandlerPathFeature polecenia , aby uzyskać dostęp do wyjątku i oryginalnej ścieżki żądania w procedurze obsługi błędów. W poniższym przykładzie użyto IExceptionHandlerPathFeature
metody , aby uzyskać więcej informacji na temat wyjątku, który został zgłoszony:
[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.";
}
}
}
Ostrzeżenie
Nie należy udostępniać klientom poufnych informacji o błędach. Obsługa błędów jest zagrożeniem bezpieczeństwa.
Procedura obsługi wyjątków lambda
Alternatywą dla niestandardowej strony obsługi wyjątków jest podanie lambda do UseExceptionHandler. Użycie lambda umożliwia dostęp do błędu przed zwróceniem odpowiedzi.
Poniższy kod używa wyrażenia lambda do obsługi wyjątków:
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();
}
Ostrzeżenie
Nie należy udostępniać klientom poufnych informacji o błędach. Obsługa błędów jest zagrożeniem bezpieczeństwa.
IExceptionHandler
IExceptionHandler to interfejs, który daje deweloperowi wywołanie zwrotne do obsługi znanych wyjątków w centralnej lokalizacji.
IExceptionHandler
implementacje są rejestrowane przez wywołanie metody IServiceCollection.AddExceptionHandler<T>
. Okres istnienia IExceptionHandler
wystąpienia jest pojedynczy. Można dodać wiele implementacji i są one wywoływane w kolejności zarejestrowanej.
Jeśli program obsługi wyjątków obsługuje żądanie, może wrócić true
do zatrzymania przetwarzania. Jeśli wyjątek nie jest obsługiwany przez żadną procedurę obsługi wyjątków, kontrolka powraca do domyślnego zachowania i opcji oprogramowania pośredniczącego. Różne metryki i dzienniki są emitowane dla obsługiwanych i nieobsługiwane wyjątki.
W poniższym przykładzie pokazano implementację IExceptionHandler
:
using Microsoft.AspNetCore.Diagnostics;
namespace ErrorHandlingSample
{
public class CustomExceptionHandler : IExceptionHandler
{
private readonly ILogger<CustomExceptionHandler> logger;
public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
{
this.logger = logger;
}
public ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{
var exceptionMessage = exception.Message;
logger.LogError(
"Error Message: {exceptionMessage}, Time of occurrence {time}",
exceptionMessage, DateTime.UtcNow);
// Return false to continue with the default behavior
// - or - return true to signal that this exception is handled
return ValueTask.FromResult(false);
}
}
}
W poniższym przykładzie pokazano, jak zarejestrować implementację IExceptionHandler
iniekcji zależności:
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
Po uruchomieniu poprzedniego kodu w środowisku programistycznym:
- Element jest wywoływany
CustomExceptionHandler
jako pierwszy w celu obsługi wyjątku. - Po rejestrowaniu wyjątku
TryHandleException
metoda zwracafalse
wartość , więc zostanie wyświetlona strona wyjątku dewelopera.
W innych środowiskach:
- Element jest wywoływany
CustomExceptionHandler
jako pierwszy w celu obsługi wyjątku. - Po rejestrowaniu wyjątku
TryHandleException
metoda zwracafalse
wartość , więc zostanie wyświetlona/Error
strona .
UseStatusCodePages
Domyślnie aplikacja ASP.NET Core nie udostępnia strony kodowej stanu kodów stanu http, takich jak 404 — Nie znaleziono. Gdy aplikacja ustawia kod stanu błędu HTTP 400-599, który nie ma treści, zwraca kod stanu i pustą treść odpowiedzi. Aby włączyć domyślne programy obsługi tylko do tekstu dla typowych kodów stanu błędów, wywołaj metodę UseStatusCodePages w pliku Program.cs
:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Przed obsługą żądań należy wywołać UseStatusCodePages
oprogramowanie pośredniczące. Na przykład wywołaj wywołanie UseStatusCodePages
przed oprogramowaniem pośredniczącym plików statycznych i oprogramowaniem pośredniczącym punktów końcowych.
Jeśli UseStatusCodePages
nie jest używany, przejście do adresu URL bez punktu końcowego zwraca komunikat o błędzie zależny od przeglądarki wskazujący, że nie można odnaleźć punktu końcowego. Po UseStatusCodePages
wywołaniu przeglądarka zwraca następującą odpowiedź:
Status Code: 404; Not Found
UseStatusCodePages
nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
Uwaga
Oprogramowanie pośredniczące stron kodu stanu nie przechwytuje wyjątków. Aby podać niestandardową stronę obsługi błędów, użyj strony obsługi wyjątków.
UseStatusCodePages z ciągiem formatu
Aby dostosować typ zawartości odpowiedzi i tekst, użyj przeciążenia UseStatusCodePages , które przyjmuje typ zawartości i ciąg formatu:
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}");
W poprzednim kodzie {0}
jest symbolem zastępczym kodu błędu.
UseStatusCodePages
ciąg formatu nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
UseStatusCodePages z lambda
Aby określić niestandardowy kod obsługi błędów i pisania odpowiedzi, użyj przeciążenia UseStatusCodePages , które przyjmuje wyrażenie 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
z lambda nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
UseStatusCodePagesWithRedirects
UseStatusCodePagesWithRedirects Metoda rozszerzenia:
- Wysyła do klienta kod stanu 302 — Znaleziono.
- Przekierowuje klienta do punktu końcowego obsługi błędów podanego w szablonie adresu URL. Punkt końcowy obsługi błędów zwykle wyświetla informacje o błędzie i zwraca błąd HTTP 200.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Szablon adresu URL może zawierać {0}
symbol zastępczy kodu stanu, jak pokazano w poprzednim kodzie. Jeśli szablon adresu URL zaczyna się od ~
(tylda), ~
element zostanie zastąpiony PathBase
przez aplikację . Podczas określania punktu końcowego w aplikacji utwórz widok MVC lub Razor stronę punktu końcowego.
Ta metoda jest często używana, gdy aplikacja:
- Powinien przekierować klienta do innego punktu końcowego, zwykle w przypadkach, gdy inna aplikacja przetwarza błąd. W przypadku aplikacji internetowych pasek adresu przeglądarki klienta odzwierciedla przekierowany punkt końcowy.
- Nie należy zachowywać i zwracać oryginalnego kodu stanu z początkową odpowiedzią przekierowania.
UseStatusCodePagesWithReExecute
UseStatusCodePagesWithReExecute Metoda rozszerzenia:
- Generuje treść odpowiedzi przez ponowne wykonanie potoku żądania przy użyciu alternatywnej ścieżki.
- Nie zmienia kodu stanu przed lub po ponownym wykonaniu potoku.
Nowe wykonanie potoku może zmienić kod stanu odpowiedzi, ponieważ nowy potok ma pełną kontrolę nad kodem stanu. Jeśli nowy potok nie zmieni kodu stanu, oryginalny kod stanu zostanie wysłany do klienta.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Jeśli określono punkt końcowy w aplikacji, utwórz widok MVC lub Razor stronę dla punktu końcowego.
Ta metoda jest często używana, gdy aplikacja powinna:
- Przetwarzanie żądania bez przekierowywania do innego punktu końcowego. W przypadku aplikacji internetowych pasek adresu przeglądarki klienta odzwierciedla pierwotnie żądany punkt końcowy.
- Zachowaj i zwróć oryginalny kod stanu z odpowiedzią.
Szablon adresu URL musi zaczynać się od /
i może zawierać symbol zastępczy {0}
kodu stanu. Aby przekazać kod stanu jako parametr ciągu zapytania, przekaż drugi argument do UseStatusCodePagesWithReExecute
elementu . Na przykład:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
Punkt końcowy, który przetwarza błąd, może uzyskać oryginalny adres URL, który wygenerował błąd, jak pokazano w poniższym przykładzie:
[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}";
}
}
}
Ponieważ to oprogramowanie pośredniczące może ponownie wykonać potok żądania:
- Oprogramowanie pośredniczące musi obsługiwać ponowne wysyłanie za pomocą tego samego żądania. Zwykle oznacza to czyszczenie ich stanu po wywołaniu
_next
lub buforowaniu ich przetwarzania na obiekcie,HttpContext
aby uniknąć jego ponownego utworzenia. W przypadku obsługi treści żądania oznacza to buforowanie lub buforowanie wyników, takich jak czytnik formularzy. - Usługi o określonym zakresie pozostają takie same.
Wyłączanie stron kodu stanu
Aby wyłączyć strony kodu stanu dla kontrolera MVC lub metody akcji, użyj atrybutu [SkipStatusCodePages].
Aby wyłączyć strony kodu stanu dla określonych żądań w Razor metodzie obsługi stron lub w kontrolerze MVC, użyj polecenia IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Kod obsługi wyjątków
Kod na stronach obsługi wyjątków może również zgłaszać wyjątki. Strony błędów produkcyjnych należy dokładnie przetestować i zachować szczególną ostrożność, aby uniknąć zgłaszania własnych wyjątków.
Nagłówki odpowiedzi
Po wysłaniu nagłówków odpowiedzi:
- Aplikacja nie może zmienić kodu stanu odpowiedzi.
- Nie można uruchomić żadnych stron wyjątków lub procedur obsługi. Odpowiedź musi zostać ukończona lub przerwane połączenie.
Obsługa wyjątków serwera
Oprócz logiki obsługi wyjątków w aplikacji implementacja serwera HTTP może obsługiwać niektóre wyjątki. Jeśli serwer przechwytuje wyjątek przed wysłaniem 500 - Internal Server Error
nagłówków odpowiedzi, serwer wysyła odpowiedź bez treści odpowiedzi. Jeśli serwer przechwytuje wyjątek po wysłaniu nagłówków odpowiedzi, serwer zamknie połączenie. Żądania, które nie są obsługiwane przez aplikację, są obsługiwane przez serwer. Każdy wyjątek, który występuje, gdy serwer obsługuje żądanie, jest obsługiwany przez obsługę wyjątków serwera. Niestandardowe strony błędów aplikacji, oprogramowanie pośredniczące obsługujące wyjątki i filtry nie mają wpływu na to zachowanie.
Obsługa wyjątków uruchamiania
Tylko warstwa hostingu może obsługiwać wyjątki, które mają miejsce podczas uruchamiania aplikacji. Host można skonfigurować do przechwytywania błędów uruchamiania i przechwytywania szczegółowych błędów.
Warstwa hostingu może wyświetlić stronę błędu przechwyconego błędu uruchamiania tylko wtedy, gdy błąd występuje po powiązaniu adresu hosta/portu. Jeśli powiązanie zakończy się niepowodzeniem:
- Warstwa hostingu rejestruje wyjątek krytyczny.
- Proces dotnet ulega awarii.
- Nie jest wyświetlana strona błędu, gdy serwer HTTP ma wartość Kestrel.
W przypadku uruchamiania w usługach IIS (lub aplikacja systemu Azure Service) lub IIS Express błąd procesu 502.5 jest zwracany przez moduł ASP.NET Core, jeśli nie można uruchomić procesu. Aby uzyskać więcej informacji, zobacz Rozwiązywanie problemów z programem ASP.NET Core w usłudze aplikacja systemu Azure i usługach IIS.
Strona błędu bazy danych
Filtr AddDatabaseDeveloperPageExceptionFilter wyjątku strony dewelopera bazy danych przechwytuje wyjątki związane z bazą danych, które można rozwiązać przy użyciu migracji programu Entity Framework Core. W przypadku wystąpienia tych wyjątków odpowiedź HTML jest generowana ze szczegółami możliwych akcji w celu rozwiązania problemu. Ta strona jest włączona tylko w środowisku deweloperów. Poniższy kod dodaje filtr wyjątku strony dewelopera bazy danych:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Filtry wyjątków
W aplikacjach MVC filtry wyjątków można skonfigurować globalnie lub na kontrolerze lub dla poszczególnych akcji. W Razor aplikacjach Pages można je skonfigurować globalnie lub na model strony. Te filtry obsługują wszelkie nieobsługiwane wyjątki występujące podczas wykonywania akcji kontrolera lub innego filtru. Aby uzyskać więcej informacji, zobacz Filtry w ASP.NET Core.
Filtry wyjątków są przydatne w przypadku podlewek wyjątków występujących w ramach akcji MVC, ale nie są tak elastyczne, jak wbudowane oprogramowanie pośredniczące obsługujące wyjątki. UseExceptionHandler Zalecamy użycie polecenia UseExceptionHandler
, chyba że musisz wykonać obsługę błędów inaczej w zależności od wybranej akcji MVC.
Błędy stanu modelu
Aby uzyskać informacje na temat obsługi błędów stanu modelu, zobacz Powiązanie modelu i Walidacja modelu.
Szczegóły problemu
Szczegóły problemu nie są jedynym formatem odpowiedzi opisujący błąd interfejsu API HTTP, jednak są one często używane do zgłaszania błędów dla interfejsów API HTTP.
Usługa szczegółów problemu IProblemDetailsService implementuje interfejs, który obsługuje tworzenie szczegółów problemu w ASP.NET Core. Metoda AddProblemDetails(IServiceCollection) rozszerzenia w systemie IServiceCollection rejestruje domyślną IProblemDetailsService
implementację.
W aplikacjach ASP.NET Core następujące oprogramowanie pośredniczące generuje szczegóły problemów odpowiedzi HTTP podczas AddProblemDetails
wywoływana, z wyjątkiem sytuacji, gdyAccept
nagłówek HTTP żądania nie zawiera jednego z typów zawartości obsługiwanych przez zarejestrowane IProblemDetailsWriter (domyślnie: application/json
):
- ExceptionHandlerMiddleware: generuje odpowiedź ze szczegółami problemu, gdy program obsługi niestandardowej nie jest zdefiniowany.
- StatusCodePagesMiddleware: domyślnie generuje odpowiedź ze szczegółami problemu.
- DeveloperExceptionPageMiddleware: generuje odpowiedź ze szczegółami problemu w programowania, gdy
Accept
nagłówek HTTP żądania nie zawieratext/html
elementu .
Poniższy kod konfiguruje aplikację w celu wygenerowania odpowiedzi ze szczegółami problemu dla wszystkich odpowiedzi na błędy klienta HTTP i serwera, które nie mają jeszcze zawartości treści:
builder.Services.AddProblemDetails();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
W następnej sekcji pokazano, jak dostosować treść odpowiedzi szczegółów problemu.
Dostosowywanie szczegółów problemu
Automatyczne tworzenie obiektu ProblemDetails
można dostosować przy użyciu dowolnej z następujących opcji:
- Korzystanie z polecenia
ProblemDetailsOptions.CustomizeProblemDetails
- Używanie niestandardowego
IProblemDetailsWriter
- Wywoływanie
IProblemDetailsService
oprogramowania pośredniczącego
CustomizeProblemDetails
operacja
Wygenerowane szczegóły problemu można dostosować przy użyciu polecenia CustomizeProblemDetails, a dostosowania są stosowane do wszystkich szczegółów problemu generowanego automatycznie.
Poniższy kod używa ProblemDetailsOptions metody do ustawienia :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();
Na przykład wynik punktu końcowego generuje następującą HTTP Status 400 Bad Request
treść odpowiedzi szczegółów problemu:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
Zwyczaj IProblemDetailsWriter
Implementację IProblemDetailsWriter można utworzyć na potrzeby zaawansowanych dostosowań.
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));
}
}
Uwaga: w przypadku korzystania z niestandardowego IProblemDetailsWriter
obiektu należy zarejestrować niestandardowy IProblemDetailsWriter
przed wywołaniem metody AddRazorPages, AddControllers, AddControllersWithViewslub 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();
Szczegóły problemu z oprogramowania pośredniczącego
Alternatywnym podejściem do używania z ProblemDetailsOptions CustomizeProblemDetails programem jest ustawienie oprogramowania pośredniczącego ProblemDetails w programie . Odpowiedź na szczegóły problemu może zostać napisana przez wywołanie metody 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();
W poprzednim kodzie minimalne punkty końcowe /divide
interfejsu API i /squareroot
zwracają oczekiwaną niestandardową odpowiedź problemu na dane wejściowe błędu.
Punkty końcowe kontrolera interfejsu API zwracają domyślną odpowiedź problemu na dane wejściowe błędu, a nie niestandardową odpowiedź na problem. Zwracana jest domyślna odpowiedź na problem, ponieważ kontroler interfejsu API zapisał się do strumienia odpowiedzi, Szczegóły problemu dla kodów stanu błędu, zanim IProblemDetailsService.WriteAsync
zostanie wywołana, a odpowiedź nie zostanie zapisana ponownie.
Poniższe ValuesController
polecenie zwraca wartość BadRequestResult, która zapisuje w strumieniu odpowiedzi i w związku z tym uniemożliwia zwracanie niestandardowej odpowiedzi na problem.
[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));
}
}
Następujące zwracany ControllerBase.Problem
jest następujący Values3Controller
wynik problemu, więc zwracany jest oczekiwany wynik problemu niestandardowego:
[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));
}
}
Tworzenie ładunku ProblemDetails dla wyjątków
Rozważmy następującą aplikację:
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();
W środowiskach nieprogramowania, gdy wystąpi wyjątek, poniżej znajduje się standardowa odpowiedź ProblemDetails zwrócona do klienta:
{
"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"
}
W przypadku większości aplikacji powyższy kod jest wymagany w przypadku wyjątków. Jednak w poniższej sekcji pokazano, jak uzyskać bardziej szczegółowe odpowiedzi na problemy.
Alternatywą dla niestandardowej strony obsługi wyjątków jest podanie lambda do UseExceptionHandler. Użycie lambda umożliwia dostęp do błędu i napisanie odpowiedzi ze szczegółami problemu za pomocą polecenia 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();
Ostrzeżenie
Nie należy udostępniać klientom poufnych informacji o błędach. Obsługa błędów jest zagrożeniem bezpieczeństwa.
Alternatywną metodą generowania szczegółów problemu jest użycie pakietu NuGet innej firmy Hellang.Middleware.ProblemDetails , który może służyć do mapowania wyjątków i błędów klienta na szczegóły problemu.
Dodatkowe zasoby
- Wyświetl lub pobierz przykładowy kod (jak pobrać)
- Rozwiązywanie problemów z typowymi błędami platformy ASP.NET Core w usłudze Azure App Service i usługach IIS
- Rozwiązywanie problemów z typowymi błędami platformy ASP.NET Core w usłudze Azure App Service i usługach IIS
- Obsługa błędów w internetowych interfejsach API opartych na kontrolerze ASP.NET Core
- Obsługa błędów w minimalnych interfejsach API.
Autor: Tom Dykstra
W tym artykule opisano typowe podejścia do obsługi błędów w aplikacjach internetowych platformy ASP.NET Core. Zobacz również Obsługa błędów w internetowych interfejsach API opartych na kontrolerze ASP.NET Core i Obsługa błędów w minimalnych interfejsach API.
Strona wyjątku dla deweloperów
Na stronie wyjątku dewelopera są wyświetlane szczegółowe informacje o nieobsługiwanych wyjątkach żądań. aplikacje ASP.NET Core domyślnie włączają stronę wyjątku dla deweloperów, gdy obie te aplikacje:
- Uruchamianie w środowisku programistycznym.
- Aplikacja utworzona przy użyciu bieżących szablonów, czyli przy użyciu aplikacji WebApplication.CreateBuilder. Aplikacje utworzone przy użyciu elementu
WebHost.CreateDefaultBuilder
muszą włączyć stronę wyjątku dla deweloperów, wywołując metodęapp.UseDeveloperExceptionPage
w plikuConfigure
.
Strona wyjątku dla deweloperów jest uruchamiana na wczesnym etapie potoku oprogramowania pośredniczącego, dzięki czemu może przechwytywać nieobsługiwane wyjątki zgłaszane w następujący sposób oprogramowania pośredniczącego.
Szczegółowe informacje o wyjątku nie powinny być wyświetlane publicznie, gdy aplikacja działa w środowisku produkcyjnym. Aby uzyskać więcej informacji na temat konfigurowania środowisk, zobacz Używanie wielu środowisk w programie ASP.NET Core.
Strona wyjątku dewelopera może zawierać następujące informacje o wyjątku i żądaniu:
- Ślad stosu
- Parametry ciągu zapytania, jeśli istnieją
- Pliki cookie, jeśli istnieją
- Nagłówki
Strona wyjątku dla deweloperów nie jest gwarantowana, aby podać żadne informacje. Użyj rejestrowania , aby uzyskać pełne informacje o błędzie.
Strona obsługi wyjątków
Aby skonfigurować niestandardową stronę obsługi błędów dla środowiska produkcyjnego, wywołaj metodę .UseExceptionHandler Ta obsługa wyjątków oprogramowanie pośredniczące:
- Przechwytuje i rejestruje nieobsługiwane wyjątki.
- Ponownie wykonuje żądanie w alternatywnym potoku przy użyciu wskazanej ścieżki. Żądanie nie jest wykonywane ponownie, jeśli odpowiedź została uruchomiona. Kod wygenerowany przez szablon ponownie wykonuje żądanie przy użyciu ścieżki
/Error
.
Ostrzeżenie
Jeśli alternatywny potok zgłasza wyjątek samodzielnie, oprogramowanie pośredniczące obsługi wyjątków ponownie wywróci oryginalny wyjątek.
Ponieważ to oprogramowanie pośredniczące może ponownie wykonać potok żądania:
- Oprogramowanie pośredniczące musi obsługiwać ponowne wysyłanie za pomocą tego samego żądania. Zwykle oznacza to czyszczenie ich stanu po wywołaniu
_next
lub buforowaniu ich przetwarzania na obiekcie,HttpContext
aby uniknąć jego ponownego utworzenia. W przypadku obsługi treści żądania oznacza to buforowanie lub buforowanie wyników, takich jak czytnik formularzy. - W przypadku przeciążenia używanego UseExceptionHandler(IApplicationBuilder, String) w szablonach tylko ścieżka żądania jest modyfikowana, a dane trasy są czyszczone. Żądania danych, takich jak nagłówki, metoda i elementy, są ponownie używane w takim przypadku.
- Usługi o określonym zakresie pozostają takie same.
W poniższym przykładzie UseExceptionHandler dodano oprogramowanie pośredniczące obsługujące wyjątki w środowiskach nieprogramowania:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Szablon Razor aplikacji Pages zawiera stronę Błąd (.cshtml
) i PageModel klasę (ErrorModel
) w folderze Pages . W przypadku aplikacji MVC szablon projektu zawiera metodę Error
akcji i widok błędu Home dla kontrolera.
Obsługa wyjątków oprogramowanie pośredniczące ponownie wykonuje żądanie przy użyciu oryginalnej metody HTTP. Jeśli punkt końcowy procedury obsługi błędów jest ograniczony do określonego zestawu metod HTTP, jest uruchamiany tylko dla tych metod HTTP. Na przykład akcja kontrolera MVC korzystająca z atrybutu [HttpGet]
jest uruchamiana tylko dla żądań GET. Aby upewnić się, że wszystkie żądania docierają do niestandardowej strony obsługi błędów, nie ograniczaj ich do określonego zestawu metod HTTP.
Aby obsłużyć wyjątki inaczej na podstawie oryginalnej metody HTTP:
- W obszarze Razor Pages utwórz wiele metod obsługi. Na przykład użyj polecenia
OnGet
, aby obsługiwać wyjątki GET i używaćOnPost
ich do obsługi wyjątków POST. - W przypadku wzorca MVC zastosuj atrybuty czasownika HTTP do wielu akcji. Na przykład użyj polecenia
[HttpGet]
, aby obsługiwać wyjątki GET i używać[HttpPost]
ich do obsługi wyjątków POST.
Aby umożliwić nieuwierzytelnionym użytkownikom wyświetlanie niestandardowej strony obsługi błędów, upewnij się, że obsługuje dostęp anonimowy.
Uzyskiwanie dostępu do wyjątku
Użyj IExceptionHandlerPathFeature polecenia , aby uzyskać dostęp do wyjątku i oryginalnej ścieżki żądania w procedurze obsługi błędów. W poniższym przykładzie użyto IExceptionHandlerPathFeature
metody , aby uzyskać więcej informacji na temat wyjątku, który został zgłoszony:
[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.";
}
}
}
Ostrzeżenie
Nie należy udostępniać klientom poufnych informacji o błędach. Obsługa błędów jest zagrożeniem bezpieczeństwa.
Procedura obsługi wyjątków lambda
Alternatywą dla niestandardowej strony obsługi wyjątków jest podanie lambda do UseExceptionHandler. Użycie lambda umożliwia dostęp do błędu przed zwróceniem odpowiedzi.
Poniższy kod używa wyrażenia lambda do obsługi wyjątków:
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();
}
Ostrzeżenie
Nie należy udostępniać klientom poufnych informacji o błędach. Obsługa błędów jest zagrożeniem bezpieczeństwa.
UseStatusCodePages
Domyślnie aplikacja ASP.NET Core nie udostępnia strony kodowej stanu kodów stanu http, takich jak 404 — Nie znaleziono. Gdy aplikacja ustawia kod stanu błędu HTTP 400-599, który nie ma treści, zwraca kod stanu i pustą treść odpowiedzi. Aby włączyć domyślne programy obsługi tylko do tekstu dla typowych kodów stanu błędów, wywołaj metodę UseStatusCodePages w pliku Program.cs
:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Przed obsługą żądań należy wywołać UseStatusCodePages
oprogramowanie pośredniczące. Na przykład wywołaj wywołanie UseStatusCodePages
przed oprogramowaniem pośredniczącym plików statycznych i oprogramowaniem pośredniczącym punktów końcowych.
Jeśli UseStatusCodePages
nie jest używany, przejście do adresu URL bez punktu końcowego zwraca komunikat o błędzie zależny od przeglądarki wskazujący, że nie można odnaleźć punktu końcowego. Po UseStatusCodePages
wywołaniu przeglądarka zwraca następującą odpowiedź:
Status Code: 404; Not Found
UseStatusCodePages
nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
Uwaga
Oprogramowanie pośredniczące stron kodu stanu nie przechwytuje wyjątków. Aby podać niestandardową stronę obsługi błędów, użyj strony obsługi wyjątków.
UseStatusCodePages z ciągiem formatu
Aby dostosować typ zawartości odpowiedzi i tekst, użyj przeciążenia UseStatusCodePages , które przyjmuje typ zawartości i ciąg formatu:
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}");
W poprzednim kodzie {0}
jest symbolem zastępczym kodu błędu.
UseStatusCodePages
ciąg formatu nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
UseStatusCodePages z lambda
Aby określić niestandardowy kod obsługi błędów i pisania odpowiedzi, użyj przeciążenia UseStatusCodePages , które przyjmuje wyrażenie 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
z lambda nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
UseStatusCodePagesWithRedirects
UseStatusCodePagesWithRedirects Metoda rozszerzenia:
- Wysyła do klienta kod stanu 302 — Znaleziono.
- Przekierowuje klienta do punktu końcowego obsługi błędów podanego w szablonie adresu URL. Punkt końcowy obsługi błędów zwykle wyświetla informacje o błędzie i zwraca błąd HTTP 200.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Szablon adresu URL może zawierać {0}
symbol zastępczy kodu stanu, jak pokazano w poprzednim kodzie. Jeśli szablon adresu URL zaczyna się od ~
(tylda), ~
element zostanie zastąpiony PathBase
przez aplikację . Podczas określania punktu końcowego w aplikacji utwórz widok MVC lub Razor stronę punktu końcowego.
Ta metoda jest często używana, gdy aplikacja:
- Powinien przekierować klienta do innego punktu końcowego, zwykle w przypadkach, gdy inna aplikacja przetwarza błąd. W przypadku aplikacji internetowych pasek adresu przeglądarki klienta odzwierciedla przekierowany punkt końcowy.
- Nie należy zachowywać i zwracać oryginalnego kodu stanu z początkową odpowiedzią przekierowania.
UseStatusCodePagesWithReExecute
UseStatusCodePagesWithReExecute Metoda rozszerzenia:
- Generuje treść odpowiedzi przez ponowne wykonanie potoku żądania przy użyciu alternatywnej ścieżki.
- Nie zmienia kodu stanu przed lub po ponownym wykonaniu potoku.
Nowe wykonanie potoku może zmienić kod stanu odpowiedzi, ponieważ nowy potok ma pełną kontrolę nad kodem stanu. Jeśli nowy potok nie zmieni kodu stanu, oryginalny kod stanu zostanie wysłany do klienta.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Jeśli określono punkt końcowy w aplikacji, utwórz widok MVC lub Razor stronę dla punktu końcowego.
Ta metoda jest często używana, gdy aplikacja powinna:
- Przetwarzanie żądania bez przekierowywania do innego punktu końcowego. W przypadku aplikacji internetowych pasek adresu przeglądarki klienta odzwierciedla pierwotnie żądany punkt końcowy.
- Zachowaj i zwróć oryginalny kod stanu z odpowiedzią.
Szablon adresu URL musi zaczynać się od /
i może zawierać symbol zastępczy {0}
kodu stanu. Aby przekazać kod stanu jako parametr ciągu zapytania, przekaż drugi argument do UseStatusCodePagesWithReExecute
elementu . Na przykład:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
Punkt końcowy, który przetwarza błąd, może uzyskać oryginalny adres URL, który wygenerował błąd, jak pokazano w poniższym przykładzie:
[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}";
}
}
}
Ponieważ to oprogramowanie pośredniczące może ponownie wykonać potok żądania:
- Oprogramowanie pośredniczące musi obsługiwać ponowne wysyłanie za pomocą tego samego żądania. Zwykle oznacza to czyszczenie ich stanu po wywołaniu
_next
lub buforowaniu ich przetwarzania na obiekcie,HttpContext
aby uniknąć jego ponownego utworzenia. W przypadku obsługi treści żądania oznacza to buforowanie lub buforowanie wyników, takich jak czytnik formularzy. - Usługi o określonym zakresie pozostają takie same.
Wyłączanie stron kodu stanu
Aby wyłączyć strony kodu stanu dla kontrolera MVC lub metody akcji, użyj atrybutu [SkipStatusCodePages].
Aby wyłączyć strony kodu stanu dla określonych żądań w Razor metodzie obsługi stron lub w kontrolerze MVC, użyj polecenia IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Kod obsługi wyjątków
Kod na stronach obsługi wyjątków może również zgłaszać wyjątki. Strony błędów produkcyjnych należy dokładnie przetestować i zachować szczególną ostrożność, aby uniknąć zgłaszania własnych wyjątków.
Nagłówki odpowiedzi
Po wysłaniu nagłówków odpowiedzi:
- Aplikacja nie może zmienić kodu stanu odpowiedzi.
- Nie można uruchomić żadnych stron wyjątków lub procedur obsługi. Odpowiedź musi zostać ukończona lub przerwane połączenie.
Obsługa wyjątków serwera
Oprócz logiki obsługi wyjątków w aplikacji implementacja serwera HTTP może obsługiwać niektóre wyjątki. Jeśli serwer przechwytuje wyjątek przed wysłaniem 500 - Internal Server Error
nagłówków odpowiedzi, serwer wysyła odpowiedź bez treści odpowiedzi. Jeśli serwer przechwytuje wyjątek po wysłaniu nagłówków odpowiedzi, serwer zamknie połączenie. Żądania, które nie są obsługiwane przez aplikację, są obsługiwane przez serwer. Każdy wyjątek, który występuje, gdy serwer obsługuje żądanie, jest obsługiwany przez obsługę wyjątków serwera. Niestandardowe strony błędów aplikacji, oprogramowanie pośredniczące obsługujące wyjątki i filtry nie mają wpływu na to zachowanie.
Obsługa wyjątków uruchamiania
Tylko warstwa hostingu może obsługiwać wyjątki, które mają miejsce podczas uruchamiania aplikacji. Host można skonfigurować do przechwytywania błędów uruchamiania i przechwytywania szczegółowych błędów.
Warstwa hostingu może wyświetlić stronę błędu przechwyconego błędu uruchamiania tylko wtedy, gdy błąd występuje po powiązaniu adresu hosta/portu. Jeśli powiązanie zakończy się niepowodzeniem:
- Warstwa hostingu rejestruje wyjątek krytyczny.
- Proces dotnet ulega awarii.
- Nie jest wyświetlana strona błędu, gdy serwer HTTP ma wartość Kestrel.
W przypadku uruchamiania w usługach IIS (lub aplikacja systemu Azure Service) lub IIS Express błąd procesu 502.5 jest zwracany przez moduł ASP.NET Core, jeśli nie można uruchomić procesu. Aby uzyskać więcej informacji, zobacz Rozwiązywanie problemów z programem ASP.NET Core w usłudze aplikacja systemu Azure i usługach IIS.
Strona błędu bazy danych
Filtr AddDatabaseDeveloperPageExceptionFilter wyjątku strony dewelopera bazy danych przechwytuje wyjątki związane z bazą danych, które można rozwiązać przy użyciu migracji programu Entity Framework Core. W przypadku wystąpienia tych wyjątków odpowiedź HTML jest generowana ze szczegółami możliwych akcji w celu rozwiązania problemu. Ta strona jest włączona tylko w środowisku deweloperów. Poniższy kod dodaje filtr wyjątku strony dewelopera bazy danych:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Filtry wyjątków
W aplikacjach MVC filtry wyjątków można skonfigurować globalnie lub na kontrolerze lub dla poszczególnych akcji. W Razor aplikacjach Pages można je skonfigurować globalnie lub na model strony. Te filtry obsługują wszelkie nieobsługiwane wyjątki występujące podczas wykonywania akcji kontrolera lub innego filtru. Aby uzyskać więcej informacji, zobacz Filtry w ASP.NET Core.
Filtry wyjątków są przydatne w przypadku podlewek wyjątków występujących w ramach akcji MVC, ale nie są tak elastyczne, jak wbudowane oprogramowanie pośredniczące obsługujące wyjątki. UseExceptionHandler Zalecamy użycie polecenia UseExceptionHandler
, chyba że musisz wykonać obsługę błędów inaczej w zależności od wybranej akcji MVC.
Błędy stanu modelu
Aby uzyskać informacje na temat obsługi błędów stanu modelu, zobacz Powiązanie modelu i Walidacja modelu.
Szczegóły problemu
Szczegóły problemu nie są jedynym formatem odpowiedzi opisujący błąd interfejsu API HTTP, jednak są one często używane do zgłaszania błędów dla interfejsów API HTTP.
Usługa szczegółów problemu IProblemDetailsService implementuje interfejs, który obsługuje tworzenie szczegółów problemu w ASP.NET Core. Metoda AddProblemDetails(IServiceCollection) rozszerzenia w systemie IServiceCollection rejestruje domyślną IProblemDetailsService
implementację.
W aplikacjach ASP.NET Core następujące oprogramowanie pośredniczące generuje szczegóły problemów odpowiedzi HTTP podczas AddProblemDetails
wywoływana, z wyjątkiem sytuacji, gdyAccept
nagłówek HTTP żądania nie zawiera jednego z typów zawartości obsługiwanych przez zarejestrowane IProblemDetailsWriter (domyślnie: application/json
):
- ExceptionHandlerMiddleware: generuje odpowiedź ze szczegółami problemu, gdy program obsługi niestandardowej nie jest zdefiniowany.
- StatusCodePagesMiddleware: domyślnie generuje odpowiedź ze szczegółami problemu.
- DeveloperExceptionPageMiddleware: generuje odpowiedź ze szczegółami problemu w programowania, gdy
Accept
nagłówek HTTP żądania nie zawieratext/html
elementu .
Poniższy kod konfiguruje aplikację w celu wygenerowania odpowiedzi ze szczegółami problemu dla wszystkich odpowiedzi na błędy klienta HTTP i serwera, które nie mają jeszcze zawartości treści:
builder.Services.AddProblemDetails();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
W następnej sekcji pokazano, jak dostosować treść odpowiedzi szczegółów problemu.
Dostosowywanie szczegółów problemu
Automatyczne tworzenie obiektu ProblemDetails
można dostosować przy użyciu dowolnej z następujących opcji:
- Korzystanie z polecenia
ProblemDetailsOptions.CustomizeProblemDetails
- Używanie niestandardowego
IProblemDetailsWriter
- Wywoływanie
IProblemDetailsService
oprogramowania pośredniczącego
CustomizeProblemDetails
operacja
Wygenerowane szczegóły problemu można dostosować przy użyciu polecenia CustomizeProblemDetails, a dostosowania są stosowane do wszystkich szczegółów problemu generowanego automatycznie.
Poniższy kod używa ProblemDetailsOptions metody do ustawienia :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();
Na przykład wynik punktu końcowego generuje następującą HTTP Status 400 Bad Request
treść odpowiedzi szczegółów problemu:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
Zwyczaj IProblemDetailsWriter
Implementację IProblemDetailsWriter można utworzyć na potrzeby zaawansowanych dostosowań.
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));
}
}
Uwaga: w przypadku korzystania z niestandardowego IProblemDetailsWriter
obiektu należy zarejestrować niestandardowy IProblemDetailsWriter
przed wywołaniem metody AddRazorPages, AddControllers, AddControllersWithViewslub 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();
Szczegóły problemu z oprogramowania pośredniczącego
Alternatywnym podejściem do używania z ProblemDetailsOptions CustomizeProblemDetails programem jest ustawienie oprogramowania pośredniczącego ProblemDetails w programie . Odpowiedź na szczegóły problemu może zostać napisana przez wywołanie metody 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();
W poprzednim kodzie minimalne punkty końcowe /divide
interfejsu API i /squareroot
zwracają oczekiwaną niestandardową odpowiedź problemu na dane wejściowe błędu.
Punkty końcowe kontrolera interfejsu API zwracają domyślną odpowiedź problemu na dane wejściowe błędu, a nie niestandardową odpowiedź na problem. Zwracana jest domyślna odpowiedź na problem, ponieważ kontroler interfejsu API zapisał się do strumienia odpowiedzi, Szczegóły problemu dla kodów stanu błędu, zanim IProblemDetailsService.WriteAsync
zostanie wywołana, a odpowiedź nie zostanie zapisana ponownie.
Poniższe ValuesController
polecenie zwraca wartość BadRequestResult, która zapisuje w strumieniu odpowiedzi i w związku z tym uniemożliwia zwracanie niestandardowej odpowiedzi na problem.
[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));
}
}
Następujące zwracany ControllerBase.Problem
jest następujący Values3Controller
wynik problemu, więc zwracany jest oczekiwany wynik problemu niestandardowego:
[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));
}
}
Tworzenie ładunku ProblemDetails dla wyjątków
Rozważmy następującą aplikację:
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();
W środowiskach nieprogramowania, gdy wystąpi wyjątek, poniżej znajduje się standardowa odpowiedź ProblemDetails zwrócona do klienta:
{
"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"
}
W przypadku większości aplikacji powyższy kod jest wymagany w przypadku wyjątków. Jednak w poniższej sekcji pokazano, jak uzyskać bardziej szczegółowe odpowiedzi na problemy.
Alternatywą dla niestandardowej strony obsługi wyjątków jest podanie lambda do UseExceptionHandler. Użycie lambda umożliwia dostęp do błędu i napisanie odpowiedzi ze szczegółami problemu za pomocą polecenia 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();
Ostrzeżenie
Nie należy udostępniać klientom poufnych informacji o błędach. Obsługa błędów jest zagrożeniem bezpieczeństwa.
Alternatywną metodą generowania szczegółów problemu jest użycie pakietu NuGet innej firmy Hellang.Middleware.ProblemDetails , który może służyć do mapowania wyjątków i błędów klienta na szczegóły problemu.
Dodatkowe zasoby
Autor: Tom Dykstra
W tym artykule opisano typowe podejścia do obsługi błędów w aplikacjach internetowych platformy ASP.NET Core. Zobacz Obsługa błędów w internetowych interfejsach API opartych na kontrolerze ASP.NET Core dla internetowych interfejsów API.
Strona wyjątku dla deweloperów
Na stronie wyjątku dewelopera są wyświetlane szczegółowe informacje o nieobsługiwanych wyjątkach żądań. aplikacje ASP.NET Core domyślnie włączają stronę wyjątku dla deweloperów, gdy obie te aplikacje:
- Uruchamianie w środowisku programistycznym.
- Aplikacja utworzona przy użyciu bieżących szablonów, czyli przy użyciu aplikacji WebApplication.CreateBuilder. Aplikacje utworzone przy użyciu elementu
WebHost.CreateDefaultBuilder
muszą włączyć stronę wyjątku dla deweloperów, wywołując metodęapp.UseDeveloperExceptionPage
w plikuConfigure
.
Strona wyjątku dla deweloperów jest uruchamiana na wczesnym etapie potoku oprogramowania pośredniczącego, dzięki czemu może przechwytywać nieobsługiwane wyjątki zgłaszane w następujący sposób oprogramowania pośredniczącego.
Szczegółowe informacje o wyjątku nie powinny być wyświetlane publicznie, gdy aplikacja działa w środowisku produkcyjnym. Aby uzyskać więcej informacji na temat konfigurowania środowisk, zobacz Używanie wielu środowisk w programie ASP.NET Core.
Strona wyjątku dewelopera może zawierać następujące informacje o wyjątku i żądaniu:
- Ślad stosu
- Parametry ciągu zapytania, jeśli istnieją
- Pliki cookie, jeśli istnieją
- Nagłówki
Strona wyjątku dla deweloperów nie jest gwarantowana, aby podać żadne informacje. Użyj rejestrowania , aby uzyskać pełne informacje o błędzie.
Strona obsługi wyjątków
Aby skonfigurować niestandardową stronę obsługi błędów dla środowiska produkcyjnego, wywołaj metodę .UseExceptionHandler Ta obsługa wyjątków oprogramowanie pośredniczące:
- Przechwytuje i rejestruje nieobsługiwane wyjątki.
- Ponownie wykonuje żądanie w alternatywnym potoku przy użyciu wskazanej ścieżki. Żądanie nie jest wykonywane ponownie, jeśli odpowiedź została uruchomiona. Kod wygenerowany przez szablon ponownie wykonuje żądanie przy użyciu ścieżki
/Error
.
Ostrzeżenie
Jeśli alternatywny potok zgłasza wyjątek samodzielnie, oprogramowanie pośredniczące obsługi wyjątków ponownie wywróci oryginalny wyjątek.
W poniższym przykładzie UseExceptionHandler dodano oprogramowanie pośredniczące obsługujące wyjątki w środowiskach nieprogramowania:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Szablon Razor aplikacji Pages zawiera stronę Błąd (.cshtml
) i PageModel klasę (ErrorModel
) w folderze Pages . W przypadku aplikacji MVC szablon projektu zawiera metodę Error
akcji i widok błędu Home dla kontrolera.
Obsługa wyjątków oprogramowanie pośredniczące ponownie wykonuje żądanie przy użyciu oryginalnej metody HTTP. Jeśli punkt końcowy procedury obsługi błędów jest ograniczony do określonego zestawu metod HTTP, jest uruchamiany tylko dla tych metod HTTP. Na przykład akcja kontrolera MVC korzystająca z atrybutu [HttpGet]
jest uruchamiana tylko dla żądań GET. Aby upewnić się, że wszystkie żądania docierają do niestandardowej strony obsługi błędów, nie ograniczaj ich do określonego zestawu metod HTTP.
Aby obsłużyć wyjątki inaczej na podstawie oryginalnej metody HTTP:
- W obszarze Razor Pages utwórz wiele metod obsługi. Na przykład użyj polecenia
OnGet
, aby obsługiwać wyjątki GET i używaćOnPost
ich do obsługi wyjątków POST. - W przypadku wzorca MVC zastosuj atrybuty czasownika HTTP do wielu akcji. Na przykład użyj polecenia
[HttpGet]
, aby obsługiwać wyjątki GET i używać[HttpPost]
ich do obsługi wyjątków POST.
Aby umożliwić nieuwierzytelnionym użytkownikom wyświetlanie niestandardowej strony obsługi błędów, upewnij się, że obsługuje dostęp anonimowy.
Uzyskiwanie dostępu do wyjątku
Użyj IExceptionHandlerPathFeature polecenia , aby uzyskać dostęp do wyjątku i oryginalnej ścieżki żądania w procedurze obsługi błędów. W poniższym przykładzie użyto IExceptionHandlerPathFeature
metody , aby uzyskać więcej informacji na temat wyjątku, który został zgłoszony:
[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.";
}
}
}
Ostrzeżenie
Nie należy udostępniać klientom poufnych informacji o błędach. Obsługa błędów jest zagrożeniem bezpieczeństwa.
Procedura obsługi wyjątków lambda
Alternatywą dla niestandardowej strony obsługi wyjątków jest podanie lambda do UseExceptionHandler. Użycie lambda umożliwia dostęp do błędu przed zwróceniem odpowiedzi.
Poniższy kod używa wyrażenia lambda do obsługi wyjątków:
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();
}
Ostrzeżenie
Nie należy udostępniać klientom poufnych informacji o błędach. Obsługa błędów jest zagrożeniem bezpieczeństwa.
UseStatusCodePages
Domyślnie aplikacja ASP.NET Core nie udostępnia strony kodowej stanu kodów stanu http, takich jak 404 — Nie znaleziono. Gdy aplikacja ustawia kod stanu błędu HTTP 400-599, który nie ma treści, zwraca kod stanu i pustą treść odpowiedzi. Aby włączyć domyślne programy obsługi tylko do tekstu dla typowych kodów stanu błędów, wywołaj metodę UseStatusCodePages w pliku Program.cs
:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Przed obsługą żądań należy wywołać UseStatusCodePages
oprogramowanie pośredniczące. Na przykład wywołaj wywołanie UseStatusCodePages
przed oprogramowaniem pośredniczącym plików statycznych i oprogramowaniem pośredniczącym punktów końcowych.
Jeśli UseStatusCodePages
nie jest używany, przejście do adresu URL bez punktu końcowego zwraca komunikat o błędzie zależny od przeglądarki wskazujący, że nie można odnaleźć punktu końcowego. Po UseStatusCodePages
wywołaniu przeglądarka zwraca następującą odpowiedź:
Status Code: 404; Not Found
UseStatusCodePages
nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
Uwaga
Oprogramowanie pośredniczące stron kodu stanu nie przechwytuje wyjątków. Aby podać niestandardową stronę obsługi błędów, użyj strony obsługi wyjątków.
UseStatusCodePages z ciągiem formatu
Aby dostosować typ zawartości odpowiedzi i tekst, użyj przeciążenia UseStatusCodePages , które przyjmuje typ zawartości i ciąg formatu:
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}");
W poprzednim kodzie {0}
jest symbolem zastępczym kodu błędu.
UseStatusCodePages
ciąg formatu nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
UseStatusCodePages z lambda
Aby określić niestandardowy kod obsługi błędów i pisania odpowiedzi, użyj przeciążenia UseStatusCodePages , które przyjmuje wyrażenie 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
z lambda nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
UseStatusCodePagesWithRedirects
UseStatusCodePagesWithRedirects Metoda rozszerzenia:
- Wysyła do klienta kod stanu 302 — Znaleziono.
- Przekierowuje klienta do punktu końcowego obsługi błędów podanego w szablonie adresu URL. Punkt końcowy obsługi błędów zwykle wyświetla informacje o błędzie i zwraca błąd HTTP 200.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Szablon adresu URL może zawierać {0}
symbol zastępczy kodu stanu, jak pokazano w poprzednim kodzie. Jeśli szablon adresu URL zaczyna się od ~
(tylda), ~
element zostanie zastąpiony PathBase
przez aplikację . Podczas określania punktu końcowego w aplikacji utwórz widok MVC lub Razor stronę punktu końcowego.
Ta metoda jest często używana, gdy aplikacja:
- Powinien przekierować klienta do innego punktu końcowego, zwykle w przypadkach, gdy inna aplikacja przetwarza błąd. W przypadku aplikacji internetowych pasek adresu przeglądarki klienta odzwierciedla przekierowany punkt końcowy.
- Nie należy zachowywać i zwracać oryginalnego kodu stanu z początkową odpowiedzią przekierowania.
UseStatusCodePagesWithReExecute
UseStatusCodePagesWithReExecute Metoda rozszerzenia:
- Zwraca oryginalny kod stanu do klienta.
- Generuje treść odpowiedzi przez ponowne wykonanie potoku żądania przy użyciu alternatywnej ścieżki.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Jeśli określono punkt końcowy w aplikacji, utwórz widok MVC lub Razor stronę dla punktu końcowego.
Ta metoda jest często używana, gdy aplikacja powinna:
- Przetwarzanie żądania bez przekierowywania do innego punktu końcowego. W przypadku aplikacji internetowych pasek adresu przeglądarki klienta odzwierciedla pierwotnie żądany punkt końcowy.
- Zachowaj i zwróć oryginalny kod stanu z odpowiedzią.
Szablon adresu URL musi zaczynać się od /
i może zawierać symbol zastępczy {0}
kodu stanu. Aby przekazać kod stanu jako parametr ciągu zapytania, przekaż drugi argument do UseStatusCodePagesWithReExecute
elementu . Na przykład:
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
Punkt końcowy, który przetwarza błąd, może uzyskać oryginalny adres URL, który wygenerował błąd, jak pokazano w poniższym przykładzie:
[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);
}
}
}
Wyłączanie stron kodu stanu
Aby wyłączyć strony kodu stanu dla kontrolera MVC lub metody akcji, użyj atrybutu [SkipStatusCodePages].
Aby wyłączyć strony kodu stanu dla określonych żądań w Razor metodzie obsługi stron lub w kontrolerze MVC, użyj polecenia IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Kod obsługi wyjątków
Kod na stronach obsługi wyjątków może również zgłaszać wyjątki. Strony błędów produkcyjnych należy dokładnie przetestować i zachować szczególną ostrożność, aby uniknąć zgłaszania własnych wyjątków.
Nagłówki odpowiedzi
Po wysłaniu nagłówków odpowiedzi:
- Aplikacja nie może zmienić kodu stanu odpowiedzi.
- Nie można uruchomić żadnych stron wyjątków lub procedur obsługi. Odpowiedź musi zostać ukończona lub przerwane połączenie.
Obsługa wyjątków serwera
Oprócz logiki obsługi wyjątków w aplikacji implementacja serwera HTTP może obsługiwać niektóre wyjątki. Jeśli serwer przechwytuje wyjątek przed wysłaniem 500 - Internal Server Error
nagłówków odpowiedzi, serwer wysyła odpowiedź bez treści odpowiedzi. Jeśli serwer przechwytuje wyjątek po wysłaniu nagłówków odpowiedzi, serwer zamknie połączenie. Żądania, które nie są obsługiwane przez aplikację, są obsługiwane przez serwer. Każdy wyjątek, który występuje, gdy serwer obsługuje żądanie, jest obsługiwany przez obsługę wyjątków serwera. Niestandardowe strony błędów aplikacji, oprogramowanie pośredniczące obsługujące wyjątki i filtry nie mają wpływu na to zachowanie.
Obsługa wyjątków uruchamiania
Tylko warstwa hostingu może obsługiwać wyjątki, które mają miejsce podczas uruchamiania aplikacji. Host można skonfigurować do przechwytywania błędów uruchamiania i przechwytywania szczegółowych błędów.
Warstwa hostingu może wyświetlić stronę błędu przechwyconego błędu uruchamiania tylko wtedy, gdy błąd występuje po powiązaniu adresu hosta/portu. Jeśli powiązanie zakończy się niepowodzeniem:
- Warstwa hostingu rejestruje wyjątek krytyczny.
- Proces dotnet ulega awarii.
- Nie jest wyświetlana strona błędu, gdy serwer HTTP ma wartość Kestrel.
W przypadku uruchamiania w usługach IIS (lub aplikacja systemu Azure Service) lub IIS Express błąd procesu 502.5 jest zwracany przez moduł ASP.NET Core, jeśli nie można uruchomić procesu. Aby uzyskać więcej informacji, zobacz Rozwiązywanie problemów z programem ASP.NET Core w usłudze aplikacja systemu Azure i usługach IIS.
Strona błędu bazy danych
Filtr AddDatabaseDeveloperPageExceptionFilter wyjątku strony dewelopera bazy danych przechwytuje wyjątki związane z bazą danych, które można rozwiązać przy użyciu migracji programu Entity Framework Core. W przypadku wystąpienia tych wyjątków odpowiedź HTML jest generowana ze szczegółami możliwych akcji w celu rozwiązania problemu. Ta strona jest włączona tylko w środowisku deweloperów. Poniższy kod dodaje filtr wyjątku strony dewelopera bazy danych:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Filtry wyjątków
W aplikacjach MVC filtry wyjątków można skonfigurować globalnie lub na kontrolerze lub dla poszczególnych akcji. W Razor aplikacjach Pages można je skonfigurować globalnie lub na model strony. Te filtry obsługują wszelkie nieobsługiwane wyjątki występujące podczas wykonywania akcji kontrolera lub innego filtru. Aby uzyskać więcej informacji, zobacz Filtry w ASP.NET Core.
Filtry wyjątków są przydatne w przypadku podlewek wyjątków występujących w ramach akcji MVC, ale nie są tak elastyczne, jak wbudowane oprogramowanie pośredniczące obsługujące wyjątki. UseExceptionHandler Zalecamy użycie polecenia UseExceptionHandler
, chyba że musisz wykonać obsługę błędów inaczej w zależności od wybranej akcji MVC.
Błędy stanu modelu
Aby uzyskać informacje na temat obsługi błędów stanu modelu, zobacz Powiązanie modelu i Walidacja modelu.
Dodatkowe zasoby
Przez Kirk Larkin, Tom Dykstra i Steve Smith
W tym artykule opisano typowe podejścia do obsługi błędów w aplikacjach internetowych platformy ASP.NET Core. Zobacz Obsługa błędów w internetowych interfejsach API opartych na kontrolerze ASP.NET Core dla internetowych interfejsów API.
Wyświetl lub pobierz kod przykładowy. (Jak pobrać). Karta sieciowa w narzędziach deweloperskich przeglądarki F12 jest przydatna podczas testowania przykładowej aplikacji.
Strona wyjątku dla deweloperów
Na stronie wyjątku dewelopera są wyświetlane szczegółowe informacje o nieobsługiwanych wyjątkach żądań. Szablony ASP.NET Core generują następujący kod:
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();
});
}
Powyższy wyróżniony kod umożliwia stronę wyjątku dewelopera, gdy aplikacja jest uruchomiona w środowisku dewelopera.
Szablony umieszczają UseDeveloperExceptionPage się na wczesnym etapie potoku oprogramowania pośredniczącego, dzięki czemu może przechwytywać nieobsługiwane wyjątki zgłaszane przez oprogramowanie pośredniczące, które następuje poniżej.
Powyższy kod włącza stronę wyjątku dewelopera tylko wtedy, gdy aplikacja działa w środowisku dewelopera. Szczegółowe informacje o wyjątku nie powinny być wyświetlane publicznie, gdy aplikacja działa w środowisku produkcyjnym. Aby uzyskać więcej informacji na temat konfigurowania środowisk, zobacz Używanie wielu środowisk w programie ASP.NET Core.
Strona wyjątku dewelopera może zawierać następujące informacje o wyjątku i żądaniu:
- Ślad stosu
- Parametry ciągu zapytania, jeśli istnieją
- Pliki cookie, jeśli istnieją
- Nagłówki
Strona wyjątku dla deweloperów nie jest gwarantowana, aby podać żadne informacje. Użyj rejestrowania , aby uzyskać pełne informacje o błędzie.
Strona obsługi wyjątków
Aby skonfigurować niestandardową stronę obsługi błędów dla środowiska produkcyjnego, wywołaj metodę .UseExceptionHandler Ta obsługa wyjątków oprogramowanie pośredniczące:
- Przechwytuje i rejestruje nieobsługiwane wyjątki.
- Ponownie wykonuje żądanie w alternatywnym potoku przy użyciu wskazanej ścieżki. Żądanie nie jest wykonywane ponownie, jeśli odpowiedź została uruchomiona. Kod wygenerowany przez szablon ponownie wykonuje żądanie przy użyciu ścieżki
/Error
.
Ostrzeżenie
Jeśli alternatywny potok zgłasza wyjątek samodzielnie, oprogramowanie pośredniczące obsługi wyjątków ponownie wywróci oryginalny wyjątek.
W poniższym przykładzie UseExceptionHandler dodano oprogramowanie pośredniczące obsługujące wyjątki w środowiskach nieprogramowania:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Szablon Razor aplikacji Pages zawiera stronę Błąd (.cshtml
) i PageModel klasę (ErrorModel
) w folderze Pages . W przypadku aplikacji MVC szablon projektu zawiera metodę Error
akcji i widok błędu Home dla kontrolera.
Obsługa wyjątków oprogramowanie pośredniczące ponownie wykonuje żądanie przy użyciu oryginalnej metody HTTP. Jeśli punkt końcowy procedury obsługi błędów jest ograniczony do określonego zestawu metod HTTP, jest uruchamiany tylko dla tych metod HTTP. Na przykład akcja kontrolera MVC korzystająca z atrybutu [HttpGet]
jest uruchamiana tylko dla żądań GET. Aby upewnić się, że wszystkie żądania docierają do niestandardowej strony obsługi błędów, nie ograniczaj ich do określonego zestawu metod HTTP.
Aby obsłużyć wyjątki inaczej na podstawie oryginalnej metody HTTP:
- W obszarze Razor Pages utwórz wiele metod obsługi. Na przykład użyj polecenia
OnGet
, aby obsługiwać wyjątki GET i używaćOnPost
ich do obsługi wyjątków POST. - W przypadku wzorca MVC zastosuj atrybuty czasownika HTTP do wielu akcji. Na przykład użyj polecenia
[HttpGet]
, aby obsługiwać wyjątki GET i używać[HttpPost]
ich do obsługi wyjątków POST.
Aby umożliwić nieuwierzytelnionym użytkownikom wyświetlanie niestandardowej strony obsługi błędów, upewnij się, że obsługuje dostęp anonimowy.
Uzyskiwanie dostępu do wyjątku
Użyj IExceptionHandlerPathFeature polecenia , aby uzyskać dostęp do wyjątku i oryginalnej ścieżki żądania w procedurze obsługi błędów. Poniższy kod dodaje ExceptionMessage
domyślny Pages/Error.cshtml.cs
kod wygenerowany przez szablony 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";
}
}
}
Ostrzeżenie
Nie należy udostępniać klientom poufnych informacji o błędach. Obsługa błędów jest zagrożeniem bezpieczeństwa.
Aby przetestować wyjątek w przykładowej aplikacji:
- Ustaw środowisko na środowisko produkcyjne.
- Usuń komentarze z
webBuilder.UseStartup<Startup>();
elementu w plikuProgram.cs
. - Wybierz pozycję Wyzwól wyjątek na home stronie.
Procedura obsługi wyjątków lambda
Alternatywą dla niestandardowej strony obsługi wyjątków jest podanie lambda do UseExceptionHandler. Użycie lambda umożliwia dostęp do błędu przed zwróceniem odpowiedzi.
Poniższy kod używa wyrażenia lambda do obsługi wyjątków:
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();
});
}
Ostrzeżenie
Nie należy obsługiwać poufnych informacji o błędach od IExceptionHandlerFeature klientów lub IExceptionHandlerPathFeature do klientów. Obsługa błędów jest zagrożeniem bezpieczeństwa.
Aby przetestować obsługę wyjątków lambda w przykładowej aplikacji:
- Ustaw środowisko na środowisko produkcyjne.
- Usuń komentarze z
webBuilder.UseStartup<StartupLambda>();
elementu w plikuProgram.cs
. - Wybierz pozycję Wyzwól wyjątek na home stronie.
UseStatusCodePages
Domyślnie aplikacja ASP.NET Core nie udostępnia strony kodowej stanu kodów stanu http, takich jak 404 — Nie znaleziono. Gdy aplikacja ustawia kod stanu błędu HTTP 400-599, który nie ma treści, zwraca kod stanu i pustą treść odpowiedzi. Aby udostępnić strony kodu stanu, użyj oprogramowania pośredniczącego stron kodu stanu. Aby włączyć domyślne programy obsługi tylko tekstowe dla typowych kodów stanu błędów, wywołaj metodę UseStatusCodePages w metodzie 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();
});
}
Przed obsługą żądań należy wywołać UseStatusCodePages
oprogramowanie pośredniczące. Na przykład wywołaj wywołanie UseStatusCodePages
przed oprogramowaniem pośredniczącym plików statycznych i oprogramowaniem pośredniczącym punktów końcowych.
Jeśli UseStatusCodePages
nie jest używany, przejście do adresu URL bez punktu końcowego zwraca komunikat o błędzie zależny od przeglądarki wskazujący, że nie można odnaleźć punktu końcowego. Na przykład przejdź do folderu Home/Privacy2
. Po UseStatusCodePages
wywołaniu przeglądarka zwraca następujące elementy:
Status Code: 404; Not Found
UseStatusCodePages
nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
Aby przetestować UseStatusCodePages
aplikację przykładową:
- Ustaw środowisko na środowisko produkcyjne.
- Usuń komentarze z
webBuilder.UseStartup<StartupUseStatusCodePages>();
elementu w plikuProgram.cs
. - Wybierz linki na home stronie na home stronie.
Uwaga
Oprogramowanie pośredniczące stron kodu stanu nie przechwytuje wyjątków. Aby podać niestandardową stronę obsługi błędów, użyj strony obsługi wyjątków.
UseStatusCodePages z ciągiem formatu
Aby dostosować typ zawartości odpowiedzi i tekst, użyj przeciążenia UseStatusCodePages , które przyjmuje typ zawartości i ciąg formatu:
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();
});
}
W poprzednim kodzie {0}
jest symbolem zastępczym kodu błędu.
UseStatusCodePages
ciąg formatu nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
Aby przetestować aplikację przykładową, usuń komentarze z webBuilder.UseStartup<StartupFormat>();
elementu w programie Program.cs
.UseStatusCodePages
UseStatusCodePages z lambda
Aby określić niestandardowy kod obsługi błędów i pisania odpowiedzi, użyj przeciążenia UseStatusCodePages , które przyjmuje wyrażenie 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
z lambda nie jest zwykle używany w środowisku produkcyjnym, ponieważ zwraca komunikat, który nie jest przydatny dla użytkowników.
Aby przetestować aplikację przykładową, usuń komentarze z webBuilder.UseStartup<StartupStatusLambda>();
elementu w programie Program.cs
.UseStatusCodePages
UseStatusCodePagesWithRedirects
UseStatusCodePagesWithRedirects Metoda rozszerzenia:
- Wysyła do klienta kod stanu 302 — Znaleziono.
- Przekierowuje klienta do punktu końcowego obsługi błędów podanego w szablonie adresu URL. Punkt końcowy obsługi błędów zwykle wyświetla informacje o błędzie i zwraca błąd 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();
});
}
Szablon adresu URL może zawierać {0}
symbol zastępczy kodu stanu, jak pokazano w poprzednim kodzie. Jeśli szablon adresu URL zaczyna się od ~
(tylda), ~
element zostanie zastąpiony PathBase
przez aplikację . Podczas określania punktu końcowego w aplikacji utwórz widok MVC lub Razor stronę punktu końcowego. Aby zapoznać się z Razor przykładem strony, zobacz Pages/MyStatusCode.cshtml w przykładowej aplikacji.
Ta metoda jest często używana, gdy aplikacja:
- Powinien przekierować klienta do innego punktu końcowego, zwykle w przypadkach, gdy inna aplikacja przetwarza błąd. W przypadku aplikacji internetowych pasek adresu przeglądarki klienta odzwierciedla przekierowany punkt końcowy.
- Nie należy zachowywać i zwracać oryginalnego kodu stanu z początkową odpowiedzią przekierowania.
Aby przetestować aplikację przykładową, usuń komentarze z webBuilder.UseStartup<StartupSCredirect>();
elementu w programie Program.cs
.UseStatusCodePages
UseStatusCodePagesWithReExecute
UseStatusCodePagesWithReExecute Metoda rozszerzenia:
- Zwraca oryginalny kod stanu do klienta.
- Generuje treść odpowiedzi przez ponowne wykonanie potoku żądania przy użyciu alternatywnej ścieżki.
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();
});
}
Jeśli określono punkt końcowy w aplikacji, utwórz widok MVC lub Razor stronę dla punktu końcowego. Upewnij się, że UseStatusCodePagesWithReExecute
zostało umieszczone przed UseRouting
, aby można było przekierować żądanie do strony stanu. Aby zapoznać się z Razor przykładem strony, zobacz Pages/MyStatusCode2.cshtml w przykładowej aplikacji.
Ta metoda jest często używana, gdy aplikacja powinna:
- Przetwarzanie żądania bez przekierowywania do innego punktu końcowego. W przypadku aplikacji internetowych pasek adresu przeglądarki klienta odzwierciedla pierwotnie żądany punkt końcowy.
- Zachowaj i zwróć oryginalny kod stanu z odpowiedzią.
Szablony adresów URL i ciągów zapytania mogą zawierać symbol zastępczy {0}
kodu stanu. Szablon adresu URL musi zaczynać się od /
.
Punkt końcowy, który przetwarza błąd, może uzyskać oryginalny adres URL, który wygenerował błąd, jak pokazano w poniższym przykładzie:
[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;
}
}
}
Aby zapoznać się z Razor przykładem strony, zobacz Pages/MyStatusCode2.cshtml w przykładowej aplikacji.
Aby przetestować aplikację przykładową, usuń komentarze z webBuilder.UseStartup<StartupSCreX>();
elementu w programie Program.cs
.UseStatusCodePages
Wyłączanie stron kodu stanu
Aby wyłączyć strony kodu stanu dla kontrolera MVC lub metody akcji, użyj atrybutu [SkipStatusCodePages].
Aby wyłączyć strony kodu stanu dla określonych żądań w Razor metodzie obsługi stron lub w kontrolerze MVC, użyj polecenia IStatusCodePagesFeature:
public void OnGet()
{
// using Microsoft.AspNetCore.Diagnostics;
var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature != null)
{
statusCodePagesFeature.Enabled = false;
}
}
Kod obsługi wyjątków
Kod na stronach obsługi wyjątków może również zgłaszać wyjątki. Strony błędów produkcyjnych należy dokładnie przetestować i zachować szczególną ostrożność, aby uniknąć zgłaszania własnych wyjątków.
Nagłówki odpowiedzi
Po wysłaniu nagłówków odpowiedzi:
- Aplikacja nie może zmienić kodu stanu odpowiedzi.
- Nie można uruchomić żadnych stron wyjątków lub procedur obsługi. Odpowiedź musi zostać ukończona lub przerwane połączenie.
Obsługa wyjątków serwera
Oprócz logiki obsługi wyjątków w aplikacji implementacja serwera HTTP może obsługiwać niektóre wyjątki. Jeśli serwer przechwytuje wyjątek przed wysłaniem 500 - Internal Server Error
nagłówków odpowiedzi, serwer wysyła odpowiedź bez treści odpowiedzi. Jeśli serwer przechwytuje wyjątek po wysłaniu nagłówków odpowiedzi, serwer zamknie połączenie. Żądania, które nie są obsługiwane przez aplikację, są obsługiwane przez serwer. Każdy wyjątek, który występuje, gdy serwer obsługuje żądanie, jest obsługiwany przez obsługę wyjątków serwera. Niestandardowe strony błędów aplikacji, oprogramowanie pośredniczące obsługujące wyjątki i filtry nie mają wpływu na to zachowanie.
Obsługa wyjątków uruchamiania
Tylko warstwa hostingu może obsługiwać wyjątki, które mają miejsce podczas uruchamiania aplikacji. Host można skonfigurować do przechwytywania błędów uruchamiania i przechwytywania szczegółowych błędów.
Warstwa hostingu może wyświetlić stronę błędu przechwyconego błędu uruchamiania tylko wtedy, gdy błąd występuje po powiązaniu adresu hosta/portu. Jeśli powiązanie zakończy się niepowodzeniem:
- Warstwa hostingu rejestruje wyjątek krytyczny.
- Proces dotnet ulega awarii.
- Nie jest wyświetlana strona błędu, gdy serwer HTTP ma wartość Kestrel.
W przypadku uruchamiania w usługach IIS (lub aplikacja systemu Azure Service) lub IIS Express błąd procesu 502.5 jest zwracany przez moduł ASP.NET Core, jeśli nie można uruchomić procesu. Aby uzyskać więcej informacji, zobacz Rozwiązywanie problemów z programem ASP.NET Core w usłudze aplikacja systemu Azure i usługach IIS.
Strona błędu bazy danych
Filtr AddDatabaseDeveloperPageExceptionFilter
wyjątku strony dewelopera bazy danych przechwytuje wyjątki związane z bazą danych, które można rozwiązać przy użyciu migracji programu Entity Framework Core. W przypadku wystąpienia tych wyjątków odpowiedź HTML jest generowana ze szczegółami możliwych akcji w celu rozwiązania problemu. Ta strona jest włączona tylko w środowisku deweloperów. Następujący kod został wygenerowany przez szablony ASP.NET Core Razor Pages po określeniu poszczególnych kont użytkowników:
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();
}
Filtry wyjątków
W aplikacjach MVC filtry wyjątków można skonfigurować globalnie lub na kontrolerze lub dla poszczególnych akcji. W Razor aplikacjach Pages można je skonfigurować globalnie lub na model strony. Te filtry obsługują wszelkie nieobsługiwane wyjątki występujące podczas wykonywania akcji kontrolera lub innego filtru. Aby uzyskać więcej informacji, zobacz Filtry w ASP.NET Core.
Filtry wyjątków są przydatne w przypadku podlewek wyjątków występujących w ramach akcji MVC, ale nie są tak elastyczne, jak wbudowane oprogramowanie pośredniczące obsługujące wyjątki. UseExceptionHandler
Zalecamy użycie polecenia UseExceptionHandler
, chyba że musisz wykonać obsługę błędów inaczej w zależności od wybranej akcji MVC.
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();
});
}
Błędy stanu modelu
Aby uzyskać informacje na temat obsługi błędów stanu modelu, zobacz Powiązanie modelu i Walidacja modelu.
Dodatkowe zasoby
Przez Tom Dykstra i Steve Smith
W tym artykule opisano typowe podejścia do obsługi błędów w aplikacjach internetowych platformy ASP.NET Core. Zobacz Obsługa błędów w internetowych interfejsach API opartych na kontrolerze ASP.NET Core dla internetowych interfejsów API.
Wyświetl lub pobierz kod przykładowy. (Jak pobrać).
Strona wyjątku dla deweloperów
Na stronie wyjątku dewelopera są wyświetlane szczegółowe informacje o wyjątkach żądań. Szablony ASP.NET Core generują następujący kod:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Powyższy kod umożliwia stronę wyjątku dewelopera, gdy aplikacja jest uruchomiona w środowisku dewelopera.
Szablony umieszczają UseDeveloperExceptionPage przed wszelkim oprogramowaniem pośredniczącym, dzięki czemu wyjątki są przechwytywane w poniższym oprogramowaniem pośredniczącym.
Powyższy kod włącza stronę wyjątku dewelopera tylko wtedy, gdy aplikacja jest uruchomiona w środowisku dewelopera. Szczegółowe informacje o wyjątku nie powinny być wyświetlane publicznie, gdy aplikacja działa w środowisku produkcyjnym. Aby uzyskać więcej informacji na temat konfigurowania środowisk, zobacz Używanie wielu środowisk w programie ASP.NET Core.
Strona wyjątku dewelopera zawiera następujące informacje o wyjątku i żądaniu:
- Ślad stosu
- Parametry ciągu zapytania, jeśli istnieją
- Pliki cookie, jeśli istnieją
- Nagłówki
Strona obsługi wyjątków
Aby skonfigurować niestandardową stronę obsługi błędów dla środowiska produkcyjnego, użyj oprogramowania pośredniczącego obsługi wyjątków. Oprogramowanie pośredniczące:
- Przechwytuje i rejestruje wyjątki.
- Ponownie wykonuje żądanie w alternatywnym potoku dla wskazanej strony lub kontrolera. Żądanie nie jest wykonywane ponownie, jeśli odpowiedź została uruchomiona. Wygenerowany kod szablonu ponownie wykonuje żądanie do
/Error
.
W poniższym przykładzie UseExceptionHandler dodano oprogramowanie pośredniczące obsługi wyjątków w środowiskach nieprogramowania:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Szablon Razor aplikacji Pages zawiera stronę Błąd (.cshtml
) i PageModel klasę (ErrorModel
) w folderze Pages . W przypadku aplikacji MVC szablon projektu zawiera metodę akcji Błąd i widok Błąd w kontrolerze Home .
Nie oznaczaj metody akcji procedury obsługi błędów z atrybutami metody HTTP, takimi jak HttpGet
. Jawne czasowniki uniemożliwiają dotarcie niektórych żądań do metody . Zezwalaj na anonimowy dostęp do metody, jeśli nieuwierzytelniony użytkownicy powinni zobaczyć widok błędu.
Uzyskiwanie dostępu do wyjątku
Użyj IExceptionHandlerPathFeature polecenia , aby uzyskać dostęp do wyjątku i oryginalnej ścieżki żądania na kontrolerze lub stronie procedury obsługi błędów:
[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";
}
}
}
Ostrzeżenie
Nie należy udostępniać klientom poufnych informacji o błędach. Obsługa błędów jest zagrożeniem bezpieczeństwa.
Aby wyzwolić poprzednią stronę obsługi wyjątków, ustaw środowisko na produkcyjne i wymusić wyjątek.
Procedura obsługi wyjątków lambda
Alternatywą dla niestandardowej strony obsługi wyjątków jest podanie lambda do UseExceptionHandler. Użycie lambda umożliwia dostęp do błędu przed zwróceniem odpowiedzi.
Oto przykład użycia wyrażenia lambda do obsługi wyjątków:
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();
}
W poprzednim kodzie jest dodawany, await context.Response.WriteAsync(new string(' ', 512));
aby przeglądarka Internet Explorer wyświetlała komunikat o błędzie, a nie komunikat o błędzie programu IE. Aby uzyskać więcej informacji, zobacz ten problem w serwisie GitHub.
Ostrzeżenie
Nie należy obsługiwać poufnych informacji o błędach od IExceptionHandlerFeature klientów lub IExceptionHandlerPathFeature do klientów. Obsługa błędów jest zagrożeniem bezpieczeństwa.
Aby wyświetlić wynik wyjątku obsługi lambda w przykładowej aplikacji, użyj ProdEnvironment
dyrektyw preprocesora i ErrorHandlerLambda
wybierz pozycję Wyzwól wyjątek na home stronie.
UseStatusCodePages
Domyślnie aplikacja ASP.NET Core nie udostępnia strony kodowej stanu kodów stanu HTTP, takich jak 404 — Nie znaleziono. Aplikacja zwraca kod stanu i pustą treść odpowiedzi. Aby udostępnić strony kodu stanu, użyj oprogramowania pośredniczącego Strony kodu stanu.
Oprogramowanie pośredniczące jest udostępniane przez pakiet Microsoft.AspNetCore.Diagnostics .
Aby włączyć domyślne programy obsługi tylko tekstowe dla typowych kodów stanu błędów, wywołaj metodę UseStatusCodePages w metodzie Startup.Configure
:
app.UseStatusCodePages();
Wywołaj przed UseStatusCodePages
żądaniem obsługi oprogramowania pośredniczącego (na przykład statyczne oprogramowanie pośredniczące plików i oprogramowanie pośredniczące MVC).
Jeśli UseStatusCodePages
nie jest używany, przejście do adresu URL bez punktu końcowego zwraca komunikat o błędzie zależny od przeglądarki wskazujący, że nie można odnaleźć punktu końcowego. Na przykład przejdź do folderu Home/Privacy2
. Po UseStatusCodePages
wywołaniu przeglądarka zwraca następujące elementy:
Status Code: 404; Not Found
UseStatusCodePages z ciągiem formatu
Aby dostosować typ zawartości odpowiedzi i tekst, użyj przeciążenia UseStatusCodePages , które przyjmuje typ zawartości i ciąg formatu:
app.UseStatusCodePages(
"text/plain", "Status code page, status code: {0}");
UseStatusCodePages z lambda
Aby określić niestandardowy kod obsługi błędów i pisania odpowiedzi, użyj przeciążenia UseStatusCodePages , które przyjmuje wyrażenie 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
UseStatusCodePagesWithRedirects Metoda rozszerzenia:
- Wysyła do klienta kod stanu 302 — Znaleziono.
- Przekierowuje klienta do lokalizacji podanej w szablonie adresu URL.
app.UseStatusCodePagesWithRedirects("/StatusCode?code={0}");
Szablon adresu URL może zawierać {0}
symbol zastępczy kodu stanu, jak pokazano w przykładzie. Jeśli szablon adresu URL zaczyna się od ~
(tylda), ~
element zostanie zastąpiony PathBase
przez aplikację . Jeśli wskażesz punkt końcowy w aplikacji, utwórz widok MVC lub Razor stronę punktu końcowego. Razor Przykład strony można znaleźć Pages/StatusCode.cshtml
w przykładowej aplikacji.
Ta metoda jest często używana, gdy aplikacja:
- Powinien przekierować klienta do innego punktu końcowego, zwykle w przypadkach, gdy inna aplikacja przetwarza błąd. W przypadku aplikacji internetowych pasek adresu przeglądarki klienta odzwierciedla przekierowany punkt końcowy.
- Nie należy zachowywać i zwracać oryginalnego kodu stanu z początkową odpowiedzią przekierowania.
UseStatusCodePagesWithReExecute
UseStatusCodePagesWithReExecute Metoda rozszerzenia:
- Zwraca oryginalny kod stanu do klienta.
- Generuje treść odpowiedzi przez ponowne wykonanie potoku żądania przy użyciu alternatywnej ścieżki.
app.UseStatusCodePagesWithReExecute("/StatusCode","?code={0}");
Jeśli wskażesz punkt końcowy w aplikacji, utwórz widok MVC lub Razor stronę punktu końcowego. Upewnij się, że UseStatusCodePagesWithReExecute
zostało umieszczone przed UseRouting
, aby można było przekierować żądanie do strony stanu. Razor Przykład strony można znaleźć Pages/StatusCode.cshtml
w przykładowej aplikacji.
Ta metoda jest często używana, gdy aplikacja powinna:
- Przetwarzanie żądania bez przekierowywania do innego punktu końcowego. W przypadku aplikacji internetowych pasek adresu przeglądarki klienta odzwierciedla pierwotnie żądany punkt końcowy.
- Zachowaj i zwróć oryginalny kod stanu z odpowiedzią.
Szablony adresów URL i ciągów zapytania mogą zawierać symbol zastępczy ({0}
) dla kodu stanu. Szablon adresu URL musi zaczynać się od ukośnika (/
). W przypadku używania symbolu zastępczego w ścieżce upewnij się, że punkt końcowy (strona lub kontroler) może przetworzyć segment ścieżki. Na przykład Razor strona błędów powinna zaakceptować opcjonalną wartość segmentu ścieżki z dyrektywą @page
:
@page "{code?}"
Punkt końcowy, który przetwarza błąd, może uzyskać oryginalny adres URL, który wygenerował błąd, jak pokazano w poniższym przykładzie:
var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature != null)
{
OriginalURL =
statusCodeReExecuteFeature.OriginalPathBase
+ statusCodeReExecuteFeature.OriginalPath
+ statusCodeReExecuteFeature.OriginalQueryString;
}
Nie oznaczaj metody akcji procedury obsługi błędów z atrybutami metody HTTP, takimi jak HttpGet
. Jawne czasowniki uniemożliwiają dotarcie niektórych żądań do metody . Zezwalaj na anonimowy dostęp do metody, jeśli nieuwierzytelniony użytkownicy powinni zobaczyć widok błędu.
Wyłączanie stron kodu stanu
Aby wyłączyć strony kodu stanu dla kontrolera MVC lub metody akcji, użyj atrybutu [SkipStatusCodePages]
.
Aby wyłączyć strony kodu stanu dla określonych żądań w Razor metodzie obsługi stron lub w kontrolerze MVC, użyj polecenia IStatusCodePagesFeature:
var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature != null)
{
statusCodePagesFeature.Enabled = false;
}
Kod obsługi wyjątków
Kod na stronach obsługi wyjątków może zgłaszać wyjątki. Często dobrym pomysłem jest utworzenie stron błędów produkcyjnych składających się z czysto statycznej zawartości.
Nagłówki odpowiedzi
Po wysłaniu nagłówków odpowiedzi:
- Aplikacja nie może zmienić kodu stanu odpowiedzi.
- Nie można uruchomić żadnych stron wyjątków lub procedur obsługi. Odpowiedź musi zostać ukończona lub przerwane połączenie.
Obsługa wyjątków serwera
Oprócz logiki obsługi wyjątków w aplikacji implementacja serwera HTTP może obsługiwać niektóre wyjątki. Jeśli serwer przechwytuje wyjątek przed wysłaniem nagłówków odpowiedzi, serwer wysyła odpowiedź 500 — wewnętrzny błąd serwera bez treści odpowiedzi. Jeśli serwer przechwytuje wyjątek po wysłaniu nagłówków odpowiedzi, serwer zamknie połączenie. Żądania, które nie są obsługiwane przez aplikację, są obsługiwane przez serwer. Każdy wyjątek, który występuje, gdy serwer obsługuje żądanie, jest obsługiwany przez obsługę wyjątków serwera. Niestandardowe strony błędów aplikacji, oprogramowanie pośredniczące obsługujące wyjątki i filtry nie mają wpływu na to zachowanie.
Obsługa wyjątków uruchamiania
Tylko warstwa hostingu może obsługiwać wyjątki, które mają miejsce podczas uruchamiania aplikacji. Host można skonfigurować do przechwytywania błędów uruchamiania i przechwytywania szczegółowych błędów.
Warstwa hostingu może wyświetlić stronę błędu przechwyconego błędu uruchamiania tylko wtedy, gdy błąd występuje po powiązaniu adresu hosta/portu. Jeśli powiązanie zakończy się niepowodzeniem:
- Warstwa hostingu rejestruje wyjątek krytyczny.
- Proces dotnet ulega awarii.
- Nie jest wyświetlana strona błędu, gdy serwer HTTP ma wartość Kestrel.
W przypadku uruchamiania w usługach IIS (lub aplikacja systemu Azure Service) lub IIS Express błąd procesu 502.5 jest zwracany przez moduł ASP.NET Core, jeśli nie można uruchomić procesu. Aby uzyskać więcej informacji, zobacz Rozwiązywanie problemów z programem ASP.NET Core w usłudze aplikacja systemu Azure i usługach IIS.
Strona błędu bazy danych
Oprogramowanie pośredniczące strony błędu bazy danych przechwytuje wyjątki związane z bazą danych, które można rozwiązać przy użyciu migracji programu Entity Framework. W przypadku wystąpienia tych wyjątków odpowiedź HTML ze szczegółami możliwych akcji w celu rozwiązania problemu jest generowana. Ta strona powinna być włączona tylko w środowisku deweloperów. Włącz stronę, dodając kod do elementu Startup.Configure
:
if (env.IsDevelopment())
{
app.UseDatabaseErrorPage();
}
UseDatabaseErrorPagewymaga pakietu NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.
Filtry wyjątków
W aplikacjach MVC filtry wyjątków można skonfigurować globalnie lub na kontrolerze lub dla poszczególnych akcji. W Razor aplikacjach Pages można je skonfigurować globalnie lub na model strony. Te filtry obsługują wszelkie nieobsługiwane wyjątki występujące podczas wykonywania akcji kontrolera lub innego filtru. Aby uzyskać więcej informacji, zobacz Filtry w ASP.NET Core.
Napiwek
Filtry wyjątków są przydatne w przypadku podlewek wyjątków występujących w akcjach MVC, ale nie są tak elastyczne, jak oprogramowanie pośredniczące obsługi wyjątków. Zalecamy używanie oprogramowania pośredniczącego. Używaj filtrów tylko wtedy, gdy musisz wykonywać obsługę błędów inaczej w zależności od wybranej akcji MVC.
Błędy stanu modelu
Aby uzyskać informacje na temat obsługi błędów stanu modelu, zobacz Powiązanie modelu i Walidacja modelu.