Fehlerbehandlung in ASP.NET Core

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

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 Web-APIs und Behandeln von Fehlern in Minimal-API-Apps.

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:

Die Seite mit Ausnahmen für Entwickler wird früh in der Middlewarepipeline ausgeführt, sodass in der nachfolgenden Middleware ausgelöste unbehandelte Ausnahmen abgefangen werden.

Bei Ausführung der App in der Produktionsumgebung dürfen keine detaillierten Informationen zu den Ausnahmen öffentlich angezeigt werden. Weitere Informationen zum Konfigurieren der Umgebung finden Sie unter Verwenden mehrerer Umgebungen in ASP.NET Core.

Die Seite mit Ausnahmen für Entwickler kann die folgenden Informationen zu der Ausnahme und der Anforderung enthalten:

  • Stapelüberwachung
  • Abfragezeichenfolgeparameter, sofern vorhanden
  • Cookies, sofern vorhanden
  • Header

Es ist nicht garantiert, dass auf der Seite mit Ausnahmen für Entwickler Informationen angezeigt werden. Vollständige Informationen zu Fehlern finden Sie unter Protokollierung.

Seite „Ausnahmehandler“

Um eine benutzerdefinierte Fehlerbehandlungsseite für die Produktionsumgebung zu konfigurieren, rufen Sie UseExceptionHandler auf. Die Aufgaben der Middleware zur Ausnahmebehandlung:

  • Dient zum Abfangen und Protokollieren unbehandelter Ausnahmen.
  • Führt erneut die Anforderung in einer anderen Pipeline im angegebenen Pfad aus. Die Anforderung wird nicht erneut ausgeführt, wenn die Antwort gestartet wurde. Der von der Vorlage generierte Code führt die Anforderung erneut mit dem /Error-Pfad aus.

Warnung

Wenn die alternative Pipeline eigenständig eine Ausnahme auslöst, löst die Middleware für die Ausnahmebehandlung erneut die ursprüngliche Ausnahme 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 _next bereinigt oder die Verarbeitung im HttpContext zwischengespeichert 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 fügt UseExceptionHandler die Middleware zur Ausnahmebehandlung in Nichtentwicklungsumgebungen hinzu:

var app = builder.Build();

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

Die Razor Pages-App-Vorlage stellt im Ordner Pages eine Fehlerseite (.cshtml) und eine PageModel-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 Anforderungen die benutzerdefinierte Seite für die Fehlerbehandlung erreichen, beschränken Sie die Endpunkte 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 OnGet zum Behandeln von GET-Ausnahmen und OnPost zum 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.";
        }
    }
}

Warnung

Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Bereitstellung von Fehlern ist ein Sicherheitsrisiko.

Lambda-Ausdruck für Ausnahmehandler

Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite ist das Angeben eines Lambda-Ausdrucks für UseExceptionHandler. Die Verwendung eines Lambda-Ausdrucks ermöglicht den Zugriff auf den Fehler, bevor die Antwort zurückgegeben wird.

Der folgende Code verwendet einen Lambdaausdruck für die 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();
}

Warnung

Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Bereitstellung von Fehlern ist ein Sicherheitsrisiko.

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 vorherige Code in der Entwicklungsumgebung ausgeführt wird:

  • Zuerst wird CustomExceptionHandler aufgerufen, um eine Ausnahme zu behandeln.
  • Nach der Protokollierung der Ausnahme gibt die TryHandleException-Methode false zurück, sodass die Entwickler-Ausnahmeseite angezeigt wird.

In anderen Umgebungen:

  • Zuerst wird CustomExceptionHandler aufgerufen, um eine Ausnahme zu behandeln.
  • Nach der Protokollierung der Ausnahme gibt die TryHandleException-Methode false zurü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 für gängige Statuscodes einfache Handler im Textformat zu aktivieren, rufen Sie UseStatusCodePages in Program.csauf:

var app = builder.Build();

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

app.UseStatusCodePages();

Rufen Sie UseStatusCodePages vor der Middleware für die Anforderungsverarbeitung auf. Rufen Sie beispielsweise 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.

Hinweis

Die Middleware für Statuscodeseiten erfasst keine Ausnahmen. Um eine benutzerdefinierte Fehlerbehandlungsseite bereitzustellen, verwenden Sie die Ausnahmehandlerseite.

UseStatusCodePages mit Formatzeichenfolge

Um den Inhaltstyp und Text der Antwort anzupassen, verwenden Sie die Überladung von UseStatusCodePages, die einen Inhaltstyp und eine Formatierungszeichenfolge erfordert:

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 einer Tilde (~) beginnt, wird ~ durch die Angabe für 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.
  • Den ursprünglichen Statuscode mit der anfänglichen Umleitungsantwort nicht beibehalten und zurückgeben.

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 = string.Join(
                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 _next bereinigt oder die Verarbeitung im HttpContext zwischengespeichert 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.

Verwenden Sie IStatusCodePagesFeature, um Statuscodeseiten für bestimmte Anforderungen in der Handlermethode von Razor Pages oder in einem MVC-Controller zu deaktivieren:

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. Produktionsfehlerseiten sollten gründlich getestet werden. Achten Sie darauf, dabei keine eigenen Ausnahmen auszulösen.

Antwortheader

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.

Sichere 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 während des Starts

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 Entwicklungsumgebung 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. Es wird empfohlen, UseExceptionHandler zu verwenden, wenn Sie für die Fehlerbehandlung auf Grundlage einer ausgewählten MVC-Aktion eine andere Strategie auswählen müssen.

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

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:

  1. Verwenden Sie ProblemDetailsOptions.CustomizeProblemDetails
  2. Verwenden einer benutzerdefinierten IProblemDetailsWriter-Datei
  3. Aufrufen von IProblemDetailsService in einer Middleware

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"
}

Benutzerdefinierter 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 obigen Code geben die Minimal-API-Endpunkte /divide und /squareroot bei einer Fehlereingabe die erwartete benutzerdefinierte Problemantwort 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));
    }

}

Erstellen von ProblemDetails-Nutzdaten für Ausnahmen

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 ist das Angeben eines Lambda-Ausdrucks für UseExceptionHandler. 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();

Warnung

Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Bereitstellung von Fehlern ist ein Sicherheitsrisiko.

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.

Zusätzliche 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 Web-APIs und Behandeln von Fehlern in Minimal-API-Apps.

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:

Die Seite mit Ausnahmen für Entwickler wird früh in der Middlewarepipeline ausgeführt, sodass in der nachfolgenden Middleware ausgelöste unbehandelte Ausnahmen abgefangen werden.

Bei Ausführung der App in der Produktionsumgebung dürfen keine detaillierten Informationen zu den Ausnahmen öffentlich angezeigt werden. Weitere Informationen zum Konfigurieren der Umgebung finden Sie unter Verwenden mehrerer Umgebungen in ASP.NET Core.

Die Seite mit Ausnahmen für Entwickler kann die folgenden Informationen zu der Ausnahme und der Anforderung enthalten:

  • Stapelüberwachung
  • Abfragezeichenfolgeparameter, sofern vorhanden
  • Cookies, sofern vorhanden
  • Header

Es ist nicht garantiert, dass auf der Seite mit Ausnahmen für Entwickler Informationen angezeigt werden. Vollständige Informationen zu Fehlern finden Sie unter Protokollierung.

Seite „Ausnahmehandler“

Um eine benutzerdefinierte Fehlerbehandlungsseite für die Produktionsumgebung zu konfigurieren, rufen Sie UseExceptionHandler auf. Die Aufgaben der Middleware zur Ausnahmebehandlung:

  • Dient zum Abfangen und Protokollieren unbehandelter Ausnahmen.
  • Führt erneut die Anforderung in einer anderen Pipeline im angegebenen Pfad aus. Die Anforderung wird nicht erneut ausgeführt, wenn die Antwort gestartet wurde. Der von der Vorlage generierte Code führt die Anforderung erneut mit dem /Error-Pfad aus.

Warnung

Wenn die alternative Pipeline eigenständig eine Ausnahme auslöst, löst die Middleware für die Ausnahmebehandlung erneut die ursprüngliche Ausnahme 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 _next bereinigt oder die Verarbeitung im HttpContext zwischengespeichert 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 fügt UseExceptionHandler die Middleware zur Ausnahmebehandlung in Nichtentwicklungsumgebungen hinzu:

var app = builder.Build();

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

Die Razor Pages-App-Vorlage stellt im Ordner Pages eine Fehlerseite (.cshtml) und eine PageModel-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 Anforderungen die benutzerdefinierte Seite für die Fehlerbehandlung erreichen, beschränken Sie die Endpunkte 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 OnGet zum Behandeln von GET-Ausnahmen und OnPost zum 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.";
        }
    }
}

Warnung

Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Bereitstellung von Fehlern ist ein Sicherheitsrisiko.

Lambda-Ausdruck für Ausnahmehandler

Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite ist das Angeben eines Lambda-Ausdrucks für UseExceptionHandler. Die Verwendung eines Lambda-Ausdrucks ermöglicht den Zugriff auf den Fehler, bevor die Antwort zurückgegeben wird.

Der folgende Code verwendet einen Lambdaausdruck für die 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();
}

Warnung

Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Bereitstellung von Fehlern ist ein Sicherheitsrisiko.

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 für gängige Statuscodes einfache Handler im Textformat zu aktivieren, rufen Sie UseStatusCodePages in Program.csauf:

var app = builder.Build();

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

app.UseStatusCodePages();

Rufen Sie UseStatusCodePages vor der Middleware für die Anforderungsverarbeitung auf. Rufen Sie beispielsweise 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.

Hinweis

Die Middleware für Statuscodeseiten erfasst keine Ausnahmen. Um eine benutzerdefinierte Fehlerbehandlungsseite bereitzustellen, verwenden Sie die Ausnahmehandlerseite.

UseStatusCodePages mit Formatzeichenfolge

Um den Inhaltstyp und Text der Antwort anzupassen, verwenden Sie die Überladung von UseStatusCodePages, die einen Inhaltstyp und eine Formatierungszeichenfolge erfordert:

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 einer Tilde (~) beginnt, wird ~ durch die Angabe für 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.
  • Den ursprünglichen Statuscode mit der anfänglichen Umleitungsantwort nicht beibehalten und zurückgeben.

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 = string.Join(
                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 _next bereinigt oder die Verarbeitung im HttpContext zwischengespeichert 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.

Verwenden Sie IStatusCodePagesFeature, um Statuscodeseiten für bestimmte Anforderungen in der Handlermethode von Razor Pages oder in einem MVC-Controller zu deaktivieren:

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. Produktionsfehlerseiten sollten gründlich getestet werden. Achten Sie darauf, dabei keine eigenen Ausnahmen auszulösen.

Antwortheader

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.

Sichere 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 während des Starts

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 Entwicklungsumgebung 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. Es wird empfohlen, UseExceptionHandler zu verwenden, wenn Sie für die Fehlerbehandlung auf Grundlage einer ausgewählten MVC-Aktion eine andere Strategie auswählen müssen.

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

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:

  1. Verwenden Sie ProblemDetailsOptions.CustomizeProblemDetails
  2. Verwenden einer benutzerdefinierten IProblemDetailsWriter-Datei
  3. Aufrufen von IProblemDetailsService in einer Middleware

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"
}

Benutzerdefinierter 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 obigen Code geben die Minimal-API-Endpunkte /divide und /squareroot bei einer Fehlereingabe die erwartete benutzerdefinierte Problemantwort 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));
    }

}

Erstellen von ProblemDetails-Nutzdaten für Ausnahmen

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 ist das Angeben eines Lambda-Ausdrucks für UseExceptionHandler. 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();

Warnung

Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Bereitstellung von Fehlern ist ein Sicherheitsrisiko.

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.

Zusätzliche Ressourcen

Von Tom Dykstra

Dieser Artikel beschreibt grundsätzliche Vorgehensweisen zur Behandlung von Fehlern in ASP.NET Core-Web-Apps. Informationen zu Web-APIs finden Sie unter Fehlerbehandlung in ASP.NET Core-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:

Die Seite mit Ausnahmen für Entwickler wird früh in der Middlewarepipeline ausgeführt, sodass in der nachfolgenden Middleware ausgelöste unbehandelte Ausnahmen abgefangen werden.

Bei Ausführung der App in der Produktionsumgebung dürfen keine detaillierten Informationen zu den Ausnahmen öffentlich angezeigt werden. Weitere Informationen zum Konfigurieren der Umgebung finden Sie unter Verwenden mehrerer Umgebungen in ASP.NET Core.

Die Seite mit Ausnahmen für Entwickler kann die folgenden Informationen zu der Ausnahme und der Anforderung enthalten:

  • Stapelüberwachung
  • Abfragezeichenfolgeparameter, sofern vorhanden
  • Cookies, sofern vorhanden
  • Header

Es ist nicht garantiert, dass auf der Seite mit Ausnahmen für Entwickler Informationen angezeigt werden. Vollständige Informationen zu Fehlern finden Sie unter Protokollierung.

Seite „Ausnahmehandler“

Um eine benutzerdefinierte Fehlerbehandlungsseite für die Produktionsumgebung zu konfigurieren, rufen Sie UseExceptionHandler auf. Die Aufgaben der Middleware zur Ausnahmebehandlung:

  • Dient zum Abfangen und Protokollieren unbehandelter Ausnahmen.
  • Führt erneut die Anforderung in einer anderen Pipeline im angegebenen Pfad aus. Die Anforderung wird nicht erneut ausgeführt, wenn die Antwort gestartet wurde. Der von der Vorlage generierte Code führt die Anforderung erneut mit dem /Error-Pfad aus.

Warnung

Wenn die alternative Pipeline eigenständig eine Ausnahme auslöst, löst die Middleware für die Ausnahmebehandlung erneut die ursprüngliche Ausnahme aus.

Im folgenden Beispiel fügt UseExceptionHandler die Middleware zur Ausnahmebehandlung in Nichtentwicklungsumgebungen hinzu:

var app = builder.Build();

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

Die Razor Pages-App-Vorlage stellt im Ordner Pages eine Fehlerseite (.cshtml) und eine PageModel-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 Anforderungen die benutzerdefinierte Seite für die Fehlerbehandlung erreichen, beschränken Sie die Endpunkte 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 OnGet zum Behandeln von GET-Ausnahmen und OnPost zum 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.";
        }
    }
}

Warnung

Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Bereitstellung von Fehlern ist ein Sicherheitsrisiko.

Lambda-Ausdruck für Ausnahmehandler

Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite ist das Angeben eines Lambda-Ausdrucks für UseExceptionHandler. Die Verwendung eines Lambda-Ausdrucks ermöglicht den Zugriff auf den Fehler, bevor die Antwort zurückgegeben wird.

Der folgende Code verwendet einen Lambdaausdruck für die 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();
}

Warnung

Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Bereitstellung von Fehlern ist ein Sicherheitsrisiko.

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 für gängige Statuscodes einfache Handler im Textformat zu aktivieren, rufen Sie UseStatusCodePages in Program.csauf:

var app = builder.Build();

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

app.UseStatusCodePages();

Rufen Sie UseStatusCodePages vor der Middleware für die Anforderungsverarbeitung auf. Rufen Sie beispielsweise 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.

Hinweis

Die Middleware für Statuscodeseiten erfasst keine Ausnahmen. Um eine benutzerdefinierte Fehlerbehandlungsseite bereitzustellen, verwenden Sie die Ausnahmehandlerseite.

UseStatusCodePages mit Formatzeichenfolge

Um den Inhaltstyp und Text der Antwort anzupassen, verwenden Sie die Überladung von UseStatusCodePages, die einen Inhaltstyp und eine Formatierungszeichenfolge erfordert:

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 einer Tilde (~) beginnt, wird ~ durch die Angabe für 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.
  • Den ursprünglichen Statuscode mit der anfänglichen Umleitungsantwort nicht beibehalten und zurückgeben.

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.

Verwenden Sie IStatusCodePagesFeature, um Statuscodeseiten für bestimmte Anforderungen in der Handlermethode von Razor Pages oder in einem MVC-Controller zu deaktivieren:

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. Produktionsfehlerseiten sollten gründlich getestet werden. Achten Sie darauf, dabei keine eigenen Ausnahmen auszulösen.

Antwortheader

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.

Sichere 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 während des Starts

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 Entwicklungsumgebung 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. Es wird empfohlen, UseExceptionHandler zu verwenden, wenn Sie für die Fehlerbehandlung auf Grundlage einer ausgewählten MVC-Aktion eine andere Strategie auswählen müssen.

Modellstatusfehler

Informationen zum Behandeln von Modellstatusfehlern finden Sie unter Modellbindung und Modellvalidierung.

Zusätzliche 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. Informationen zu Web-APIs finden Sie unter Fehlerbehandlung in ASP.NET Core-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 vorangehende hervorgehobene Code aktiviert die Seite mit Ausnahmen für Entwickler, wenn die App in der Entwicklungsumgebung ausgeführt wird.

Die Vorlagen platzieren UseDeveloperExceptionPage am Anfang der Middlewarepipeline, sodass die in der nachfolgenden Middleware ausgelösten unbehandelten Ausnahmen abgefangen werden.

Der vorangehende Code aktiviert die Seite mit Ausnahmen für Entwickler nur dann, wenn die App in der Entwicklungsumgebung ausgeführt wird. Bei Ausführung der App in der Produktionsumgebung dürfen keine detaillierten Informationen zu den Ausnahmen öffentlich angezeigt werden. Weitere Informationen zum Konfigurieren der Umgebung finden Sie unter Verwenden mehrerer Umgebungen in ASP.NET Core.

Die Seite mit Ausnahmen für Entwickler kann die folgenden Informationen zu der Ausnahme und der Anforderung enthalten:

  • Stapelüberwachung
  • Abfragezeichenfolgeparameter, sofern vorhanden
  • Cookies, sofern vorhanden
  • Header

Es ist nicht garantiert, dass auf der Seite mit Ausnahmen für Entwickler Informationen angezeigt werden. Vollständige Informationen zu Fehlern finden Sie unter Protokollierung.

Seite „Ausnahmehandler“

Um eine benutzerdefinierte Fehlerbehandlungsseite für die Produktionsumgebung zu konfigurieren, rufen Sie UseExceptionHandler auf. Die Aufgaben der Middleware zur Ausnahmebehandlung:

  • Dient zum Abfangen und Protokollieren unbehandelter Ausnahmen.
  • Führt erneut die Anforderung in einer anderen Pipeline im angegebenen Pfad aus. Die Anforderung wird nicht erneut ausgeführt, wenn die Antwort gestartet wurde. Der von der Vorlage generierte Code führt die Anforderung erneut mit dem /Error-Pfad aus.

Warnung

Wenn die alternative Pipeline eigenständig eine Ausnahme auslöst, löst die Middleware für die Ausnahmebehandlung erneut die ursprüngliche Ausnahme aus.

Im folgenden Beispiel fügt UseExceptionHandler die Middleware zur Ausnahmebehandlung in Nichtentwicklungsumgebungen hinzu:

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

Die Razor Pages-App-Vorlage stellt im Ordner Pages eine Fehlerseite (.cshtml) und eine PageModel-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 Anforderungen die benutzerdefinierte Seite für die Fehlerbehandlung erreichen, beschränken Sie die Endpunkte 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 OnGet zum Behandeln von GET-Ausnahmen und OnPost zum 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 dem Standard Pages/Error.cshtml.cs die von den ASP.NET Core-Vorlagen generierte ExceptionMessage 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";
        }
    }
}

Warnung

Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Bereitstellung von Fehlern ist ein Sicherheitsrisiko.

So testen Sie die Ausnahme in der Beispiel-App

  • Legen Sie die Umgebung auf Produktion fest.
  • Entfernen Sie die Kommentare aus webBuilder.UseStartup<Startup>(); in Program.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 ist das Angeben eines Lambda-Ausdrucks für UseExceptionHandler. Die Verwendung eines Lambda-Ausdrucks ermöglicht den Zugriff auf den Fehler, bevor die Antwort zurückgegeben wird.

Der folgende Code verwendet einen Lambdaausdruck für die 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();
    });
}

Warnung

Stellen Sie keine vertraulichen Fehlerinformationen aus IExceptionHandlerFeature oder IExceptionHandlerPathFeature für Clients bereit. Die Bereitstellung von Fehlern ist ein Sicherheitsrisiko.

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>(); in Program.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 für gängige Statuscodes einfache Handler im Textformat zu aktivieren, rufen Sie in der Startup.Configure-Methode UseStatusCodePages 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 für die Anforderungsverarbeitung auf. Rufen Sie beispielsweise 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 zum Beispiel 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>(); in Program.cs.
  • Klicken Sie auf die Links auf der Startseite.

Hinweis

Die Middleware für Statuscodeseiten erfasst keine Ausnahmen. Um eine benutzerdefinierte Fehlerbehandlungsseite bereitzustellen, verwenden Sie die Ausnahmehandlerseite.

UseStatusCodePages mit Formatzeichenfolge

Um den Inhaltstyp und Text der Antwort anzupassen, verwenden Sie die Überladung von UseStatusCodePages, die einen Inhaltstyp und eine Formatierungszeichenfolge erfordert:

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 einer Tilde (~) beginnt, wird ~ durch die Angabe für 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.
  • Den ursprünglichen Statuscode mit der anfänglichen Umleitungsantwort nicht beibehalten und zurückgeben.

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 einem Schrägstrich (/) 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.

Verwenden Sie IStatusCodePagesFeature, um Statuscodeseiten für bestimmte Anforderungen in der Handlermethode von Razor Pages oder in einem MVC-Controller zu deaktivieren:

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. Produktionsfehlerseiten sollten gründlich getestet werden. Achten Sie darauf, dabei keine eigenen Ausnahmen auszulösen.

Antwortheader

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.

Sichere 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 während des Starts

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 Entwicklungsumgebung 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. Es wird empfohlen, UseExceptionHandler zu verwenden, wenn Sie für die Fehlerbehandlung auf Grundlage einer ausgewählten MVC-Aktion eine andere Strategie auswählen müssen.

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.

Zusätzliche Ressourcen

Von Tom Dykstra und Steve Smith

Dieser Artikel beschreibt grundsätzliche Vorgehensweisen zur Behandlung von Fehlern in ASP.NET Core-Web-Apps. Informationen zu Web-APIs finden Sie unter Fehlerbehandlung in ASP.NET Core-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();
}

Der vorangehende Code aktiviert die Seite mit Ausnahmen für Entwickler, wenn die App in der Entwicklungsumgebung ausgeführt wird.

Die Vorlagen platzieren UseDeveloperExceptionPage vor jeder Middleware, sodass Ausnahmen in der nachfolgenden Middleware abgefangen werden.

Der vorangehende Code aktiviert die Seite mit Ausnahmen für Entwickler nur dann, wenn die App in der Entwicklungsumgebung 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 der Umgebung finden Sie unter Verwenden mehrerer Umgebungen in ASP.NET Core.

Die Seite mit Ausnahmen für Entwickler enthält die folgenden Informationen zu der Ausnahme und der Anforderung:

  • Stapelüberwachung
  • Abfragezeichenfolgeparameter, sofern vorhanden
  • Cookies, sofern vorhanden
  • Header

Seite „Ausnahmehandler“

Um eine benutzerdefinierte Fehlerbehandlung für die Produktionsumgebung zu konfigurieren, verwenden Sie Middleware zur Ausnahmebehandlung. 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 Anforderung wird nicht erneut ausgeführt, wenn die Antwort gestartet wurde. Der von der Vorlage generierte Code führt die Anforderung erneut mit /Error aus.

Im folgenden Beispiel fügt UseExceptionHandler die Middleware zur Ausnahmebehandlung in Nichtentwicklungsumgebungen hinzu:

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

Die Razor Pages-App-Vorlage stellt im Ordner Pages eine Fehlerseite ( .cshtml) und PageModel-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. Durch explizite Verben könnte bei einigen Anforderungen verhindert werden, dass diese Methode zum Einsatz kommt. 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";
        }
    }
}

Warnung

Stellen Sie Clients keine vertraulichen Fehlerinformationen bereit. Die Bereitstellung von Fehlern ist ein Sicherheitsrisiko.

Legen Sie die Umgebung auf Produktionen fest, und erzwingen Sie eine Ausnahme, um die vorherige Seite zur Ausnahmebehandlung zu aktivieren.

Lambda-Ausdruck für Ausnahmehandler

Eine Alternative zu einer benutzerdefinierten Ausnahmebehandlungsseite ist das Angeben eines Lambda-Ausdrucks für UseExceptionHandler. Die Verwendung eines Lambda-Ausdrucks ermöglicht den Zugriff auf den Fehler, bevor die Antwort zurückgegeben wird.

Hier ist ein Beispiel der Verwendung eines Lambda-Ausdrucks für die 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.

Warnung

Stellen Sie keine vertraulichen Fehlerinformationen aus IExceptionHandlerFeature oder IExceptionHandlerPathFeature für Clients bereit. Die Bereitstellung von Fehlern ist ein Sicherheitsrisiko.

Um in der Beispiel-App das Ergebnis des Lambda-Ausdrucks für die Ausnahmebehandlung anzuzeigen, verwenden Sie die Präprozessordirektiven 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 für gängige Statuscodes einfache Handler im Textformat zu aktivieren, rufen Sie in der Startup.Configure-Methode UseStatusCodePages auf:

app.UseStatusCodePages();

Rufen Sie UseStatusCodePages vor Middleware zur 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 zum Beispiel 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 Text der Antwort anzupassen, verwenden Sie die Überladung von UseStatusCodePages, die einen Inhaltstyp und eine Formatierungszeichenfolge erfordert:

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 einer Tilde (~) beginnt, wird ~ durch die Angabe für 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.
  • Den ursprünglichen Statuscode mit der anfänglichen Umleitungsantwort nicht beibehalten und zurückgeben.

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 Page für Fehler sollte z. B. 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. Durch explizite Verben könnte bei einigen Anforderungen verhindert werden, dass diese Methode zum Einsatz kommt. 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.

Verwenden Sie IStatusCodePagesFeature, um Statuscodeseiten für bestimmte Anforderungen in der Handlermethode von Razor Pages oder in einem MVC-Controller zu deaktivieren:

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.

Antwortheader

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.

Sichere 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 während des Starts

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 „Datenbank-Fehlerseite“ erfasst datenbankbezogene Ausnahmen, die mithilfe von Entity Framework-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 darf nur in der Entwicklungsumgebung aktiviert werden. Aktivieren Sie die Seite, indem Sie Startup.Configure Code 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.

Tipp

Ausnahmefilter eignen sich zum Auffangen von Ausnahmen, die in MVC-Aktionen auftreten. Sie sind jedoch nicht so flexibel wie 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.

Zusätzliche Ressourcen