Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Note
Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.
Warning
Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der .NET- und .NET Core-Supportrichtlinie. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.
Dieser Artikel beschreibt grundsätzliche Vorgehensweisen zur Behandlung von Fehlern in ASP.NET Core-Web-Apps. Siehe auch Behandeln von Fehlern in ASP.NET Core-APIs.
Anleitungen zur Fehlerbehandlung in Blazor, die die Anleitungen in diesem Artikel ergänzen oder ersetzen, finden Sie unter Fehlerbehandlung in ASP.NET Core-Blazor-Apps.
Seite mit Ausnahmen für Entwickler
Die Seite mit Ausnahmen für Entwickler enthält ausführliche Informationen zu unbehandelten Anforderungsausnahmen. Es verwendet DeveloperExceptionPageMiddleware, um synchrone und asynchrone Ausnahmen aus der HTTP-Pipeline zu erfassen und um Fehlerantworten zu generieren. Die Entwicklerausnahmeseite wird früh in der Middleware-Pipeline ausgeführt, damit sie unbehandelte Ausnahmen abfangen kann, die in nachfolgender Middleware ausgelöst werden.
ASP.NET Core-Apps aktivieren standardmäßig die Seite mit Ausnahmen für Entwickler, wenn die folgenden Aussagen beide zutreffen:
- Wird in der
DevelopmentUmgebung ausgeführt. - Die App wurde mit den aktuellen Vorlagen erstellt, d. h. mithilfe von WebApplication.CreateBuilder.
Apps, die mit früheren Vorlagen erstellt wurden, d. h. mithilfe von WebHost.CreateDefaultBuilder, können die Seite mit Ausnahmen für Entwickler durch Aufrufen von app.UseDeveloperExceptionPage aktivieren.
Warning
Aktivieren Sie die Entwickler-Ausnahmeseite nicht , es sei denn, die App wird in der Development Umgebung ausgeführt. Wenn die App in der Produktionsumgebung ausgeführt wird, sollten Sie keine detaillierten Ausnahmeinformationen öffentlich teilen. Weitere Informationen zum Konfigurieren von Umgebungen finden Sie unter ASP.NET Core-Laufzeitumgebungen.
Die Seite mit Ausnahmen für Entwickler kann die folgenden Informationen zu der Ausnahme und der Anforderung enthalten:
- Stapelüberwachung
- Abfragezeichenfolgeparameter, sofern vorhanden
- Cookies, falls vorhanden
- Headers
- Endpunktmetadaten (falls vorhanden)
Es ist nicht garantiert, dass auf der Seite mit Ausnahmen für Entwickler Informationen angezeigt werden. Verwenden Sie Logging für vollständige Fehlerinformationen.
Die folgende Abbildung zeigt eine Beispielseite für Entwicklerausnahmen mit Animation, um die Registerkarten und die angezeigten Informationen anzuzeigen:
Als Reaktion auf eine Anforderung mit einem Accept: text/plain-Header gibt die Seite mit Ausnahmen für Entwickler nur Text anstelle von HTML zurück. Beispiel:
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
Seite „Ausnahmehandler“
Um eine benutzerdefinierte Fehlerbehandlungsseite für die Production Umgebung zu konfigurieren, rufen Sie UseExceptionHandler auf. Diese Middleware für die Ausnahmebehandlung:
- Dient zum Abfangen und Protokollieren unbehandelter Ausnahmen.
- Führt die Anfrage unter Verwendung des angegebenen Pfads erneut in einer alternativen Pipeline aus. Die Anfrage wird nicht erneut ausgeführt, wenn bereits mit der Antwort begonnen wurde. Der von der Vorlage generierte Code führt die Anfrage über den
/Error-Pfad erneut aus.
Warning
Wenn die alternative Pipeline eine eigene Ausnahme wirft, löst die Middleware zur Ausnahmebehandlung die ursprüngliche Ausnahme erneut aus.
Da diese Middleware die Anforderungspipeline erneut ausführen kann, gilt Folgendes:
- Middlewares müssen die erneute Ausführung mit derselben Anforderung verarbeiten. Dies bedeutet normalerweise, dass ihr Zustand nach dem Aufrufen von
_nextbereinigt oder die Verarbeitung imHttpContextzwischengespeichert wird, um eine Wiederholung zu vermeiden. Bei der Verarbeitung des Anforderungstexts bedeutet dies, dass Ergebnisse wie beispielsweise der Formularleser entweder gepuffert oder zwischengespeichert werden. - Für die UseExceptionHandler(IApplicationBuilder, String)-Überladung, die in Vorlagen verwendet wird, wird nur der Anforderungspfad geändert, und die Routendaten werden gelöscht. Anforderungsdaten wie Header, Methoden und Elemente werden unverändert wiederverwendet.
- Bereichsbezogene Dienste bleiben gleich.
Im folgenden Beispiel wird die Ausnahmebehandlungs-Middleware in Umgebungen, die nicht UseExceptionHandler sind, mithilfe von Development hinzugefügt:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Die App-Vorlage für Razor Pages stellt im Ordner .cshtml eine Fehlerseite (PageModel) und die Klasse ErrorModel () bereit. Die Projektvorlage für eine MVC-App enthält eine Error-Aktionsmethode und eine Fehleransicht für den Home-Controller.
Die Middleware für die Ausnahmebehandlung führt die Anforderung unter Verwendung der ursprünglichen HTTP-Methode erneut aus. Wenn ein Endpunkt für die Fehlerbehandlung auf bestimmte HTTP-Methoden beschränkt ist, wird er nur für diese Methoden ausgeführt. Beispielsweise wird eine MVC-Controller-Aktion, die das Attribut [HttpGet] verwendet, nur für GET-Anforderungen ausgeführt. Um sicherzustellen, dass alle Anfragen die benutzerdefinierte Fehlerbehandlungsseite erreichen, beschränken Sie sie nicht auf bestimmte HTTP-Methoden.
So behandeln Sie Ausnahmen unterschiedlich, basierend auf der ursprünglichen HTTP-Methode:
- Für Razor Pages erstellen Sie mehrere Handlermethoden. Verwenden Sie beispielsweise
OnGetzum Behandeln von GET-Ausnahmen undOnPostzum Behandeln von POST-Ausnahmen. - Für MVC wenden Sie HTTP-Verbattribute auf mehrere Aktionen an. Verwenden Sie beispielsweise
[HttpGet]zum Behandeln von GET-Ausnahmen und[HttpPost]zum Behandeln von POST-Ausnahmen.
Um nicht authentifizierten Benutzern die Anzeige der benutzerdefinierten Seite für die Fehlerbehandlung zu ermöglichen, stellen Sie sicher, dass der anonyme Zugriff unterstützt wird.
Zugreifen auf die Ausnahme
Verwenden Sie IExceptionHandlerPathFeature, um auf die Ausnahme oder den Pfad der ursprünglichen Anforderung in einem Fehlerhandler zuzugreifen. Im folgenden Beispiel wird IExceptionHandlerPathFeature verwendet, um weitere Informationen zur ausgelösten Ausnahme zu erhalten:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string? ExceptionMessage { get; set; }
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "The file was not found.";
}
if (exceptionHandlerPathFeature?.Path == "/")
{
ExceptionMessage ??= string.Empty;
ExceptionMessage += " Page: Home.";
}
}
}
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
Lambda-Ausdruck für Ausnahmehandler
Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite besteht darin, an UseExceptionHandler eine Lambda-Funktion zu übergeben. Durch die Verwendung eines Lambda kann auf den Fehler zugegriffen werden, bevor die Antwort zurückgegeben wird.
Der folgende Code verwendet eine Lambda-Funktion zur Ausnahmebehandlung:
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();
}
Eine andere Möglichkeit, ein Lambda zu verwenden, besteht darin, den Statuscode auf der Grundlage des Ausnahmetyps festzulegen, wie im folgenden Beispiel:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError
});
}
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
IExceptionHandler
IExceptionHandler ist eine Schnittstelle, die dem Entwickler die Möglichkeit zum Rückruf und Behandeln bekannter Ausnahmen an einem zentralen Ort bietet. Die Schnittstelle enthält eine einzelne Methode, TryHandleAsyncdie einen HttpContext und einen Exception Parameter empfängt.
IExceptionHandler-Implementierungen werden durch Aufrufen von IServiceCollection.AddExceptionHandler<T> registriert. Die Lebensdauer einer IExceptionHandler-Instanz ist Singleton. Es können mehrere Implementierungen hinzugefügt werden, die in der registrierten Reihenfolge aufgerufen werden.
Die Middleware zur Ausnahmebehandlung durchläuft die registrierten Ausnahme-Handler in Reihenfolge, bis einer von ihnen einen Wert als true zurückgibt aus TryHandleAsync und somit angibt, dass die Ausnahme behandelt wurde. Wenn ein Ausnahmehandler eine Ausnahme behandelt, kann er true zurückgeben, um die Verarbeitung zu stoppen. Wenn eine Ausnahme von keinem Ausnahmehandler behandelt wird, greift das Steuerelement auf das Standardverhalten und die Standardoptionen der Middleware zurück.
Ab .NET 10 ist es das Standardverhalten, die Ausgabe von Diagnosemeldungen wie Protokollen und Metriken für behandelte Ausnahmen zu unterdrücken (wenn TryHandleAsync zurückgegeben true). Dies unterscheidet sich von früheren Versionen (.NET 8 und 9), bei denen die Diagnose immer ausgegeben wurde, unabhängig davon, ob die Ausnahme behandelt wurde. Das Standardverhalten kann durch Festlegen von SuppressDiagnosticsCallback geändert werden.
Das folgende Beispiel zeigt eine Implementierung von 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);
}
}
}
Das folgende Beispiel zeigt, wie Sie eine IExceptionHandler-Implementierung für die Abhängigkeitsinjektion registrieren:
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
Wenn der vorangehende Code in der Development Umgebung ausgeführt wird:
- Zuerst wird
CustomExceptionHandleraufgerufen, um eine Ausnahme zu behandeln. - Nach der Protokollierung der Ausnahme gibt die
TryHandleAsync-Methodefalsezurück, sodass die Entwickler-Ausnahmeseite angezeigt wird.
In anderen Umgebungen:
- Zuerst wird
CustomExceptionHandleraufgerufen, um eine Ausnahme zu behandeln. - Nach der Protokollierung der Ausnahme gibt die
TryHandleAsync-Methodefalsezurück, sodass die/Error-Seite angezeigt wird.
SuppressDiagnosticsCallback
Ab .NET 10 können Sie steuern, ob die Ausnahmebehandlungs-Middleware Diagnoseinformationen für behandelte Ausnahmen protokolliert, indem Sie die SuppressDiagnosticsCallback-Eigenschaft für ExceptionHandlerOptions konfigurieren. Dieser Callback empfängt den Ausnahmekontext und ermöglicht es Ihnen zu bestimmen, ob Diagnosen basierend auf der spezifischen Ausnahme oder Anforderung unterdrückt werden sollen.
Um auf das .NET 8- und 9-Verhalten zurückzusetzen, bei dem Diagnosen immer für behandelte Ausnahmen ausgegeben werden, legen Sie den Callback so fest, dass immer false zurückgegeben wird:
app.UseExceptionHandler(new ExceptionHandlerOptions
{
SuppressDiagnosticsCallback = context => false
});
Sie können die Diagnose auch bedingt unterdrücken, basierend auf dem Ausnahmetyp oder einem anderen Kontext:
app.UseExceptionHandler(new ExceptionHandlerOptions
{
SuppressDiagnosticsCallback = context => context.Exception is ArgumentException
});
Wenn eine Ausnahme von keiner IExceptionHandler-Implementierung behandelt wird (alle Handler false von TryHandleAsync zurückgeben), kehrt die Steuerung zum Standardverhalten und zu den Optionen der Middleware zurück, und Diagnosen werden gemäß dem Standardverhalten der Middleware ausgegeben.
UseStatusCodePages
Die ASP.NET Core-App stellt für HTTP-Fehlerstatuscodes wie 404 – Nicht gefunden standardmäßig keine Statuscodeseite zur Verfügung. Wenn die App einen HTTP-Fehlerstatuscode im Bereich 400 bis 599 ohne Text festlegt, werden der Statuscode und ein leerer Antworttext zurückgegeben. Um standardmäßige reine Texthandler für gängige Fehlerstatuscodes zu aktivieren, rufen Sie UseStatusCodePages in Program.cs auf:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Rufen Sie UseStatusCodePages vor der Middleware zur Anforderungsverarbeitung auf. Rufen Sie z. B. UseStatusCodePages vor der Middleware für statische Dateien und der Middleware für Endpunkte auf.
Wenn UseStatusCodePages nicht verwendet wird, wird beim Navigieren zu einer URL ohne Endpunkt eine browserabhängige Fehlermeldung zurückgegeben, die angibt, dass der Endpunkt nicht gefunden werden kann. Wenn UseStatusCodePages aufgerufen wird, gibt der Browser die folgende Antwort zurück:
Status Code: 404; Not Found
UseStatusCodePages wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
Note
Die Middleware für Statuscodeseiten erfasst keine Ausnahmen. Um eine benutzerdefinierte Fehlerbehandlungsseite bereitzustellen, verwenden Sie die Ausnahmehandlerseite.
UseStatusCodePages mit Formatzeichenfolge
Um den Inhaltstyp und den Text der Antwort anzupassen, verwenden Sie die Überladung von UseStatusCodePages, die einen Inhaltstyp und eine Formatzeichenfolge akzeptiert:
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}");
Im vorangehenden Code ist {0} ein Platzhalter für den Fehlercode.
UseStatusCodePages mit Formatzeichenfolge wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
UseStatusCodePages mit Lambda-Ausdruck
Um benutzerdefinierten Code für die Fehlerbehandlung und das Schreiben von Antworten anzugeben, verwenden Sie die Überladung von UseStatusCodePages, die einen Lambda-Ausdruck verwendet:
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 mit Lambdaausdruck wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
UseStatusCodePagesWithRedirects
Die Erweiterungsmethode UseStatusCodePagesWithRedirects:
- Sendet den Statuscode 302 Found (Gefunden) an den Client.
- Leitet den Client an den in der URL-Vorlage angegebenen Fehlerbehandlungsendpunkt weiter. Der Fehlerbehandlungsendpunkt zeigt in der Regel Fehlerinformationen an und gibt den HTTP-Code 200 zurück.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Die URL-Vorlage kann, wie im vorangehenden Code gezeigt, einen {0}-Platzhalter für den Statuscode enthalten. Wenn die URL-Vorlage mit ~ (Tilde) beginnt, wird ~ durch das PathBase der App ersetzt. Wenn Sie in der App einen Endpunkt angeben, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Den Client an einen anderen Endpunkt umleiten, in der Regel in Fällen, in denen eine andere App den Fehler verarbeitet. Für Web-Apps gibt die Adressleiste des Browsers des Clients den umgeleiteten Endpunkt wieder.
- Der ursprüngliche Statuscode sollte nicht beibehalten und mit der ursprünglichen Weiterleitungsantwort zurückgegeben werden.
UseStatusCodePagesWithReExecute
Die Erweiterungsmethode UseStatusCodePagesWithReExecute:
- Generiert den Antworttext durch die erneute Ausführung der Anforderungspipeline mithilfe eines alternativen Pfads.
- Ändert den Statuscode nicht vor oder nach der erneuten Ausführung der Pipeline.
Die neue Pipelineausführung kann den Statuscode der Antwort ändern, da sie die vollständige Kontrolle über den Statuscode hat. Wenn die neue Pipeline den Statuscode nicht ändert, wird der ursprüngliche Statuscode an den Client gesendet.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Wenn in der App ein Endpunkt angegeben ist, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Die Anforderung ohne Umleitung an einen anderen Endpunkt verarbeiten. Für Web-Apps gibt die Adressleiste des Browsers des Clients den ursprünglich angeforderten Endpunkt wieder.
- Den ursprünglichen Statuscode mit der Antwort beibehalten und zurückgeben.
Die URL-Vorlage muss mit / beginnen und kann den Platzhalter {0} für den Statuscode enthalten. Um den Statuscode als Abfragezeichenfolgenparameter zu übergeben, übergeben Sie ein zweites Argument an UseStatusCodePagesWithReExecute. Beispiel:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
Der Endpunkt, der den Fehler verarbeitet, kann die ursprüngliche URL abrufen, die den Fehler generiert hat (siehe folgendes Beispiel):
[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}";
}
}
}
Da diese Middleware die Anforderungspipeline erneut ausführen kann, gilt Folgendes:
- Middlewares müssen die erneute Ausführung mit derselben Anforderung verarbeiten. Dies bedeutet normalerweise, dass ihr Zustand nach dem Aufrufen von
_nextbereinigt oder die Verarbeitung imHttpContextzwischengespeichert wird, um eine Wiederholung zu vermeiden. Bei der Verarbeitung des Anforderungstexts bedeutet dies, dass Ergebnisse wie beispielsweise der Formularleser entweder gepuffert oder zwischengespeichert werden. - Bereichsbezogene Dienste bleiben gleich.
Deaktivieren von Statuscodeseiten
Verwenden Sie das Attribut [SkipStatusCodePages], um Statuscodeseiten für einen MVC-Controller oder eine MVC-Aktionsmethode zu deaktivieren.
Um Statuscode-Seiten für bestimmte Anfragen in einer Razor-Pages-Handlermethode oder in einem MVC-Controller zu deaktivieren, verwenden Sie IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Ausnahmebehandlungscode
Code auf Seiten zur Ausnahmebehandlung kann ebenfalls Ausnahmen auslösen. Fehlerseiten in der Produktionsumgebung sollten gründlich getestet werden, und es sollte besonders darauf geachtet werden, dass sie nicht selbst Ausnahmen auslösen.
Antwortkopfzeilen
Nachdem die Header für eine Antwort gesendet wurden:
- Die App kann den Statuscode der Antwort nicht ändern.
- Weder Ausnahmeseiten noch Handler können ausgeführt werden. Die Antwort muss abgeschlossen oder die Verbindung unterbrochen werden.
Server-Ausnahmebehandlung
Neben der Ausnahmebehandlungslogik in einer App kann die HTTP-Serverimplementierung einige Ausnahmebehandlungen durchführen. Wenn der Server eine Ausnahme erfasst, bevor die Antwortheader gesendet werden, sendet der Server die Antwort 500 - Internal Server Error ohne Antworttext. Wenn der Server eine Ausnahme auffängt, nachdem die Antwortheader gesendet wurden, schließt der Server die Verbindung. Anforderungen, die nicht von der App verarbeitet werden, werden vom Server verarbeitet. Jede Ausnahme, die bei der Anforderungsverarbeitung durch den Server auftritt, wird durch die Ausnahmebehandlung des Servers verarbeitet. Die benutzerdefinierten Fehlerseiten der App, Middleware zur Fehlerbehandlung und Filter haben keine Auswirkungen auf dieses Verhalten.
Fehlerbehandlung beim Start
Nur auf Hostebene können Ausnahmen behandelt werden, die während des Starts einer App auftreten. Der Host kann für das Erfassen von Startfehlern und Erfassen detaillierter Fehler konfiguriert werden.
Die Hostingebene kann nur dann für einen beim Start erfassten Fehler eine Fehlerseite anzeigen, wenn der Fehler nach der Bindung an die Hostadresse bzw. den Port auftritt. Wenn die Bindung misslingt:
- Die Hostebene protokolliert eine kritische Ausnahme.
- Der DotNet-Prozess stürzt ab.
- Wenn der HTTP-Server Kestrel heißt, wird keine Fehlerseite angezeigt.
Wenn sie auf IIS (oder Azure App Service) oder IIS Express ausgeführt wird, wird ein Prozessfehler 502.5 vom ASP.NET Core-Modul zurückgegeben, wenn der Prozess nicht starten kann. Weitere Informationen finden Sie unter Problembehandlung bei ASP.NET Core in Azure App Service und IIS.
Datenbank-Fehlerseite
Der Ausnahmefilter der Datenbankentwicklerseite AddDatabaseDeveloperPageExceptionFilter erfasst datenbankbezogene Ausnahmen, die mithilfe von Entity Framework Core-Migrationen aufgelöst werden können. Wenn diese Ausnahmen auftreten, wird eine HTML-Antwort mit Details zu möglichen Aktionen zum Beheben des Problems generiert. Diese Seite ist nur in der Development Umgebung aktiviert. Der folgende Code fügt den Ausnahmefilter der Datenbankentwicklerseite hinzu:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Ausnahmefilter
In MVC-Apps können Ausnahmefilter global oder auf einen Controller oder eine Aktion bezogen konfiguriert werden. In Razor Pages-Apps können sie global oder auf ein Seitenmodell bezogen konfiguriert werden. Diese Filter verarbeiten jede nicht behandelte Ausnahme, die während der Ausführung eine Controlleraktion oder eines anderen Filters auftritt. Weitere Informationen finden Sie unter Filter in ASP.NET Core.
Ausnahmefilter eignen sich zum Auffangen von Ausnahmen, die in MVC-Aktionen ausgelöst werden. Sie sind jedoch nicht so flexibel wie die integrierte Middleware für die Ausnahmebehandlung, UseExceptionHandler. Wir empfehlen die Verwendung von UseExceptionHandler, es sei denn, Sie müssen die Fehlerbehandlung je nachdem, welche MVC-Aktion ausgewählt wird, anders handhaben.
Modellstatusfehler
Informationen zum Behandeln von Modellstatusfehlern finden Sie unter Modellbindung und Modellvalidierung.
Problemdetails
Problemdetails sind nicht das einzige Antwortformat zur Beschreibung eines HTTP-API-Fehlers, sie werden jedoch häufig verwendet, um Fehler für HTTP-APIs zu melden.
Der Dienst für Problemdetails implementiert die IProblemDetailsService-Schnittstelle, die das Erstellen von Problemdetails in ASP.NET Core unterstützt. Die Erweiterungsmethode AddProblemDetails(IServiceCollection) in IServiceCollection registriert die Standardimplementierung von IProblemDetailsService.
In ASP.NET Core-Apps generiert die folgende Middleware HTTP-Antworten mit Problemdetails, wenn AddProblemDetails aufgerufen wird. Dies gilt jedoch nicht, wenn der Accept-HTTP-Anforderungsheader keinen der vom registrierten IProblemDetailsWriter unterstützten Inhaltstyp enthält (Standard: application/json):
- ExceptionHandlerMiddleware generiert eine Problemdetailantwort, wenn kein benutzerdefinierter Handler definiert ist.
- StatusCodePagesMiddleware generiert standardmäßig eine Problemdetailantwort.
-
DeveloperExceptionPageMiddleware generiert eine Problemdetailantwort während der Entwicklungsphase, wenn der
Accept-HTTP-Anforderungsheader nichttext/htmlenthält.
Mit dem folgenden Code wird die App so konfiguriert, dass eine Problemdetailantwort für alle Fehlerantworten von HTTP-Client und -Server generiert wird, die noch keinen Textinhalt aufweisen:
builder.Services.AddProblemDetails();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
Im nächsten Abschnitt wird gezeigt, wie Sie den Antworttext mit den Problemdetails anpassen.
Anpassen von Problemdetails
Die automatische Erstellung von ProblemDetails kann mit einer der folgenden Optionen angepasst werden:
- Verwenden Sie
ProblemDetailsOptions.CustomizeProblemDetails - Verwenden Sie eine benutzerdefinierte
IProblemDetailsWriter - Rufen Sie
IProblemDetailsServicein einer Middleware auf
CustomizeProblemDetails Vorgang
Die generierten Problemdetails können mithilfe von CustomizeProblemDetails angepasst werden, und die Anpassungen werden auf alle automatisch generierten Problemdetails angewandt.
Der folgende Code verwendet ProblemDetailsOptions, um CustomizeProblemDetails festzulegen:
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();
Beispielsweise erzeugt ein HTTP Status 400 Bad Request-Endpunkt den folgenden Antworttext mit Problemdetails:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
Benutzerdefiniert IProblemDetailsWriter
Eine IProblemDetailsWriter-Implementierung kann für erweiterte Anpassungen erstellt werden.
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));
}
}
Hinweis: Wenn Sie einen benutzerdefinierten IProblemDetailsWriter verwenden, muss der benutzerdefinierte IProblemDetailsWriter vor dem Aufrufen von AddRazorPages, AddControllers, AddControllersWithViews oder AddMvc registriert werden:
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();
Problemdetails von Middleware
Ein alternativer Ansatz zur Verwendung von ProblemDetailsOptions mit CustomizeProblemDetails ist das Festlegen von ProblemDetails in Middleware. Eine Problemdetailantwort kann durch Aufrufen von IProblemDetailsService.WriteAsync geschrieben werden:
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();
Im vorherigen Code geben die minimalen API-Endpunkte /divide und /squareroot die erwartete benutzerdefinierte Problemantwort bei fehlerhaften Eingaben zurück.
Die API-Controllerendpunkte geben bei einer Fehlereingabe die Standardproblemantwort zurück, nicht die benutzerdefinierte Problemantwort. Die Standardproblemantwort wird zurückgegeben, da der API-Controller in den Antwortdatenstrom (Problemdetails für Fehlerstatuscodes) geschrieben hat, bevor IProblemDetailsService.WriteAsync aufgerufen wird, und die Antwort nicht erneut geschrieben wird.
Der folgende ValuesController gibt BadRequestResult zurück, das in den Antwortdatenstrom geschrieben wird. Damit wird verhindert, dass die benutzerdefinierte Problemantwort zurückgegeben wird.
[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));
}
}
Der folgende Values3Controller gibt ControllerBase.Problem zurück, sodass das erwartete Ergebnis für das benutzerdefinierte Problem zurückgegeben wird:
[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));
}
}
ProblemDetails-Nutzlast für Ausnahmen erzeugen
Betrachten Sie die folgende App:
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();
Wenn in Nicht-Entwicklungsumgebungen eine Ausnahme auftritt, wird die folgende standardmäßige ProblemDetails-Antwort an den Client zurückgegeben:
{
"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"
}
Für die meisten Apps ist für Ausnahmen nur der oben stehende Code erforderlich. Der folgende Abschnitt zeigt jedoch, wie Sie detailliertere Problemantworten erhalten.
Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite besteht darin, an UseExceptionHandler eine Lambda-Funktion zu übergeben. Die Verwendung einer Lambdafunktion ermöglicht den Zugriff auf den Fehler und das Schreiben einer Problemdetailantwort mit IProblemDetailsService.WriteAsync:
using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = Text.Plain;
var title = "Bad Input";
var detail = "Invalid input";
var type = "https://errors.example.com/badInput";
if (context.RequestServices.GetService<IProblemDetailsService>() is
{ } problemDetailsService)
{
var exceptionHandlerFeature =
context.Features.Get<IExceptionHandlerFeature>();
var exceptionType = exceptionHandlerFeature?.Error;
if (exceptionType != null &&
exceptionType.Message.Contains("infinity"))
{
title = "Argument exception";
detail = "Invalid input";
type = "https://errors.example.com/argumentException";
}
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = title,
Detail = detail,
Type = type
}
});
}
});
});
}
app.MapControllers();
app.Run();
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
Ein alternativer Ansatz zum Generieren von Problemdetails besteht darin, das Drittanbieter-NuGet-Paket Hellang.Middleware.ProblemDetails zu verwenden, mit dem Ausnahmen und Clientfehler zu Problemdetails zugeordnet werden können.
Weitere Ressourcen
- Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)
- Problembehandlung bei ASP.NET Core in Azure App Service und IIS
- Problembehandlung für häufige Fehler bei Azure App Service und IIS mit ASP.NET Core
- Behandeln von Fehlern in ASP.NET Core-APIs
-
Breaking Change: Ausnahmediagnosen werden unterdrückt, wenn
IExceptionHandler.TryHandleAsyncden Wert true zurückgibt
Dieser Artikel beschreibt grundsätzliche Vorgehensweisen zur Behandlung von Fehlern in ASP.NET Core-Web-Apps. Siehe auch Behandeln von Fehlern in ASP.NET Core-APIs.
Anleitungen zur Fehlerbehandlung in Blazor, die die Anleitungen in diesem Artikel ergänzen oder ersetzen, finden Sie unter Fehlerbehandlung in ASP.NET Core-Blazor-Apps.
Seite mit Ausnahmen für Entwickler
Die Seite mit Ausnahmen für Entwickler enthält ausführliche Informationen zu unbehandelten Anforderungsausnahmen. Es verwendet DeveloperExceptionPageMiddleware, um synchrone und asynchrone Ausnahmen aus der HTTP-Pipeline zu erfassen und um Fehlerantworten zu generieren. Die Entwicklerausnahmeseite wird früh in der Middleware-Pipeline ausgeführt, damit sie unbehandelte Ausnahmen abfangen kann, die in nachfolgender Middleware ausgelöst werden.
ASP.NET Core-Apps aktivieren standardmäßig die Seite mit Ausnahmen für Entwickler, wenn die folgenden Aussagen beide zutreffen:
- Wird in der
DevelopmentUmgebung ausgeführt. - Die App wurde mit den aktuellen Vorlagen erstellt, d. h. mithilfe von WebApplication.CreateBuilder.
Apps, die mit früheren Vorlagen erstellt wurden, d. h. mithilfe von WebHost.CreateDefaultBuilder, können die Seite mit Ausnahmen für Entwickler durch Aufrufen von app.UseDeveloperExceptionPage aktivieren.
Warning
Aktivieren Sie die Entwickler-Ausnahmeseite nicht , es sei denn, die App wird in der Development Umgebung ausgeführt. Wenn die App in der Produktionsumgebung ausgeführt wird, sollten Sie keine detaillierten Ausnahmeinformationen öffentlich teilen. Weitere Informationen zum Konfigurieren von Umgebungen finden Sie unter ASP.NET Core-Laufzeitumgebungen.
Die Seite mit Ausnahmen für Entwickler kann die folgenden Informationen zu der Ausnahme und der Anforderung enthalten:
- Stapelüberwachung
- Abfragezeichenfolgeparameter, sofern vorhanden
- Cookies, falls vorhanden
- Headers
- Endpunktmetadaten (falls vorhanden)
Es ist nicht garantiert, dass auf der Seite mit Ausnahmen für Entwickler Informationen angezeigt werden. Verwenden Sie Logging für vollständige Fehlerinformationen.
Die folgende Abbildung zeigt eine Beispielseite für Entwicklerausnahmen mit Animation, um die Registerkarten und die angezeigten Informationen anzuzeigen:
Als Reaktion auf eine Anforderung mit einem Accept: text/plain-Header gibt die Seite mit Ausnahmen für Entwickler nur Text anstelle von HTML zurück. Beispiel:
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
Seite „Ausnahmehandler“
Um eine benutzerdefinierte Fehlerbehandlungsseite für die Production Umgebung zu konfigurieren, rufen Sie UseExceptionHandler auf. Diese Middleware für die Ausnahmebehandlung:
- Dient zum Abfangen und Protokollieren unbehandelter Ausnahmen.
- Führt die Anfrage unter Verwendung des angegebenen Pfads erneut in einer alternativen Pipeline aus. Die Anfrage wird nicht erneut ausgeführt, wenn bereits mit der Antwort begonnen wurde. Der von der Vorlage generierte Code führt die Anfrage über den
/Error-Pfad erneut aus.
Warning
Wenn die alternative Pipeline eine eigene Ausnahme wirft, löst die Middleware zur Ausnahmebehandlung die ursprüngliche Ausnahme erneut aus.
Da diese Middleware die Anforderungspipeline erneut ausführen kann, gilt Folgendes:
- Middlewares müssen die erneute Ausführung mit derselben Anforderung verarbeiten. Dies bedeutet normalerweise, dass ihr Zustand nach dem Aufrufen von
_nextbereinigt oder die Verarbeitung imHttpContextzwischengespeichert wird, um eine Wiederholung zu vermeiden. Bei der Verarbeitung des Anforderungstexts bedeutet dies, dass Ergebnisse wie beispielsweise der Formularleser entweder gepuffert oder zwischengespeichert werden. - Für die UseExceptionHandler(IApplicationBuilder, String)-Überladung, die in Vorlagen verwendet wird, wird nur der Anforderungspfad geändert, und die Routendaten werden gelöscht. Anforderungsdaten wie Header, Methoden und Elemente werden unverändert wiederverwendet.
- Bereichsbezogene Dienste bleiben gleich.
Im folgenden Beispiel wird die Ausnahmebehandlungs-Middleware in Umgebungen, die nicht UseExceptionHandler sind, mithilfe von Development hinzugefügt:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Die App-Vorlage für Razor Pages stellt im Ordner .cshtml eine Fehlerseite (PageModel) und die Klasse ErrorModel () bereit. Die Projektvorlage für eine MVC-App enthält eine Error-Aktionsmethode und eine Fehleransicht für den Home-Controller.
Die Middleware für die Ausnahmebehandlung führt die Anforderung unter Verwendung der ursprünglichen HTTP-Methode erneut aus. Wenn ein Endpunkt für die Fehlerbehandlung auf bestimmte HTTP-Methoden beschränkt ist, wird er nur für diese Methoden ausgeführt. Beispielsweise wird eine MVC-Controller-Aktion, die das Attribut [HttpGet] verwendet, nur für GET-Anforderungen ausgeführt. Um sicherzustellen, dass alle Anfragen die benutzerdefinierte Fehlerbehandlungsseite erreichen, beschränken Sie sie nicht auf bestimmte HTTP-Methoden.
So behandeln Sie Ausnahmen unterschiedlich, basierend auf der ursprünglichen HTTP-Methode:
- Für Razor Pages erstellen Sie mehrere Handlermethoden. Verwenden Sie beispielsweise
OnGetzum Behandeln von GET-Ausnahmen undOnPostzum Behandeln von POST-Ausnahmen. - Für MVC wenden Sie HTTP-Verbattribute auf mehrere Aktionen an. Verwenden Sie beispielsweise
[HttpGet]zum Behandeln von GET-Ausnahmen und[HttpPost]zum Behandeln von POST-Ausnahmen.
Um nicht authentifizierten Benutzern die Anzeige der benutzerdefinierten Seite für die Fehlerbehandlung zu ermöglichen, stellen Sie sicher, dass der anonyme Zugriff unterstützt wird.
Zugreifen auf die Ausnahme
Verwenden Sie IExceptionHandlerPathFeature, um auf die Ausnahme oder den Pfad der ursprünglichen Anforderung in einem Fehlerhandler zuzugreifen. Im folgenden Beispiel wird IExceptionHandlerPathFeature verwendet, um weitere Informationen zur ausgelösten Ausnahme zu erhalten:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string? ExceptionMessage { get; set; }
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "The file was not found.";
}
if (exceptionHandlerPathFeature?.Path == "/")
{
ExceptionMessage ??= string.Empty;
ExceptionMessage += " Page: Home.";
}
}
}
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
Lambda-Ausdruck für Ausnahmehandler
Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite besteht darin, an UseExceptionHandler eine Lambda-Funktion zu übergeben. Durch die Verwendung eines Lambda kann auf den Fehler zugegriffen werden, bevor die Antwort zurückgegeben wird.
Der folgende Code verwendet eine Lambda-Funktion zur Ausnahmebehandlung:
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();
}
Eine andere Möglichkeit, ein Lambda zu verwenden, besteht darin, den Statuscode auf der Grundlage des Ausnahmetyps festzulegen, wie im folgenden Beispiel:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError
});
}
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
IExceptionHandler
IExceptionHandler ist eine Schnittstelle, die dem Entwickler die Möglichkeit zum Rückruf und Behandeln bekannter Ausnahmen an einem zentralen Ort bietet.
IExceptionHandler-Implementierungen werden durch Aufrufen von IServiceCollection.AddExceptionHandler<T> registriert. Die Lebensdauer einer IExceptionHandler-Instanz ist Singleton. Es können mehrere Implementierungen hinzugefügt werden, die in der registrierten Reihenfolge aufgerufen werden.
Wenn ein Ausnahmehandler eine Anforderung verarbeitet, kann er true zurückgeben, um die Verarbeitung zu beenden. Wenn eine Ausnahme von keinem Ausnahmehandler behandelt wird, greift das Steuerelement auf das Standardverhalten und die Standardoptionen der Middleware zurück. Für behandelte und nicht behandelte Ausnahmen werden unterschiedliche Metriken und Protokolle ausgegeben.
Das folgende Beispiel zeigt eine Implementierung von 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);
}
}
}
Das folgende Beispiel zeigt, wie Sie eine IExceptionHandler-Implementierung für die Abhängigkeitsinjektion registrieren:
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
Wenn der vorangehende Code in der Development Umgebung ausgeführt wird:
- Zuerst wird
CustomExceptionHandleraufgerufen, um eine Ausnahme zu behandeln. - Nach der Protokollierung der Ausnahme gibt die
TryHandleAsync-Methodefalsezurück, sodass die Entwickler-Ausnahmeseite angezeigt wird.
In anderen Umgebungen:
- Zuerst wird
CustomExceptionHandleraufgerufen, um eine Ausnahme zu behandeln. - Nach der Protokollierung der Ausnahme gibt die
TryHandleAsync-Methodefalsezurück, sodass die/Error-Seite angezeigt wird.
UseStatusCodePages
Die ASP.NET Core-App stellt für HTTP-Fehlerstatuscodes wie 404 – Nicht gefunden standardmäßig keine Statuscodeseite zur Verfügung. Wenn die App einen HTTP-Fehlerstatuscode im Bereich 400 bis 599 ohne Text festlegt, werden der Statuscode und ein leerer Antworttext zurückgegeben. Um standardmäßige reine Texthandler für gängige Fehlerstatuscodes zu aktivieren, rufen Sie UseStatusCodePages in Program.cs auf:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Rufen Sie UseStatusCodePages vor der Middleware zur Anforderungsverarbeitung auf. Rufen Sie z. B. UseStatusCodePages vor der Middleware für statische Dateien und der Middleware für Endpunkte auf.
Wenn UseStatusCodePages nicht verwendet wird, wird beim Navigieren zu einer URL ohne Endpunkt eine browserabhängige Fehlermeldung zurückgegeben, die angibt, dass der Endpunkt nicht gefunden werden kann. Wenn UseStatusCodePages aufgerufen wird, gibt der Browser die folgende Antwort zurück:
Status Code: 404; Not Found
UseStatusCodePages wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
Note
Die Middleware für Statuscodeseiten erfasst keine Ausnahmen. Um eine benutzerdefinierte Fehlerbehandlungsseite bereitzustellen, verwenden Sie die Ausnahmehandlerseite.
UseStatusCodePages mit Formatzeichenfolge
Um den Inhaltstyp und den Text der Antwort anzupassen, verwenden Sie die Überladung von UseStatusCodePages, die einen Inhaltstyp und eine Formatzeichenfolge akzeptiert:
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}");
Im vorangehenden Code ist {0} ein Platzhalter für den Fehlercode.
UseStatusCodePages mit Formatzeichenfolge wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
UseStatusCodePages mit Lambda-Ausdruck
Um benutzerdefinierten Code für die Fehlerbehandlung und das Schreiben von Antworten anzugeben, verwenden Sie die Überladung von UseStatusCodePages, die einen Lambda-Ausdruck verwendet:
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 mit Lambdaausdruck wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
UseStatusCodePagesWithRedirects
Die Erweiterungsmethode UseStatusCodePagesWithRedirects:
- Sendet den Statuscode 302 Found (Gefunden) an den Client.
- Leitet den Client an den in der URL-Vorlage angegebenen Fehlerbehandlungsendpunkt weiter. Der Fehlerbehandlungsendpunkt zeigt in der Regel Fehlerinformationen an und gibt den HTTP-Code 200 zurück.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Die URL-Vorlage kann, wie im vorangehenden Code gezeigt, einen {0}-Platzhalter für den Statuscode enthalten. Wenn die URL-Vorlage mit ~ (Tilde) beginnt, wird ~ durch das PathBase der App ersetzt. Wenn Sie in der App einen Endpunkt angeben, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Den Client an einen anderen Endpunkt umleiten, in der Regel in Fällen, in denen eine andere App den Fehler verarbeitet. Für Web-Apps gibt die Adressleiste des Browsers des Clients den umgeleiteten Endpunkt wieder.
- Der ursprüngliche Statuscode sollte nicht beibehalten und mit der ursprünglichen Weiterleitungsantwort zurückgegeben werden.
UseStatusCodePagesWithReExecute
Die Erweiterungsmethode UseStatusCodePagesWithReExecute:
- Generiert den Antworttext durch die erneute Ausführung der Anforderungspipeline mithilfe eines alternativen Pfads.
- Ändert den Statuscode nicht vor oder nach der erneuten Ausführung der Pipeline.
Die neue Pipelineausführung kann den Statuscode der Antwort ändern, da sie die vollständige Kontrolle über den Statuscode hat. Wenn die neue Pipeline den Statuscode nicht ändert, wird der ursprüngliche Statuscode an den Client gesendet.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Wenn in der App ein Endpunkt angegeben ist, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Die Anforderung ohne Umleitung an einen anderen Endpunkt verarbeiten. Für Web-Apps gibt die Adressleiste des Browsers des Clients den ursprünglich angeforderten Endpunkt wieder.
- Den ursprünglichen Statuscode mit der Antwort beibehalten und zurückgeben.
Die URL-Vorlage muss mit / beginnen und kann den Platzhalter {0} für den Statuscode enthalten. Um den Statuscode als Abfragezeichenfolgenparameter zu übergeben, übergeben Sie ein zweites Argument an UseStatusCodePagesWithReExecute. Beispiel:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
Der Endpunkt, der den Fehler verarbeitet, kann die ursprüngliche URL abrufen, die den Fehler generiert hat (siehe folgendes Beispiel):
[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}";
}
}
}
Da diese Middleware die Anforderungspipeline erneut ausführen kann, gilt Folgendes:
- Middlewares müssen die erneute Ausführung mit derselben Anforderung verarbeiten. Dies bedeutet normalerweise, dass ihr Zustand nach dem Aufrufen von
_nextbereinigt oder die Verarbeitung imHttpContextzwischengespeichert wird, um eine Wiederholung zu vermeiden. Bei der Verarbeitung des Anforderungstexts bedeutet dies, dass Ergebnisse wie beispielsweise der Formularleser entweder gepuffert oder zwischengespeichert werden. - Bereichsbezogene Dienste bleiben gleich.
Deaktivieren von Statuscodeseiten
Verwenden Sie das Attribut [SkipStatusCodePages], um Statuscodeseiten für einen MVC-Controller oder eine MVC-Aktionsmethode zu deaktivieren.
Um Statuscode-Seiten für bestimmte Anfragen in einer Razor-Pages-Handlermethode oder in einem MVC-Controller zu deaktivieren, verwenden Sie IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Ausnahmebehandlungscode
Code auf Seiten zur Ausnahmebehandlung kann ebenfalls Ausnahmen auslösen. Fehlerseiten in der Produktionsumgebung sollten gründlich getestet werden, und es sollte besonders darauf geachtet werden, dass sie nicht selbst Ausnahmen auslösen.
Antwortkopfzeilen
Nachdem die Header für eine Antwort gesendet wurden:
- Die App kann den Statuscode der Antwort nicht ändern.
- Weder Ausnahmeseiten noch Handler können ausgeführt werden. Die Antwort muss abgeschlossen oder die Verbindung unterbrochen werden.
Server-Ausnahmebehandlung
Neben der Ausnahmebehandlungslogik in einer App kann die HTTP-Serverimplementierung einige Ausnahmebehandlungen durchführen. Wenn der Server eine Ausnahme erfasst, bevor die Antwortheader gesendet werden, sendet der Server die Antwort 500 - Internal Server Error ohne Antworttext. Wenn der Server eine Ausnahme auffängt, nachdem die Antwortheader gesendet wurden, schließt der Server die Verbindung. Anforderungen, die nicht von der App verarbeitet werden, werden vom Server verarbeitet. Jede Ausnahme, die bei der Anforderungsverarbeitung durch den Server auftritt, wird durch die Ausnahmebehandlung des Servers verarbeitet. Die benutzerdefinierten Fehlerseiten der App, Middleware zur Fehlerbehandlung und Filter haben keine Auswirkungen auf dieses Verhalten.
Fehlerbehandlung beim Start
Nur auf Hostebene können Ausnahmen behandelt werden, die während des Starts einer App auftreten. Der Host kann für das Erfassen von Startfehlern und Erfassen detaillierter Fehler konfiguriert werden.
Die Hostingebene kann nur dann für einen beim Start erfassten Fehler eine Fehlerseite anzeigen, wenn der Fehler nach der Bindung an die Hostadresse bzw. den Port auftritt. Wenn die Bindung misslingt:
- Die Hostebene protokolliert eine kritische Ausnahme.
- Der DotNet-Prozess stürzt ab.
- Wenn der HTTP-Server Kestrel heißt, wird keine Fehlerseite angezeigt.
Wenn sie auf IIS (oder Azure App Service) oder IIS Express ausgeführt wird, wird ein Prozessfehler 502.5 vom ASP.NET Core-Modul zurückgegeben, wenn der Prozess nicht starten kann. Weitere Informationen finden Sie unter Problembehandlung bei ASP.NET Core in Azure App Service und IIS.
Datenbank-Fehlerseite
Der Ausnahmefilter der Datenbankentwicklerseite AddDatabaseDeveloperPageExceptionFilter erfasst datenbankbezogene Ausnahmen, die mithilfe von Entity Framework Core-Migrationen aufgelöst werden können. Wenn diese Ausnahmen auftreten, wird eine HTML-Antwort mit Details zu möglichen Aktionen zum Beheben des Problems generiert. Diese Seite ist nur in der Development Umgebung aktiviert. Der folgende Code fügt den Ausnahmefilter der Datenbankentwicklerseite hinzu:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Ausnahmefilter
In MVC-Apps können Ausnahmefilter global oder auf einen Controller oder eine Aktion bezogen konfiguriert werden. In Razor Pages-Apps können sie global oder auf ein Seitenmodell bezogen konfiguriert werden. Diese Filter verarbeiten jede nicht behandelte Ausnahme, die während der Ausführung eine Controlleraktion oder eines anderen Filters auftritt. Weitere Informationen finden Sie unter Filter in ASP.NET Core.
Ausnahmefilter eignen sich zum Auffangen von Ausnahmen, die in MVC-Aktionen ausgelöst werden. Sie sind jedoch nicht so flexibel wie die integrierte Middleware für die Ausnahmebehandlung, UseExceptionHandler. Wir empfehlen die Verwendung von UseExceptionHandler, es sei denn, Sie müssen die Fehlerbehandlung je nachdem, welche MVC-Aktion ausgewählt wird, anders handhaben.
Modellstatusfehler
Informationen zum Behandeln von Modellstatusfehlern finden Sie unter Modellbindung und Modellvalidierung.
Problemdetails
Problemdetails sind nicht das einzige Antwortformat zur Beschreibung eines HTTP-API-Fehlers, sie werden jedoch häufig verwendet, um Fehler für HTTP-APIs zu melden.
Der Dienst für Problemdetails implementiert die IProblemDetailsService-Schnittstelle, die das Erstellen von Problemdetails in ASP.NET Core unterstützt. Die Erweiterungsmethode AddProblemDetails(IServiceCollection) in IServiceCollection registriert die Standardimplementierung von IProblemDetailsService.
In ASP.NET Core-Apps generiert die folgende Middleware HTTP-Antworten mit Problemdetails, wenn AddProblemDetails aufgerufen wird. Dies gilt jedoch nicht, wenn der Accept-HTTP-Anforderungsheader keinen der vom registrierten IProblemDetailsWriter unterstützten Inhaltstyp enthält (Standard: application/json):
- ExceptionHandlerMiddleware generiert eine Problemdetailantwort, wenn kein benutzerdefinierter Handler definiert ist.
- StatusCodePagesMiddleware generiert standardmäßig eine Problemdetailantwort.
-
DeveloperExceptionPageMiddleware generiert eine Problemdetailantwort während der Entwicklungsphase, wenn der
Accept-HTTP-Anforderungsheader nichttext/htmlenthält.
Mit dem folgenden Code wird die App so konfiguriert, dass eine Problemdetailantwort für alle Fehlerantworten von HTTP-Client und -Server generiert wird, die noch keinen Textinhalt aufweisen:
builder.Services.AddProblemDetails();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
Im nächsten Abschnitt wird gezeigt, wie Sie den Antworttext mit den Problemdetails anpassen.
Anpassen von Problemdetails
Die automatische Erstellung von ProblemDetails kann mit einer der folgenden Optionen angepasst werden:
- Verwenden Sie
ProblemDetailsOptions.CustomizeProblemDetails - Verwenden Sie eine benutzerdefinierte
IProblemDetailsWriter - Rufen Sie
IProblemDetailsServicein einer Middleware auf
CustomizeProblemDetails Vorgang
Die generierten Problemdetails können mithilfe von CustomizeProblemDetails angepasst werden, und die Anpassungen werden auf alle automatisch generierten Problemdetails angewandt.
Der folgende Code verwendet ProblemDetailsOptions, um CustomizeProblemDetails festzulegen:
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();
Beispielsweise erzeugt ein HTTP Status 400 Bad Request-Endpunkt den folgenden Antworttext mit Problemdetails:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
Benutzerdefiniert IProblemDetailsWriter
Eine IProblemDetailsWriter-Implementierung kann für erweiterte Anpassungen erstellt werden.
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));
}
}
Hinweis: Wenn Sie einen benutzerdefinierten IProblemDetailsWriter verwenden, muss der benutzerdefinierte IProblemDetailsWriter vor dem Aufrufen von AddRazorPages, AddControllers, AddControllersWithViews oder AddMvc registriert werden:
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();
Problemdetails von Middleware
Ein alternativer Ansatz zur Verwendung von ProblemDetailsOptions mit CustomizeProblemDetails ist das Festlegen von ProblemDetails in Middleware. Eine Problemdetailantwort kann durch Aufrufen von IProblemDetailsService.WriteAsync geschrieben werden:
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();
Im vorherigen Code geben die minimalen API-Endpunkte /divide und /squareroot die erwartete benutzerdefinierte Problemantwort bei fehlerhaften Eingaben zurück.
Die API-Controllerendpunkte geben bei einer Fehlereingabe die Standardproblemantwort zurück, nicht die benutzerdefinierte Problemantwort. Die Standardproblemantwort wird zurückgegeben, da der API-Controller in den Antwortdatenstrom (Problemdetails für Fehlerstatuscodes) geschrieben hat, bevor IProblemDetailsService.WriteAsync aufgerufen wird, und die Antwort nicht erneut geschrieben wird.
Der folgende ValuesController gibt BadRequestResult zurück, das in den Antwortdatenstrom geschrieben wird. Damit wird verhindert, dass die benutzerdefinierte Problemantwort zurückgegeben wird.
[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));
}
}
Der folgende Values3Controller gibt ControllerBase.Problem zurück, sodass das erwartete Ergebnis für das benutzerdefinierte Problem zurückgegeben wird:
[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));
}
}
ProblemDetails-Nutzlast für Ausnahmen erzeugen
Betrachten Sie die folgende App:
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();
Wenn in Nicht-Entwicklungsumgebungen eine Ausnahme auftritt, wird die folgende standardmäßige ProblemDetails-Antwort an den Client zurückgegeben:
{
"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"
}
Für die meisten Apps ist für Ausnahmen nur der oben stehende Code erforderlich. Der folgende Abschnitt zeigt jedoch, wie Sie detailliertere Problemantworten erhalten.
Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite besteht darin, an UseExceptionHandler eine Lambda-Funktion zu übergeben. Die Verwendung einer Lambdafunktion ermöglicht den Zugriff auf den Fehler und das Schreiben einer Problemdetailantwort mit IProblemDetailsService.WriteAsync:
using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = Text.Plain;
var title = "Bad Input";
var detail = "Invalid input";
var type = "https://errors.example.com/badInput";
if (context.RequestServices.GetService<IProblemDetailsService>() is
{ } problemDetailsService)
{
var exceptionHandlerFeature =
context.Features.Get<IExceptionHandlerFeature>();
var exceptionType = exceptionHandlerFeature?.Error;
if (exceptionType != null &&
exceptionType.Message.Contains("infinity"))
{
title = "Argument exception";
detail = "Invalid input";
type = "https://errors.example.com/argumentException";
}
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = title,
Detail = detail,
Type = type
}
});
}
});
});
}
app.MapControllers();
app.Run();
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
Ein alternativer Ansatz zum Generieren von Problemdetails besteht darin, das Drittanbieter-NuGet-Paket Hellang.Middleware.ProblemDetails zu verwenden, mit dem Ausnahmen und Clientfehler zu Problemdetails zugeordnet werden können.
Weitere Ressourcen
Von Tom Dykstra
Dieser Artikel beschreibt grundsätzliche Vorgehensweisen zur Behandlung von Fehlern in ASP.NET Core-Web-Apps. Siehe auch Behandeln von Fehlern in ASP.NET Core-APIs.
Seite mit Ausnahmen für Entwickler
Die Seite mit Ausnahmen für Entwickler enthält ausführliche Informationen zu unbehandelten Anforderungsausnahmen. ASP.NET Core-Apps aktivieren standardmäßig die Seite mit Ausnahmen für Entwickler, wenn die folgenden Aussagen beide zutreffen:
- Wird in der
DevelopmentUmgebung ausgeführt. - Die App wird mit den aktuellen Vorlagen erstellt, also mit WebApplication.CreateBuilder. Apps, die mit
WebHost.CreateDefaultBuildererstellt wurden, müssen die Entwicklerausnahmeseite aktivieren, indemapp.UseDeveloperExceptionPageinConfigureaufgerufen wird.
Die Entwicklerausnahmeseite wird früh in der Middleware-Pipeline ausgeführt, damit sie unbehandelte Ausnahmen abfangen kann, die in nachfolgender Middleware ausgelöst werden.
Detaillierte Ausnahmeinformationen sollten nicht öffentlich angezeigt werden, wenn die App in der Production Umgebung ausgeführt wird. Weitere Informationen zum Konfigurieren von Umgebungen finden Sie unter ASP.NET Core-Laufzeitumgebungen.
Die Seite mit Ausnahmen für Entwickler kann die folgenden Informationen zu der Ausnahme und der Anforderung enthalten:
- Stapelüberwachung
- Abfragezeichenfolgeparameter, sofern vorhanden
- Cookies, falls vorhanden
- Headers
Es ist nicht garantiert, dass auf der Seite mit Ausnahmen für Entwickler Informationen angezeigt werden. Verwenden Sie Logging für vollständige Fehlerinformationen.
Seite „Ausnahmehandler“
Um eine benutzerdefinierte Fehlerbehandlungsseite für die Production Umgebung zu konfigurieren, rufen Sie UseExceptionHandler auf. Diese Middleware für die Ausnahmebehandlung:
- Dient zum Abfangen und Protokollieren unbehandelter Ausnahmen.
- Führt die Anfrage unter Verwendung des angegebenen Pfads erneut in einer alternativen Pipeline aus. Die Anfrage wird nicht erneut ausgeführt, wenn bereits mit der Antwort begonnen wurde. Der von der Vorlage generierte Code führt die Anfrage über den
/Error-Pfad erneut aus.
Warning
Wenn die alternative Pipeline eine eigene Ausnahme wirft, löst die Middleware zur Ausnahmebehandlung die ursprüngliche Ausnahme erneut aus.
Da diese Middleware die Anforderungspipeline erneut ausführen kann, gilt Folgendes:
- Middlewares müssen die erneute Ausführung mit derselben Anforderung verarbeiten. Dies bedeutet normalerweise, dass ihr Zustand nach dem Aufrufen von
_nextbereinigt oder die Verarbeitung imHttpContextzwischengespeichert wird, um eine Wiederholung zu vermeiden. Bei der Verarbeitung des Anforderungstexts bedeutet dies, dass Ergebnisse wie beispielsweise der Formularleser entweder gepuffert oder zwischengespeichert werden. - Für die UseExceptionHandler(IApplicationBuilder, String)-Überladung, die in Vorlagen verwendet wird, wird nur der Anforderungspfad geändert, und die Routendaten werden gelöscht. Anforderungsdaten wie Header, Methoden und Elemente werden unverändert wiederverwendet.
- Bereichsbezogene Dienste bleiben gleich.
Im folgenden Beispiel wird die Ausnahmebehandlungs-Middleware in Umgebungen, die nicht UseExceptionHandler sind, mithilfe von Development hinzugefügt:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Die App-Vorlage für Razor Pages stellt im Ordner .cshtml eine Fehlerseite (PageModel) und die Klasse ErrorModel () bereit. Die Projektvorlage für eine MVC-App enthält eine Error-Aktionsmethode und eine Fehleransicht für den Home-Controller.
Die Middleware für die Ausnahmebehandlung führt die Anforderung unter Verwendung der ursprünglichen HTTP-Methode erneut aus. Wenn ein Endpunkt für die Fehlerbehandlung auf bestimmte HTTP-Methoden beschränkt ist, wird er nur für diese Methoden ausgeführt. Beispielsweise wird eine MVC-Controller-Aktion, die das Attribut [HttpGet] verwendet, nur für GET-Anforderungen ausgeführt. Um sicherzustellen, dass alle Anfragen die benutzerdefinierte Fehlerbehandlungsseite erreichen, beschränken Sie sie nicht auf bestimmte HTTP-Methoden.
So behandeln Sie Ausnahmen unterschiedlich, basierend auf der ursprünglichen HTTP-Methode:
- Für Razor Pages erstellen Sie mehrere Handlermethoden. Verwenden Sie beispielsweise
OnGetzum Behandeln von GET-Ausnahmen undOnPostzum Behandeln von POST-Ausnahmen. - Für MVC wenden Sie HTTP-Verbattribute auf mehrere Aktionen an. Verwenden Sie beispielsweise
[HttpGet]zum Behandeln von GET-Ausnahmen und[HttpPost]zum Behandeln von POST-Ausnahmen.
Um nicht authentifizierten Benutzern die Anzeige der benutzerdefinierten Seite für die Fehlerbehandlung zu ermöglichen, stellen Sie sicher, dass der anonyme Zugriff unterstützt wird.
Zugreifen auf die Ausnahme
Verwenden Sie IExceptionHandlerPathFeature, um auf die Ausnahme oder den Pfad der ursprünglichen Anforderung in einem Fehlerhandler zuzugreifen. Im folgenden Beispiel wird IExceptionHandlerPathFeature verwendet, um weitere Informationen zur ausgelösten Ausnahme zu erhalten:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string? ExceptionMessage { get; set; }
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "The file was not found.";
}
if (exceptionHandlerPathFeature?.Path == "/")
{
ExceptionMessage ??= string.Empty;
ExceptionMessage += " Page: Home.";
}
}
}
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
Lambda-Ausdruck für Ausnahmehandler
Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite besteht darin, an UseExceptionHandler eine Lambda-Funktion zu übergeben. Durch die Verwendung eines Lambda kann auf den Fehler zugegriffen werden, bevor die Antwort zurückgegeben wird.
Der folgende Code verwendet eine Lambda-Funktion zur Ausnahmebehandlung:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
// using static System.Net.Mime.MediaTypeNames;
context.Response.ContentType = Text.Plain;
await context.Response.WriteAsync("An exception was thrown.");
var exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
await context.Response.WriteAsync(" The file was not found.");
}
if (exceptionHandlerPathFeature?.Path == "/")
{
await context.Response.WriteAsync(" Page: Home.");
}
});
});
app.UseHsts();
}
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
IExceptionHandler
IExceptionHandler ist eine Schnittstelle, über die der Entwickler einen Rückruf für die Behandlung bekannter Ausnahmen erhält.
IExceptionHandler-Implementierungen werden durch das Aufrufen von IServiceCollection.AddExceptionHandler<T> registriert [IServiceCollection.AddExceptionHandler<T>]. Die Lebensdauer einer IExceptionHandler-Instanz ist Singleton. Es können mehrere Implementierungen hinzugefügt werden, die in der registrierten Reihenfolge aufgerufen werden.
Wenn ein Ausnahmehandler eine Anforderung verarbeitet, kann er true zurückgeben, um die Verarbeitung zu beenden. Wenn eine Ausnahme von keinem Ausnahmehandler behandelt wird, greift das Steuerelement auf das Standardverhalten und die Standardoptionen der Middleware zurück. Für behandelte und nicht behandelte Ausnahmen werden unterschiedliche Metriken und Protokolle ausgegeben.
Das folgende Beispiel zeigt eine Implementierung von 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);
}
}
}
Das folgende Beispiel zeigt, wie Sie eine IExceptionHandler-Implementierung für die Abhängigkeitsinjektion registrieren:
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
Wenn der vorangehende Code in der Development Umgebung ausgeführt wird:
- Zuerst wird
CustomExceptionHandleraufgerufen, um eine Ausnahme zu behandeln. - Nach der Protokollierung der Ausnahme gibt die
TryHandleException-Methodefalsezurück, sodass die Entwickler-Ausnahmeseite angezeigt wird.
In anderen Umgebungen:
- Zuerst wird
CustomExceptionHandleraufgerufen, um eine Ausnahme zu behandeln. - Nach der Protokollierung der Ausnahme gibt die
TryHandleException-Methodefalsezurück, sodass die/Error-Seite angezeigt wird.
UseStatusCodePages
Die ASP.NET Core-App stellt für HTTP-Fehlerstatuscodes wie 404 – Nicht gefunden standardmäßig keine Statuscodeseite zur Verfügung. Wenn die App einen HTTP-Fehlerstatuscode im Bereich 400 bis 599 ohne Text festlegt, werden der Statuscode und ein leerer Antworttext zurückgegeben. Um standardmäßige reine Texthandler für gängige Fehlerstatuscodes zu aktivieren, rufen Sie UseStatusCodePages in Program.cs auf:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Rufen Sie UseStatusCodePages vor der Middleware zur Anforderungsverarbeitung auf. Rufen Sie z. B. UseStatusCodePages vor der Middleware für statische Dateien und der Middleware für Endpunkte auf.
Wenn UseStatusCodePages nicht verwendet wird, wird beim Navigieren zu einer URL ohne Endpunkt eine browserabhängige Fehlermeldung zurückgegeben, die angibt, dass der Endpunkt nicht gefunden werden kann. Wenn UseStatusCodePages aufgerufen wird, gibt der Browser die folgende Antwort zurück:
Status Code: 404; Not Found
UseStatusCodePages wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
Note
Die Middleware für Statuscodeseiten erfasst keine Ausnahmen. Um eine benutzerdefinierte Fehlerbehandlungsseite bereitzustellen, verwenden Sie die Ausnahmehandlerseite.
UseStatusCodePages mit Formatzeichenfolge
Um den Inhaltstyp und den Text der Antwort anzupassen, verwenden Sie die Überladung von UseStatusCodePages, die einen Inhaltstyp und eine Formatzeichenfolge akzeptiert:
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}");
Im vorangehenden Code ist {0} ein Platzhalter für den Fehlercode.
UseStatusCodePages mit Formatzeichenfolge wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
UseStatusCodePages mit Lambda-Ausdruck
Um benutzerdefinierten Code für die Fehlerbehandlung und das Schreiben von Antworten anzugeben, verwenden Sie die Überladung von UseStatusCodePages, die einen Lambda-Ausdruck verwendet:
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 mit Lambdaausdruck wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
UseStatusCodePagesWithRedirects
Die Erweiterungsmethode UseStatusCodePagesWithRedirects:
- Sendet den Statuscode 302 Found (Gefunden) an den Client.
- Leitet den Client an den in der URL-Vorlage angegebenen Fehlerbehandlungsendpunkt weiter. Der Fehlerbehandlungsendpunkt zeigt in der Regel Fehlerinformationen an und gibt den HTTP-Code 200 zurück.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Die URL-Vorlage kann, wie im vorangehenden Code gezeigt, einen {0}-Platzhalter für den Statuscode enthalten. Wenn die URL-Vorlage mit ~ (Tilde) beginnt, wird ~ durch das PathBase der App ersetzt. Wenn Sie in der App einen Endpunkt angeben, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Den Client an einen anderen Endpunkt umleiten, in der Regel in Fällen, in denen eine andere App den Fehler verarbeitet. Für Web-Apps gibt die Adressleiste des Browsers des Clients den umgeleiteten Endpunkt wieder.
- Der ursprüngliche Statuscode sollte nicht beibehalten und mit der ursprünglichen Weiterleitungsantwort zurückgegeben werden.
UseStatusCodePagesWithReExecute
Die Erweiterungsmethode UseStatusCodePagesWithReExecute:
- Generiert den Antworttext durch die erneute Ausführung der Anforderungspipeline mithilfe eines alternativen Pfads.
- Ändert den Statuscode nicht vor oder nach der erneuten Ausführung der Pipeline.
Die neue Pipelineausführung kann den Statuscode der Antwort ändern, da sie die vollständige Kontrolle über den Statuscode hat. Wenn die neue Pipeline den Statuscode nicht ändert, wird der ursprüngliche Statuscode an den Client gesendet.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Wenn in der App ein Endpunkt angegeben ist, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Die Anforderung ohne Umleitung an einen anderen Endpunkt verarbeiten. Für Web-Apps gibt die Adressleiste des Browsers des Clients den ursprünglich angeforderten Endpunkt wieder.
- Den ursprünglichen Statuscode mit der Antwort beibehalten und zurückgeben.
Die URL-Vorlage muss mit / beginnen und kann den Platzhalter {0} für den Statuscode enthalten. Um den Statuscode als Abfragezeichenfolgenparameter zu übergeben, übergeben Sie ein zweites Argument an UseStatusCodePagesWithReExecute. Beispiel:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
Der Endpunkt, der den Fehler verarbeitet, kann die ursprüngliche URL abrufen, die den Fehler generiert hat (siehe folgendes Beispiel):
[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}";
}
}
}
Da diese Middleware die Anforderungspipeline erneut ausführen kann, gilt Folgendes:
- Middlewares müssen die erneute Ausführung mit derselben Anforderung verarbeiten. Dies bedeutet normalerweise, dass ihr Zustand nach dem Aufrufen von
_nextbereinigt oder die Verarbeitung imHttpContextzwischengespeichert wird, um eine Wiederholung zu vermeiden. Bei der Verarbeitung des Anforderungstexts bedeutet dies, dass Ergebnisse wie beispielsweise der Formularleser entweder gepuffert oder zwischengespeichert werden. - Bereichsbezogene Dienste bleiben gleich.
Deaktivieren von Statuscodeseiten
Verwenden Sie das Attribut [SkipStatusCodePages], um Statuscodeseiten für einen MVC-Controller oder eine MVC-Aktionsmethode zu deaktivieren.
Um Statuscode-Seiten für bestimmte Anfragen in einer Razor-Pages-Handlermethode oder in einem MVC-Controller zu deaktivieren, verwenden Sie IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Ausnahmebehandlungscode
Code auf Seiten zur Ausnahmebehandlung kann ebenfalls Ausnahmen auslösen. Fehlerseiten in der Produktionsumgebung sollten gründlich getestet werden, und es sollte besonders darauf geachtet werden, dass sie nicht selbst Ausnahmen auslösen.
Antwortkopfzeilen
Nachdem die Header für eine Antwort gesendet wurden:
- Die App kann den Statuscode der Antwort nicht ändern.
- Weder Ausnahmeseiten noch Handler können ausgeführt werden. Die Antwort muss abgeschlossen oder die Verbindung unterbrochen werden.
Server-Ausnahmebehandlung
Neben der Ausnahmebehandlungslogik in einer App kann die HTTP-Serverimplementierung einige Ausnahmebehandlungen durchführen. Wenn der Server eine Ausnahme erfasst, bevor die Antwortheader gesendet werden, sendet der Server die Antwort 500 - Internal Server Error ohne Antworttext. Wenn der Server eine Ausnahme auffängt, nachdem die Antwortheader gesendet wurden, schließt der Server die Verbindung. Anforderungen, die nicht von der App verarbeitet werden, werden vom Server verarbeitet. Jede Ausnahme, die bei der Anforderungsverarbeitung durch den Server auftritt, wird durch die Ausnahmebehandlung des Servers verarbeitet. Die benutzerdefinierten Fehlerseiten der App, Middleware zur Fehlerbehandlung und Filter haben keine Auswirkungen auf dieses Verhalten.
Fehlerbehandlung beim Start
Nur auf Hostebene können Ausnahmen behandelt werden, die während des Starts einer App auftreten. Der Host kann für das Erfassen von Startfehlern und Erfassen detaillierter Fehler konfiguriert werden.
Die Hostingebene kann nur dann für einen beim Start erfassten Fehler eine Fehlerseite anzeigen, wenn der Fehler nach der Bindung an die Hostadresse bzw. den Port auftritt. Wenn die Bindung misslingt:
- Die Hostebene protokolliert eine kritische Ausnahme.
- Der DotNet-Prozess stürzt ab.
- Wenn der HTTP-Server Kestrel heißt, wird keine Fehlerseite angezeigt.
Wenn sie auf IIS (oder Azure App Service) oder IIS Express ausgeführt wird, wird ein Prozessfehler 502.5 vom ASP.NET Core-Modul zurückgegeben, wenn der Prozess nicht starten kann. Weitere Informationen finden Sie unter Problembehandlung bei ASP.NET Core in Azure App Service und IIS.
Datenbank-Fehlerseite
Der Ausnahmefilter der Datenbankentwicklerseite AddDatabaseDeveloperPageExceptionFilter erfasst datenbankbezogene Ausnahmen, die mithilfe von Entity Framework Core-Migrationen aufgelöst werden können. Wenn diese Ausnahmen auftreten, wird eine HTML-Antwort mit Details zu möglichen Aktionen zum Beheben des Problems generiert. Diese Seite ist nur in der Development Umgebung aktiviert. Der folgende Code fügt den Ausnahmefilter der Datenbankentwicklerseite hinzu:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Ausnahmefilter
In MVC-Apps können Ausnahmefilter global oder auf einen Controller oder eine Aktion bezogen konfiguriert werden. In Razor Pages-Apps können sie global oder auf ein Seitenmodell bezogen konfiguriert werden. Diese Filter verarbeiten jede nicht behandelte Ausnahme, die während der Ausführung eine Controlleraktion oder eines anderen Filters auftritt. Weitere Informationen finden Sie unter Filter in ASP.NET Core.
Ausnahmefilter eignen sich zum Auffangen von Ausnahmen, die in MVC-Aktionen ausgelöst werden. Sie sind jedoch nicht so flexibel wie die integrierte Middleware für die Ausnahmebehandlung, UseExceptionHandler. Wir empfehlen die Verwendung von UseExceptionHandler, es sei denn, Sie müssen die Fehlerbehandlung je nachdem, welche MVC-Aktion ausgewählt wird, anders handhaben.
Modellstatusfehler
Informationen zum Behandeln von Modellstatusfehlern finden Sie unter Modellbindung und Modellvalidierung.
Problemdetails
Problemdetails sind nicht das einzige Antwortformat zur Beschreibung eines HTTP-API-Fehlers, sie werden jedoch häufig verwendet, um Fehler für HTTP-APIs zu melden.
Der Dienst für Problemdetails implementiert die IProblemDetailsService-Schnittstelle, die das Erstellen von Problemdetails in ASP.NET Core unterstützt. Die Erweiterungsmethode AddProblemDetails(IServiceCollection) in IServiceCollection registriert die Standardimplementierung von IProblemDetailsService.
In ASP.NET Core-Apps generiert die folgende Middleware HTTP-Antworten mit Problemdetails, wenn AddProblemDetails aufgerufen wird. Dies gilt jedoch nicht, wenn der Accept-HTTP-Anforderungsheader keinen der vom registrierten IProblemDetailsWriter unterstützten Inhaltstyp enthält (Standard: application/json):
- ExceptionHandlerMiddleware generiert eine Problemdetailantwort, wenn kein benutzerdefinierter Handler definiert ist.
- StatusCodePagesMiddleware generiert standardmäßig eine Problemdetailantwort.
-
DeveloperExceptionPageMiddleware generiert eine Problemdetailantwort während der Entwicklungsphase, wenn der
Accept-HTTP-Anforderungsheader nichttext/htmlenthält.
Mit dem folgenden Code wird die App so konfiguriert, dass eine Problemdetailantwort für alle Fehlerantworten von HTTP-Client und -Server generiert wird, die noch keinen Textinhalt aufweisen:
builder.Services.AddProblemDetails();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
Im nächsten Abschnitt wird gezeigt, wie Sie den Antworttext mit den Problemdetails anpassen.
Anpassen von Problemdetails
Die automatische Erstellung von ProblemDetails kann mit einer der folgenden Optionen angepasst werden:
- Verwenden Sie
ProblemDetailsOptions.CustomizeProblemDetails - Verwenden Sie eine benutzerdefinierte
IProblemDetailsWriter - Rufen Sie
IProblemDetailsServicein einer Middleware auf
CustomizeProblemDetails Vorgang
Die generierten Problemdetails können mithilfe von CustomizeProblemDetails angepasst werden, und die Anpassungen werden auf alle automatisch generierten Problemdetails angewandt.
Der folgende Code verwendet ProblemDetailsOptions, um CustomizeProblemDetails festzulegen:
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();
Beispielsweise erzeugt ein HTTP Status 400 Bad Request-Endpunkt den folgenden Antworttext mit Problemdetails:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
Benutzerdefiniert IProblemDetailsWriter
Eine IProblemDetailsWriter-Implementierung kann für erweiterte Anpassungen erstellt werden.
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));
}
}
Hinweis: Wenn Sie einen benutzerdefinierten IProblemDetailsWriter verwenden, muss der benutzerdefinierte IProblemDetailsWriter vor dem Aufrufen von AddRazorPages, AddControllers, AddControllersWithViews oder AddMvc registriert werden:
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();
Problemdetails von Middleware
Ein alternativer Ansatz zur Verwendung von ProblemDetailsOptions mit CustomizeProblemDetails ist das Festlegen von ProblemDetails in Middleware. Eine Problemdetailantwort kann durch Aufrufen von IProblemDetailsService.WriteAsync geschrieben werden:
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();
Im vorherigen Code geben die minimalen API-Endpunkte /divide und /squareroot die erwartete benutzerdefinierte Problemantwort bei fehlerhaften Eingaben zurück.
Die API-Controllerendpunkte geben bei einer Fehlereingabe die Standardproblemantwort zurück, nicht die benutzerdefinierte Problemantwort. Die Standardproblemantwort wird zurückgegeben, da der API-Controller in den Antwortdatenstrom (Problemdetails für Fehlerstatuscodes) geschrieben hat, bevor IProblemDetailsService.WriteAsync aufgerufen wird, und die Antwort nicht erneut geschrieben wird.
Der folgende ValuesController gibt BadRequestResult zurück, das in den Antwortdatenstrom geschrieben wird. Damit wird verhindert, dass die benutzerdefinierte Problemantwort zurückgegeben wird.
[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));
}
}
Der folgende Values3Controller gibt ControllerBase.Problem zurück, sodass das erwartete Ergebnis für das benutzerdefinierte Problem zurückgegeben wird:
[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));
}
}
ProblemDetails-Nutzlast für Ausnahmen erzeugen
Betrachten Sie die folgende App:
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();
Wenn in Nicht-Entwicklungsumgebungen eine Ausnahme auftritt, wird die folgende standardmäßige ProblemDetails-Antwort an den Client zurückgegeben:
{
"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"
}
Für die meisten Apps ist für Ausnahmen nur der oben stehende Code erforderlich. Der folgende Abschnitt zeigt jedoch, wie Sie detailliertere Problemantworten erhalten.
Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite besteht darin, an UseExceptionHandler eine Lambda-Funktion zu übergeben. Die Verwendung einer Lambdafunktion ermöglicht den Zugriff auf den Fehler und das Schreiben einer Problemdetailantwort mit IProblemDetailsService.WriteAsync:
using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = Text.Plain;
var title = "Bad Input";
var detail = "Invalid input";
var type = "https://errors.example.com/badInput";
if (context.RequestServices.GetService<IProblemDetailsService>() is
{ } problemDetailsService)
{
var exceptionHandlerFeature =
context.Features.Get<IExceptionHandlerFeature>();
var exceptionType = exceptionHandlerFeature?.Error;
if (exceptionType != null &&
exceptionType.Message.Contains("infinity"))
{
title = "Argument exception";
detail = "Invalid input";
type = "https://errors.example.com/argumentException";
}
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = title,
Detail = detail,
Type = type
}
});
}
});
});
}
app.MapControllers();
app.Run();
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
Ein alternativer Ansatz zum Generieren von Problemdetails besteht darin, das Drittanbieter-NuGet-Paket Hellang.Middleware.ProblemDetails zu verwenden, mit dem Ausnahmen und Clientfehler zu Problemdetails zugeordnet werden können.
Weitere Ressourcen
- Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)
- Problembehandlung bei ASP.NET Core in Azure App Service und IIS
- Problembehandlung für häufige Fehler bei Azure App Service und IIS mit ASP.NET Core
- Behandeln von Fehlern in ASP.NET Core-APIs
- Behandeln von Fehlern in ASP.NET Core-APIs.
Von Tom Dykstra
Dieser Artikel beschreibt grundsätzliche Vorgehensweisen zur Behandlung von Fehlern in ASP.NET Core-Web-Apps. Siehe auch Behandeln von Fehlern in ASP.NET Core-APIs.
Seite mit Ausnahmen für Entwickler
Die Seite mit Ausnahmen für Entwickler enthält ausführliche Informationen zu unbehandelten Anforderungsausnahmen. ASP.NET Core-Apps aktivieren standardmäßig die Seite mit Ausnahmen für Entwickler, wenn die folgenden Aussagen beide zutreffen:
- Wird in der
DevelopmentUmgebung ausgeführt. - Die App wird mit den aktuellen Vorlagen erstellt, also mit WebApplication.CreateBuilder. Apps, die mit
WebHost.CreateDefaultBuildererstellt wurden, müssen die Entwicklerausnahmeseite aktivieren, indemapp.UseDeveloperExceptionPageinConfigureaufgerufen wird.
Die Entwicklerausnahmeseite wird früh in der Middleware-Pipeline ausgeführt, damit sie unbehandelte Ausnahmen abfangen kann, die in nachfolgender Middleware ausgelöst werden.
Detaillierte Ausnahmeinformationen sollten nicht öffentlich angezeigt werden, wenn die App in der Production Umgebung ausgeführt wird. Weitere Informationen zum Konfigurieren von Umgebungen finden Sie unter ASP.NET Core-Laufzeitumgebungen.
Die Seite mit Ausnahmen für Entwickler kann die folgenden Informationen zu der Ausnahme und der Anforderung enthalten:
- Stapelüberwachung
- Abfragezeichenfolgeparameter, sofern vorhanden
- Cookies, falls vorhanden
- Headers
Es ist nicht garantiert, dass auf der Seite mit Ausnahmen für Entwickler Informationen angezeigt werden. Verwenden Sie Logging für vollständige Fehlerinformationen.
Seite „Ausnahmehandler“
Um eine benutzerdefinierte Fehlerbehandlungsseite für die Production Umgebung zu konfigurieren, rufen Sie UseExceptionHandler auf. Diese Middleware für die Ausnahmebehandlung:
- Dient zum Abfangen und Protokollieren unbehandelter Ausnahmen.
- Führt die Anfrage unter Verwendung des angegebenen Pfads erneut in einer alternativen Pipeline aus. Die Anfrage wird nicht erneut ausgeführt, wenn bereits mit der Antwort begonnen wurde. Der von der Vorlage generierte Code führt die Anfrage über den
/Error-Pfad erneut aus.
Warning
Wenn die alternative Pipeline eine eigene Ausnahme wirft, löst die Middleware zur Ausnahmebehandlung die ursprüngliche Ausnahme erneut aus.
Da diese Middleware die Anforderungspipeline erneut ausführen kann, gilt Folgendes:
- Middlewares müssen die erneute Ausführung mit derselben Anforderung verarbeiten. Dies bedeutet normalerweise, dass ihr Zustand nach dem Aufrufen von
_nextbereinigt oder die Verarbeitung imHttpContextzwischengespeichert wird, um eine Wiederholung zu vermeiden. Bei der Verarbeitung des Anforderungstexts bedeutet dies, dass Ergebnisse wie beispielsweise der Formularleser entweder gepuffert oder zwischengespeichert werden. - Für die UseExceptionHandler(IApplicationBuilder, String)-Überladung, die in Vorlagen verwendet wird, wird nur der Anforderungspfad geändert, und die Routendaten werden gelöscht. Anforderungsdaten wie Header, Methoden und Elemente werden unverändert wiederverwendet.
- Bereichsbezogene Dienste bleiben gleich.
Im folgenden Beispiel wird die Ausnahmebehandlungs-Middleware in Umgebungen, die nicht UseExceptionHandler sind, mithilfe von Development hinzugefügt:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Die App-Vorlage für Razor Pages stellt im Ordner .cshtml eine Fehlerseite (PageModel) und die Klasse ErrorModel () bereit. Die Projektvorlage für eine MVC-App enthält eine Error-Aktionsmethode und eine Fehleransicht für den Home-Controller.
Die Middleware für die Ausnahmebehandlung führt die Anforderung unter Verwendung der ursprünglichen HTTP-Methode erneut aus. Wenn ein Endpunkt für die Fehlerbehandlung auf bestimmte HTTP-Methoden beschränkt ist, wird er nur für diese Methoden ausgeführt. Beispielsweise wird eine MVC-Controller-Aktion, die das Attribut [HttpGet] verwendet, nur für GET-Anforderungen ausgeführt. Um sicherzustellen, dass alle Anfragen die benutzerdefinierte Fehlerbehandlungsseite erreichen, beschränken Sie sie nicht auf bestimmte HTTP-Methoden.
So behandeln Sie Ausnahmen unterschiedlich, basierend auf der ursprünglichen HTTP-Methode:
- Für Razor Pages erstellen Sie mehrere Handlermethoden. Verwenden Sie beispielsweise
OnGetzum Behandeln von GET-Ausnahmen undOnPostzum Behandeln von POST-Ausnahmen. - Für MVC wenden Sie HTTP-Verbattribute auf mehrere Aktionen an. Verwenden Sie beispielsweise
[HttpGet]zum Behandeln von GET-Ausnahmen und[HttpPost]zum Behandeln von POST-Ausnahmen.
Um nicht authentifizierten Benutzern die Anzeige der benutzerdefinierten Seite für die Fehlerbehandlung zu ermöglichen, stellen Sie sicher, dass der anonyme Zugriff unterstützt wird.
Zugreifen auf die Ausnahme
Verwenden Sie IExceptionHandlerPathFeature, um auf die Ausnahme oder den Pfad der ursprünglichen Anforderung in einem Fehlerhandler zuzugreifen. Im folgenden Beispiel wird IExceptionHandlerPathFeature verwendet, um weitere Informationen zur ausgelösten Ausnahme zu erhalten:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string? ExceptionMessage { get; set; }
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "The file was not found.";
}
if (exceptionHandlerPathFeature?.Path == "/")
{
ExceptionMessage ??= string.Empty;
ExceptionMessage += " Page: Home.";
}
}
}
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
Lambda-Ausdruck für Ausnahmehandler
Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite besteht darin, an UseExceptionHandler eine Lambda-Funktion zu übergeben. Durch die Verwendung eines Lambda kann auf den Fehler zugegriffen werden, bevor die Antwort zurückgegeben wird.
Der folgende Code verwendet eine Lambda-Funktion zur Ausnahmebehandlung:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
// using static System.Net.Mime.MediaTypeNames;
context.Response.ContentType = Text.Plain;
await context.Response.WriteAsync("An exception was thrown.");
var exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
await context.Response.WriteAsync(" The file was not found.");
}
if (exceptionHandlerPathFeature?.Path == "/")
{
await context.Response.WriteAsync(" Page: Home.");
}
});
});
app.UseHsts();
}
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
UseStatusCodePages
Die ASP.NET Core-App stellt für HTTP-Fehlerstatuscodes wie 404 – Nicht gefunden standardmäßig keine Statuscodeseite zur Verfügung. Wenn die App einen HTTP-Fehlerstatuscode im Bereich 400 bis 599 ohne Text festlegt, werden der Statuscode und ein leerer Antworttext zurückgegeben. Um standardmäßige reine Texthandler für gängige Fehlerstatuscodes zu aktivieren, rufen Sie UseStatusCodePages in Program.cs auf:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Rufen Sie UseStatusCodePages vor der Middleware zur Anforderungsverarbeitung auf. Rufen Sie z. B. UseStatusCodePages vor der Middleware für statische Dateien und der Middleware für Endpunkte auf.
Wenn UseStatusCodePages nicht verwendet wird, wird beim Navigieren zu einer URL ohne Endpunkt eine browserabhängige Fehlermeldung zurückgegeben, die angibt, dass der Endpunkt nicht gefunden werden kann. Wenn UseStatusCodePages aufgerufen wird, gibt der Browser die folgende Antwort zurück:
Status Code: 404; Not Found
UseStatusCodePages wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
Note
Die Middleware für Statuscodeseiten erfasst keine Ausnahmen. Um eine benutzerdefinierte Fehlerbehandlungsseite bereitzustellen, verwenden Sie die Ausnahmehandlerseite.
UseStatusCodePages mit Formatzeichenfolge
Um den Inhaltstyp und den Text der Antwort anzupassen, verwenden Sie die Überladung von UseStatusCodePages, die einen Inhaltstyp und eine Formatzeichenfolge akzeptiert:
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}");
Im vorangehenden Code ist {0} ein Platzhalter für den Fehlercode.
UseStatusCodePages mit Formatzeichenfolge wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
UseStatusCodePages mit Lambda-Ausdruck
Um benutzerdefinierten Code für die Fehlerbehandlung und das Schreiben von Antworten anzugeben, verwenden Sie die Überladung von UseStatusCodePages, die einen Lambda-Ausdruck verwendet:
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 mit Lambdaausdruck wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
UseStatusCodePagesWithRedirects
Die Erweiterungsmethode UseStatusCodePagesWithRedirects:
- Sendet den Statuscode 302 Found (Gefunden) an den Client.
- Leitet den Client an den in der URL-Vorlage angegebenen Fehlerbehandlungsendpunkt weiter. Der Fehlerbehandlungsendpunkt zeigt in der Regel Fehlerinformationen an und gibt den HTTP-Code 200 zurück.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Die URL-Vorlage kann, wie im vorangehenden Code gezeigt, einen {0}-Platzhalter für den Statuscode enthalten. Wenn die URL-Vorlage mit ~ (Tilde) beginnt, wird ~ durch das PathBase der App ersetzt. Wenn Sie in der App einen Endpunkt angeben, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Den Client an einen anderen Endpunkt umleiten, in der Regel in Fällen, in denen eine andere App den Fehler verarbeitet. Für Web-Apps gibt die Adressleiste des Browsers des Clients den umgeleiteten Endpunkt wieder.
- Der ursprüngliche Statuscode sollte nicht beibehalten und mit der ursprünglichen Weiterleitungsantwort zurückgegeben werden.
UseStatusCodePagesWithReExecute
Die Erweiterungsmethode UseStatusCodePagesWithReExecute:
- Generiert den Antworttext durch die erneute Ausführung der Anforderungspipeline mithilfe eines alternativen Pfads.
- Ändert den Statuscode nicht vor oder nach der erneuten Ausführung der Pipeline.
Die neue Pipelineausführung kann den Statuscode der Antwort ändern, da sie die vollständige Kontrolle über den Statuscode hat. Wenn die neue Pipeline den Statuscode nicht ändert, wird der ursprüngliche Statuscode an den Client gesendet.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Wenn in der App ein Endpunkt angegeben ist, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Die Anforderung ohne Umleitung an einen anderen Endpunkt verarbeiten. Für Web-Apps gibt die Adressleiste des Browsers des Clients den ursprünglich angeforderten Endpunkt wieder.
- Den ursprünglichen Statuscode mit der Antwort beibehalten und zurückgeben.
Die URL-Vorlage muss mit / beginnen und kann den Platzhalter {0} für den Statuscode enthalten. Um den Statuscode als Abfragezeichenfolgenparameter zu übergeben, übergeben Sie ein zweites Argument an UseStatusCodePagesWithReExecute. Beispiel:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
Der Endpunkt, der den Fehler verarbeitet, kann die ursprüngliche URL abrufen, die den Fehler generiert hat (siehe folgendes Beispiel):
[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}";
}
}
}
Da diese Middleware die Anforderungspipeline erneut ausführen kann, gilt Folgendes:
- Middlewares müssen die erneute Ausführung mit derselben Anforderung verarbeiten. Dies bedeutet normalerweise, dass ihr Zustand nach dem Aufrufen von
_nextbereinigt oder die Verarbeitung imHttpContextzwischengespeichert wird, um eine Wiederholung zu vermeiden. Bei der Verarbeitung des Anforderungstexts bedeutet dies, dass Ergebnisse wie beispielsweise der Formularleser entweder gepuffert oder zwischengespeichert werden. - Bereichsbezogene Dienste bleiben gleich.
Deaktivieren von Statuscodeseiten
Verwenden Sie das Attribut [SkipStatusCodePages], um Statuscodeseiten für einen MVC-Controller oder eine MVC-Aktionsmethode zu deaktivieren.
Um Statuscode-Seiten für bestimmte Anfragen in einer Razor-Pages-Handlermethode oder in einem MVC-Controller zu deaktivieren, verwenden Sie IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Ausnahmebehandlungscode
Code auf Seiten zur Ausnahmebehandlung kann ebenfalls Ausnahmen auslösen. Fehlerseiten in der Produktionsumgebung sollten gründlich getestet werden, und es sollte besonders darauf geachtet werden, dass sie nicht selbst Ausnahmen auslösen.
Antwortkopfzeilen
Nachdem die Header für eine Antwort gesendet wurden:
- Die App kann den Statuscode der Antwort nicht ändern.
- Weder Ausnahmeseiten noch Handler können ausgeführt werden. Die Antwort muss abgeschlossen oder die Verbindung unterbrochen werden.
Server-Ausnahmebehandlung
Neben der Ausnahmebehandlungslogik in einer App kann die HTTP-Serverimplementierung einige Ausnahmebehandlungen durchführen. Wenn der Server eine Ausnahme erfasst, bevor die Antwortheader gesendet werden, sendet der Server die Antwort 500 - Internal Server Error ohne Antworttext. Wenn der Server eine Ausnahme auffängt, nachdem die Antwortheader gesendet wurden, schließt der Server die Verbindung. Anforderungen, die nicht von der App verarbeitet werden, werden vom Server verarbeitet. Jede Ausnahme, die bei der Anforderungsverarbeitung durch den Server auftritt, wird durch die Ausnahmebehandlung des Servers verarbeitet. Die benutzerdefinierten Fehlerseiten der App, Middleware zur Fehlerbehandlung und Filter haben keine Auswirkungen auf dieses Verhalten.
Fehlerbehandlung beim Start
Nur auf Hostebene können Ausnahmen behandelt werden, die während des Starts einer App auftreten. Der Host kann für das Erfassen von Startfehlern und Erfassen detaillierter Fehler konfiguriert werden.
Die Hostingebene kann nur dann für einen beim Start erfassten Fehler eine Fehlerseite anzeigen, wenn der Fehler nach der Bindung an die Hostadresse bzw. den Port auftritt. Wenn die Bindung misslingt:
- Die Hostebene protokolliert eine kritische Ausnahme.
- Der DotNet-Prozess stürzt ab.
- Wenn der HTTP-Server Kestrel heißt, wird keine Fehlerseite angezeigt.
Wenn sie auf IIS (oder Azure App Service) oder IIS Express ausgeführt wird, wird ein Prozessfehler 502.5 vom ASP.NET Core-Modul zurückgegeben, wenn der Prozess nicht starten kann. Weitere Informationen finden Sie unter Problembehandlung bei ASP.NET Core in Azure App Service und IIS.
Datenbank-Fehlerseite
Der Ausnahmefilter der Datenbankentwicklerseite AddDatabaseDeveloperPageExceptionFilter erfasst datenbankbezogene Ausnahmen, die mithilfe von Entity Framework Core-Migrationen aufgelöst werden können. Wenn diese Ausnahmen auftreten, wird eine HTML-Antwort mit Details zu möglichen Aktionen zum Beheben des Problems generiert. Diese Seite ist nur in der Development Umgebung aktiviert. Der folgende Code fügt den Ausnahmefilter der Datenbankentwicklerseite hinzu:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Ausnahmefilter
In MVC-Apps können Ausnahmefilter global oder auf einen Controller oder eine Aktion bezogen konfiguriert werden. In Razor Pages-Apps können sie global oder auf ein Seitenmodell bezogen konfiguriert werden. Diese Filter verarbeiten jede nicht behandelte Ausnahme, die während der Ausführung eine Controlleraktion oder eines anderen Filters auftritt. Weitere Informationen finden Sie unter Filter in ASP.NET Core.
Ausnahmefilter eignen sich zum Auffangen von Ausnahmen, die in MVC-Aktionen ausgelöst werden. Sie sind jedoch nicht so flexibel wie die integrierte Middleware für die Ausnahmebehandlung, UseExceptionHandler. Wir empfehlen die Verwendung von UseExceptionHandler, es sei denn, Sie müssen die Fehlerbehandlung je nachdem, welche MVC-Aktion ausgewählt wird, anders handhaben.
Modellstatusfehler
Informationen zum Behandeln von Modellstatusfehlern finden Sie unter Modellbindung und Modellvalidierung.
Problemdetails
Problemdetails sind nicht das einzige Antwortformat zur Beschreibung eines HTTP-API-Fehlers, sie werden jedoch häufig verwendet, um Fehler für HTTP-APIs zu melden.
Der Dienst für Problemdetails implementiert die IProblemDetailsService-Schnittstelle, die das Erstellen von Problemdetails in ASP.NET Core unterstützt. Die Erweiterungsmethode AddProblemDetails(IServiceCollection) in IServiceCollection registriert die Standardimplementierung von IProblemDetailsService.
In ASP.NET Core-Apps generiert die folgende Middleware HTTP-Antworten mit Problemdetails, wenn AddProblemDetails aufgerufen wird. Dies gilt jedoch nicht, wenn der Accept-HTTP-Anforderungsheader keinen der vom registrierten IProblemDetailsWriter unterstützten Inhaltstyp enthält (Standard: application/json):
- ExceptionHandlerMiddleware generiert eine Problemdetailantwort, wenn kein benutzerdefinierter Handler definiert ist.
- StatusCodePagesMiddleware generiert standardmäßig eine Problemdetailantwort.
-
DeveloperExceptionPageMiddleware generiert eine Problemdetailantwort während der Entwicklungsphase, wenn der
Accept-HTTP-Anforderungsheader nichttext/htmlenthält.
Mit dem folgenden Code wird die App so konfiguriert, dass eine Problemdetailantwort für alle Fehlerantworten von HTTP-Client und -Server generiert wird, die noch keinen Textinhalt aufweisen:
builder.Services.AddProblemDetails();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
Im nächsten Abschnitt wird gezeigt, wie Sie den Antworttext mit den Problemdetails anpassen.
Anpassen von Problemdetails
Die automatische Erstellung von ProblemDetails kann mit einer der folgenden Optionen angepasst werden:
- Verwenden Sie
ProblemDetailsOptions.CustomizeProblemDetails - Verwenden Sie eine benutzerdefinierte
IProblemDetailsWriter - Rufen Sie
IProblemDetailsServicein einer Middleware auf
CustomizeProblemDetails Vorgang
Die generierten Problemdetails können mithilfe von CustomizeProblemDetails angepasst werden, und die Anpassungen werden auf alle automatisch generierten Problemdetails angewandt.
Der folgende Code verwendet ProblemDetailsOptions, um CustomizeProblemDetails festzulegen:
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();
Beispielsweise erzeugt ein HTTP Status 400 Bad Request-Endpunkt den folgenden Antworttext mit Problemdetails:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
Benutzerdefiniert IProblemDetailsWriter
Eine IProblemDetailsWriter-Implementierung kann für erweiterte Anpassungen erstellt werden.
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));
}
}
Hinweis: Wenn Sie einen benutzerdefinierten IProblemDetailsWriter verwenden, muss der benutzerdefinierte IProblemDetailsWriter vor dem Aufrufen von AddRazorPages, AddControllers, AddControllersWithViews oder AddMvc registriert werden:
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();
Problemdetails von Middleware
Ein alternativer Ansatz zur Verwendung von ProblemDetailsOptions mit CustomizeProblemDetails ist das Festlegen von ProblemDetails in Middleware. Eine Problemdetailantwort kann durch Aufrufen von IProblemDetailsService.WriteAsync geschrieben werden:
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();
Im vorherigen Code geben die minimalen API-Endpunkte /divide und /squareroot die erwartete benutzerdefinierte Problemantwort bei fehlerhaften Eingaben zurück.
Die API-Controllerendpunkte geben bei einer Fehlereingabe die Standardproblemantwort zurück, nicht die benutzerdefinierte Problemantwort. Die Standardproblemantwort wird zurückgegeben, da der API-Controller in den Antwortdatenstrom (Problemdetails für Fehlerstatuscodes) geschrieben hat, bevor IProblemDetailsService.WriteAsync aufgerufen wird, und die Antwort nicht erneut geschrieben wird.
Der folgende ValuesController gibt BadRequestResult zurück, das in den Antwortdatenstrom geschrieben wird. Damit wird verhindert, dass die benutzerdefinierte Problemantwort zurückgegeben wird.
[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));
}
}
Der folgende Values3Controller gibt ControllerBase.Problem zurück, sodass das erwartete Ergebnis für das benutzerdefinierte Problem zurückgegeben wird:
[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));
}
}
ProblemDetails-Nutzlast für Ausnahmen erzeugen
Betrachten Sie die folgende App:
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();
Wenn in Nicht-Entwicklungsumgebungen eine Ausnahme auftritt, wird die folgende standardmäßige ProblemDetails-Antwort an den Client zurückgegeben:
{
"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"
}
Für die meisten Apps ist für Ausnahmen nur der oben stehende Code erforderlich. Der folgende Abschnitt zeigt jedoch, wie Sie detailliertere Problemantworten erhalten.
Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite besteht darin, an UseExceptionHandler eine Lambda-Funktion zu übergeben. Die Verwendung einer Lambdafunktion ermöglicht den Zugriff auf den Fehler und das Schreiben einer Problemdetailantwort mit IProblemDetailsService.WriteAsync:
using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = Text.Plain;
var title = "Bad Input";
var detail = "Invalid input";
var type = "https://errors.example.com/badInput";
if (context.RequestServices.GetService<IProblemDetailsService>() is
{ } problemDetailsService)
{
var exceptionHandlerFeature =
context.Features.Get<IExceptionHandlerFeature>();
var exceptionType = exceptionHandlerFeature?.Error;
if (exceptionType != null &&
exceptionType.Message.Contains("infinity"))
{
title = "Argument exception";
detail = "Invalid input";
type = "https://errors.example.com/argumentException";
}
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = title,
Detail = detail,
Type = type
}
});
}
});
});
}
app.MapControllers();
app.Run();
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
Ein alternativer Ansatz zum Generieren von Problemdetails besteht darin, das Drittanbieter-NuGet-Paket Hellang.Middleware.ProblemDetails zu verwenden, mit dem Ausnahmen und Clientfehler zu Problemdetails zugeordnet werden können.
Weitere Ressourcen
Von Tom Dykstra
Dieser Artikel beschreibt grundsätzliche Vorgehensweisen zur Behandlung von Fehlern in ASP.NET Core-Web-Apps. Siehe Behandeln von Fehlern in ASP.NET Core-APIs für Web-APIs.
Seite mit Ausnahmen für Entwickler
Die Seite mit Ausnahmen für Entwickler enthält ausführliche Informationen zu unbehandelten Anforderungsausnahmen. ASP.NET Core-Apps aktivieren standardmäßig die Seite mit Ausnahmen für Entwickler, wenn die folgenden Aussagen beide zutreffen:
- Wird in der
DevelopmentUmgebung ausgeführt. - Die App wird mit den aktuellen Vorlagen erstellt, also mit WebApplication.CreateBuilder. Apps, die mit
WebHost.CreateDefaultBuildererstellt wurden, müssen die Entwicklerausnahmeseite aktivieren, indemapp.UseDeveloperExceptionPageinConfigureaufgerufen wird.
Die Entwicklerausnahmeseite wird früh in der Middleware-Pipeline ausgeführt, damit sie unbehandelte Ausnahmen abfangen kann, die in nachfolgender Middleware ausgelöst werden.
Detaillierte Ausnahmeinformationen sollten nicht öffentlich angezeigt werden, wenn die App in der Production Umgebung ausgeführt wird. Weitere Informationen zum Konfigurieren von Umgebungen finden Sie unter ASP.NET Core-Laufzeitumgebungen.
Die Seite mit Ausnahmen für Entwickler kann die folgenden Informationen zu der Ausnahme und der Anforderung enthalten:
- Stapelüberwachung
- Abfragezeichenfolgeparameter, sofern vorhanden
- Cookies, falls vorhanden
- Headers
Es ist nicht garantiert, dass auf der Seite mit Ausnahmen für Entwickler Informationen angezeigt werden. Verwenden Sie Logging für vollständige Fehlerinformationen.
Seite „Ausnahmehandler“
Um eine benutzerdefinierte Fehlerbehandlungsseite für die Production Umgebung zu konfigurieren, rufen Sie UseExceptionHandler auf. Diese Middleware für die Ausnahmebehandlung:
- Dient zum Abfangen und Protokollieren unbehandelter Ausnahmen.
- Führt die Anfrage unter Verwendung des angegebenen Pfads erneut in einer alternativen Pipeline aus. Die Anfrage wird nicht erneut ausgeführt, wenn bereits mit der Antwort begonnen wurde. Der von der Vorlage generierte Code führt die Anfrage über den
/Error-Pfad erneut aus.
Warning
Wenn die alternative Pipeline eine eigene Ausnahme wirft, löst die Middleware zur Ausnahmebehandlung die ursprüngliche Ausnahme erneut aus.
Im folgenden Beispiel wird die Ausnahmebehandlungs-Middleware in Umgebungen, die nicht UseExceptionHandler sind, mithilfe von Development hinzugefügt:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Die App-Vorlage für Razor Pages stellt im Ordner .cshtml eine Fehlerseite (PageModel) und die Klasse ErrorModel () bereit. Die Projektvorlage für eine MVC-App enthält eine Error-Aktionsmethode und eine Fehleransicht für den Home-Controller.
Die Middleware für die Ausnahmebehandlung führt die Anforderung unter Verwendung der ursprünglichen HTTP-Methode erneut aus. Wenn ein Endpunkt für die Fehlerbehandlung auf bestimmte HTTP-Methoden beschränkt ist, wird er nur für diese Methoden ausgeführt. Beispielsweise wird eine MVC-Controller-Aktion, die das Attribut [HttpGet] verwendet, nur für GET-Anforderungen ausgeführt. Um sicherzustellen, dass alle Anfragen die benutzerdefinierte Fehlerbehandlungsseite erreichen, beschränken Sie sie nicht auf bestimmte HTTP-Methoden.
So behandeln Sie Ausnahmen unterschiedlich, basierend auf der ursprünglichen HTTP-Methode:
- Für Razor Pages erstellen Sie mehrere Handlermethoden. Verwenden Sie beispielsweise
OnGetzum Behandeln von GET-Ausnahmen undOnPostzum Behandeln von POST-Ausnahmen. - Für MVC wenden Sie HTTP-Verbattribute auf mehrere Aktionen an. Verwenden Sie beispielsweise
[HttpGet]zum Behandeln von GET-Ausnahmen und[HttpPost]zum Behandeln von POST-Ausnahmen.
Um nicht authentifizierten Benutzern die Anzeige der benutzerdefinierten Seite für die Fehlerbehandlung zu ermöglichen, stellen Sie sicher, dass der anonyme Zugriff unterstützt wird.
Zugreifen auf die Ausnahme
Verwenden Sie IExceptionHandlerPathFeature, um auf die Ausnahme oder den Pfad der ursprünglichen Anforderung in einem Fehlerhandler zuzugreifen. Im folgenden Beispiel wird IExceptionHandlerPathFeature verwendet, um weitere Informationen zur ausgelösten Ausnahme zu erhalten:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string? ExceptionMessage { get; set; }
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "The file was not found.";
}
if (exceptionHandlerPathFeature?.Path == "/")
{
ExceptionMessage ??= string.Empty;
ExceptionMessage += " Page: Home.";
}
}
}
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
Lambda-Ausdruck für Ausnahmehandler
Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite besteht darin, an UseExceptionHandler eine Lambda-Funktion zu übergeben. Durch die Verwendung eines Lambda kann auf den Fehler zugegriffen werden, bevor die Antwort zurückgegeben wird.
Der folgende Code verwendet eine Lambda-Funktion zur Ausnahmebehandlung:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
// using static System.Net.Mime.MediaTypeNames;
context.Response.ContentType = Text.Plain;
await context.Response.WriteAsync("An exception was thrown.");
var exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
await context.Response.WriteAsync(" The file was not found.");
}
if (exceptionHandlerPathFeature?.Path == "/")
{
await context.Response.WriteAsync(" Page: Home.");
}
});
});
app.UseHsts();
}
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
UseStatusCodePages
Die ASP.NET Core-App stellt für HTTP-Fehlerstatuscodes wie 404 – Nicht gefunden standardmäßig keine Statuscodeseite zur Verfügung. Wenn die App einen HTTP-Fehlerstatuscode im Bereich 400 bis 599 ohne Text festlegt, werden der Statuscode und ein leerer Antworttext zurückgegeben. Um standardmäßige reine Texthandler für gängige Fehlerstatuscodes zu aktivieren, rufen Sie UseStatusCodePages in Program.cs auf:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Rufen Sie UseStatusCodePages vor der Middleware zur Anforderungsverarbeitung auf. Rufen Sie z. B. UseStatusCodePages vor der Middleware für statische Dateien und der Middleware für Endpunkte auf.
Wenn UseStatusCodePages nicht verwendet wird, wird beim Navigieren zu einer URL ohne Endpunkt eine browserabhängige Fehlermeldung zurückgegeben, die angibt, dass der Endpunkt nicht gefunden werden kann. Wenn UseStatusCodePages aufgerufen wird, gibt der Browser die folgende Antwort zurück:
Status Code: 404; Not Found
UseStatusCodePages wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
Note
Die Middleware für Statuscodeseiten erfasst keine Ausnahmen. Um eine benutzerdefinierte Fehlerbehandlungsseite bereitzustellen, verwenden Sie die Ausnahmehandlerseite.
UseStatusCodePages mit Formatzeichenfolge
Um den Inhaltstyp und den Text der Antwort anzupassen, verwenden Sie die Überladung von UseStatusCodePages, die einen Inhaltstyp und eine Formatzeichenfolge akzeptiert:
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}");
Im vorangehenden Code ist {0} ein Platzhalter für den Fehlercode.
UseStatusCodePages mit Formatzeichenfolge wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
UseStatusCodePages mit Lambda-Ausdruck
Um benutzerdefinierten Code für die Fehlerbehandlung und das Schreiben von Antworten anzugeben, verwenden Sie die Überladung von UseStatusCodePages, die einen Lambda-Ausdruck verwendet:
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 mit Lambdaausdruck wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
UseStatusCodePagesWithRedirects
Die Erweiterungsmethode UseStatusCodePagesWithRedirects:
- Sendet den Statuscode 302 Found (Gefunden) an den Client.
- Leitet den Client an den in der URL-Vorlage angegebenen Fehlerbehandlungsendpunkt weiter. Der Fehlerbehandlungsendpunkt zeigt in der Regel Fehlerinformationen an und gibt den HTTP-Code 200 zurück.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Die URL-Vorlage kann, wie im vorangehenden Code gezeigt, einen {0}-Platzhalter für den Statuscode enthalten. Wenn die URL-Vorlage mit ~ (Tilde) beginnt, wird ~ durch das PathBase der App ersetzt. Wenn Sie in der App einen Endpunkt angeben, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Den Client an einen anderen Endpunkt umleiten, in der Regel in Fällen, in denen eine andere App den Fehler verarbeitet. Für Web-Apps gibt die Adressleiste des Browsers des Clients den umgeleiteten Endpunkt wieder.
- Der ursprüngliche Statuscode sollte nicht beibehalten und mit der ursprünglichen Weiterleitungsantwort zurückgegeben werden.
UseStatusCodePagesWithReExecute
Die Erweiterungsmethode UseStatusCodePagesWithReExecute:
- Gibt den ursprünglichen Statuscode an den Client zurück.
- Generiert den Antworttext durch die erneute Ausführung der Anforderungspipeline mithilfe eines alternativen Pfads.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Wenn in der App ein Endpunkt angegeben ist, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Die Anforderung ohne Umleitung an einen anderen Endpunkt verarbeiten. Für Web-Apps gibt die Adressleiste des Browsers des Clients den ursprünglich angeforderten Endpunkt wieder.
- Den ursprünglichen Statuscode mit der Antwort beibehalten und zurückgeben.
Die URL-Vorlage muss mit / beginnen und kann den Platzhalter {0} für den Statuscode enthalten. Um den Statuscode als Abfragezeichenfolgenparameter zu übergeben, übergeben Sie ein zweites Argument an UseStatusCodePagesWithReExecute. Beispiel:
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
Der Endpunkt, der den Fehler verarbeitet, kann die ursprüngliche URL abrufen, die den Fehler generiert hat (siehe folgendes Beispiel):
[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);
}
}
}
Deaktivieren von Statuscodeseiten
Verwenden Sie das Attribut [SkipStatusCodePages], um Statuscodeseiten für einen MVC-Controller oder eine MVC-Aktionsmethode zu deaktivieren.
Um Statuscode-Seiten für bestimmte Anfragen in einer Razor-Pages-Handlermethode oder in einem MVC-Controller zu deaktivieren, verwenden Sie IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Ausnahmebehandlungscode
Code auf Seiten zur Ausnahmebehandlung kann ebenfalls Ausnahmen auslösen. Fehlerseiten in der Produktionsumgebung sollten gründlich getestet werden, und es sollte besonders darauf geachtet werden, dass sie nicht selbst Ausnahmen auslösen.
Antwortkopfzeilen
Nachdem die Header für eine Antwort gesendet wurden:
- Die App kann den Statuscode der Antwort nicht ändern.
- Weder Ausnahmeseiten noch Handler können ausgeführt werden. Die Antwort muss abgeschlossen oder die Verbindung unterbrochen werden.
Server-Ausnahmebehandlung
Neben der Ausnahmebehandlungslogik in einer App kann die HTTP-Serverimplementierung einige Ausnahmebehandlungen durchführen. Wenn der Server eine Ausnahme erfasst, bevor die Antwortheader gesendet werden, sendet der Server die Antwort 500 - Internal Server Error ohne Antworttext. Wenn der Server eine Ausnahme auffängt, nachdem die Antwortheader gesendet wurden, schließt der Server die Verbindung. Anforderungen, die nicht von der App verarbeitet werden, werden vom Server verarbeitet. Jede Ausnahme, die bei der Anforderungsverarbeitung durch den Server auftritt, wird durch die Ausnahmebehandlung des Servers verarbeitet. Die benutzerdefinierten Fehlerseiten der App, Middleware zur Fehlerbehandlung und Filter haben keine Auswirkungen auf dieses Verhalten.
Fehlerbehandlung beim Start
Nur auf Hostebene können Ausnahmen behandelt werden, die während des Starts einer App auftreten. Der Host kann für das Erfassen von Startfehlern und Erfassen detaillierter Fehler konfiguriert werden.
Die Hostingebene kann nur dann für einen beim Start erfassten Fehler eine Fehlerseite anzeigen, wenn der Fehler nach der Bindung an die Hostadresse bzw. den Port auftritt. Wenn die Bindung misslingt:
- Die Hostebene protokolliert eine kritische Ausnahme.
- Der DotNet-Prozess stürzt ab.
- Wenn der HTTP-Server Kestrel heißt, wird keine Fehlerseite angezeigt.
Wenn sie auf IIS (oder Azure App Service) oder IIS Express ausgeführt wird, wird ein Prozessfehler 502.5 vom ASP.NET Core-Modul zurückgegeben, wenn der Prozess nicht starten kann. Weitere Informationen finden Sie unter Problembehandlung bei ASP.NET Core in Azure App Service und IIS.
Datenbank-Fehlerseite
Der Ausnahmefilter der Datenbankentwicklerseite AddDatabaseDeveloperPageExceptionFilter erfasst datenbankbezogene Ausnahmen, die mithilfe von Entity Framework Core-Migrationen aufgelöst werden können. Wenn diese Ausnahmen auftreten, wird eine HTML-Antwort mit Details zu möglichen Aktionen zum Beheben des Problems generiert. Diese Seite ist nur in der Development Umgebung aktiviert. Der folgende Code fügt den Ausnahmefilter der Datenbankentwicklerseite hinzu:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Ausnahmefilter
In MVC-Apps können Ausnahmefilter global oder auf einen Controller oder eine Aktion bezogen konfiguriert werden. In Razor Pages-Apps können sie global oder auf ein Seitenmodell bezogen konfiguriert werden. Diese Filter verarbeiten jede nicht behandelte Ausnahme, die während der Ausführung eine Controlleraktion oder eines anderen Filters auftritt. Weitere Informationen finden Sie unter Filter in ASP.NET Core.
Ausnahmefilter eignen sich zum Auffangen von Ausnahmen, die in MVC-Aktionen ausgelöst werden. Sie sind jedoch nicht so flexibel wie die integrierte Middleware für die Ausnahmebehandlung, UseExceptionHandler. Wir empfehlen die Verwendung von UseExceptionHandler, es sei denn, Sie müssen die Fehlerbehandlung je nachdem, welche MVC-Aktion ausgewählt wird, anders handhaben.
Modellstatusfehler
Informationen zum Behandeln von Modellstatusfehlern finden Sie unter Modellbindung und Modellvalidierung.
Weitere Ressourcen
Von Kirk Larkin, Tom Dykstra und Steve Smith
Dieser Artikel beschreibt grundsätzliche Vorgehensweisen zur Behandlung von Fehlern in ASP.NET Core-Web-Apps. Siehe Behandeln von Fehlern in ASP.NET Core-APIs für Web-APIs.
Anzeigen oder Herunterladen von Beispielcode (Informationen zum Herunterladen.) Die Registerkarte „Netzwerk“ in den F12-Entwicklertools im Browser ist beim Testen der Beispiel-App nützlich.
Seite mit Ausnahmen für Entwickler
Die Seite mit Ausnahmen für Entwickler enthält ausführliche Informationen zu unbehandelten Anforderungsausnahmen. Die ASP.NET Core-Vorlagen generieren den folgenden Code:
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();
});
}
Der oben hervorgehobene Code aktiviert die Entwickler ausnahmeseite, wenn die App in der Development Umgebung ausgeführt wird.
Die Vorlagen platzieren UseDeveloperExceptionPage früh in der Middleware-Pipeline, sodass es unbehandelte Ausnahmen abfangen kann, die von nachfolgender Middleware ausgelöst werden.
Der vorangehende Code aktiviert die Entwickler exception Page nur , wenn die App in der Development Umgebung ausgeführt wird. Detaillierte Ausnahmeinformationen sollten nicht öffentlich angezeigt werden, wenn die App in der Production Umgebung ausgeführt wird. Weitere Informationen zum Konfigurieren von Umgebungen finden Sie unter ASP.NET Core-Laufzeitumgebungen.
Die Seite mit Ausnahmen für Entwickler kann die folgenden Informationen zu der Ausnahme und der Anforderung enthalten:
- Stapelüberwachung
- Abfragezeichenfolgeparameter, sofern vorhanden
- Cookies wenn vorhanden
- Headers
Es ist nicht garantiert, dass auf der Seite mit Ausnahmen für Entwickler Informationen angezeigt werden. Verwenden Sie Logging für vollständige Fehlerinformationen.
Seite „Ausnahmehandler“
Um eine benutzerdefinierte Fehlerbehandlungsseite für die Production Umgebung zu konfigurieren, rufen Sie UseExceptionHandler auf. Diese Middleware für die Ausnahmebehandlung:
- Dient zum Abfangen und Protokollieren unbehandelter Ausnahmen.
- Führt die Anfrage unter Verwendung des angegebenen Pfads erneut in einer alternativen Pipeline aus. Die Anfrage wird nicht erneut ausgeführt, wenn bereits mit der Antwort begonnen wurde. Der von der Vorlage generierte Code führt die Anfrage über den
/Error-Pfad erneut aus.
Warning
Wenn die alternative Pipeline eine eigene Ausnahme wirft, löst die Middleware zur Ausnahmebehandlung die ursprüngliche Ausnahme erneut aus.
Im folgenden Beispiel wird die Ausnahmebehandlungs-Middleware in Umgebungen, die nicht UseExceptionHandler sind, mithilfe von Development hinzugefügt:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Die App-Vorlage für Razor Pages stellt im Ordner .cshtml eine Fehlerseite (PageModel) und die Klasse ErrorModel () bereit. Die Projektvorlage für eine MVC-App enthält eine Error-Aktionsmethode und eine Fehleransicht für den Home-Controller.
Die Middleware für die Ausnahmebehandlung führt die Anforderung unter Verwendung der ursprünglichen HTTP-Methode erneut aus. Wenn ein Endpunkt für die Fehlerbehandlung auf bestimmte HTTP-Methoden beschränkt ist, wird er nur für diese Methoden ausgeführt. Beispielsweise wird eine MVC-Controller-Aktion, die das Attribut [HttpGet] verwendet, nur für GET-Anforderungen ausgeführt. Um sicherzustellen, dass alle Anfragen die benutzerdefinierte Fehlerbehandlungsseite erreichen, beschränken Sie sie nicht auf bestimmte HTTP-Methoden.
So behandeln Sie Ausnahmen unterschiedlich, basierend auf der ursprünglichen HTTP-Methode:
- Für Razor Pages erstellen Sie mehrere Handlermethoden. Verwenden Sie beispielsweise
OnGetzum Behandeln von GET-Ausnahmen undOnPostzum Behandeln von POST-Ausnahmen. - Für MVC wenden Sie HTTP-Verbattribute auf mehrere Aktionen an. Verwenden Sie beispielsweise
[HttpGet]zum Behandeln von GET-Ausnahmen und[HttpPost]zum Behandeln von POST-Ausnahmen.
Um nicht authentifizierten Benutzern die Anzeige der benutzerdefinierten Seite für die Fehlerbehandlung zu ermöglichen, stellen Sie sicher, dass der anonyme Zugriff unterstützt wird.
Zugreifen auf die Ausnahme
Verwenden Sie IExceptionHandlerPathFeature, um auf die Ausnahme oder den Pfad der ursprünglichen Anforderung in einem Fehlerhandler zuzugreifen. Der folgende Code fügt ExceptionMessage dem standardmäßig von den ASP.NET Core-Vorlagen generierten Pages/Error.cshtml.cs hinzu:
[ResponseCache(Duration=0, Location=ResponseCacheLocation.None, NoStore=true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string ExceptionMessage { get; set; }
private readonly ILogger<ErrorModel> _logger;
public ErrorModel(ILogger<ErrorModel> logger)
{
_logger = logger;
}
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "File error thrown";
_logger.LogError(ExceptionMessage);
}
if (exceptionHandlerPathFeature?.Path == "/index")
{
ExceptionMessage += " from home page";
}
}
}
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
So testen Sie die Ausnahme in der Beispiel-App
- Legen Sie die Umgebung auf Produktion fest.
- Entfernen Sie die Kommentare aus
webBuilder.UseStartup<Startup>();inProgram.cs. - Wählen Sie Trigger an exception (Ausnahme auslösen) auf der Startseite aus.
Lambda-Ausdruck für Ausnahmehandler
Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite besteht darin, an UseExceptionHandler eine Lambda-Funktion zu übergeben. Durch die Verwendung eines Lambda kann auf den Fehler zugegriffen werden, bevor die Antwort zurückgegeben wird.
Der folgende Code verwendet eine Lambda-Funktion zur Ausnahmebehandlung:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;;
context.Response.ContentType = "text/html";
await context.Response.WriteAsync("<html lang=\"en\"><body>\r\n");
await context.Response.WriteAsync("ERROR!<br><br>\r\n");
var exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
await context.Response.WriteAsync(
"File error thrown!<br><br>\r\n");
}
await context.Response.WriteAsync(
"<a href=\"/\">Home</a><br>\r\n");
await context.Response.WriteAsync("</body></html>\r\n");
await context.Response.WriteAsync(new string(' ', 512));
});
});
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Warning
Stellen Sie keine vertraulichen Fehlerinformationen aus IExceptionHandlerFeature oder IExceptionHandlerPathFeature für Clients bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
So testen Sie den Lambdaausdruck für die Ausnahmebehandlung in der Beispiel-App
- Legen Sie die Umgebung auf Produktion fest.
- Entfernen Sie die Kommentare aus
webBuilder.UseStartup<StartupLambda>();inProgram.cs. - Wählen Sie Trigger an exception (Ausnahme auslösen) auf der Startseite aus.
UseStatusCodePages
Die ASP.NET Core-App stellt für HTTP-Fehlerstatuscodes wie 404 – Nicht gefunden standardmäßig keine Statuscodeseite zur Verfügung. Wenn die App einen HTTP-Fehlerstatuscode im Bereich 400 bis 599 ohne Text festlegt, werden der Statuscode und ein leerer Antworttext zurückgegeben. Verwenden Sie zum Bereitstellen von Statuscodeseiten Middleware für Statuscodeseiten. Um standardmäßige Nur-Text-Handler für gängige Fehlerstatuscodes zu aktivieren, rufen Sie UseStatusCodePages in der Methode Startup.Configure auf:
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();
});
}
Rufen Sie UseStatusCodePages vor der Middleware zur Anforderungsverarbeitung auf. Rufen Sie z. B. UseStatusCodePages vor der Middleware für statische Dateien und der Middleware für Endpunkte auf.
Wenn UseStatusCodePages nicht verwendet wird, wird beim Navigieren zu einer URL ohne Endpunkt eine browserabhängige Fehlermeldung zurückgegeben, die angibt, dass der Endpunkt nicht gefunden werden kann. Navigieren Sie beispielsweise zu Home/Privacy2. Wenn UseStatusCodePages aufgerufen wird, gibt der Browser Folgendes zurück:
Status Code: 404; Not Found
UseStatusCodePages wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
So testen Sie UseStatusCodePages in der Beispiel-App
- Legen Sie die Umgebung auf Produktion fest.
- Entfernen Sie die Kommentare aus
webBuilder.UseStartup<StartupUseStatusCodePages>();inProgram.cs. - Klicken Sie auf die Links auf der Startseite.
Note
Die Middleware für Statuscodeseiten erfasst keine Ausnahmen. Um eine benutzerdefinierte Fehlerbehandlungsseite bereitzustellen, verwenden Sie die Ausnahmehandlerseite.
UseStatusCodePages mit Formatzeichenfolge
Um den Inhaltstyp und den Text der Antwort anzupassen, verwenden Sie die Überladung von UseStatusCodePages, die einen Inhaltstyp und eine Formatzeichenfolge akzeptiert:
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();
});
}
Im vorangehenden Code ist {0} ein Platzhalter für den Fehlercode.
UseStatusCodePages mit Formatzeichenfolge wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
Um UseStatusCodePages in der Beispiel-App zu testen, entfernen Sie die Kommentare aus webBuilder.UseStartup<StartupFormat>(); in Program.cs.
UseStatusCodePages mit Lambda-Ausdruck
Um benutzerdefinierten Code für die Fehlerbehandlung und das Schreiben von Antworten anzugeben, verwenden Sie die Überladung von UseStatusCodePages, die einen Lambda-Ausdruck verwendet:
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 mit Lambdaausdruck wird normalerweise nicht in der Produktionsumgebung verwendet, da diese Methode eine Meldung zurückgibt, die für Benutzer nicht nützlich ist.
Um UseStatusCodePages in der Beispiel-App zu testen, entfernen Sie die Kommentare aus webBuilder.UseStartup<StartupStatusLambda>(); in Program.cs.
UseStatusCodePagesWithRedirects
Die Erweiterungsmethode UseStatusCodePagesWithRedirects:
- Sendet den Statuscode 302 Found (Gefunden) an den Client.
- Leitet den Client an den in der URL-Vorlage angegebenen Fehlerbehandlungsendpunkt weiter. Der Fehlerbehandlungsendpunkt zeigt in der Regel Fehlerinformationen an und gibt den HTTP-Code 200 zurück.
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();
});
}
Die URL-Vorlage kann, wie im vorangehenden Code gezeigt, einen {0}-Platzhalter für den Statuscode enthalten. Wenn die URL-Vorlage mit ~ (Tilde) beginnt, wird ~ durch das PathBase der App ersetzt. Wenn Sie in der App einen Endpunkt angeben, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen. Ein Razor Pages-Beispiel finden Sie unter Pages/MyStatusCode.cshtml in der Beispiel-App.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Den Client an einen anderen Endpunkt umleiten, in der Regel in Fällen, in denen eine andere App den Fehler verarbeitet. Für Web-Apps gibt die Adressleiste des Browsers des Clients den umgeleiteten Endpunkt wieder.
- Der ursprüngliche Statuscode sollte nicht beibehalten und mit der ursprünglichen Weiterleitungsantwort zurückgegeben werden.
Um UseStatusCodePages in der Beispiel-App zu testen, entfernen Sie die Kommentare aus webBuilder.UseStartup<StartupSCredirect>(); in Program.cs.
UseStatusCodePagesWithReExecute
Die Erweiterungsmethode UseStatusCodePagesWithReExecute:
- Gibt den ursprünglichen Statuscode an den Client zurück.
- Generiert den Antworttext durch die erneute Ausführung der Anforderungspipeline mithilfe eines alternativen Pfads.
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();
});
}
Wenn in der App ein Endpunkt angegeben ist, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen. Stellen Sie sicher, dass UseStatusCodePagesWithReExecute vor UseRouting platziert wird, damit die Anforderung an die Statusseite umgeleitet werden kann. Ein Razor Pages-Beispiel finden Sie unter Pages/MyStatusCode2.cshtml in der Beispiel-App.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Die Anforderung ohne Umleitung an einen anderen Endpunkt verarbeiten. Für Web-Apps gibt die Adressleiste des Browsers des Clients den ursprünglich angeforderten Endpunkt wieder.
- Den ursprünglichen Statuscode mit der Antwort beibehalten und zurückgeben.
Die Vorlagen für URL und Abfragezeichenfolgen können für den Statuscode einen Platzhalter ({0}) enthalten. Die URL-Vorlage muss mit / beginnen.
Der Endpunkt, der den Fehler verarbeitet, kann die ursprüngliche URL abrufen, die den Fehler generiert hat (siehe folgendes Beispiel):
[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;
}
}
}
Ein Razor Pages-Beispiel finden Sie unter Pages/MyStatusCode2.cshtml in der Beispiel-App.
Um UseStatusCodePages in der Beispiel-App zu testen, entfernen Sie die Kommentare aus webBuilder.UseStartup<StartupSCreX>(); in Program.cs.
Deaktivieren von Statuscodeseiten
Verwenden Sie das Attribut [SkipStatusCodePages], um Statuscodeseiten für einen MVC-Controller oder eine MVC-Aktionsmethode zu deaktivieren.
Um Statuscode-Seiten für bestimmte Anfragen in einer Razor-Pages-Handlermethode oder in einem MVC-Controller zu deaktivieren, verwenden Sie IStatusCodePagesFeature:
public void OnGet()
{
// using Microsoft.AspNetCore.Diagnostics;
var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature != null)
{
statusCodePagesFeature.Enabled = false;
}
}
Ausnahmebehandlungscode
Code auf Seiten zur Ausnahmebehandlung kann ebenfalls Ausnahmen auslösen. Fehlerseiten in der Produktionsumgebung sollten gründlich getestet werden, und es sollte besonders darauf geachtet werden, dass sie nicht selbst Ausnahmen auslösen.
Antwortkopfzeilen
Nachdem die Header für eine Antwort gesendet wurden:
- Die App kann den Statuscode der Antwort nicht ändern.
- Weder Ausnahmeseiten noch Handler können ausgeführt werden. Die Antwort muss abgeschlossen oder die Verbindung unterbrochen werden.
Server-Ausnahmebehandlung
Neben der Ausnahmebehandlungslogik in einer App kann die HTTP-Serverimplementierung einige Ausnahmebehandlungen durchführen. Wenn der Server eine Ausnahme erfasst, bevor die Antwortheader gesendet werden, sendet der Server die Antwort 500 - Internal Server Error ohne Antworttext. Wenn der Server eine Ausnahme auffängt, nachdem die Antwortheader gesendet wurden, schließt der Server die Verbindung. Anforderungen, die nicht von der App verarbeitet werden, werden vom Server verarbeitet. Jede Ausnahme, die bei der Anforderungsverarbeitung durch den Server auftritt, wird durch die Ausnahmebehandlung des Servers verarbeitet. Die benutzerdefinierten Fehlerseiten der App, Middleware zur Fehlerbehandlung und Filter haben keine Auswirkungen auf dieses Verhalten.
Fehlerbehandlung beim Start
Nur auf Hostebene können Ausnahmen behandelt werden, die während des Starts einer App auftreten. Der Host kann für das Erfassen von Startfehlern und Erfassen detaillierter Fehler konfiguriert werden.
Die Hostingebene kann nur dann für einen beim Start erfassten Fehler eine Fehlerseite anzeigen, wenn der Fehler nach der Bindung an die Hostadresse bzw. den Port auftritt. Wenn die Bindung misslingt:
- Die Hostebene protokolliert eine kritische Ausnahme.
- Der DotNet-Prozess stürzt ab.
- Wenn der HTTP-Server Kestrel heißt, wird keine Fehlerseite angezeigt.
Wenn sie auf IIS (oder Azure App Service) oder IIS Express ausgeführt wird, wird ein Prozessfehler 502.5 vom ASP.NET Core-Modul zurückgegeben, wenn der Prozess nicht starten kann. Weitere Informationen finden Sie unter Problembehandlung bei ASP.NET Core in Azure App Service und IIS.
Datenbank-Fehlerseite
Der Ausnahmefilter der Datenbankentwicklerseite AddDatabaseDeveloperPageExceptionFilter erfasst datenbankbezogene Ausnahmen, die mithilfe von Entity Framework Core-Migrationen aufgelöst werden können. Wenn diese Ausnahmen auftreten, wird eine HTML-Antwort mit Details zu möglichen Aktionen zum Beheben des Problems generiert. Diese Seite ist nur in der Development Umgebung aktiviert. Der folgende Code wurde von den ASP.NET Core Razor Pages-Vorlagen generiert, als einzelne Benutzerkonten angegeben wurden:
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();
}
Ausnahmefilter
In MVC-Apps können Ausnahmefilter global oder auf einen Controller oder eine Aktion bezogen konfiguriert werden. In Razor Pages-Apps können sie global oder auf ein Seitenmodell bezogen konfiguriert werden. Diese Filter verarbeiten jede nicht behandelte Ausnahme, die während der Ausführung eine Controlleraktion oder eines anderen Filters auftritt. Weitere Informationen finden Sie unter Filter in ASP.NET Core.
Ausnahmefilter eignen sich zum Auffangen von Ausnahmen, die in MVC-Aktionen ausgelöst werden. Sie sind jedoch nicht so flexibel wie die integrierte Middleware für die Ausnahmebehandlung, UseExceptionHandler. Wir empfehlen die Verwendung von UseExceptionHandler, es sei denn, Sie müssen die Fehlerbehandlung je nachdem, welche MVC-Aktion ausgewählt wird, anders handhaben.
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();
});
}
Modellstatusfehler
Informationen zum Behandeln von Modellstatusfehlern finden Sie unter Modellbindung und Modellvalidierung.
Weitere Ressourcen
Von Tom Dykstra und Steve Smith
Dieser Artikel beschreibt grundsätzliche Vorgehensweisen zur Behandlung von Fehlern in ASP.NET Core-Web-Apps. Siehe Behandeln von Fehlern in ASP.NET Core-APIs für Web-APIs.
Anzeigen oder Herunterladen von Beispielcode (Informationen zum Herunterladen.)
Seite mit Ausnahmen für Entwickler
Die Seite mit Ausnahmen für Entwickler enthält ausführliche Informationen zu Anforderungsausnahmen. Die ASP.NET Core-Vorlagen generieren den folgenden Code:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Im vorherigen Code wird die Entwickler ausnahmeseite aktiviert, wenn die App in der Development Umgebung ausgeführt wird.
Die Vorlagen platzieren UseDeveloperExceptionPage vor jeder Middleware, sodass Ausnahmen in der nachfolgenden Middleware abgefangen werden.
Im vorherigen Code wird die Entwickler exception Page nur aktiviert, wenn die App in der Development Umgebung ausgeführt wird. Bei Ausführung der App in der Produktionsumgebung sollten keine detaillierten Informationen zu den Ausnahmen öffentlich angezeigt werden. Weitere Informationen zum Konfigurieren von Umgebungen finden Sie unter ASP.NET Core-Laufzeitumgebungen.
Die Seite mit Ausnahmen für Entwickler enthält die folgenden Informationen zu der Ausnahme und der Anforderung:
- Stapelüberwachung
- Abfragezeichenfolgeparameter, sofern vorhanden
- Cookies wenn vorhanden
- Headers
Seite „Ausnahmehandler“
Verwenden Sie die Middleware für die Ausnahmebehandlung, um eine benutzerdefinierte Fehlerbehandlungsseite für die Production Umgebung zu konfigurieren. Die Middleware:
- Dient zum Abfangen und Protokollieren von Ausnahmen.
- Führt die Anforderung für die angegebene Seite oder den Controller in einer anderen Pipeline erneut aus. Die Anfrage wird nicht erneut ausgeführt, wenn bereits mit der Antwort begonnen wurde. Der durch die Vorlage generierte Code führt die Anfrage an
/Errorerneut aus.
Im folgenden Beispiel wird die Ausnahmebehandlungs-Middleware in Umgebungen, die nicht UseExceptionHandler sind, mithilfe von Development hinzugefügt:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Die App-Vorlage für Razor Pages stellt im Ordner .cshtml eine Fehlerseite (PageModel) und die Klasse ErrorModel () bereit. Die Projektvorlage für eine MVC-App enthält eine Fehleraktionsmethode und eine Fehleransicht im Home-Controller.
Markieren Sie die Aktionsmethode für die Fehlerbehandlung nicht mit HTTP-Methodenattributen wie HttpGet. Explizite Verben verhindern, dass einige Anfragen die Methode erreichen. Lassen Sie den anonymen Zugriff auf die Methode zu, wenn nicht authentifizierten Benutzern die Fehleransicht angezeigt werden soll.
Zugreifen auf die Ausnahme
Verwenden Sie IExceptionHandlerPathFeature, um auf die Ausnahme oder den Pfad der ursprünglichen Anforderung in einem Controller für die Fehlerbehandlung oder auf einer Seite zuzugreifen:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string ExceptionMessage { get; set; }
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "File error thrown";
}
if (exceptionHandlerPathFeature?.Path == "/index")
{
ExceptionMessage += " from home page";
}
}
}
Warning
Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
Um die vorherige Ausnahmebehandlungsseite aufzurufen, setzen Sie die Umgebung auf Produktion und erzwingen Sie eine Ausnahme.
Lambda-Ausdruck für Ausnahmehandler
Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite besteht darin, an UseExceptionHandler eine Lambda-Funktion zu übergeben. Durch die Verwendung eines Lambda kann auf den Fehler zugegriffen werden, bevor die Antwort zurückgegeben wird.
Hier ist ein Beispiel für die Verwendung eines Lambda-Ausdrucks zur Ausnahmebehandlung:
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();
}
Im vorangehenden Code wird await context.Response.WriteAsync(new string(' ', 512)); hinzugefügt, sodass der Internet Explorer-Browser die Fehlermeldung anstelle einer IE-Fehlermeldung anzeigt. Weitere Informationen finden Sie in diesem GitHub-Issue.
Warning
Stellen Sie keine vertraulichen Fehlerinformationen aus IExceptionHandlerFeature oder IExceptionHandlerPathFeature für Clients bereit. Die Ausgabe von Fehlermeldungen stellt ein Sicherheitsrisiko dar.
Um das Ergebnis der Ausnahmebehandlungs-Lambda-Funktion in der Beispiel-Appanzuzeigen, verwenden Sie die Präprozessor-Direktiven ProdEnvironment und ErrorHandlerLambda und wählen Sie auf der Startseite 'Ausnahme auslösen' aus.
UseStatusCodePages
Ihre ASP.NET Core-App stellt für HTTP-Statuscodes wie 404 – Nicht gefunden standardmäßig keine Statuscodeseite zur Verfügung. Die App gibt einen Statuscode und einen leeren Antworttext zurück. Verwenden Sie zum Bereitstellen von Statuscodeseiten Middleware für Statuscodeseiten.
Die Middleware wird vom Paket Microsoft.AspNetCore.Diagnostics zur Verfügung gestellt.
Um standardmäßige Nur-Text-Handler für gängige Fehlerstatuscodes zu aktivieren, rufen Sie UseStatusCodePages in der Methode Startup.Configure auf:
app.UseStatusCodePages();
Rufen Sie UseStatusCodePages vor der Middleware für die Anforderungsverarbeitung auf (z. B. Middleware für statische Dateien und MVC-Middleware).
Wenn UseStatusCodePages nicht verwendet wird, wird beim Navigieren zu einer URL ohne Endpunkt eine browserabhängige Fehlermeldung zurückgegeben, die angibt, dass der Endpunkt nicht gefunden werden kann. Navigieren Sie beispielsweise zu Home/Privacy2. Wenn UseStatusCodePages aufgerufen wird, gibt der Browser Folgendes zurück:
Status Code: 404; Not Found
UseStatusCodePages mit Formatzeichenfolge
Um den Inhaltstyp und den Text der Antwort anzupassen, verwenden Sie die Überladung von UseStatusCodePages, die einen Inhaltstyp und eine Formatzeichenfolge akzeptiert:
app.UseStatusCodePages(
"text/plain", "Status code page, status code: {0}");
UseStatusCodePages mit Lambda-Ausdruck
Um benutzerdefinierten Code für die Fehlerbehandlung und das Schreiben von Antworten anzugeben, verwenden Sie die Überladung von UseStatusCodePages, die einen Lambda-Ausdruck verwendet:
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
Die Erweiterungsmethode UseStatusCodePagesWithRedirects:
- Sendet den Statuscode 302 Found (Gefunden) an den Client.
- Der Client wird an den in der URL-Vorlage angegebenen Standort umgeleitet.
app.UseStatusCodePagesWithRedirects("/StatusCode?code={0}");
Die URL-Vorlage kann, wie im Beispiel gezeigt, einen {0}-Platzhalter für den Statuscode enthalten. Wenn die URL-Vorlage mit ~ (Tilde) beginnt, wird ~ durch das PathBase der App ersetzt. Wenn Sie in der App auf einen Endpunkt verweisen, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen. Ein Razor Pages-Beispiel finden Sie unter Pages/StatusCode.cshtml in der Beispiel-App.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Den Client an einen anderen Endpunkt umleiten, in der Regel in Fällen, in denen eine andere App den Fehler verarbeitet. Für Web-Apps gibt die Adressleiste des Browsers des Clients den umgeleiteten Endpunkt wieder.
- Der ursprüngliche Statuscode sollte nicht beibehalten und mit der ursprünglichen Weiterleitungsantwort zurückgegeben werden.
UseStatusCodePagesWithReExecute
Die Erweiterungsmethode UseStatusCodePagesWithReExecute:
- Gibt den ursprünglichen Statuscode an den Client zurück.
- Generiert den Antworttext durch die erneute Ausführung der Anforderungspipeline mithilfe eines alternativen Pfads.
app.UseStatusCodePagesWithReExecute("/StatusCode","?code={0}");
Wenn Sie in der App auf einen Endpunkt verweisen, müssen Sie eine MVC-Ansicht oder Razor-Seite für den Endpunkt erstellen. Stellen Sie sicher, dass UseStatusCodePagesWithReExecute vor UseRouting platziert wird, damit die Anforderung an die Statusseite umgeleitet werden kann. Ein Razor Pages-Beispiel finden Sie unter Pages/StatusCode.cshtml in der Beispiel-App.
Diese Methode wird häufig verwendet, wenn die App Folgendes tun soll:
- Die Anforderung ohne Umleitung an einen anderen Endpunkt verarbeiten. Für Web-Apps gibt die Adressleiste des Browsers des Clients den ursprünglich angeforderten Endpunkt wieder.
- Den ursprünglichen Statuscode mit der Antwort beibehalten und zurückgeben.
Die Vorlagen für URL und Abfragezeichenfolgen können für den Statuscode einen Platzhalter ({0}) enthalten. Die URL-Vorlage muss mit einem Schrägstrich (/) beginnen. Wenn Sie im Pfad einen Platzhalter verwenden, vergewissern Sie sich, dass der Endpunkt (Seite oder Controller) das Pfadsegment verarbeiten kann. Eine Razor Fehlerseite sollte beispielsweise den Wert des optionalen Pfadsegments mit der @page-Anweisung akzeptieren:
@page "{code?}"
Der Endpunkt, der den Fehler verarbeitet, kann die ursprüngliche URL abrufen, die den Fehler generiert hat (siehe folgendes Beispiel):
var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature != null)
{
OriginalURL =
statusCodeReExecuteFeature.OriginalPathBase
+ statusCodeReExecuteFeature.OriginalPath
+ statusCodeReExecuteFeature.OriginalQueryString;
}
Markieren Sie die Aktionsmethode für die Fehlerbehandlung nicht mit HTTP-Methodenattributen wie HttpGet. Explizite Verben verhindern, dass einige Anfragen die Methode erreichen. Lassen Sie den anonymen Zugriff auf die Methode zu, wenn nicht authentifizierten Benutzern die Fehleransicht angezeigt werden soll.
Deaktivieren von Statuscodeseiten
Verwenden Sie das Attribut [SkipStatusCodePages], um Statuscodeseiten für einen MVC-Controller oder eine MVC-Aktionsmethode zu deaktivieren.
Um Statuscode-Seiten für bestimmte Anfragen in einer Razor-Pages-Handlermethode oder in einem MVC-Controller zu deaktivieren, verwenden Sie IStatusCodePagesFeature:
var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature != null)
{
statusCodePagesFeature.Enabled = false;
}
Ausnahmebehandlungscode
Code auf Seiten zur Ausnahmebehandlung kann Ausnahmen auslösen. Es ist häufig empfehlenswert, wenn Produktionsfehlerseiten nur statische Inhalte aufweisen.
Antwortkopfzeilen
Nachdem die Header für eine Antwort gesendet wurden:
- Die App kann den Statuscode der Antwort nicht ändern.
- Weder Ausnahmeseiten noch Handler können ausgeführt werden. Die Antwort muss abgeschlossen oder die Verbindung unterbrochen werden.
Server-Ausnahmebehandlung
Neben der Ausnahmebehandlungslogik in Ihrer App kann die HTTP-Serverimplementierung einige Ausnahmebehandlungen durchführen. Wenn der Server eine Ausnahme erfasst, bevor die Antwortheader gesendet werden, sendet der Server die Antwort 500 – Interner Serverfehler ohne Antworttext. Wenn der Server eine Ausnahme auffängt, nachdem die Antwortheader gesendet wurden, schließt der Server die Verbindung. Anforderungen, die nicht von Ihrer App verarbeitet werden, werden vom Server verarbeitet. Jede Ausnahme, die bei der Anforderungsverarbeitung durch den Server auftritt, wird durch die Ausnahmebehandlung des Servers verarbeitet. Die benutzerdefinierten Fehlerseiten der App, Middleware zur Fehlerbehandlung und Filter haben keine Auswirkungen auf dieses Verhalten.
Fehlerbehandlung beim Start
Nur auf Hostebene können Ausnahmen behandelt werden, die während des Starts einer App auftreten. Der Host kann für das Erfassen von Startfehlern und Erfassen detaillierter Fehler konfiguriert werden.
Die Hostingebene kann nur dann für einen beim Start erfassten Fehler eine Fehlerseite anzeigen, wenn der Fehler nach der Bindung an die Hostadresse bzw. den Port auftritt. Wenn die Bindung misslingt:
- Die Hostebene protokolliert eine kritische Ausnahme.
- Der DotNet-Prozess stürzt ab.
- Wenn der HTTP-Server Kestrel heißt, wird keine Fehlerseite angezeigt.
Wenn sie auf IIS (oder Azure App Service) oder IIS Express ausgeführt wird, wird ein Prozessfehler 502.5 vom ASP.NET Core-Modul zurückgegeben, wenn der Prozess nicht starten kann. Weitere Informationen finden Sie unter Problembehandlung bei ASP.NET Core in Azure App Service und IIS.
Datenbank-Fehlerseite
Die Middleware für Datenbankfehlerseiten fängt datenbankbezogene Ausnahmen ab, die durch die Verwendung von Entity Framework-Migrationen behoben werden können. Wenn diese Ausnahmen auftreten, wird eine HTML-Antwort mit Details zu möglichen Aktionen zum Beheben des Problems generiert. Diese Seite sollte nur in der Development Umgebung aktiviert werden. Aktivieren Sie die Seite, indem Sie Code zu Startup.Configure hinzufügen:
if (env.IsDevelopment())
{
app.UseDatabaseErrorPage();
}
UseDatabaseErrorPage benötigt das Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore-NuGet-Paket.
Ausnahmefilter
In MVC-Apps können Ausnahmefilter global oder auf einen Controller oder eine Aktion bezogen konfiguriert werden. In Razor Pages-Apps können sie global oder auf ein Seitenmodell bezogen konfiguriert werden. Diese Filter verarbeiten jede nicht behandelte Ausnahme, die während der Ausführung eine Controlleraktion oder eines anderen Filters auftritt. Weitere Informationen finden Sie unter Filter in ASP.NET Core.
Tip
Ausnahmefilter sind nützlich für das Abfangen von Ausnahmen, die in MVC-Aktionen auftreten, aber sie sind nicht so flexibel wie die Middleware für die Ausnahmebehandlung. Es wird empfohlen, die Middleware zu verwenden. Verwenden Sie Filter nur dann, wenn Sie für die Fehlerbehandlung auf Grundlage einer ausgewählten MVC-Aktion eine andere Strategie wählen müssen.
Modellstatusfehler
Informationen zum Behandeln von Modellstatusfehlern finden Sie unter Modellbindung und Modellvalidierung.