Delen via


Fouten in ASP.NET Core verwerken

Note

Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.

Warning

Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.

Important

Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.

Zie de .NET 9-versie van dit artikelvoor de huidige release.

Door Tom Dykstra

In dit artikel worden veelvoorkomende benaderingen besproken voor het afhandelen van fouten in ASP.NET Core-web-apps. Zie ook Fouten verwerken in ASP.NET Core-web-API's op basis van een controller en fouten verwerken in minimale API's.

Zie BlazorFouten afhandelen in ASP.NET Core-apps Blazorvoor hulp bij het afhandelen van fouten, die worden toegevoegd aan of vervangen door de richtlijnen in dit artikel.

Uitzonderingspagina voor ontwikkelaars

Op de uitzonderingspagina voor ontwikkelaars wordt gedetailleerde informatie weergegeven over niet-verwerkte aanvraag-uitzonderingen. Het maakt gebruik DeveloperExceptionPageMiddleware van het vastleggen van synchrone en asynchrone uitzonderingen van de HTTP-pijplijn en voor het genereren van foutreacties. De ontwikkelaarsexceptiepagina draait vroeg in de middleware-pijplijn, zodat onopgevangen uitzonderingen die in de daaropvolgende middleware worden gegooid, kunnen worden opgevangen.

ASP.NET Core-apps de uitzonderingspagina voor ontwikkelaars standaard inschakelen wanneer beide:

Apps die zijn gemaakt met behulp van eerdere sjablonen, dat wil zeggen door WebHost.CreateDefaultBuilder te gebruiken, kunnen de uitzonderingspagina voor ontwikkelaars inschakelen door app.UseDeveloperExceptionPage aan te roepen.

Warning

Schakel de uitzonderingspagina voor ontwikkelaars alleen in als de app wordt uitgevoerd in de ontwikkelomgeving. Deel geen gedetailleerde uitzonderingsgegevens openbaar wanneer de app in productie wordt uitgevoerd. Zie Meerdere omgevingen gebruiken in ASP.NET Core voor meer informatie over het configureren van omgevingen.

De uitzonderingspagina voor ontwikkelaars kan de volgende informatie bevatten over de uitzondering en de aanvraag:

  • Stack trace
  • Queryreeksparameters, indien van toepassing
  • Cookies, indien van toepassing
  • Headers
  • Eindpuntmetagegevens, indien van toepassing

De uitzonderingspagina voor ontwikkelaars is niet gegarandeerd informatie te verstrekken. Gebruik Logging voor volledige foutinformatie.

In de volgende afbeelding ziet u een voorbeeld van een uitzonderingspagina voor ontwikkelaars met animatie om de tabbladen en de weergegeven informatie weer te geven:

Ontwikkelaar uitzondering pagina geanimeerd om te laten zien welk tabblad is geselecteerd.

Als reactie op een aanvraag met een Accept: text/plain header retourneert de uitzonderingspagina voor ontwikkelaars tekst zonder opmaak in plaats van HTML. For example:

Status: 500 Internal Server Error
Time: 9.39 msSize: 480 bytes
FormattedRawHeadersRequest
Body
text/plain; charset=utf-8, 480 bytes
System.InvalidOperationException: Sample Exception
   at WebApplicationMinimal.Program.<>c.<Main>b__0_0() in C:\Source\WebApplicationMinimal\Program.cs:line 12
   at lambda_method1(Closure, Object, HttpContext)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

HEADERS
=======
Accept: text/plain
Host: localhost:7267
traceparent: 00-0eab195ea19d07b90a46cd7d6bf2f

Uitzonderingshandlerpagina

Als u een aangepaste pagina voor foutafhandeling voor de productieomgeving wilt configureren, roept UseExceptionHandleru aan. Deze middleware voor uitzonderingafhandeling:

  • Niet-verwerkte uitzonderingen worden onderschept en in logboeken opgeslagen.
  • Voert de aanvraag opnieuw uit in een alternatieve pijplijn met behulp van het aangegeven pad. De aanvraag wordt niet opnieuw uitgevoerd als het antwoord is gestart. De door de sjabloon gegenereerde code voert de aanvraag opnieuw uit met behulp van het /Error pad.

Warning

Als de alternatieve pijplijn een eigen uitzondering genereert, wordt de oorspronkelijke uitzondering opnieuw door de Middleware gegooid.

Omdat deze middleware de aanvraagpijplijn opnieuw kan uitvoeren:

  • Middleware moet herinvoerbaarheid bij dezelfde aanvraag afhandelen. Dit betekent normaal gesproken dat, na het aanroepen van _next, de status wordt opgeschoond of dat de verwerking in de cache van HttpContext wordt opgeslagen om te voorkomen dat deze opnieuw wordt uitgevoerd. Bij het verwerken van de aanvraagtekst betekent dit het bufferen of cachen van de resultaten, zoals de formulierlezer.
  • Voor de UseExceptionHandler(IApplicationBuilder, String) overbelasting die in sjablonen wordt gebruikt, wordt alleen het aanvraagpad gewijzigd en worden de routegegevens gewist. Aanvraaggegevens zoals headers, methoden en items worden allemaal opnieuw gebruikt as-is.
  • Scoped services blijven hetzelfde.

In het volgende voorbeeld voegt UseExceptionHandler de middleware voor uitzonderingsafhandeling toe in niet-ontwikkelingsomgevingen.

var app = builder.Build();

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

De Razor app-sjabloon .cshtml bevat een foutpagina (PageModel) en ErrorModel klasse () in de map Pagina's. Voor een MVC-app bevat de projectsjabloon een Error actiemethode en een foutweergave voor de Home controller.

Met de uitzonderingsafhandeling van middleware wordt de aanvraag opnieuw uitgevoerd met behulp van de oorspronkelijke HTTP-methode. Als een fouthandlereindpunt is beperkt tot een specifieke set HTTP-methoden, wordt het alleen uitgevoerd voor deze HTTP-methoden. Een MVC-controlleractie die gebruikmaakt van het [HttpGet] kenmerk wordt bijvoorbeeld alleen uitgevoerd voor GET-aanvragen. Als u ervoor wilt zorgen dat alle aanvragen de aangepaste pagina voor foutafhandeling bereiken, beperkt u deze niet tot een specifieke set HTTP-methoden.

Uitzonderingen op een andere manier verwerken op basis van de oorspronkelijke HTTP-methode:

  • Voor Razor Pagina's maakt u meerdere handlermethoden. Gebruik bijvoorbeeld OnGet om GET-uitzonderingen af te handelen en om OnPost POST-uitzonderingen af te handelen.
  • Pas voor MVC HTTP-werkwoordkenmerken toe op meerdere acties. Gebruik bijvoorbeeld [HttpGet] om GET-uitzonderingen af te handelen en om [HttpPost] POST-uitzonderingen af te handelen.

Als u niet-geverifieerde gebruikers wilt toestaan om de aangepaste pagina voor foutafhandeling weer te geven, moet u ervoor zorgen dat deze anonieme toegang ondersteunt.

Toegang tot de uitzondering

Gebruik IExceptionHandlerPathFeature om toegang te krijgen tot de uitzondering en het oorspronkelijke aanvraagpad in een foutafhandelaar. In het volgende voorbeeld wordt IExceptionHandlerPathFeature gebruikt om meer informatie te krijgen over de uitzonderingsfout die is opgetreden.

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

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

    public string? ExceptionMessage { get; set; }

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

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

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

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

Warning

Geef geen gevoelige foutinformatie aan clients. Het doorgeven van fouten is een beveiligingsrisico.

Uitzonderingshandler lambda

Een alternatief voor een aangepaste uitzonderingshandlerpagina is een lambda aanbieden aan UseExceptionHandler. Als u een lambda gebruikt, heeft u toegang tot de fout voordat u het antwoord retourneert.

De volgende code maakt gebruik van een lambda voor het verwerken van uitzonderingen:

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();
}

Een andere manier om een lambda te gebruiken, is door de statuscode in te stellen op basis van het uitzonderingstype, zoals in het volgende voorbeeld:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
    app.UseExceptionHandler(new ExceptionHandlerOptions
    {
        StatusCodeSelector = ex => ex is TimeoutException
            ? StatusCodes.Status503ServiceUnavailable
            : StatusCodes.Status500InternalServerError
    });
}

Warning

Geef geen gevoelige foutinformatie aan clients. Het doorgeven van fouten is een beveiligingsrisico.

IExceptionHandler

IExceptionHandler is een interface die de ontwikkelaar een callback geeft voor het verwerken van bekende uitzonderingen op een centrale locatie.

IExceptionHandler implementaties worden geregistreerd door IServiceCollection.AddExceptionHandler<T>aan te roepen. De levensduur van een IExceptionHandler exemplaar is singleton. Er kunnen meerdere implementaties worden toegevoegd en ze worden aangeroepen in de geregistreerde volgorde.

Als een uitzonderingshandler een aanvraag verwerkt, kan deze true retourneren om de verwerking te stoppen. Als een uitzondering niet wordt afgehandeld door een uitzonderingshandler, valt de besturing terug op het standaardgedrag en de opties van de middleware. Verschillende metrische gegevens en logboeken worden verzonden voor verwerkte en niet-verwerkte uitzonderingen.

In het volgende voorbeeld ziet u een IExceptionHandler implementatie:

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);
        }
    }
}

In het volgende voorbeeld ziet u hoe u een IExceptionHandler implementatie registreert voor afhankelijkheidsinjectie:

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

Wanneer de voorgaande code wordt uitgevoerd in de ontwikkelomgeving:

  • De CustomExceptionHandler wordt als eerste aangeroepen om een uitzondering af te handelen.
  • Nadat de uitzondering is gelogd, retourneert de TryHandleAsync methode false, zodat de uitzonderingspagina voor ontwikkelaars wordt weergegeven.

In andere omgevingen:

  • De CustomExceptionHandler wordt als eerste aangeroepen om een uitzondering af te handelen.
  • Nadat de uitzondering is aangemeld, wordt de TryHandleAsync methode geretourneerd false, zodat de /Error pagina wordt weergegeven.

UseStatusCodePages

Een ASP.NET Core-app biedt standaard geen pagina met statuscode voor HTTP-foutcodes, zoals 404 - Niet gevonden. Wanneer de app een HTTP 400-599-foutcode instelt die geen hoofdtekst heeft, wordt de statuscode en een lege antwoordtekst geretourneerd. Als u standaardhandlers voor alleen-tekst wilt inschakelen voor veelvoorkomende foutcodes, roept u UseStatusCodePages aan in Program.cs.

var app = builder.Build();

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

app.UseStatusCodePages();

Roep UseStatusCodePages aan voordat de middleware voor aanvraagverwerking wordt afgehandeld. Roep bijvoorbeeld UseStatusCodePages aan vóór de Static File Middleware en de Endpoints Middleware.

Wanneer UseStatusCodePages dit niet wordt gebruikt, retourneert het navigeren naar een URL zonder eindpunt een browserafhankelijk foutbericht dat aangeeft dat het eindpunt niet kan worden gevonden. Wanneer UseStatusCodePages wordt aangeroepen, retourneert de browser het volgende antwoord:

Status Code: 404; Not Found

UseStatusCodePages wordt meestal niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

Note

De middleware van de statuscodepagina's vangt geen uitzonderingen op. Als u een aangepaste foutafhandelingspagina wilt opgeven, gebruikt u de uitzonderingshandlerpagina.

UseStatusCodePages met formaattekenreeks

Om het inhoudstype en de tekst van de reactie aan te passen, gebruikt u de overload van UseStatusCodePages die een inhoudstype en een opmaaktekenreeks vereist:

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

In de voorgaande code {0} is een tijdelijke aanduiding voor de foutcode.

UseStatusCodePages met een notatietekenreeks wordt doorgaans niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

UseStatusCodePages met lambda

Als u aangepaste code voor foutafhandeling en het schrijven van reacties wilt opgeven, gebruikt u de overload van UseStatusCodePages die een lambda-expressie accepteert:

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 met een lambda wordt meestal niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

UseStatusCodePagesWithRedirects

De UseStatusCodePagesWithRedirects extensiemethode:

  • Een 302 - Found statuscode wordt naar de client gestuurd.
  • Hiermee wordt de client omgeleid naar het eindpunt voor foutafhandeling dat is opgegeven in de URL-sjabloon. Het eindpunt voor foutafhandeling geeft doorgaans foutinformatie weer en retourneert HTTP 200.
var app = builder.Build();

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

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

De URL-sjabloon kan een {0} tijdelijke aanduiding voor de statuscode bevatten, zoals te zien is in de voorgaande code. Als de URL-sjabloon begint met ~ (tilde), wordt de ~ vervangen door de PathBase van de app. Wanneer u een eindpunt in de app opgeeft, maakt u een MVC-weergave of Razor -pagina voor het eindpunt.

Deze methode wordt vaak gebruikt wanneer de app:

  • Moet de client omleiden naar een ander eindpunt, meestal in gevallen waarin een andere app de fout verwerkt. Voor web-apps weerspiegelt de adresbalk van de client het omgeleide eindpunt.
  • Bewaar de oorspronkelijke statuscode niet en retourneer deze niet met het eerste omleidingsantwoord.

UseStatusCodePagesWithReExecute

De UseStatusCodePagesWithReExecute extensiemethode:

  • Hiermee wordt de hoofdtekst van het antwoord gegenereerd door de aanvraagpijplijn opnieuw uit te voeren met behulp van een alternatief pad.
  • Wijzigt de statuscode niet voor of na het opnieuw uitvoeren van de pijplijn.

De uitvoering van de nieuwe pijplijn kan de statuscode van het antwoord wijzigen, omdat de nieuwe pijplijn volledige controle heeft over de statuscode. Als de nieuwe pijplijn de statuscode niet wijzigt, wordt de oorspronkelijke statuscode naar de client verzonden.

var app = builder.Build();

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

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

Als er een eindpunt in de app is opgegeven, maakt u een MVC-weergave of Razor -pagina voor het eindpunt.

Deze methode wordt vaak gebruikt wanneer de app het volgende moet doen:

  • De aanvraag verwerken zonder om te leiden naar een ander eindpunt. Voor web-apps weerspiegelt de adresbalk van de browser van de client het oorspronkelijk aangevraagde eindpunt.
  • Behoud en retourneer de oorspronkelijke statuscode met het antwoord.

De URL-sjabloon moet beginnen met / en kan een tijdelijke aanduiding {0} voor de statuscode bevatten. Als u de statuscode wilt doorgeven als een querytekenreeksparameter, geeft u een tweede argument door aan UseStatusCodePagesWithReExecute. For example:

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

Het eindpunt dat de fout verwerkt, kan de oorspronkelijke URL ophalen die de fout heeft gegenereerd, zoals wordt weergegeven in het volgende voorbeeld:

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

    public string? OriginalPathAndQuery { get; set; }

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

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

        if (statusCodeReExecuteFeature is not null)
        {
            OriginalPathAndQuery = $"{statusCodeReExecuteFeature.OriginalPathBase}"
                                    + $"{statusCodeReExecuteFeature.OriginalPath}"
                                    + $"{statusCodeReExecuteFeature.OriginalQueryString}";

        }
    }
}

Omdat deze middleware de aanvraagpijplijn opnieuw kan uitvoeren:

  • Middleware moet herinvoerbaarheid bij dezelfde aanvraag afhandelen. Dit betekent normaal gesproken dat, na het aanroepen van _next, de status wordt opgeschoond of dat de verwerking in de cache van HttpContext wordt opgeslagen om te voorkomen dat deze opnieuw wordt uitgevoerd. Bij het verwerken van de aanvraagtekst betekent dit het bufferen of cachen van de resultaten, zoals de formulierlezer.
  • Scoped services blijven hetzelfde.

Statuscodepagina's uitschakelen

Gebruik het kenmerk [SkipStatusCodePages] om statuscodepagina's voor een MVC-controller of actiemethode uit te schakelen.

Als u statuscodepagina's wilt uitschakelen voor specifieke aanvragen in een Razor Pagina-handlermethode of in een MVC-controller, gebruikt u IStatusCodePagesFeature:

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

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

Exception-handling code

Code in uitzonderingsafhandelingspagina's kan ook uitzonderingen genereren. Productiefoutpagina's moeten grondig worden getest en extra voorzichtig zijn om te voorkomen dat ze zelf uitzonderingen genereren.

Response headers

Zodra de headers voor een antwoord zijn verzonden:

  • De app kan de statuscode van het antwoord niet wijzigen.
  • Eventuele uitzonderingspagina's of handlers kunnen niet worden uitgevoerd. Het antwoord moet worden voltooid of de verbinding is afgebroken.

Afhandeling van serveronderzondering

Naast de logica voor het afhandelen van uitzonderingen in een app kan de IMPLEMENTATIE van de HTTP-server enkele uitzonderingen verwerken. Als de server een uitzondering onderschept voordat antwoordheaders worden verzonden, verzendt de server een 500 - Internal Server Error antwoord zonder antwoordtekst. Als de server een uitzondering opmerkt nadat de responsheaders zijn verzonden, sluit de server de verbinding. Aanvragen die niet door de app worden verwerkt, worden verwerkt door de server. Eventuele uitzonderingen die optreden wanneer de server de aanvraag verwerkt, wordt verwerkt door de uitzonderingsafhandeling van de server. De aangepaste foutpagina's van de app, uitzonderingsafhandeling van middleware en filters hebben geen invloed op dit gedrag.

Afhandeling van opstartonderzondering

Alleen de hostinglaag kan uitzonderingen verwerken die plaatsvinden tijdens het opstarten van de app. De host kan worden geconfigureerd om opstartfouten vast te leggen en gedetailleerde fouten vast te leggen.

De hostinglaag kan alleen een foutpagina voor een vastgelegde opstartfout weergeven als de fout optreedt na hostadres-/poortbinding. Als de binding mislukt:

  • De hostinglaag registreert een kritieke uitzondering.
  • Het dotnet-proces loopt vast.
  • Er wordt geen foutpagina weergegeven wanneer de HTTP-server is Kestrel.

Bij uitvoering op IIS (of Azure App Service) of IIS Express wordt een procesfout van 502.5 geretourneerd door de ASP.NET Core-module als het proces niet kan worden gestart. Zie Problemen met ASP.NET Core in Azure App Service en IIS oplossen voor meer informatie.

Databasefoutpagina

Met het uitzonderingsfilter AddDatabaseDeveloperPageExceptionFilter voor databaseontwikkelaarspagina's worden databasegerelateerde uitzonderingen vastgelegd die kunnen worden opgelost met behulp van Entity Framework Core-migraties. Wanneer deze uitzonderingen optreden, wordt er een HTML-antwoord gegenereerd met details van mogelijke acties om het probleem op te lossen. Deze pagina is alleen ingeschakeld in de ontwikkelomgeving. Met de volgende code wordt het uitzonderingsfilter voor de databaseontwikkelaarspagina toegevoegd:

var builder = WebApplication.CreateBuilder(args);

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

Exception filters

In MVC-apps kunnen uitzonderingsfilters globaal of per controller of per actie worden geconfigureerd. In Razor Pagina-apps kunnen ze globaal of per paginamodel worden geconfigureerd. Deze filters verwerken eventuele niet-verwerkte uitzonderingen die optreden tijdens de uitvoering van een controlleractie of een ander filter. Zie Filters in ASP.NET Core voor meer informatie.

Uitzonderingsfilters zijn handig voor uitzonderingen die optreden binnen MVC-acties, maar ze zijn niet zo flexibel als de ingebouwde middleware voor het verwerken van uitzonderingen. UseExceptionHandler We raden aan UseExceptionHandler te gebruiken, tenzij u foutafhandeling anders moet uitvoeren op basis van welke MVC-actie wordt gekozen.

Fouten in modelstatus

Zie Modelbinding en Modelvalidatie voor informatie over het afhandelen van modelstatusfouten.

Problem details

Probleemdetails zijn niet de enige antwoordindeling voor het beschrijven van een HTTP-API-fout. Ze worden echter vaak gebruikt om fouten voor HTTP-API's te rapporteren.

De service met probleemdetails implementeert de IProblemDetailsService interface, die ondersteuning biedt voor het maken van probleemdetails in ASP.NET Core. De AddProblemDetails(IServiceCollection)-extensiemethode op IServiceCollection registreert de standaard IProblemDetailsService-implementatie.

In ASP.NET Core-apps genereert de volgende middleware problemoplossings-HTTP-antwoorden wanneer AddProblemDetails wordt aangeroepen, behalve wanneer de Accept request-HTTP-header geen van de inhoudstypen bevat die worden ondersteund door de geregistreerde IProblemDetailsWriter (standaard: application/json):

Met de volgende code wordt de app geconfigureerd voor het genereren van een antwoord op probleemdetails voor alle HTTP-client- en serverfoutreacties die nog geen hoofdtekstinhoud hebben:

builder.Services.AddProblemDetails();

var app = builder.Build();        

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

app.UseStatusCodePages();

In de volgende sectie ziet u hoe u de antwoordtekst met details van het probleem kunt aanpassen.

Probleemdetails aanpassen

Het automatisch maken van een ProblemDetails kan worden aangepast met een van de volgende opties:

  1. Gebruik ProblemDetailsOptions.CustomizeProblemDetails
  2. Een aangepaste IProblemDetailsWriter gebruiken
  3. Roep de IProblemDetailsService aan in een middleware

CustomizeProblemDetails operatie

De gegenereerde probleemdetails kunnen worden aangepast met behulp van CustomizeProblemDetailsen de aanpassingen worden toegepast op alle automatisch gegenereerde probleemdetails.

De volgende code gebruikt ProblemDetailsOptions om in te stellen CustomizeProblemDetails:

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

var app = builder.Build();        

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

app.UseStatusCodePages();

Een eindpuntresultaat produceert bijvoorbeeld HTTP Status 400 Bad Request de volgende hoofdtekst met probleemdetails:

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

Gewoonte IProblemDetailsWriter

Er kan een IProblemDetailsWriter implementatie worden gemaakt voor geavanceerde aanpassingen.

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));
    }
}

Opmerking: Wanneer u een aangepaste IProblemDetailsWriter gebruikt, moet de aangepaste IProblemDetailsWriter worden geregistreerd voordat u AddRazorPages, AddControllers, AddControllersWithViews of AddMvc belt.

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();

Probleemdetails van Middleware

Een alternatieve methode voor het gebruik ProblemDetailsOptions met CustomizeProblemDetails is de ProblemDetails in middleware instellen. Een antwoord met probleemdetails kan worden geschreven door het aanroepen van IProblemDetailsService.WriteAsync:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddProblemDetails();

var app = builder.Build();

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

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

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

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

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

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

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

app.MapControllers();

app.Run();

In de voorgaande code retourneren de minimale API-eindpunten /divide en /squareroot de verwachte aangepaste probleemreactie bij foutinvoer.

De EINDPUNTen van de API-controller retourneren het standaardprobleemantwoord op foutinvoer, niet het aangepaste probleemantwoord. Het standaardprobleemantwoord wordt geretourneerd omdat de API-controller naar de responsestroom heeft geschreven, probleemdetails voor foutcodes, voordat IProblemDetailsService.WriteAsync wordt aangeroepen en de reactie niet opnieuw wordt geschreven.

Het volgende ValuesController retourneert BadRequestResult, dat naar de antwoordstroom schrijft en daarom voorkomt dat het aangepaste probleemantwoord wordt geretourneerd.

[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));
    }

}

Het volgende Values3Controller retourneert ControllerBase.Problem zodat het verwachte aangepaste probleemresultaat wordt geretourneerd:

[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));
    }

}

Een ProblemDetails-payload genereren voor uitzonderingen

Houd rekening met de volgende 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();

In niet-ontwikkelomgevingen, wanneer er een uitzondering optreedt, is het volgende een standaard ProblemDetails-antwoord dat wordt geretourneerd aan de client:

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

Voor de meeste apps is de voorgaande code alles wat nodig is voor uitzonderingen. In de volgende sectie ziet u echter hoe u gedetailleerdere reacties op problemen krijgt.

Een alternatief voor een aangepaste uitzonderingshandlerpagina is een lambda aanbieden aan UseExceptionHandler. Het gebruik van een lambda biedt toegang tot de fout en het schrijven van een antwoord met betrekking tot probleemdetails met IProblemDetailsService.WriteAsync:

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

var builder = WebApplication.CreateBuilder(args);

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

var app = builder.Build();

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

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

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

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

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

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

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

Warning

Geef geen gevoelige foutinformatie aan clients. Het doorgeven van fouten is een beveiligingsrisico.

Een alternatieve benadering voor het genereren van probleemdetails is het gebruik van het NuGet-pakket Hellang.Middleware.ProblemDetails van derden dat kan worden gebruikt om uitzonderingen en clientfouten toe te wijzen aan probleemdetails.

Additional resources

Door Tom Dykstra

In dit artikel worden veelvoorkomende benaderingen besproken voor het afhandelen van fouten in ASP.NET Core-web-apps. Zie ook Fouten verwerken in ASP.NET Core-web-API's op basis van een controller en fouten verwerken in minimale API's.

Uitzonderingspagina voor ontwikkelaars

Op de uitzonderingspagina voor ontwikkelaars wordt gedetailleerde informatie weergegeven over niet-verwerkte aanvraag-uitzonderingen. ASP.NET Core-apps de uitzonderingspagina voor ontwikkelaars standaard inschakelen wanneer beide:

De ontwikkelaarsexceptiepagina draait vroeg in de middleware-pijplijn, zodat onopgevangen uitzonderingen die in de daaropvolgende middleware worden gegooid, kunnen worden opgevangen.

Gedetailleerde uitzonderingsinformatie mag niet openbaar worden weergegeven wanneer de app wordt uitgevoerd in de productieomgeving. Zie Meerdere omgevingen gebruiken in ASP.NET Core voor meer informatie over het configureren van omgevingen.

De uitzonderingspagina voor ontwikkelaars kan de volgende informatie bevatten over de uitzondering en de aanvraag:

  • Stack trace
  • Queryreeksparameters, indien van toepassing
  • Cookies, indien van toepassing
  • Headers

De uitzonderingspagina voor ontwikkelaars is niet gegarandeerd informatie te verstrekken. Gebruik Logging voor volledige foutinformatie.

Uitzonderingshandlerpagina

Als u een aangepaste pagina voor foutafhandeling voor de productieomgeving wilt configureren, roept UseExceptionHandleru aan. Deze middleware voor uitzonderingafhandeling:

  • Niet-verwerkte uitzonderingen worden onderschept en in logboeken opgeslagen.
  • Voert de aanvraag opnieuw uit in een alternatieve pijplijn met behulp van het aangegeven pad. De aanvraag wordt niet opnieuw uitgevoerd als het antwoord is gestart. De door de sjabloon gegenereerde code voert de aanvraag opnieuw uit met behulp van het /Error pad.

Warning

Als de alternatieve pijplijn een eigen uitzondering genereert, wordt de oorspronkelijke uitzondering opnieuw door de Middleware gegooid.

Omdat deze middleware de aanvraagpijplijn opnieuw kan uitvoeren:

  • Middleware moet herinvoerbaarheid bij dezelfde aanvraag afhandelen. Dit betekent normaal gesproken dat, na het aanroepen van _next, de status wordt opgeschoond of dat de verwerking in de cache van HttpContext wordt opgeslagen om te voorkomen dat deze opnieuw wordt uitgevoerd. Bij het verwerken van de aanvraagtekst betekent dit het bufferen of cachen van de resultaten, zoals de formulierlezer.
  • Voor de UseExceptionHandler(IApplicationBuilder, String) overbelasting die in sjablonen wordt gebruikt, wordt alleen het aanvraagpad gewijzigd en worden de routegegevens gewist. Aanvraaggegevens zoals headers, methoden en items worden allemaal opnieuw gebruikt as-is.
  • Scoped services blijven hetzelfde.

In het volgende voorbeeld voegt UseExceptionHandler de middleware voor uitzonderingsafhandeling toe in niet-ontwikkelingsomgevingen.

var app = builder.Build();

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

De Razor app-sjabloon .cshtml bevat een foutpagina (PageModel) en ErrorModel klasse () in de map Pagina's. Voor een MVC-app bevat de projectsjabloon een Error actiemethode en een foutweergave voor de Home controller.

Met de uitzonderingsafhandeling van middleware wordt de aanvraag opnieuw uitgevoerd met behulp van de oorspronkelijke HTTP-methode. Als een fouthandlereindpunt is beperkt tot een specifieke set HTTP-methoden, wordt het alleen uitgevoerd voor deze HTTP-methoden. Een MVC-controlleractie die gebruikmaakt van het [HttpGet] kenmerk wordt bijvoorbeeld alleen uitgevoerd voor GET-aanvragen. Als u ervoor wilt zorgen dat alle aanvragen de aangepaste pagina voor foutafhandeling bereiken, beperkt u deze niet tot een specifieke set HTTP-methoden.

Uitzonderingen op een andere manier verwerken op basis van de oorspronkelijke HTTP-methode:

  • Voor Razor Pagina's maakt u meerdere handlermethoden. Gebruik bijvoorbeeld OnGet om GET-uitzonderingen af te handelen en om OnPost POST-uitzonderingen af te handelen.
  • Pas voor MVC HTTP-werkwoordkenmerken toe op meerdere acties. Gebruik bijvoorbeeld [HttpGet] om GET-uitzonderingen af te handelen en om [HttpPost] POST-uitzonderingen af te handelen.

Als u niet-geverifieerde gebruikers wilt toestaan om de aangepaste pagina voor foutafhandeling weer te geven, moet u ervoor zorgen dat deze anonieme toegang ondersteunt.

Toegang tot de uitzondering

Gebruik IExceptionHandlerPathFeature om toegang te krijgen tot de uitzondering en het oorspronkelijke aanvraagpad in een foutafhandelaar. In het volgende voorbeeld wordt IExceptionHandlerPathFeature gebruikt om meer informatie te krijgen over de uitzonderingsfout die is opgetreden.

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

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

    public string? ExceptionMessage { get; set; }

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

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

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

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

Warning

Geef geen gevoelige foutinformatie aan clients. Het doorgeven van fouten is een beveiligingsrisico.

Uitzonderingshandler lambda

Een alternatief voor een aangepaste uitzonderingshandlerpagina is een lambda aanbieden aan UseExceptionHandler. Als u een lambda gebruikt, heeft u toegang tot de fout voordat u het antwoord retourneert.

De volgende code maakt gebruik van een lambda voor het verwerken van uitzonderingen:

var app = builder.Build();

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

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

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

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

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

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

    app.UseHsts();
}

Warning

Geef geen gevoelige foutinformatie aan clients. Het doorgeven van fouten is een beveiligingsrisico.

IExceptionHandler

IExceptionHandler is een interface die de ontwikkelaar een callback geeft voor het verwerken van bekende uitzonderingen op een centrale locatie.

IExceptionHandlerimplementaties worden geregistreerd door IServiceCollection.AddExceptionHandler<T> aan te roepen [IServiceCollection.AddExceptionHandler<T>]. De levensduur van een IExceptionHandler exemplaar is singleton. Er kunnen meerdere implementaties worden toegevoegd en ze worden aangeroepen in de geregistreerde volgorde.

Als een uitzonderingshandler een aanvraag verwerkt, kan deze true retourneren om de verwerking te stoppen. Als een uitzondering niet wordt afgehandeld door een uitzonderingshandler, valt de besturing terug op het standaardgedrag en de opties van de middleware. Verschillende metrische gegevens en logboeken worden verzonden voor verwerkte en niet-verwerkte uitzonderingen.

In het volgende voorbeeld ziet u een IExceptionHandler implementatie:

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);
        }
    }
}

In het volgende voorbeeld ziet u hoe u een IExceptionHandler implementatie registreert voor afhankelijkheidsinjectie:

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

Wanneer de voorgaande code wordt uitgevoerd in de ontwikkelomgeving:

  • De CustomExceptionHandler wordt als eerste aangeroepen om een uitzondering af te handelen.
  • Nadat de uitzondering is gelogd, retourneert de TryHandleException methode false, zodat de uitzonderingspagina voor ontwikkelaars wordt weergegeven.

In andere omgevingen:

  • De CustomExceptionHandler wordt als eerste aangeroepen om een uitzondering af te handelen.
  • Nadat de uitzondering is aangemeld, wordt de TryHandleException methode geretourneerd false, zodat de /Error pagina wordt weergegeven.

UseStatusCodePages

Een ASP.NET Core-app biedt standaard geen pagina met statuscode voor HTTP-foutcodes, zoals 404 - Niet gevonden. Wanneer de app een HTTP 400-599-foutcode instelt die geen hoofdtekst heeft, wordt de statuscode en een lege antwoordtekst geretourneerd. Als u standaardhandlers voor alleen-tekst wilt inschakelen voor veelvoorkomende foutcodes, roept u UseStatusCodePages aan in Program.cs.

var app = builder.Build();

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

app.UseStatusCodePages();

Roep UseStatusCodePages aan voordat de middleware voor aanvraagverwerking wordt afgehandeld. Roep bijvoorbeeld UseStatusCodePages aan vóór de Static File Middleware en de Endpoints Middleware.

Wanneer UseStatusCodePages dit niet wordt gebruikt, retourneert het navigeren naar een URL zonder eindpunt een browserafhankelijk foutbericht dat aangeeft dat het eindpunt niet kan worden gevonden. Wanneer UseStatusCodePages wordt aangeroepen, retourneert de browser het volgende antwoord:

Status Code: 404; Not Found

UseStatusCodePages wordt meestal niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

Note

De middleware van de statuscodepagina's vangt geen uitzonderingen op. Als u een aangepaste foutafhandelingspagina wilt opgeven, gebruikt u de uitzonderingshandlerpagina.

UseStatusCodePages met formaattekenreeks

Om het inhoudstype en de tekst van de reactie aan te passen, gebruikt u de overload van UseStatusCodePages die een inhoudstype en een opmaaktekenreeks vereist:

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

In de voorgaande code {0} is een tijdelijke aanduiding voor de foutcode.

UseStatusCodePages met een notatietekenreeks wordt doorgaans niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

UseStatusCodePages met lambda

Als u aangepaste code voor foutafhandeling en het schrijven van reacties wilt opgeven, gebruikt u de overload van UseStatusCodePages die een lambda-expressie accepteert:

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 met een lambda wordt meestal niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

UseStatusCodePagesWithRedirects

De UseStatusCodePagesWithRedirects extensiemethode:

  • Een 302 - Found statuscode wordt naar de client gestuurd.
  • Hiermee wordt de client omgeleid naar het eindpunt voor foutafhandeling dat is opgegeven in de URL-sjabloon. Het eindpunt voor foutafhandeling geeft doorgaans foutinformatie weer en retourneert HTTP 200.
var app = builder.Build();

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

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

De URL-sjabloon kan een {0} tijdelijke aanduiding voor de statuscode bevatten, zoals te zien is in de voorgaande code. Als de URL-sjabloon begint met ~ (tilde), wordt de ~ vervangen door de PathBase van de app. Wanneer u een eindpunt in de app opgeeft, maakt u een MVC-weergave of Razor -pagina voor het eindpunt.

Deze methode wordt vaak gebruikt wanneer de app:

  • Moet de client omleiden naar een ander eindpunt, meestal in gevallen waarin een andere app de fout verwerkt. Voor web-apps weerspiegelt de adresbalk van de client het omgeleide eindpunt.
  • Bewaar de oorspronkelijke statuscode niet en retourneer deze niet met het eerste omleidingsantwoord.

UseStatusCodePagesWithReExecute

De UseStatusCodePagesWithReExecute extensiemethode:

  • Hiermee wordt de hoofdtekst van het antwoord gegenereerd door de aanvraagpijplijn opnieuw uit te voeren met behulp van een alternatief pad.
  • Wijzigt de statuscode niet voor of na het opnieuw uitvoeren van de pijplijn.

De uitvoering van de nieuwe pijplijn kan de statuscode van het antwoord wijzigen, omdat de nieuwe pijplijn volledige controle heeft over de statuscode. Als de nieuwe pijplijn de statuscode niet wijzigt, wordt de oorspronkelijke statuscode naar de client verzonden.

var app = builder.Build();

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

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

Als er een eindpunt in de app is opgegeven, maakt u een MVC-weergave of Razor -pagina voor het eindpunt.

Deze methode wordt vaak gebruikt wanneer de app het volgende moet doen:

  • De aanvraag verwerken zonder om te leiden naar een ander eindpunt. Voor web-apps weerspiegelt de adresbalk van de browser van de client het oorspronkelijk aangevraagde eindpunt.
  • Behoud en retourneer de oorspronkelijke statuscode met het antwoord.

De URL-sjabloon moet beginnen met / en kan een tijdelijke aanduiding {0} voor de statuscode bevatten. Als u de statuscode wilt doorgeven als een querytekenreeksparameter, geeft u een tweede argument door aan UseStatusCodePagesWithReExecute. For example:

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

Het eindpunt dat de fout verwerkt, kan de oorspronkelijke URL ophalen die de fout heeft gegenereerd, zoals wordt weergegeven in het volgende voorbeeld:

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

    public string? OriginalPathAndQuery { get; set; }

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

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

        if (statusCodeReExecuteFeature is not null)
        {
            OriginalPathAndQuery = $"{statusCodeReExecuteFeature.OriginalPathBase}"
                                    + $"{statusCodeReExecuteFeature.OriginalPath}"
                                    + $"{statusCodeReExecuteFeature.OriginalQueryString}";

        }
    }
}

Omdat deze middleware de aanvraagpijplijn opnieuw kan uitvoeren:

  • Middleware moet herinvoerbaarheid bij dezelfde aanvraag afhandelen. Dit betekent normaal gesproken dat, na het aanroepen van _next, de status wordt opgeschoond of dat de verwerking in de cache van HttpContext wordt opgeslagen om te voorkomen dat deze opnieuw wordt uitgevoerd. Bij het verwerken van de aanvraagtekst betekent dit het bufferen of cachen van de resultaten, zoals de formulierlezer.
  • Scoped services blijven hetzelfde.

Statuscodepagina's uitschakelen

Gebruik het kenmerk [SkipStatusCodePages] om statuscodepagina's voor een MVC-controller of actiemethode uit te schakelen.

Als u statuscodepagina's wilt uitschakelen voor specifieke aanvragen in een Razor Pagina-handlermethode of in een MVC-controller, gebruikt u IStatusCodePagesFeature:

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

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

Exception-handling code

Code in uitzonderingsafhandelingspagina's kan ook uitzonderingen genereren. Productiefoutpagina's moeten grondig worden getest en extra voorzichtig zijn om te voorkomen dat ze zelf uitzonderingen genereren.

Response headers

Zodra de headers voor een antwoord zijn verzonden:

  • De app kan de statuscode van het antwoord niet wijzigen.
  • Eventuele uitzonderingspagina's of handlers kunnen niet worden uitgevoerd. Het antwoord moet worden voltooid of de verbinding is afgebroken.

Afhandeling van serveronderzondering

Naast de logica voor het afhandelen van uitzonderingen in een app kan de IMPLEMENTATIE van de HTTP-server enkele uitzonderingen verwerken. Als de server een uitzondering onderschept voordat antwoordheaders worden verzonden, verzendt de server een 500 - Internal Server Error antwoord zonder antwoordtekst. Als de server een uitzondering opmerkt nadat de responsheaders zijn verzonden, sluit de server de verbinding. Aanvragen die niet door de app worden verwerkt, worden verwerkt door de server. Eventuele uitzonderingen die optreden wanneer de server de aanvraag verwerkt, wordt verwerkt door de uitzonderingsafhandeling van de server. De aangepaste foutpagina's van de app, uitzonderingsafhandeling van middleware en filters hebben geen invloed op dit gedrag.

Afhandeling van opstartonderzondering

Alleen de hostinglaag kan uitzonderingen verwerken die plaatsvinden tijdens het opstarten van de app. De host kan worden geconfigureerd om opstartfouten vast te leggen en gedetailleerde fouten vast te leggen.

De hostinglaag kan alleen een foutpagina voor een vastgelegde opstartfout weergeven als de fout optreedt na hostadres-/poortbinding. Als de binding mislukt:

  • De hostinglaag registreert een kritieke uitzondering.
  • Het dotnet-proces loopt vast.
  • Er wordt geen foutpagina weergegeven wanneer de HTTP-server is Kestrel.

Bij uitvoering op IIS (of Azure App Service) of IIS Express wordt een procesfout van 502.5 geretourneerd door de ASP.NET Core-module als het proces niet kan worden gestart. Zie Problemen met ASP.NET Core in Azure App Service en IIS oplossen voor meer informatie.

Databasefoutpagina

Met het uitzonderingsfilter AddDatabaseDeveloperPageExceptionFilter voor databaseontwikkelaarspagina's worden databasegerelateerde uitzonderingen vastgelegd die kunnen worden opgelost met behulp van Entity Framework Core-migraties. Wanneer deze uitzonderingen optreden, wordt er een HTML-antwoord gegenereerd met details van mogelijke acties om het probleem op te lossen. Deze pagina is alleen ingeschakeld in de ontwikkelomgeving. Met de volgende code wordt het uitzonderingsfilter voor de databaseontwikkelaarspagina toegevoegd:

var builder = WebApplication.CreateBuilder(args);

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

Exception filters

In MVC-apps kunnen uitzonderingsfilters globaal of per controller of per actie worden geconfigureerd. In Razor Pagina-apps kunnen ze globaal of per paginamodel worden geconfigureerd. Deze filters verwerken eventuele niet-verwerkte uitzonderingen die optreden tijdens de uitvoering van een controlleractie of een ander filter. Zie Filters in ASP.NET Core voor meer informatie.

Uitzonderingsfilters zijn handig voor uitzonderingen die optreden binnen MVC-acties, maar ze zijn niet zo flexibel als de ingebouwde middleware voor het verwerken van uitzonderingen. UseExceptionHandler We raden aan UseExceptionHandler te gebruiken, tenzij u foutafhandeling anders moet uitvoeren op basis van welke MVC-actie wordt gekozen.

Fouten in modelstatus

Zie Modelbinding en Modelvalidatie voor informatie over het afhandelen van modelstatusfouten.

Problem details

Probleemdetails zijn niet de enige antwoordindeling voor het beschrijven van een HTTP-API-fout. Ze worden echter vaak gebruikt om fouten voor HTTP-API's te rapporteren.

De service met probleemdetails implementeert de IProblemDetailsService interface, die ondersteuning biedt voor het maken van probleemdetails in ASP.NET Core. De AddProblemDetails(IServiceCollection)-extensiemethode op IServiceCollection registreert de standaard IProblemDetailsService-implementatie.

In ASP.NET Core-apps genereert de volgende middleware problemoplossings-HTTP-antwoorden wanneer AddProblemDetails wordt aangeroepen, behalve wanneer de Accept request-HTTP-header geen van de inhoudstypen bevat die worden ondersteund door de geregistreerde IProblemDetailsWriter (standaard: application/json):

Met de volgende code wordt de app geconfigureerd voor het genereren van een antwoord op probleemdetails voor alle HTTP-client- en serverfoutreacties die nog geen hoofdtekstinhoud hebben:

builder.Services.AddProblemDetails();

var app = builder.Build();        

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

app.UseStatusCodePages();

In de volgende sectie ziet u hoe u de antwoordtekst met details van het probleem kunt aanpassen.

Probleemdetails aanpassen

Het automatisch maken van een ProblemDetails kan worden aangepast met een van de volgende opties:

  1. Gebruik ProblemDetailsOptions.CustomizeProblemDetails
  2. Een aangepaste IProblemDetailsWriter gebruiken
  3. Roep de IProblemDetailsService aan in een middleware

CustomizeProblemDetails operatie

De gegenereerde probleemdetails kunnen worden aangepast met behulp van CustomizeProblemDetailsen de aanpassingen worden toegepast op alle automatisch gegenereerde probleemdetails.

De volgende code gebruikt ProblemDetailsOptions om in te stellen CustomizeProblemDetails:

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

var app = builder.Build();        

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

app.UseStatusCodePages();

Een eindpuntresultaat produceert bijvoorbeeld HTTP Status 400 Bad Request de volgende hoofdtekst met probleemdetails:

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

Gewoonte IProblemDetailsWriter

Er kan een IProblemDetailsWriter implementatie worden gemaakt voor geavanceerde aanpassingen.

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));
    }
}

Opmerking: Wanneer u een aangepaste IProblemDetailsWriter gebruikt, moet de aangepaste IProblemDetailsWriter worden geregistreerd voordat u AddRazorPages, AddControllers, AddControllersWithViews of AddMvc belt.

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();

Probleemdetails van Middleware

Een alternatieve methode voor het gebruik ProblemDetailsOptions met CustomizeProblemDetails is de ProblemDetails in middleware instellen. Een antwoord met probleemdetails kan worden geschreven door het aanroepen van IProblemDetailsService.WriteAsync:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddProblemDetails();

var app = builder.Build();

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

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

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

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

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

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

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

app.MapControllers();

app.Run();

In de voorgaande code retourneren de minimale API-eindpunten /divide en /squareroot de verwachte aangepaste probleemreactie bij foutinvoer.

De EINDPUNTen van de API-controller retourneren het standaardprobleemantwoord op foutinvoer, niet het aangepaste probleemantwoord. Het standaardprobleemantwoord wordt geretourneerd omdat de API-controller naar de responsestroom heeft geschreven, probleemdetails voor foutcodes, voordat IProblemDetailsService.WriteAsync wordt aangeroepen en de reactie niet opnieuw wordt geschreven.

Het volgende ValuesController retourneert BadRequestResult, dat naar de antwoordstroom schrijft en daarom voorkomt dat het aangepaste probleemantwoord wordt geretourneerd.

[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));
    }

}

Het volgende Values3Controller retourneert ControllerBase.Problem zodat het verwachte aangepaste probleemresultaat wordt geretourneerd:

[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));
    }

}

Een ProblemDetails-payload genereren voor uitzonderingen

Houd rekening met de volgende 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();

In niet-ontwikkelomgevingen, wanneer er een uitzondering optreedt, is het volgende een standaard ProblemDetails-antwoord dat wordt geretourneerd aan de client:

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

Voor de meeste apps is de voorgaande code alles wat nodig is voor uitzonderingen. In de volgende sectie ziet u echter hoe u gedetailleerdere reacties op problemen krijgt.

Een alternatief voor een aangepaste uitzonderingshandlerpagina is een lambda aanbieden aan UseExceptionHandler. Het gebruik van een lambda biedt toegang tot de fout en het schrijven van een antwoord met betrekking tot probleemdetails met IProblemDetailsService.WriteAsync:

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

var builder = WebApplication.CreateBuilder(args);

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

var app = builder.Build();

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

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

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

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

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

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

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

Warning

Geef geen gevoelige foutinformatie aan clients. Het doorgeven van fouten is een beveiligingsrisico.

Een alternatieve benadering voor het genereren van probleemdetails is het gebruik van het NuGet-pakket Hellang.Middleware.ProblemDetails van derden dat kan worden gebruikt om uitzonderingen en clientfouten toe te wijzen aan probleemdetails.

Additional resources

Door Tom Dykstra

In dit artikel worden veelvoorkomende benaderingen besproken voor het afhandelen van fouten in ASP.NET Core-web-apps. Zie ook Fouten verwerken in ASP.NET Core-web-API's op basis van een controller en fouten verwerken in minimale API's.

Uitzonderingspagina voor ontwikkelaars

Op de uitzonderingspagina voor ontwikkelaars wordt gedetailleerde informatie weergegeven over niet-verwerkte aanvraag-uitzonderingen. ASP.NET Core-apps de uitzonderingspagina voor ontwikkelaars standaard inschakelen wanneer beide:

De ontwikkelaarsexceptiepagina draait vroeg in de middleware-pijplijn, zodat onopgevangen uitzonderingen die in de daaropvolgende middleware worden gegooid, kunnen worden opgevangen.

Gedetailleerde uitzonderingsinformatie mag niet openbaar worden weergegeven wanneer de app wordt uitgevoerd in de productieomgeving. Zie Meerdere omgevingen gebruiken in ASP.NET Core voor meer informatie over het configureren van omgevingen.

De uitzonderingspagina voor ontwikkelaars kan de volgende informatie bevatten over de uitzondering en de aanvraag:

  • Stack trace
  • Queryreeksparameters, indien van toepassing
  • Cookies, indien van toepassing
  • Headers

De uitzonderingspagina voor ontwikkelaars is niet gegarandeerd informatie te verstrekken. Gebruik Logging voor volledige foutinformatie.

Uitzonderingshandlerpagina

Als u een aangepaste pagina voor foutafhandeling voor de productieomgeving wilt configureren, roept UseExceptionHandleru aan. Deze middleware voor uitzonderingafhandeling:

  • Niet-verwerkte uitzonderingen worden onderschept en in logboeken opgeslagen.
  • Voert de aanvraag opnieuw uit in een alternatieve pijplijn met behulp van het aangegeven pad. De aanvraag wordt niet opnieuw uitgevoerd als het antwoord is gestart. De door de sjabloon gegenereerde code voert de aanvraag opnieuw uit met behulp van het /Error pad.

Warning

Als de alternatieve pijplijn een eigen uitzondering genereert, wordt de oorspronkelijke uitzondering opnieuw door de Middleware gegooid.

Omdat deze middleware de aanvraagpijplijn opnieuw kan uitvoeren:

  • Middleware moet herinvoerbaarheid bij dezelfde aanvraag afhandelen. Dit betekent normaal gesproken dat, na het aanroepen van _next, de status wordt opgeschoond of dat de verwerking in de cache van HttpContext wordt opgeslagen om te voorkomen dat deze opnieuw wordt uitgevoerd. Bij het verwerken van de aanvraagtekst betekent dit het bufferen of cachen van de resultaten, zoals de formulierlezer.
  • Voor de UseExceptionHandler(IApplicationBuilder, String) overbelasting die in sjablonen wordt gebruikt, wordt alleen het aanvraagpad gewijzigd en worden de routegegevens gewist. Aanvraaggegevens zoals headers, methoden en items worden allemaal opnieuw gebruikt as-is.
  • Scoped services blijven hetzelfde.

In het volgende voorbeeld voegt UseExceptionHandler de middleware voor uitzonderingsafhandeling toe in niet-ontwikkelingsomgevingen.

var app = builder.Build();

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

De Razor app-sjabloon .cshtml bevat een foutpagina (PageModel) en ErrorModel klasse () in de map Pagina's. Voor een MVC-app bevat de projectsjabloon een Error actiemethode en een foutweergave voor de Home controller.

Met de uitzonderingsafhandeling van middleware wordt de aanvraag opnieuw uitgevoerd met behulp van de oorspronkelijke HTTP-methode. Als een fouthandlereindpunt is beperkt tot een specifieke set HTTP-methoden, wordt het alleen uitgevoerd voor deze HTTP-methoden. Een MVC-controlleractie die gebruikmaakt van het [HttpGet] kenmerk wordt bijvoorbeeld alleen uitgevoerd voor GET-aanvragen. Als u ervoor wilt zorgen dat alle aanvragen de aangepaste pagina voor foutafhandeling bereiken, beperkt u deze niet tot een specifieke set HTTP-methoden.

Uitzonderingen op een andere manier verwerken op basis van de oorspronkelijke HTTP-methode:

  • Voor Razor Pagina's maakt u meerdere handlermethoden. Gebruik bijvoorbeeld OnGet om GET-uitzonderingen af te handelen en om OnPost POST-uitzonderingen af te handelen.
  • Pas voor MVC HTTP-werkwoordkenmerken toe op meerdere acties. Gebruik bijvoorbeeld [HttpGet] om GET-uitzonderingen af te handelen en om [HttpPost] POST-uitzonderingen af te handelen.

Als u niet-geverifieerde gebruikers wilt toestaan om de aangepaste pagina voor foutafhandeling weer te geven, moet u ervoor zorgen dat deze anonieme toegang ondersteunt.

Toegang tot de uitzondering

Gebruik IExceptionHandlerPathFeature om toegang te krijgen tot de uitzondering en het oorspronkelijke aanvraagpad in een foutafhandelaar. In het volgende voorbeeld wordt IExceptionHandlerPathFeature gebruikt om meer informatie te krijgen over de uitzonderingsfout die is opgetreden.

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

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

    public string? ExceptionMessage { get; set; }

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

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

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

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

Warning

Geef geen gevoelige foutinformatie aan clients. Het doorgeven van fouten is een beveiligingsrisico.

Uitzonderingshandler lambda

Een alternatief voor een aangepaste uitzonderingshandlerpagina is een lambda aanbieden aan UseExceptionHandler. Als u een lambda gebruikt, heeft u toegang tot de fout voordat u het antwoord retourneert.

De volgende code maakt gebruik van een lambda voor het verwerken van uitzonderingen:

var app = builder.Build();

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

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

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

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

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

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

    app.UseHsts();
}

Warning

Geef geen gevoelige foutinformatie aan clients. Het doorgeven van fouten is een beveiligingsrisico.

UseStatusCodePages

Een ASP.NET Core-app biedt standaard geen pagina met statuscode voor HTTP-foutcodes, zoals 404 - Niet gevonden. Wanneer de app een HTTP 400-599-foutcode instelt die geen hoofdtekst heeft, wordt de statuscode en een lege antwoordtekst geretourneerd. Als u standaardhandlers voor alleen-tekst wilt inschakelen voor veelvoorkomende foutcodes, roept u UseStatusCodePages aan in Program.cs.

var app = builder.Build();

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

app.UseStatusCodePages();

Roep UseStatusCodePages aan voordat de middleware voor aanvraagverwerking wordt afgehandeld. Roep bijvoorbeeld UseStatusCodePages aan vóór de Static File Middleware en de Endpoints Middleware.

Wanneer UseStatusCodePages dit niet wordt gebruikt, retourneert het navigeren naar een URL zonder eindpunt een browserafhankelijk foutbericht dat aangeeft dat het eindpunt niet kan worden gevonden. Wanneer UseStatusCodePages wordt aangeroepen, retourneert de browser het volgende antwoord:

Status Code: 404; Not Found

UseStatusCodePages wordt meestal niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

Note

De middleware van de statuscodepagina's vangt geen uitzonderingen op. Als u een aangepaste foutafhandelingspagina wilt opgeven, gebruikt u de uitzonderingshandlerpagina.

UseStatusCodePages met formaattekenreeks

Om het inhoudstype en de tekst van de reactie aan te passen, gebruikt u de overload van UseStatusCodePages die een inhoudstype en een opmaaktekenreeks vereist:

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

In de voorgaande code {0} is een tijdelijke aanduiding voor de foutcode.

UseStatusCodePages met een notatietekenreeks wordt doorgaans niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

UseStatusCodePages met lambda

Als u aangepaste code voor foutafhandeling en het schrijven van reacties wilt opgeven, gebruikt u de overload van UseStatusCodePages die een lambda-expressie accepteert:

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 met een lambda wordt meestal niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

UseStatusCodePagesWithRedirects

De UseStatusCodePagesWithRedirects extensiemethode:

  • Een 302 - Found statuscode wordt naar de client gestuurd.
  • Hiermee wordt de client omgeleid naar het eindpunt voor foutafhandeling dat is opgegeven in de URL-sjabloon. Het eindpunt voor foutafhandeling geeft doorgaans foutinformatie weer en retourneert HTTP 200.
var app = builder.Build();

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

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

De URL-sjabloon kan een {0} tijdelijke aanduiding voor de statuscode bevatten, zoals te zien is in de voorgaande code. Als de URL-sjabloon begint met ~ (tilde), wordt de ~ vervangen door de PathBase van de app. Wanneer u een eindpunt in de app opgeeft, maakt u een MVC-weergave of Razor -pagina voor het eindpunt.

Deze methode wordt vaak gebruikt wanneer de app:

  • Moet de client omleiden naar een ander eindpunt, meestal in gevallen waarin een andere app de fout verwerkt. Voor web-apps weerspiegelt de adresbalk van de client het omgeleide eindpunt.
  • Bewaar de oorspronkelijke statuscode niet en retourneer deze niet met het eerste omleidingsantwoord.

UseStatusCodePagesWithReExecute

De UseStatusCodePagesWithReExecute extensiemethode:

  • Hiermee wordt de hoofdtekst van het antwoord gegenereerd door de aanvraagpijplijn opnieuw uit te voeren met behulp van een alternatief pad.
  • Wijzigt de statuscode niet voor of na het opnieuw uitvoeren van de pijplijn.

De uitvoering van de nieuwe pijplijn kan de statuscode van het antwoord wijzigen, omdat de nieuwe pijplijn volledige controle heeft over de statuscode. Als de nieuwe pijplijn de statuscode niet wijzigt, wordt de oorspronkelijke statuscode naar de client verzonden.

var app = builder.Build();

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

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

Als er een eindpunt in de app is opgegeven, maakt u een MVC-weergave of Razor -pagina voor het eindpunt.

Deze methode wordt vaak gebruikt wanneer de app het volgende moet doen:

  • De aanvraag verwerken zonder om te leiden naar een ander eindpunt. Voor web-apps weerspiegelt de adresbalk van de browser van de client het oorspronkelijk aangevraagde eindpunt.
  • Behoud en retourneer de oorspronkelijke statuscode met het antwoord.

De URL-sjabloon moet beginnen met / en kan een tijdelijke aanduiding {0} voor de statuscode bevatten. Als u de statuscode wilt doorgeven als een querytekenreeksparameter, geeft u een tweede argument door aan UseStatusCodePagesWithReExecute. For example:

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

Het eindpunt dat de fout verwerkt, kan de oorspronkelijke URL ophalen die de fout heeft gegenereerd, zoals wordt weergegeven in het volgende voorbeeld:

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

    public string? OriginalPathAndQuery { get; set; }

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

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

        if (statusCodeReExecuteFeature is not null)
        {
            OriginalPathAndQuery = $"{statusCodeReExecuteFeature.OriginalPathBase}"
                                    + $"{statusCodeReExecuteFeature.OriginalPath}"
                                    + $"{statusCodeReExecuteFeature.OriginalQueryString}";

        }
    }
}

Omdat deze middleware de aanvraagpijplijn opnieuw kan uitvoeren:

  • Middleware moet herinvoerbaarheid bij dezelfde aanvraag afhandelen. Dit betekent normaal gesproken dat, na het aanroepen van _next, de status wordt opgeschoond of dat de verwerking in de cache van HttpContext wordt opgeslagen om te voorkomen dat deze opnieuw wordt uitgevoerd. Bij het verwerken van de aanvraagtekst betekent dit het bufferen of cachen van de resultaten, zoals de formulierlezer.
  • Scoped services blijven hetzelfde.

Statuscodepagina's uitschakelen

Gebruik het kenmerk [SkipStatusCodePages] om statuscodepagina's voor een MVC-controller of actiemethode uit te schakelen.

Als u statuscodepagina's wilt uitschakelen voor specifieke aanvragen in een Razor Pagina-handlermethode of in een MVC-controller, gebruikt u IStatusCodePagesFeature:

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

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

Exception-handling code

Code in uitzonderingsafhandelingspagina's kan ook uitzonderingen genereren. Productiefoutpagina's moeten grondig worden getest en extra voorzichtig zijn om te voorkomen dat ze zelf uitzonderingen genereren.

Response headers

Zodra de headers voor een antwoord zijn verzonden:

  • De app kan de statuscode van het antwoord niet wijzigen.
  • Eventuele uitzonderingspagina's of handlers kunnen niet worden uitgevoerd. Het antwoord moet worden voltooid of de verbinding is afgebroken.

Afhandeling van serveronderzondering

Naast de logica voor het afhandelen van uitzonderingen in een app kan de IMPLEMENTATIE van de HTTP-server enkele uitzonderingen verwerken. Als de server een uitzondering onderschept voordat antwoordheaders worden verzonden, verzendt de server een 500 - Internal Server Error antwoord zonder antwoordtekst. Als de server een uitzondering opmerkt nadat de responsheaders zijn verzonden, sluit de server de verbinding. Aanvragen die niet door de app worden verwerkt, worden verwerkt door de server. Eventuele uitzonderingen die optreden wanneer de server de aanvraag verwerkt, wordt verwerkt door de uitzonderingsafhandeling van de server. De aangepaste foutpagina's van de app, uitzonderingsafhandeling van middleware en filters hebben geen invloed op dit gedrag.

Afhandeling van opstartonderzondering

Alleen de hostinglaag kan uitzonderingen verwerken die plaatsvinden tijdens het opstarten van de app. De host kan worden geconfigureerd om opstartfouten vast te leggen en gedetailleerde fouten vast te leggen.

De hostinglaag kan alleen een foutpagina voor een vastgelegde opstartfout weergeven als de fout optreedt na hostadres-/poortbinding. Als de binding mislukt:

  • De hostinglaag registreert een kritieke uitzondering.
  • Het dotnet-proces loopt vast.
  • Er wordt geen foutpagina weergegeven wanneer de HTTP-server is Kestrel.

Bij uitvoering op IIS (of Azure App Service) of IIS Express wordt een procesfout van 502.5 geretourneerd door de ASP.NET Core-module als het proces niet kan worden gestart. Zie Problemen met ASP.NET Core in Azure App Service en IIS oplossen voor meer informatie.

Databasefoutpagina

Met het uitzonderingsfilter AddDatabaseDeveloperPageExceptionFilter voor databaseontwikkelaarspagina's worden databasegerelateerde uitzonderingen vastgelegd die kunnen worden opgelost met behulp van Entity Framework Core-migraties. Wanneer deze uitzonderingen optreden, wordt er een HTML-antwoord gegenereerd met details van mogelijke acties om het probleem op te lossen. Deze pagina is alleen ingeschakeld in de ontwikkelomgeving. Met de volgende code wordt het uitzonderingsfilter voor de databaseontwikkelaarspagina toegevoegd:

var builder = WebApplication.CreateBuilder(args);

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

Exception filters

In MVC-apps kunnen uitzonderingsfilters globaal of per controller of per actie worden geconfigureerd. In Razor Pagina-apps kunnen ze globaal of per paginamodel worden geconfigureerd. Deze filters verwerken eventuele niet-verwerkte uitzonderingen die optreden tijdens de uitvoering van een controlleractie of een ander filter. Zie Filters in ASP.NET Core voor meer informatie.

Uitzonderingsfilters zijn handig voor uitzonderingen die optreden binnen MVC-acties, maar ze zijn niet zo flexibel als de ingebouwde middleware voor het verwerken van uitzonderingen. UseExceptionHandler We raden aan UseExceptionHandler te gebruiken, tenzij u foutafhandeling anders moet uitvoeren op basis van welke MVC-actie wordt gekozen.

Fouten in modelstatus

Zie Modelbinding en Modelvalidatie voor informatie over het afhandelen van modelstatusfouten.

Problem details

Probleemdetails zijn niet de enige antwoordindeling voor het beschrijven van een HTTP-API-fout. Ze worden echter vaak gebruikt om fouten voor HTTP-API's te rapporteren.

De service met probleemdetails implementeert de IProblemDetailsService interface, die ondersteuning biedt voor het maken van probleemdetails in ASP.NET Core. De AddProblemDetails(IServiceCollection)-extensiemethode op IServiceCollection registreert de standaard IProblemDetailsService-implementatie.

In ASP.NET Core-apps genereert de volgende middleware problemoplossings-HTTP-antwoorden wanneer AddProblemDetails wordt aangeroepen, behalve wanneer de Accept request-HTTP-header geen van de inhoudstypen bevat die worden ondersteund door de geregistreerde IProblemDetailsWriter (standaard: application/json):

Met de volgende code wordt de app geconfigureerd voor het genereren van een antwoord op probleemdetails voor alle HTTP-client- en serverfoutreacties die nog geen hoofdtekstinhoud hebben:

builder.Services.AddProblemDetails();

var app = builder.Build();        

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

app.UseStatusCodePages();

In de volgende sectie ziet u hoe u de antwoordtekst met details van het probleem kunt aanpassen.

Probleemdetails aanpassen

Het automatisch maken van een ProblemDetails kan worden aangepast met een van de volgende opties:

  1. Gebruik ProblemDetailsOptions.CustomizeProblemDetails
  2. Een aangepaste IProblemDetailsWriter gebruiken
  3. Roep de IProblemDetailsService aan in een middleware

CustomizeProblemDetails operatie

De gegenereerde probleemdetails kunnen worden aangepast met behulp van CustomizeProblemDetailsen de aanpassingen worden toegepast op alle automatisch gegenereerde probleemdetails.

De volgende code gebruikt ProblemDetailsOptions om in te stellen CustomizeProblemDetails:

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

var app = builder.Build();        

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

app.UseStatusCodePages();

Een eindpuntresultaat produceert bijvoorbeeld HTTP Status 400 Bad Request de volgende hoofdtekst met probleemdetails:

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

Gewoonte IProblemDetailsWriter

Er kan een IProblemDetailsWriter implementatie worden gemaakt voor geavanceerde aanpassingen.

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));
    }
}

Opmerking: Wanneer u een aangepaste IProblemDetailsWriter gebruikt, moet de aangepaste IProblemDetailsWriter worden geregistreerd voordat u AddRazorPages, AddControllers, AddControllersWithViews of AddMvc belt.

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();

Probleemdetails van Middleware

Een alternatieve methode voor het gebruik ProblemDetailsOptions met CustomizeProblemDetails is de ProblemDetails in middleware instellen. Een antwoord met probleemdetails kan worden geschreven door het aanroepen van IProblemDetailsService.WriteAsync:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddProblemDetails();

var app = builder.Build();

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

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

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

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

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

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

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

app.MapControllers();

app.Run();

In de voorgaande code retourneren de minimale API-eindpunten /divide en /squareroot de verwachte aangepaste probleemreactie bij foutinvoer.

De EINDPUNTen van de API-controller retourneren het standaardprobleemantwoord op foutinvoer, niet het aangepaste probleemantwoord. Het standaardprobleemantwoord wordt geretourneerd omdat de API-controller naar de responsestroom heeft geschreven, probleemdetails voor foutcodes, voordat IProblemDetailsService.WriteAsync wordt aangeroepen en de reactie niet opnieuw wordt geschreven.

Het volgende ValuesController retourneert BadRequestResult, dat naar de antwoordstroom schrijft en daarom voorkomt dat het aangepaste probleemantwoord wordt geretourneerd.

[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));
    }

}

Het volgende Values3Controller retourneert ControllerBase.Problem zodat het verwachte aangepaste probleemresultaat wordt geretourneerd:

[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));
    }

}

Een ProblemDetails-payload genereren voor uitzonderingen

Houd rekening met de volgende 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();

In niet-ontwikkelomgevingen, wanneer er een uitzondering optreedt, is het volgende een standaard ProblemDetails-antwoord dat wordt geretourneerd aan de client:

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

Voor de meeste apps is de voorgaande code alles wat nodig is voor uitzonderingen. In de volgende sectie ziet u echter hoe u gedetailleerdere reacties op problemen krijgt.

Een alternatief voor een aangepaste uitzonderingshandlerpagina is een lambda aanbieden aan UseExceptionHandler. Het gebruik van een lambda biedt toegang tot de fout en het schrijven van een antwoord met betrekking tot probleemdetails met IProblemDetailsService.WriteAsync:

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

var builder = WebApplication.CreateBuilder(args);

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

var app = builder.Build();

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

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

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

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

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

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

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

Warning

Geef geen gevoelige foutinformatie aan clients. Het doorgeven van fouten is een beveiligingsrisico.

Een alternatieve benadering voor het genereren van probleemdetails is het gebruik van het NuGet-pakket Hellang.Middleware.ProblemDetails van derden dat kan worden gebruikt om uitzonderingen en clientfouten toe te wijzen aan probleemdetails.

Additional resources

Door Tom Dykstra

In dit artikel worden veelvoorkomende benaderingen besproken voor het afhandelen van fouten in ASP.NET Core-web-apps. Zie Fouten afhandelen in ASP.NET Core-controllergebaseerde web-API's.

Uitzonderingspagina voor ontwikkelaars

Op de uitzonderingspagina voor ontwikkelaars wordt gedetailleerde informatie weergegeven over niet-verwerkte aanvraag-uitzonderingen. ASP.NET Core-apps de uitzonderingspagina voor ontwikkelaars standaard inschakelen wanneer beide:

De ontwikkelaarsexceptiepagina draait vroeg in de middleware-pijplijn, zodat onopgevangen uitzonderingen die in de daaropvolgende middleware worden gegooid, kunnen worden opgevangen.

Gedetailleerde uitzonderingsinformatie mag niet openbaar worden weergegeven wanneer de app wordt uitgevoerd in de productieomgeving. Zie Meerdere omgevingen gebruiken in ASP.NET Core voor meer informatie over het configureren van omgevingen.

De uitzonderingspagina voor ontwikkelaars kan de volgende informatie bevatten over de uitzondering en de aanvraag:

  • Stack trace
  • Queryreeksparameters, indien van toepassing
  • Cookies, indien van toepassing
  • Headers

De uitzonderingspagina voor ontwikkelaars is niet gegarandeerd informatie te verstrekken. Gebruik Logging voor volledige foutinformatie.

Uitzonderingshandlerpagina

Als u een aangepaste pagina voor foutafhandeling voor de productieomgeving wilt configureren, roept UseExceptionHandleru aan. Deze middleware voor uitzonderingafhandeling:

  • Niet-verwerkte uitzonderingen worden onderschept en in logboeken opgeslagen.
  • Voert de aanvraag opnieuw uit in een alternatieve pijplijn met behulp van het aangegeven pad. De aanvraag wordt niet opnieuw uitgevoerd als het antwoord is gestart. De door de sjabloon gegenereerde code voert de aanvraag opnieuw uit met behulp van het /Error pad.

Warning

Als de alternatieve pijplijn een eigen uitzondering genereert, wordt de oorspronkelijke uitzondering opnieuw door de Middleware gegooid.

In het volgende voorbeeld voegt UseExceptionHandler de middleware voor uitzonderingsafhandeling toe in niet-ontwikkelingsomgevingen.

var app = builder.Build();

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

De Razor app-sjabloon .cshtml bevat een foutpagina (PageModel) en ErrorModel klasse () in de map Pagina's. Voor een MVC-app bevat de projectsjabloon een Error actiemethode en een foutweergave voor de Home controller.

Met de uitzonderingsafhandeling van middleware wordt de aanvraag opnieuw uitgevoerd met behulp van de oorspronkelijke HTTP-methode. Als een fouthandlereindpunt is beperkt tot een specifieke set HTTP-methoden, wordt het alleen uitgevoerd voor deze HTTP-methoden. Een MVC-controlleractie die gebruikmaakt van het [HttpGet] kenmerk wordt bijvoorbeeld alleen uitgevoerd voor GET-aanvragen. Als u ervoor wilt zorgen dat alle aanvragen de aangepaste pagina voor foutafhandeling bereiken, beperkt u deze niet tot een specifieke set HTTP-methoden.

Uitzonderingen op een andere manier verwerken op basis van de oorspronkelijke HTTP-methode:

  • Voor Razor Pagina's maakt u meerdere handlermethoden. Gebruik bijvoorbeeld OnGet om GET-uitzonderingen af te handelen en om OnPost POST-uitzonderingen af te handelen.
  • Pas voor MVC HTTP-werkwoordkenmerken toe op meerdere acties. Gebruik bijvoorbeeld [HttpGet] om GET-uitzonderingen af te handelen en om [HttpPost] POST-uitzonderingen af te handelen.

Als u niet-geverifieerde gebruikers wilt toestaan om de aangepaste pagina voor foutafhandeling weer te geven, moet u ervoor zorgen dat deze anonieme toegang ondersteunt.

Toegang tot de uitzondering

Gebruik IExceptionHandlerPathFeature om toegang te krijgen tot de uitzondering en het oorspronkelijke aanvraagpad in een foutafhandelaar. In het volgende voorbeeld wordt IExceptionHandlerPathFeature gebruikt om meer informatie te krijgen over de uitzonderingsfout die is opgetreden.

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

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

    public string? ExceptionMessage { get; set; }

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

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

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

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

Warning

Geef geen gevoelige foutinformatie aan clients. Het doorgeven van fouten is een beveiligingsrisico.

Uitzonderingshandler lambda

Een alternatief voor een aangepaste uitzonderingshandlerpagina is een lambda aanbieden aan UseExceptionHandler. Als u een lambda gebruikt, heeft u toegang tot de fout voordat u het antwoord retourneert.

De volgende code maakt gebruik van een lambda voor het verwerken van uitzonderingen:

var app = builder.Build();

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

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

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

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

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

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

    app.UseHsts();
}

Warning

Geef geen gevoelige foutinformatie aan clients. Het doorgeven van fouten is een beveiligingsrisico.

UseStatusCodePages

Een ASP.NET Core-app biedt standaard geen pagina met statuscode voor HTTP-foutcodes, zoals 404 - Niet gevonden. Wanneer de app een HTTP 400-599-foutcode instelt die geen hoofdtekst heeft, wordt de statuscode en een lege antwoordtekst geretourneerd. Als u standaardhandlers voor alleen-tekst wilt inschakelen voor veelvoorkomende foutcodes, roept u UseStatusCodePages aan in Program.cs.

var app = builder.Build();

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

app.UseStatusCodePages();

Roep UseStatusCodePages aan voordat de middleware voor aanvraagverwerking wordt afgehandeld. Roep bijvoorbeeld UseStatusCodePages aan vóór de Static File Middleware en de Endpoints Middleware.

Wanneer UseStatusCodePages dit niet wordt gebruikt, retourneert het navigeren naar een URL zonder eindpunt een browserafhankelijk foutbericht dat aangeeft dat het eindpunt niet kan worden gevonden. Wanneer UseStatusCodePages wordt aangeroepen, retourneert de browser het volgende antwoord:

Status Code: 404; Not Found

UseStatusCodePages wordt meestal niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

Note

De middleware van de statuscodepagina's vangt geen uitzonderingen op. Als u een aangepaste foutafhandelingspagina wilt opgeven, gebruikt u de uitzonderingshandlerpagina.

UseStatusCodePages met formaattekenreeks

Om het inhoudstype en de tekst van de reactie aan te passen, gebruikt u de overload van UseStatusCodePages die een inhoudstype en een opmaaktekenreeks vereist:

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

In de voorgaande code {0} is een tijdelijke aanduiding voor de foutcode.

UseStatusCodePages met een notatietekenreeks wordt doorgaans niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

UseStatusCodePages met lambda

Als u aangepaste code voor foutafhandeling en het schrijven van reacties wilt opgeven, gebruikt u de overload van UseStatusCodePages die een lambda-expressie accepteert:

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 met een lambda wordt meestal niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

UseStatusCodePagesWithRedirects

De UseStatusCodePagesWithRedirects extensiemethode:

  • Een 302 - Found statuscode wordt naar de client gestuurd.
  • Hiermee wordt de client omgeleid naar het eindpunt voor foutafhandeling dat is opgegeven in de URL-sjabloon. Het eindpunt voor foutafhandeling geeft doorgaans foutinformatie weer en retourneert HTTP 200.
var app = builder.Build();

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

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

De URL-sjabloon kan een {0} tijdelijke aanduiding voor de statuscode bevatten, zoals te zien is in de voorgaande code. Als de URL-sjabloon begint met ~ (tilde), wordt de ~ vervangen door de PathBase van de app. Wanneer u een eindpunt in de app opgeeft, maakt u een MVC-weergave of Razor -pagina voor het eindpunt.

Deze methode wordt vaak gebruikt wanneer de app:

  • Moet de client omleiden naar een ander eindpunt, meestal in gevallen waarin een andere app de fout verwerkt. Voor web-apps weerspiegelt de adresbalk van de client het omgeleide eindpunt.
  • Bewaar de oorspronkelijke statuscode niet en retourneer deze niet met het eerste omleidingsantwoord.

UseStatusCodePagesWithReExecute

De UseStatusCodePagesWithReExecute extensiemethode:

  • Retourneert de oorspronkelijke statuscode naar de client.
  • Hiermee wordt de hoofdtekst van het antwoord gegenereerd door de aanvraagpijplijn opnieuw uit te voeren met behulp van een alternatief pad.
var app = builder.Build();

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

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

Als er een eindpunt in de app is opgegeven, maakt u een MVC-weergave of Razor -pagina voor het eindpunt.

Deze methode wordt vaak gebruikt wanneer de app het volgende moet doen:

  • De aanvraag verwerken zonder om te leiden naar een ander eindpunt. Voor web-apps weerspiegelt de adresbalk van de browser van de client het oorspronkelijk aangevraagde eindpunt.
  • Behoud en retourneer de oorspronkelijke statuscode met het antwoord.

De URL-sjabloon moet beginnen met / en kan een tijdelijke aanduiding {0} voor de statuscode bevatten. Als u de statuscode wilt doorgeven als een querytekenreeksparameter, geeft u een tweede argument door aan UseStatusCodePagesWithReExecute. For example:

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

Het eindpunt dat de fout verwerkt, kan de oorspronkelijke URL ophalen die de fout heeft gegenereerd, zoals wordt weergegeven in het volgende voorbeeld:

[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);
        }
    }
}

Statuscodepagina's uitschakelen

Gebruik het kenmerk [SkipStatusCodePages] om statuscodepagina's voor een MVC-controller of actiemethode uit te schakelen.

Als u statuscodepagina's wilt uitschakelen voor specifieke aanvragen in een Razor Pagina-handlermethode of in een MVC-controller, gebruikt u IStatusCodePagesFeature:

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

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

Exception-handling code

Code in uitzonderingsafhandelingspagina's kan ook uitzonderingen genereren. Productiefoutpagina's moeten grondig worden getest en extra voorzichtig zijn om te voorkomen dat ze zelf uitzonderingen genereren.

Response headers

Zodra de headers voor een antwoord zijn verzonden:

  • De app kan de statuscode van het antwoord niet wijzigen.
  • Eventuele uitzonderingspagina's of handlers kunnen niet worden uitgevoerd. Het antwoord moet worden voltooid of de verbinding is afgebroken.

Afhandeling van serveronderzondering

Naast de logica voor het afhandelen van uitzonderingen in een app kan de IMPLEMENTATIE van de HTTP-server enkele uitzonderingen verwerken. Als de server een uitzondering onderschept voordat antwoordheaders worden verzonden, verzendt de server een 500 - Internal Server Error antwoord zonder antwoordtekst. Als de server een uitzondering opmerkt nadat de responsheaders zijn verzonden, sluit de server de verbinding. Aanvragen die niet door de app worden verwerkt, worden verwerkt door de server. Eventuele uitzonderingen die optreden wanneer de server de aanvraag verwerkt, wordt verwerkt door de uitzonderingsafhandeling van de server. De aangepaste foutpagina's van de app, uitzonderingsafhandeling van middleware en filters hebben geen invloed op dit gedrag.

Afhandeling van opstartonderzondering

Alleen de hostinglaag kan uitzonderingen verwerken die plaatsvinden tijdens het opstarten van de app. De host kan worden geconfigureerd om opstartfouten vast te leggen en gedetailleerde fouten vast te leggen.

De hostinglaag kan alleen een foutpagina voor een vastgelegde opstartfout weergeven als de fout optreedt na hostadres-/poortbinding. Als de binding mislukt:

  • De hostinglaag registreert een kritieke uitzondering.
  • Het dotnet-proces loopt vast.
  • Er wordt geen foutpagina weergegeven wanneer de HTTP-server is Kestrel.

Bij uitvoering op IIS (of Azure App Service) of IIS Express wordt een procesfout van 502.5 geretourneerd door de ASP.NET Core-module als het proces niet kan worden gestart. Zie Problemen met ASP.NET Core in Azure App Service en IIS oplossen voor meer informatie.

Databasefoutpagina

Met het uitzonderingsfilter AddDatabaseDeveloperPageExceptionFilter voor databaseontwikkelaarspagina's worden databasegerelateerde uitzonderingen vastgelegd die kunnen worden opgelost met behulp van Entity Framework Core-migraties. Wanneer deze uitzonderingen optreden, wordt er een HTML-antwoord gegenereerd met details van mogelijke acties om het probleem op te lossen. Deze pagina is alleen ingeschakeld in de ontwikkelomgeving. Met de volgende code wordt het uitzonderingsfilter voor de databaseontwikkelaarspagina toegevoegd:

var builder = WebApplication.CreateBuilder(args);

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

Exception filters

In MVC-apps kunnen uitzonderingsfilters globaal of per controller of per actie worden geconfigureerd. In Razor Pagina-apps kunnen ze globaal of per paginamodel worden geconfigureerd. Deze filters verwerken eventuele niet-verwerkte uitzonderingen die optreden tijdens de uitvoering van een controlleractie of een ander filter. Zie Filters in ASP.NET Core voor meer informatie.

Uitzonderingsfilters zijn handig voor uitzonderingen die optreden binnen MVC-acties, maar ze zijn niet zo flexibel als de ingebouwde middleware voor het verwerken van uitzonderingen. UseExceptionHandler We raden aan UseExceptionHandler te gebruiken, tenzij u foutafhandeling anders moet uitvoeren op basis van welke MVC-actie wordt gekozen.

Fouten in modelstatus

Zie Modelbinding en Modelvalidatie voor informatie over het afhandelen van modelstatusfouten.

Additional resources

Door Kirk Larkin, Tom Dykstra en Steve Smith

In dit artikel worden veelvoorkomende benaderingen besproken voor het afhandelen van fouten in ASP.NET Core-web-apps. Zie Fouten afhandelen in ASP.NET Core-controllergebaseerde web-API's.

voorbeeldcode weergeven of downloaden. (Downloaden.) Het netwerktabblad in de ontwikkelhulpprogramma's van de F12-browser is handig bij het testen van de voorbeeld-app.

Uitzonderingspagina voor ontwikkelaars

Op de uitzonderingspagina voor ontwikkelaars wordt gedetailleerde informatie weergegeven over niet-verwerkte aanvraag-uitzonderingen. De ASP.NET Core-sjablonen genereren de volgende 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();
    });
}

Met de voorgaande gemarkeerde code wordt de uitzonderingspagina voor ontwikkelaars ingeschakeld wanneer de app wordt uitgevoerd in de ontwikkelomgeving.

De sjablonen plaatsen UseDeveloperExceptionPage vroeg in de middleware-pijplijn, zodat niet-afgehandelde uitzonderingen kunnen worden opgevangen die worden gegenereerd in volgende middleware.

Met de voorgaande code wordt de uitzonderingspagina voor ontwikkelaars alleen ingeschakeld wanneer de app wordt uitgevoerd in de ontwikkelomgeving. Gedetailleerde uitzonderingsinformatie mag niet openbaar worden weergegeven wanneer de app wordt uitgevoerd in de productieomgeving. Zie Meerdere omgevingen gebruiken in ASP.NET Core voor meer informatie over het configureren van omgevingen.

De uitzonderingspagina voor ontwikkelaars kan de volgende informatie bevatten over de uitzondering en de aanvraag:

  • Stack trace
  • Queryreeksparameters indien van toepassing
  • Cookies indien van toepassing
  • Headers

De uitzonderingspagina voor ontwikkelaars is niet gegarandeerd informatie te verstrekken. Gebruik Logging voor volledige foutinformatie.

Uitzonderingshandlerpagina

Als u een aangepaste pagina voor foutafhandeling voor de productieomgeving wilt configureren, roept UseExceptionHandleru aan. Deze middleware voor uitzonderingafhandeling:

  • Niet-verwerkte uitzonderingen worden onderschept en in logboeken opgeslagen.
  • Voert de aanvraag opnieuw uit in een alternatieve pijplijn met behulp van het aangegeven pad. De aanvraag wordt niet opnieuw uitgevoerd als het antwoord is gestart. De door de sjabloon gegenereerde code voert de aanvraag opnieuw uit met behulp van het /Error pad.

Warning

Als de alternatieve pijplijn een eigen uitzondering genereert, wordt de oorspronkelijke uitzondering opnieuw door de Middleware gegooid.

In het volgende voorbeeld voegt UseExceptionHandler de middleware voor uitzonderingsafhandeling toe in niet-ontwikkelingsomgevingen.

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

De Razor app-sjabloon .cshtml bevat een foutpagina (PageModel) en ErrorModel klasse () in de map Pagina's. Voor een MVC-app bevat de projectsjabloon een Error actiemethode en een foutweergave voor de Home controller.

Met de uitzonderingsafhandeling van middleware wordt de aanvraag opnieuw uitgevoerd met behulp van de oorspronkelijke HTTP-methode. Als een fouthandlereindpunt is beperkt tot een specifieke set HTTP-methoden, wordt het alleen uitgevoerd voor deze HTTP-methoden. Een MVC-controlleractie die gebruikmaakt van het [HttpGet] kenmerk wordt bijvoorbeeld alleen uitgevoerd voor GET-aanvragen. Als u ervoor wilt zorgen dat alle aanvragen de aangepaste pagina voor foutafhandeling bereiken, beperkt u deze niet tot een specifieke set HTTP-methoden.

Uitzonderingen op een andere manier verwerken op basis van de oorspronkelijke HTTP-methode:

  • Voor Razor Pagina's maakt u meerdere handlermethoden. Gebruik bijvoorbeeld OnGet om GET-uitzonderingen af te handelen en om OnPost POST-uitzonderingen af te handelen.
  • Pas voor MVC HTTP-werkwoordkenmerken toe op meerdere acties. Gebruik bijvoorbeeld [HttpGet] om GET-uitzonderingen af te handelen en om [HttpPost] POST-uitzonderingen af te handelen.

Als u niet-geverifieerde gebruikers wilt toestaan om de aangepaste pagina voor foutafhandeling weer te geven, moet u ervoor zorgen dat deze anonieme toegang ondersteunt.

Toegang tot de uitzondering

Gebruik IExceptionHandlerPathFeature om toegang te krijgen tot de uitzondering en het oorspronkelijke aanvraagpad in een foutafhandelaar. De volgende code voegt ExceptionMessage toe aan de standaard Pages/Error.cshtml.cs die gegenereerd wordt door de ASP.NET Core-sjablonen.

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

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

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

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

Warning

Geef geen gevoelige foutinformatie aan clients. Het doorgeven van fouten is een beveiligingsrisico.

De uitzondering testen in de voorbeeld-app:

  • Stel de omgeving in op productie.
  • Verwijder de opmerkingen uit webBuilder.UseStartup<Startup>(); in Program.cs.
  • Selecteer Een uitzondering activeren op de startpagina.

Uitzonderingshandler lambda

Een alternatief voor een aangepaste uitzonderingshandlerpagina is een lambda aanbieden aan UseExceptionHandler. Als u een lambda gebruikt, heeft u toegang tot de fout voordat u het antwoord retourneert.

De volgende code maakt gebruik van een lambda voor het verwerken van uitzonderingen:

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

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

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

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

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

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

    app.UseRouting();

    app.UseAuthorization();

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

Warning

Gebruik geen gevoelige foutinformatie van IExceptionHandlerFeature of IExceptionHandlerPathFeature naar clients. Het doorgeven van fouten is een beveiligingsrisico.

De uitzonderingsafhandeling van lambda testen in de voorbeeld-app:

  • Stel de omgeving in op productie.
  • Verwijder de opmerkingen uit webBuilder.UseStartup<StartupLambda>(); in Program.cs.
  • Selecteer Een uitzondering activeren op de startpagina.

UseStatusCodePages

Een ASP.NET Core-app biedt standaard geen pagina met statuscode voor HTTP-foutcodes, zoals 404 - Niet gevonden. Wanneer de app een HTTP 400-599-foutcode instelt die geen hoofdtekst heeft, wordt de statuscode en een lege antwoordtekst geretourneerd. Als u statuscodepagina's wilt opgeven, gebruikt u de middleware voor statuscodepagina's. Om standaardhandlers voor alleen-tekst in te schakelen voor veelvoorkomende foutstatuscodes, roept u UseStatusCodePages aan in de Startup.Configure-methode:

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();
    });
}

Roep UseStatusCodePages aan voordat de middleware voor aanvraagverwerking wordt afgehandeld. Roep bijvoorbeeld UseStatusCodePages aan vóór de Static File Middleware en de Endpoints Middleware.

Wanneer UseStatusCodePages dit niet wordt gebruikt, retourneert het navigeren naar een URL zonder eindpunt een browserafhankelijk foutbericht dat aangeeft dat het eindpunt niet kan worden gevonden. Ga bijvoorbeeld naar Home/Privacy2. Wanneer UseStatusCodePages wordt aangeroepen, retourneert de browser:

Status Code: 404; Not Found

UseStatusCodePages wordt meestal niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

Testen UseStatusCodePages in de voorbeeld-app:

  • Stel de omgeving in op productie.
  • Verwijder de opmerkingen uit webBuilder.UseStartup<StartupUseStatusCodePages>(); in Program.cs.
  • Selecteer de koppelingen op de startpagina op de startpagina.

Note

De middleware van de statuscodepagina's vangt geen uitzonderingen op. Als u een aangepaste foutafhandelingspagina wilt opgeven, gebruikt u de uitzonderingshandlerpagina.

UseStatusCodePages met formaattekenreeks

Om het inhoudstype en de tekst van de reactie aan te passen, gebruikt u de overload van UseStatusCodePages die een inhoudstype en een opmaaktekenreeks vereist:

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();
    });
}

In de voorgaande code {0} is een tijdelijke aanduiding voor de foutcode.

UseStatusCodePages met een notatietekenreeks wordt doorgaans niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

Als u wilt testen UseStatusCodePages in de voorbeeld-app, verwijdert u de opmerkingen in webBuilder.UseStartup<StartupFormat>();Program.cs.

UseStatusCodePages met lambda

Als u aangepaste code voor foutafhandeling en het schrijven van reacties wilt opgeven, gebruikt u de overload van UseStatusCodePages die een lambda-expressie accepteert:

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 met een lambda wordt meestal niet gebruikt in productie omdat het een bericht retourneert dat niet nuttig is voor gebruikers.

Als u wilt testen UseStatusCodePages in de voorbeeld-app, verwijdert u de opmerkingen in webBuilder.UseStartup<StartupStatusLambda>();Program.cs.

UseStatusCodePagesWithRedirects

De UseStatusCodePagesWithRedirects extensiemethode:

  • Een 302 - Found statuscode wordt naar de client gestuurd.
  • Hiermee wordt de client omgeleid naar het eindpunt voor foutafhandeling dat is opgegeven in de URL-sjabloon. Het eindpunt voor foutafhandeling geeft doorgaans foutinformatie weer en retourneert HTTP 200.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

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

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

    app.UseRouting();

    app.UseAuthorization();

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

De URL-sjabloon kan een {0} tijdelijke aanduiding voor de statuscode bevatten, zoals te zien is in de voorgaande code. Als de URL-sjabloon begint met ~ (tilde), wordt de ~ vervangen door de PathBase van de app. Wanneer u een eindpunt in de app opgeeft, maakt u een MVC-weergave of Razor -pagina voor het eindpunt. Zie Razor in de voorbeeld-app voor een voorbeeldpagina.

Deze methode wordt vaak gebruikt wanneer de app:

  • Moet de client omleiden naar een ander eindpunt, meestal in gevallen waarin een andere app de fout verwerkt. Voor web-apps weerspiegelt de adresbalk van de client het omgeleide eindpunt.
  • Bewaar de oorspronkelijke statuscode niet en retourneer deze niet met het eerste omleidingsantwoord.

Als u wilt testen UseStatusCodePages in de voorbeeld-app, verwijdert u de opmerkingen in webBuilder.UseStartup<StartupSCredirect>();Program.cs.

UseStatusCodePagesWithReExecute

De UseStatusCodePagesWithReExecute extensiemethode:

  • Retourneert de oorspronkelijke statuscode naar de client.
  • Hiermee wordt de hoofdtekst van het antwoord gegenereerd door de aanvraagpijplijn opnieuw uit te voeren met behulp van een alternatief pad.
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();
    });
}

Als er een eindpunt in de app is opgegeven, maakt u een MVC-weergave of Razor -pagina voor het eindpunt. Zorg ervoor dat UseStatusCodePagesWithReExecute vóór UseRouting wordt geplaatst, zodat de aanvraag kan worden omgeleid naar de statuspagina. Zie Razor in de voorbeeld-app voor een voorbeeldpagina.

Deze methode wordt vaak gebruikt wanneer de app het volgende moet doen:

  • De aanvraag verwerken zonder om te leiden naar een ander eindpunt. Voor web-apps weerspiegelt de adresbalk van de browser van de client het oorspronkelijk aangevraagde eindpunt.
  • Behoud en retourneer de oorspronkelijke statuscode met het antwoord.

De URL- en querytekenreekssjablonen kunnen een tijdelijke aanduiding {0} voor de statuscode bevatten. De URL-sjabloon moet beginnen met /.

Het eindpunt dat de fout verwerkt, kan de oorspronkelijke URL ophalen die de fout heeft gegenereerd, zoals wordt weergegeven in het volgende voorbeeld:

[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;
        }
    }
}

Zie Razor in de voorbeeld-app voor een voorbeeldpagina.

Als u wilt testen UseStatusCodePages in de voorbeeld-app, verwijdert u de opmerkingen in webBuilder.UseStartup<StartupSCreX>();Program.cs.

Statuscodepagina's uitschakelen

Gebruik het kenmerk [SkipStatusCodePages] om statuscodepagina's voor een MVC-controller of actiemethode uit te schakelen.

Als u statuscodepagina's wilt uitschakelen voor specifieke aanvragen in een Razor Pagina-handlermethode of in een MVC-controller, gebruikt u IStatusCodePagesFeature:

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

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

Exception-handling code

Code in uitzonderingsafhandelingspagina's kan ook uitzonderingen genereren. Productiefoutpagina's moeten grondig worden getest en extra voorzichtig zijn om te voorkomen dat ze zelf uitzonderingen genereren.

Response headers

Zodra de headers voor een antwoord zijn verzonden:

  • De app kan de statuscode van het antwoord niet wijzigen.
  • Eventuele uitzonderingspagina's of handlers kunnen niet worden uitgevoerd. Het antwoord moet worden voltooid of de verbinding is afgebroken.

Afhandeling van serveronderzondering

Naast de logica voor het afhandelen van uitzonderingen in een app kan de IMPLEMENTATIE van de HTTP-server enkele uitzonderingen verwerken. Als de server een uitzondering onderschept voordat antwoordheaders worden verzonden, verzendt de server een 500 - Internal Server Error antwoord zonder antwoordtekst. Als de server een uitzondering opmerkt nadat de responsheaders zijn verzonden, sluit de server de verbinding. Aanvragen die niet door de app worden verwerkt, worden verwerkt door de server. Eventuele uitzonderingen die optreden wanneer de server de aanvraag verwerkt, wordt verwerkt door de uitzonderingsafhandeling van de server. De aangepaste foutpagina's van de app, uitzonderingsafhandeling van middleware en filters hebben geen invloed op dit gedrag.

Afhandeling van opstartonderzondering

Alleen de hostinglaag kan uitzonderingen verwerken die plaatsvinden tijdens het opstarten van de app. De host kan worden geconfigureerd om opstartfouten vast te leggen en gedetailleerde fouten vast te leggen.

De hostinglaag kan alleen een foutpagina voor een vastgelegde opstartfout weergeven als de fout optreedt na hostadres-/poortbinding. Als de binding mislukt:

  • De hostinglaag registreert een kritieke uitzondering.
  • Het dotnet-proces loopt vast.
  • Er wordt geen foutpagina weergegeven wanneer de HTTP-server is Kestrel.

Bij uitvoering op IIS (of Azure App Service) of IIS Express wordt een procesfout van 502.5 geretourneerd door de ASP.NET Core-module als het proces niet kan worden gestart. Zie Problemen met ASP.NET Core in Azure App Service en IIS oplossen voor meer informatie.

Databasefoutpagina

Met het uitzonderingsfilter AddDatabaseDeveloperPageExceptionFilter voor databaseontwikkelaarspagina's worden databasegerelateerde uitzonderingen vastgelegd die kunnen worden opgelost met behulp van Entity Framework Core-migraties. Wanneer deze uitzonderingen optreden, wordt er een HTML-antwoord gegenereerd met details van mogelijke acties om het probleem op te lossen. Deze pagina is alleen ingeschakeld in de ontwikkelomgeving. De volgende code is gegenereerd door de ASP.NET Core Razor Pages-sjablonen wanneer afzonderlijke gebruikersaccounts zijn opgegeven:

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();
}

Exception filters

In MVC-apps kunnen uitzonderingsfilters globaal of per controller of per actie worden geconfigureerd. In Razor Pagina-apps kunnen ze globaal of per paginamodel worden geconfigureerd. Deze filters verwerken eventuele niet-verwerkte uitzonderingen die optreden tijdens de uitvoering van een controlleractie of een ander filter. Zie Filters in ASP.NET Core voor meer informatie.

Uitzonderingsfilters zijn handig voor uitzonderingen die optreden binnen MVC-acties, maar ze zijn niet zo flexibel als de ingebouwde middleware voor het verwerken van uitzonderingen. UseExceptionHandler We raden aan UseExceptionHandler te gebruiken, tenzij u foutafhandeling anders moet uitvoeren op basis van welke MVC-actie wordt gekozen.

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();
    });
}

Fouten in modelstatus

Zie Modelbinding en Modelvalidatie voor informatie over het afhandelen van modelstatusfouten.

Additional resources

Door Tom Dykstra en Steve Smith

In dit artikel worden veelvoorkomende benaderingen besproken voor het afhandelen van fouten in ASP.NET Core-web-apps. Zie Fouten afhandelen in ASP.NET Core-controllergebaseerde web-API's.

voorbeeldcode weergeven of downloaden. (Hoe te downloaden.)

Uitzonderingspagina voor ontwikkelaars

Op de pagina Uitzondering voor ontwikkelaars wordt gedetailleerde informatie weergegeven over aanvraag-uitzonderingen. De ASP.NET Core-sjablonen genereren de volgende code:

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

Met de voorgaande code wordt de uitzonderingspagina voor ontwikkelaars ingeschakeld wanneer de app wordt uitgevoerd in de ontwikkelomgeving.

De sjablonen plaatsen UseDeveloperExceptionPage vóór enige middleware, zodat uitzonderingen worden opgevangen in de daaropvolgende middleware.

Met de voorgaande code wordt de uitzonderingspagina voor ontwikkelaars alleen ingeschakeld wanneer de app wordt uitgevoerd in de ontwikkelomgeving. Gedetailleerde uitzonderingsinformatie mag niet openbaar worden weergegeven wanneer de app in productie wordt uitgevoerd. Zie Meerdere omgevingen gebruiken in ASP.NET Core voor meer informatie over het configureren van omgevingen.

De uitzonderingspagina voor ontwikkelaars bevat de volgende informatie over de uitzondering en de aanvraag:

  • Stack trace
  • Queryreeksparameters indien van toepassing
  • Cookies indien van toepassing
  • Headers

Uitzonderingshandlerpagina

Als u een aangepaste pagina voor foutafhandeling voor de productieomgeving wilt configureren, gebruikt u de Middleware voor het afhandelen van uitzonderingen. The middleware:

  • Vangt en registreert uitzonderingen.
  • Voert de aanvraag opnieuw uit in een alternatieve pijplijn voor de aangegeven pagina of controller. De aanvraag wordt niet opnieuw uitgevoerd als het antwoord is gestart. De door de sjabloon gegenereerde code voert de aanvraag opnieuw uit naar /Error.

In het volgende voorbeeld UseExceptionHandler voegt u de Middleware voor uitzonderingsafhandeling toe in niet-ontwikkelomgevingen:

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

De Razor app-sjabloon .cshtml bevat een foutpagina (PageModel) en ErrorModel klasse () in de map Pagina's. Voor een MVC-app bevat de projectsjabloon een fout-actiemethode en een foutweergave in de Home controller.

Markeer de actiemethode voor de fouthandler niet met HTTP-methodekenmerken, zoals HttpGet. Expliciete werkwoorden voorkomen dat sommige aanvragen de methode bereiken. Anonieme toegang tot de methode toestaan als niet-geverifieerde gebruikers de foutweergave moeten zien.

Toegang tot de uitzondering

Gebruik IExceptionHandlerPathFeature om toegang te krijgen tot de uitzondering en het oorspronkelijke aanvraagpad in een fouthandler-controller, of -pagina.

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

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

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

Warning

Geef geen gevoelige foutinformatie aan clients. Het doorgeven van fouten is een beveiligingsrisico.

Als u de voorgaande pagina voor het afhandelen van uitzonderingen wilt activeren, stelt u de omgeving in op producties en dwingt u een uitzondering af.

Uitzonderingshandler lambda

Een alternatief voor een aangepaste uitzonderingshandlerpagina is een lambda aanbieden aan UseExceptionHandler. Als u een lambda gebruikt, heeft u toegang tot de fout voordat u het antwoord retourneert.

Hier volgt een voorbeeld van het gebruik van een lambda voor het verwerken van uitzonderingen:

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();
}

In de voorgaande code wordt toegevoegd, await context.Response.WriteAsync(new string(' ', 512)); zodat in de browser Internet Explorer het foutbericht wordt weergegeven in plaats van een IE-foutbericht. Zie dit GitHub-probleem voor meer informatie.

Warning

Gebruik geen gevoelige foutinformatie van IExceptionHandlerFeature of IExceptionHandlerPathFeature naar clients. Het doorgeven van fouten is een beveiligingsrisico.

Als u het resultaat van de uitzonderingsafhandeling van lambda in de voorbeeld-app wilt zien, gebruikt u de ProdEnvironment richtlijnen en ErrorHandlerLambda preprocessorrichtlijnen en selecteert u Een uitzondering activeren op de startpagina.

UseStatusCodePages

Een ASP.NET Core-app biedt standaard geen pagina met statuscode voor HTTP-statuscodes, zoals 404 - Niet gevonden. De app retourneert een statuscode en een lege hoofdtekst van het antwoord. Als u statuscodepagina's wilt opgeven, gebruikt u middleware voor statuscodepagina's.

De middleware wordt beschikbaar gesteld door het Microsoft.AspNetCore.Diagnostics-pakket .

Om standaardhandlers voor alleen-tekst in te schakelen voor veelvoorkomende foutstatuscodes, roept u UseStatusCodePages aan in de Startup.Configure-methode:

app.UseStatusCodePages();

Roep UseStatusCodePages aan voordat de middleware voor het verwerken van aanvragen wordt uitgevoerd (bijvoorbeeld Static File Middleware en MVC Middleware).

Wanneer UseStatusCodePages dit niet wordt gebruikt, retourneert het navigeren naar een URL zonder eindpunt een browserafhankelijk foutbericht dat aangeeft dat het eindpunt niet kan worden gevonden. Ga bijvoorbeeld naar Home/Privacy2. Wanneer UseStatusCodePages wordt aangeroepen, retourneert de browser:

Status Code: 404; Not Found

UseStatusCodePages met formaattekenreeks

Om het inhoudstype en de tekst van de reactie aan te passen, gebruikt u de overload van UseStatusCodePages die een inhoudstype en een opmaaktekenreeks vereist:

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

UseStatusCodePages met lambda

Als u aangepaste code voor foutafhandeling en het schrijven van reacties wilt opgeven, gebruikt u de overload van UseStatusCodePages die een lambda-expressie accepteert:

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

De UseStatusCodePagesWithRedirects extensiemethode:

  • Een 302 - Found statuscode wordt naar de client gestuurd.
  • Hiermee wordt de client omgeleid naar de locatie die is opgegeven in de URL-sjabloon.
app.UseStatusCodePagesWithRedirects("/StatusCode?code={0}");

De URL-sjabloon kan een {0} tijdelijke aanduiding voor de statuscode bevatten, zoals in het voorbeeld is te zien. Als de URL-sjabloon begint met ~ (tilde), wordt de ~ vervangen door de PathBase van de app. Als u een eindpunt in de app aanwijst, maakt u een MVC-weergave of Razor -pagina voor het eindpunt. Zie voor een Razor paginavoorbeeld Pages/StatusCode.cshtml in de voorbeeld-app.

Deze methode wordt vaak gebruikt wanneer de app:

  • Moet de client omleiden naar een ander eindpunt, meestal in gevallen waarin een andere app de fout verwerkt. Voor web-apps weerspiegelt de adresbalk van de client het omgeleide eindpunt.
  • Bewaar de oorspronkelijke statuscode niet en retourneer deze niet met het eerste omleidingsantwoord.

UseStatusCodePagesWithReExecute

De UseStatusCodePagesWithReExecute extensiemethode:

  • Retourneert de oorspronkelijke statuscode naar de client.
  • Hiermee wordt de hoofdtekst van het antwoord gegenereerd door de aanvraagpijplijn opnieuw uit te voeren met behulp van een alternatief pad.
app.UseStatusCodePagesWithReExecute("/StatusCode","?code={0}");

Als u een eindpunt in de app aanwijst, maakt u een MVC-weergave of Razor -pagina voor het eindpunt. Zorg ervoor dat UseStatusCodePagesWithReExecute vóór UseRouting wordt geplaatst, zodat de aanvraag kan worden omgeleid naar de statuspagina. Zie voor een Razor paginavoorbeeld Pages/StatusCode.cshtml in de voorbeeld-app.

Deze methode wordt vaak gebruikt wanneer de app het volgende moet doen:

  • De aanvraag verwerken zonder om te leiden naar een ander eindpunt. Voor web-apps weerspiegelt de adresbalk van de browser van de client het oorspronkelijk aangevraagde eindpunt.
  • Behoud en retourneer de oorspronkelijke statuscode met het antwoord.

De URL- en querytekenreekssjablonen kunnen een tijdelijke aanduiding ({0}) bevatten voor de statuscode. De URL-sjabloon moet beginnen met een slash (/). Wanneer u een tijdelijke aanduiding in het pad gebruikt, controleert u of het eindpunt (pagina of controller) het padsegment kan verwerken. Een pagina voor fouten moet bijvoorbeeld Razor de optionele padsegmentwaarde met de @page instructie accepteren:

@page "{code?}"

Het eindpunt dat de fout verwerkt, kan de oorspronkelijke URL ophalen die de fout heeft gegenereerd, zoals wordt weergegeven in het volgende voorbeeld:

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

Markeer de actiemethode voor de fouthandler niet met HTTP-methodekenmerken, zoals HttpGet. Expliciete werkwoorden voorkomen dat sommige aanvragen de methode bereiken. Anonieme toegang tot de methode toestaan als niet-geverifieerde gebruikers de foutweergave moeten zien.

Statuscodepagina's uitschakelen

Als u de statuscodepagina's voor een MVC-controller of actiemethode wilt uitschakelen, gebruikt u het [SkipStatusCodePages] kenmerk.

Als u statuscodepagina's wilt uitschakelen voor specifieke aanvragen in een Razor Pagina-handlermethode of in een MVC-controller, gebruikt u IStatusCodePagesFeature:

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

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

Exception-handling code

Code in uitzonderingsafhandelingspagina's kan uitzonderingen genereren. Het is vaak een goed idee voor pagina's met productiefouten om te bestaan uit puur statische inhoud.

Response headers

Zodra de headers voor een antwoord zijn verzonden:

  • De app kan de statuscode van het antwoord niet wijzigen.
  • Eventuele uitzonderingspagina's of handlers kunnen niet worden uitgevoerd. Het antwoord moet worden voltooid of de verbinding is afgebroken.

Afhandeling van serveronderzondering

Naast de logica voor het afhandelen van uitzonderingen in uw app, kan de IMPLEMENTATIE van de HTTP-server enkele uitzonderingen verwerken. Als de server een uitzondering onderschept voordat antwoordheaders worden verzonden, verzendt de server een antwoord van 500 - Interne serverfout zonder antwoordtekst. Als de server een uitzondering opmerkt nadat de responsheaders zijn verzonden, sluit de server de verbinding. Aanvragen die niet door uw app worden verwerkt, worden verwerkt door de server. Eventuele uitzonderingen die optreden wanneer de server de aanvraag verwerkt, wordt verwerkt door de uitzonderingsafhandeling van de server. De aangepaste foutpagina's van de app, uitzonderingsafhandeling van middleware en filters hebben geen invloed op dit gedrag.

Afhandeling van opstartonderzondering

Alleen de hostinglaag kan uitzonderingen verwerken die plaatsvinden tijdens het opstarten van de app. De host kan worden geconfigureerd om opstartfouten vast te leggen en gedetailleerde fouten vast te leggen.

De hostinglaag kan alleen een foutpagina voor een vastgelegde opstartfout weergeven als de fout optreedt na hostadres-/poortbinding. Als de binding mislukt:

  • De hostinglaag registreert een kritieke uitzondering.
  • Het dotnet-proces loopt vast.
  • Er wordt geen foutpagina weergegeven wanneer de HTTP-server is Kestrel.

Bij uitvoering op IIS (of Azure App Service) of IIS Express wordt een procesfout van 502.5 geretourneerd door de ASP.NET Core-module als het proces niet kan worden gestart. Zie Problemen met ASP.NET Core in Azure App Service en IIS oplossen voor meer informatie.

Databasefoutpagina

Middleware voor databasefoutpagina's legt databasegerelateerde uitzonderingen vast die kunnen worden opgelost met behulp van Entity Framework-migraties. Wanneer deze uitzonderingen optreden, wordt er een HTML-antwoord met details van mogelijke acties gegenereerd om het probleem op te lossen. Deze pagina mag alleen worden ingeschakeld in de ontwikkelomgeving. Schakel de pagina in door code toe te voegen aan Startup.Configure:

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

UseDatabaseErrorPage vereist het NuGet-pakket Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore .

Exception filters

In MVC-apps kunnen uitzonderingsfilters globaal of per controller of per actie worden geconfigureerd. In Razor Pagina-apps kunnen ze globaal of per paginamodel worden geconfigureerd. Deze filters verwerken eventuele niet-verwerkte uitzonderingen die optreden tijdens de uitvoering van een controlleractie of een ander filter. Zie Filters in ASP.NET Core voor meer informatie.

Tip

Uitzonderingsfilters zijn handig voor uitzonderingen die optreden binnen MVC-acties, maar ze zijn niet zo flexibel als de Middleware voor het verwerken van uitzonderingen. U wordt aangeraden de middleware te gebruiken. Gebruik alleen filters waarbij u foutafhandeling anders moet uitvoeren op basis van welke MVC-actie is gekozen.

Fouten in modelstatus

Zie Modelbinding en Modelvalidatie voor informatie over het afhandelen van modelstatusfouten.

Additional resources