Zpracování chyb v ASP.NET Core

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Od Tom Dykstra

Tento článek popisuje běžné přístupy ke zpracování chyb ve webových aplikacích ASP.NET Core. Podívejte se také na zpracování chyb ve webových rozhraních API ASP.NET Core a zpracování chyb v aplikacích s minimálním rozhraním API.

Stránka výjimky pro vývojáře

Na stránce výjimka pro vývojáře se zobrazují podrobné informace o neošetřených výjimkách požadavků. ASP.NET aplikace Core ve výchozím nastavení povolí stránku výjimky pro vývojáře, pokud jsou obě:

Stránka výjimky vývojáře se spouští v rané fázi kanálu middlewaru, aby bylo možné zachytit neošetřené výjimky vyvolané v middlewaru, které následuje.

Podrobné informace o výjimce by se neměly zobrazovat veřejně při spuštění aplikace v produkčním prostředí. Další informace o konfiguraci prostředí najdete v tématu Použití více prostředí v ASP.NET Core.

Stránka výjimky pro vývojáře může obsahovat následující informace o výjimce a požadavku:

  • Trasování zásobníku
  • Parametry řetězce dotazu, pokud nějaké
  • Cookies, pokud existuje
  • Hlavičky
  • Metadata koncového bodu, pokud existuje

Následující obrázek znázorňuje ukázkovou stránku výjimky vývojáře se zobrazenými metadaty směrování a koncových bodů:

Stránka výjimky pro vývojáře se zobrazenými metadaty směrování a metadat koncového bodu

Na stránce výjimky pro vývojáře není zaručeno, že poskytnete žádné informace. Protokolování slouží k úplným informacím o chybě.

Stránka obslužné rutiny výjimky

Chcete-li nakonfigurovat vlastní stránku zpracování chyb pro produkční prostředí, zavolejte UseExceptionHandler. Tento middleware pro zpracování výjimek:

  • Zachytává a zaznamenává neošetřené výjimky.
  • Znovu spustí požadavek v alternativním kanálu pomocí uvedené cesty. Pokud se odpověď spustila, požadavek se znovu nespustí. Vygenerovaný kód šablony znovu spustí požadavek pomocí /Error cesty.

Upozorňující

Pokud alternativní kanál vyvolá výjimku vlastní, middleware zpracování výjimek znovu zvětšuje původní výjimku.

Vzhledem k tomu, že tento middleware může kanál požadavku znovu spustit:

  • Middleware musí zpracovávat opakování se stejným požadavkem. Obvykle to znamená, že po zavolání _next nebo uložení zpracování do mezipaměti HttpContext na zařízení, aby se zabránilo jeho opětovnému vytvoření. Při práci s textem požadavku to znamená ukládání do vyrovnávací paměti nebo ukládání výsledků do mezipaměti, jako je čtečka formulářů.
  • UseExceptionHandler(IApplicationBuilder, String) Pro přetížení, které se používá v šablonách, je změněna pouze cesta požadavku a směrovací data jsou vymazána. Všechna data požadavku, jako jsou hlavičky, metoda a položky, se znovu používají tak, jak jsou.
  • Služby s vymezeným oborem zůstávají stejné.

V následujícím příkladu UseExceptionHandler přidá middleware pro zpracování výjimek v prostředích, která nejsou vývojová:

var app = builder.Build();

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

Šablona Razor aplikace Pages poskytuje ve složce Pages chybovou stránku (.cshtml) a PageModel třídu (ErrorModel). V případě aplikace MVC obsahuje šablona projektu metodu Error akce a zobrazení Chyba kontroleru Home .

Middleware zpracování výjimek znovu spustí požadavek pomocí původní metody HTTP. Pokud je koncový bod obslužné rutiny chyby omezen na konkrétní sadu metod HTTP, spustí se pouze pro tyto metody HTTP. Například akce kontroleru MVC, která používá [HttpGet] atribut, se spouští pouze pro požadavky GET. Abyste zajistili, že všechny požadavky dosáhnou vlastní stránky zpracování chyb, neomezovat je na konkrétní sadu metod HTTP.

Zpracování výjimek odlišně na základě původní metody HTTP:

  • Pro Razor Stránky vytvořte více metod obslužné rutiny. Slouží OnGet například ke zpracování výjimek GET a zpracování OnPost výjimek POST.
  • Pro MVC použijte atributy příkazů HTTP na více akcí. Slouží [HttpGet] například ke zpracování výjimek GET a zpracování [HttpPost] výjimek POST.

Pokud chcete umožnit neověřeným uživatelům zobrazit vlastní stránku pro zpracování chyb, ujistěte se, že podporuje anonymní přístup.

Přístup k výjimce

Slouží IExceptionHandlerPathFeature k přístupu k výjimce a původní cestě požadavku v obslužné rutině chyby. Následující příklad používá IExceptionHandlerPathFeature k získání dalších informací o výjimce, která byla vyvolán:

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

Upozorňující

Neservírujte klientům citlivé informace o chybách. Obsluha chyb představuje bezpečnostní riziko.

Lambda obslužné rutiny výjimek

Alternativou k vlastní stránce obslužné rutiny výjimky je poskytnutí lambda UseExceptionHandler. Použití lambda umožňuje přístup k chybě před vrácením odpovědi.

Následující kód používá lambda pro zpracování výjimek:

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

Upozorňující

Neservírujte klientům citlivé informace o chybách. Obsluha chyb představuje bezpečnostní riziko.

IExceptionHandler

IExceptionHandler je rozhraní, které vývojářům poskytuje zpětné volání pro zpracování známých výjimek v centrálním umístění.

IExceptionHandler implementace jsou registrovány voláním IServiceCollection.AddExceptionHandler<T>. Životnost IExceptionHandler instance je singleton. Je možné přidat více implementací a volají se v zaregistrované objednávce.

Pokud obslužná rutina výjimky zpracovává požadavek, může se vrátit true a zastavit zpracování. Pokud žádná obslužná rutina výjimky nezpracuje výjimku, ovládací prvek se vrátí do výchozího chování a možností z middlewaru. Pro zpracovávané a neošetřené výjimky se vygenerují různé metriky a protokoly.

Následující příklad ukazuje implementaci IExceptionHandler :

using Microsoft.AspNetCore.Diagnostics;

namespace ErrorHandlingSample
{
    public class CustomExceptionHandler : IExceptionHandler
    {
        private readonly ILogger<CustomExceptionHandler> logger;
        public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
        {
            this.logger = logger;
        }
        public ValueTask<bool> TryHandleAsync(
            HttpContext httpContext,
            Exception exception,
            CancellationToken cancellationToken)
        {
            var exceptionMessage = exception.Message;
            logger.LogError(
                "Error Message: {exceptionMessage}, Time of occurrence {time}",
                exceptionMessage, DateTime.UtcNow);
            // Return false to continue with the default behavior
            // - or - return true to signal that this exception is handled
            return ValueTask.FromResult(false);
        }
    }
}

Následující příklad ukazuje, jak zaregistrovat IExceptionHandler implementaci pro injektáž závislostí:

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

Při spuštění předchozího kódu ve vývojovém prostředí:

  • Volá se CustomExceptionHandler jako první pro zpracování výjimky.
  • Po protokolování výjimky TryHandleException metoda vrátí false, takže se zobrazí stránka výjimky vývojáře.

V jiných prostředích:

  • Volá se CustomExceptionHandler jako první pro zpracování výjimky.
  • Po protokolování výjimky TryHandleException metoda vrátí false, takže /Error stránka je zobrazena.

UseStatusCodePages

Ve výchozím nastavení aplikace ASP.NET Core neposkytuje stavovou znakovou stránku pro stavové kódy chyb HTTP, například 404 – Nenalezena. Když aplikace nastaví stavový kód chyby HTTP 400-599, který nemá tělo, vrátí stavový kód a prázdný text odpovědi. Pokud chcete povolit výchozí obslužné rutiny jen pro text pro běžné stavové kódy chyb, zavolejte do UseStatusCodePagesProgram.cs:

var app = builder.Build();

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

app.UseStatusCodePages();

Před zpracováním middlewaru žádostí zavolejte UseStatusCodePages . Například zavolejte UseStatusCodePages před middlewarem statického souboru a middlewarem koncových bodů.

Pokud UseStatusCodePages se nepoužívá, přechod na adresu URL bez koncového bodu vrátí chybovou zprávu závislá na prohlížeči, která značí, že se koncový bod nenašel. Při UseStatusCodePages zavolání vrátí prohlížeč následující odpověď:

Status Code: 404; Not Found

UseStatusCodePages se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

Poznámka:

Middleware stavových kódů nezachytává výjimky. Pokud chcete poskytnout vlastní stránku zpracování chyb, použijte stránku obslužné rutiny výjimky.

UseStatusCodePages s formátovacím řetězcem

Pokud chcete přizpůsobit typ obsahu odpovědi a text, použijte přetížení UseStatusCodePages , které přebírá typ obsahu a formátovací řetězec:

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

V předchozím kódu {0} je zástupný symbol pro kód chyby.

UseStatusCodePages formátovací řetězec se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

PoužitíStatusCodePages s lambda

Pokud chcete zadat vlastní zpracování chyb a kód pro zápis odpovědí, použijte přetížení UseStatusCodePages , které přebírá výraz lambda:

var app = builder.Build();

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

app.UseStatusCodePages(async statusCodeContext =>
{
    // using static System.Net.Mime.MediaTypeNames;
    statusCodeContext.HttpContext.Response.ContentType = Text.Plain;

    await statusCodeContext.HttpContext.Response.WriteAsync(
        $"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});

UseStatusCodePages s lambda se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

UseStatusCodePagesWithRedirects

Metoda UseStatusCodePagesWithRedirects rozšíření:

  • Odešle klientovi stavový kód 302 .
  • Přesměruje klienta na koncový bod zpracování chyb zadaný v šabloně adresy URL. Koncový bod zpracování chyb obvykle zobrazuje informace o chybě a vrací http 200.
var app = builder.Build();

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

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

Šablona adresy URL může obsahovat {0} zástupný symbol pro stavový kód, jak je znázorněno v předchozím kódu. Pokud šablona adresy URL začíná vlnovkou ~ , ~ nahradí se aplikací PathBase. Při zadávání koncového bodu v aplikaci vytvořte zobrazení nebo Razor stránku MVC pro koncový bod.

Tato metoda se běžně používá v případě, že aplikace:

  • Měl by přesměrovat klienta na jiný koncový bod, obvykle v případech, kdy jiná aplikace zpracovává chybu. U webových aplikací se adresní řádek prohlížeče klienta odráží přesměrovaný koncový bod.
  • Nemělo by se zachovat a vrátit původní stavový kód s počáteční odpovědí přesměrování.

UseStatusCodePagesWithReExecute

Metoda UseStatusCodePagesWithReExecute rozšíření:

  • Vygeneruje tělo odpovědi opětovným spuštěním kanálu požadavku pomocí alternativní cesty.
  • Nezmění stavový kód před opětovným spuštěním kanálu ani po jeho opětovném spuštění.

Nové spuštění kanálu může změnit stavový kód odpovědi, protože nový kanál má plnou kontrolu nad stavovým kódem. Pokud nový kanál nezmění stavový kód, původní stavový kód se odešle klientovi.

var app = builder.Build();

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

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

Pokud je zadaný koncový bod v aplikaci, vytvořte zobrazení nebo Razor stránku MVC pro koncový bod.

Tato metoda se běžně používá, když by aplikace měla:

  • Zpracujte požadavek bez přesměrování na jiný koncový bod. U webových aplikací se adresní řádek prohlížeče klienta odráží původně požadovaný koncový bod.
  • Zachování a vrácení původního stavového kódu s odpovědí

Šablona adresy URL musí začínat / a může obsahovat zástupný symbol {0} pro stavový kód. Chcete-li předat stavový kód jako parametr řetězce dotazu, předejte druhý argument do UseStatusCodePagesWithReExecute. Příklad:

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

Koncový bod, který chybu zpracuje, může získat původní adresu URL, která chybu vygenerovala, jak je znázorněno v následujícím příkladu:

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

Vzhledem k tomu, že tento middleware může kanál požadavku znovu spustit:

  • Middleware musí zpracovávat opakování se stejným požadavkem. Obvykle to znamená, že po zavolání _next nebo uložení zpracování do mezipaměti HttpContext na zařízení, aby se zabránilo jeho opětovnému vytvoření. Při práci s textem požadavku to znamená ukládání do vyrovnávací paměti nebo ukládání výsledků do mezipaměti, jako je čtečka formulářů.
  • Služby s vymezeným oborem zůstávají stejné.

Zakázat stavové kódové stránky

Chcete-li zakázat stránky stavového kódu pro kontroler MVC nebo metodu akce, použijte atribut [SkipStatusCodePages].

Chcete-li zakázat stavové kódové stránky pro konkrétní požadavky v Razor metodě obslužné rutiny stránky nebo v kontroleru MVC, použijte IStatusCodePagesFeature:

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

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

Kód pro zpracování výjimek

Kód na stránkách zpracování výjimek může také vyvolat výjimky. Produkční chybové stránky by se měly důkladně testovat a pečlivě se postarat, aby nedocházelo k výjimkám jejich vlastních.

Hlavičky odpovědi

Po odeslání hlaviček odpovědi:

  • Aplikace nemůže změnit stavový kód odpovědi.
  • Všechny stránky výjimek nebo obslužné rutiny se nedají spustit. Odpověď musí být dokončena nebo přerušeno připojení.

Zpracování výjimek serveru

Kromě logiky zpracování výjimek v aplikaci může implementace serveru HTTP zpracovávat některé výjimky. Pokud server zachytí výjimku před odesláním hlaviček odpovědi, server odešle 500 - Internal Server Error odpověď bez textu odpovědi. Pokud server zachytí výjimku po odeslání hlaviček odpovědi, server připojení zavře. Požadavky, které aplikace nezpracuje, zpracovává server. Všechny výjimky, ke kterým dochází, když server zpracovává požadavek, se zpracovává zpracováním výjimek serveru. Vlastní chybové stránky aplikace, middleware pro zpracování výjimek a filtry toto chování neovlivní.

Ošetření výjimek při spuštění

Pouze vrstva hostování může zpracovávat výjimky, které probíhají při spuštění aplikace. Hostitel lze nakonfigurovat tak, aby zaznamenával chyby při spuštění a zaznamenával podrobné chyby.

Vrstva hostování může zobrazit chybovou stránku zachycené chyby při spuštění pouze v případě, že k chybě dojde po vazbě adresy hostitele nebo portu. Pokud se vazba nezdaří:

  • Vrstva hostování zaznamená kritickou výjimku.
  • Proces dotnet se chybově ukončí.
  • Pokud je Kestrelserver HTTP, nezobrazí se žádná chybová stránka.

Pokud je proces spuštěný ve službě IIS (nebo ve službě Aplikace Azure) nebo IIS Express, vrátí modul ASP.NET Core chybu procesu 502.5. Další informace najdete v tématu Řešení potíží ASP.NET Core ve službě Aplikace Azure a službě IIS.

Chybová stránka databáze

Filtr AddDatabaseDeveloperPageExceptionFilter výjimek stránky pro vývojáře databáze zachycuje výjimky související s databází, které je možné vyřešit pomocí migrací Entity Framework Core. Pokud dojde k těmto výjimkám, vygeneruje se odpověď HTML s podrobnostmi o možných akcích k vyřešení problému. Tato stránka je povolená pouze ve vývojovém prostředí. Následující kód přidá filtr výjimek stránky pro vývojáře databáze:

var builder = WebApplication.CreateBuilder(args);

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

Filtry výjimek

V aplikacích MVC je možné nakonfigurovat filtry výjimek globálně nebo podle kontroleru nebo podle akce. V Razor aplikacích Pages je možné je nakonfigurovat globálně nebo podle modelu stránky. Tyto filtry zpracovávají všechny neošetřené výjimky, ke kterým dochází během provádění akce kontroleru nebo jiného filtru. Další informace najdete v tématu Filtry v ASP.NET Core.

Filtry výjimek jsou užitečné pro zachycení výjimek, ke kterým dochází v rámci akcí MVC, ale nejsou tak flexibilní jako integrovaný middlewareUseExceptionHandler pro zpracování výjimek. Doporučujeme použít UseExceptionHandler, pokud nepotřebujete provádět zpracování chyb odlišně podle toho, jakou akci MVC zvolíte.

Chyby stavu modelu

Informace o tom, jak zpracovávat chyby stavu modelu, naleznete v tématu Vazby modelu a Ověření modelu.

Podrobnosti o problému

Podrobnosti o problému nejsou jediným formátem odpovědi, který popisuje chybu rozhraní HTTP API, ale běžně se používají k hlášení chyb pro rozhraní HTTP API.

Služba podrobností problému implementuje IProblemDetailsService rozhraní, které podporuje vytváření podrobností o problému v ASP.NET Core. Metoda AddProblemDetails rozšíření při IServiceCollection registraci výchozí IProblemDetailsService implementace.

V aplikacích ASP.NET Core generuje následující middleware při zavolání podrobnosti o problému odpovědi AddProblemDetails HTTP, s výjimkou případů, kdy Accept hlavička HTTP požadavku neobsahuje jeden z typů obsahu podporovaných zaregistrovaným IProblemDetailsWriter (výchozí: application/json):

Následující kód nakonfiguruje aplikaci tak, aby vygenerovala odpověď na podrobnosti o problému pro všechny odpovědi na chyby klienta HTTP a serveru, které ještě nemají základní obsah:

builder.Services.AddProblemDetails();

var app = builder.Build();        

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

app.UseStatusCodePages();

V další části se dozvíte, jak přizpůsobit text odpovědi s podrobnostmi o problému.

Přizpůsobení podrobností o problému

Automatické vytváření ProblemDetails můžete přizpůsobit pomocí některé z následujících možností:

  1. Použití ProblemDetailsOptions.CustomizeProblemDetails
  2. Použití vlastního IProblemDetailsWriter
  3. IProblemDetailsService Volání v middlewaru

CustomizeProblemDetails Operace

Vygenerované podrobnosti o problému lze přizpůsobit pomocí CustomizeProblemDetailsa vlastní nastavení se použijí na všechny automaticky generované podrobnosti o problému.

Následující kód používá ProblemDetailsOptions k nastavení 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();

Výsledek koncového HTTP Status 400 Bad Request bodu například vytvoří následující text odpovědi s podrobnostmi o problému:

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

Vlastní IProblemDetailsWriter

Pro IProblemDetailsWriter pokročilá přizpůsobení je možné vytvořit implementaci.

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

Poznámka: Při použití vlastní IProblemDetailsWriter, vlastní IProblemDetailsWriter musí být registrován před voláním AddRazorPages, AddControllers, , AddControllersWithViewsnebo AddMvc:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<IProblemDetailsWriter, SampleProblemDetailsWriter>();

var app = builder.Build();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsWriter>() is
            { } problemDetailsService)
        {

            if (problemDetailsService.CanWrite(new ProblemDetailsContext() { HttpContext = context }))
            {
                (string Detail, string Type) details = mathErrorFeature.MathError switch
                {
                    MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                        "https://en.wikipedia.org/wiki/Division_by_zero"),
                    _ => ("Negative or complex numbers are not valid input.",
                        "https://en.wikipedia.org/wiki/Square_root")
                };

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

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

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

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

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

app.Run();

Podrobnosti o problému z middlewaru

Alternativním přístupem k použití s ProblemDetailsOptionsCustomizeProblemDetails je nastavení v middlewaru ProblemDetails . Odpověď s podrobnostmi o problému může být napsána voláním 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();

V předchozím kódu minimální koncové body /divide rozhraní API a /squareroot vrátí očekávanou vlastní odpověď na problém při zadávání chyb.

Koncové body kontroleru rozhraní API vrací výchozí odpověď na problém při zadávání chyb, nikoli vlastní odpověď na problém. Výchozí odpověď na problém je vrácena, protože kontroler rozhraní API zapisoval do streamu odpovědi, podrobnosti o problému pro stavové kódy chyb, před IProblemDetailsService.WriteAsync voláním a odpověď se znovu nezapíše .

Následující ValuesController vrátí hodnotu BadRequestResult, která zapisuje do streamu odpovědi, a proto zabraňuje vrácení vlastní odpovědi na problém.

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

}

Následující Values3Controller vrátí ControllerBase.Problem výsledek očekávaného vlastního problému:

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

}

Vytvoření datové části ProblemDetails pro výjimky

Zvažte následující aplikaci:

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

Pokud dojde k výjimce v prostředích, která nejsou vývojová, jedná se o standardní odpověď ProblemDetails, která se vrátí klientovi:

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

U většiny aplikací je předchozí kód vše, co je potřeba pro výjimky. Následující část ale ukazuje, jak získat podrobnější odpovědi na problémy.

Alternativou k vlastní stránce obslužné rutiny výjimky je poskytnutí lambda UseExceptionHandler. Použití lambda umožňuje přístup k chybě a napsání odpovědi s podrobnostmi o problému pomocí 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();

Upozorňující

Neservírujte klientům citlivé informace o chybách. Obsluha chyb představuje bezpečnostní riziko.

Alternativním přístupem k vygenerování podrobností o problému je použití balíčku NuGet jiného výrobce Hellang.Middleware.ProblemDetails , který lze použít k mapování výjimek a chyb klienta na podrobnosti o problému.

Další materiály

Od Tom Dykstra

Tento článek popisuje běžné přístupy ke zpracování chyb ve webových aplikacích ASP.NET Core. Podívejte se také na zpracování chyb ve webových rozhraních API ASP.NET Core a zpracování chyb v aplikacích s minimálním rozhraním API.

Stránka výjimky pro vývojáře

Na stránce výjimka pro vývojáře se zobrazují podrobné informace o neošetřených výjimkách požadavků. ASP.NET aplikace Core ve výchozím nastavení povolí stránku výjimky pro vývojáře, pokud jsou obě:

Stránka výjimky vývojáře se spouští v rané fázi kanálu middlewaru, aby bylo možné zachytit neošetřené výjimky vyvolané v middlewaru, které následuje.

Podrobné informace o výjimce by se neměly zobrazovat veřejně při spuštění aplikace v produkčním prostředí. Další informace o konfiguraci prostředí najdete v tématu Použití více prostředí v ASP.NET Core.

Stránka výjimky pro vývojáře může obsahovat následující informace o výjimce a požadavku:

  • Trasování zásobníku
  • Parametry řetězce dotazu, pokud nějaké
  • Cookies, pokud existuje
  • Hlavičky

Na stránce výjimky pro vývojáře není zaručeno, že poskytnete žádné informace. Protokolování slouží k úplným informacím o chybě.

Stránka obslužné rutiny výjimky

Chcete-li nakonfigurovat vlastní stránku zpracování chyb pro produkční prostředí, zavolejte UseExceptionHandler. Tento middleware pro zpracování výjimek:

  • Zachytává a zaznamenává neošetřené výjimky.
  • Znovu spustí požadavek v alternativním kanálu pomocí uvedené cesty. Pokud se odpověď spustila, požadavek se znovu nespustí. Vygenerovaný kód šablony znovu spustí požadavek pomocí /Error cesty.

Upozorňující

Pokud alternativní kanál vyvolá výjimku vlastní, middleware zpracování výjimek znovu zvětšuje původní výjimku.

Vzhledem k tomu, že tento middleware může kanál požadavku znovu spustit:

  • Middleware musí zpracovávat opakování se stejným požadavkem. Obvykle to znamená, že po zavolání _next nebo uložení zpracování do mezipaměti HttpContext na zařízení, aby se zabránilo jeho opětovnému vytvoření. Při práci s textem požadavku to znamená ukládání do vyrovnávací paměti nebo ukládání výsledků do mezipaměti, jako je čtečka formulářů.
  • UseExceptionHandler(IApplicationBuilder, String) Pro přetížení, které se používá v šablonách, je změněna pouze cesta požadavku a směrovací data jsou vymazána. Všechna data požadavku, jako jsou hlavičky, metoda a položky, se znovu používají tak, jak jsou.
  • Služby s vymezeným oborem zůstávají stejné.

V následujícím příkladu UseExceptionHandler přidá middleware pro zpracování výjimek v prostředích, která nejsou vývojová:

var app = builder.Build();

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

Šablona Razor aplikace Pages poskytuje ve složce Pages chybovou stránku (.cshtml) a PageModel třídu (ErrorModel). V případě aplikace MVC obsahuje šablona projektu metodu Error akce a zobrazení Chyba kontroleru Home .

Middleware zpracování výjimek znovu spustí požadavek pomocí původní metody HTTP. Pokud je koncový bod obslužné rutiny chyby omezen na konkrétní sadu metod HTTP, spustí se pouze pro tyto metody HTTP. Například akce kontroleru MVC, která používá [HttpGet] atribut, se spouští pouze pro požadavky GET. Abyste zajistili, že všechny požadavky dosáhnou vlastní stránky zpracování chyb, neomezovat je na konkrétní sadu metod HTTP.

Zpracování výjimek odlišně na základě původní metody HTTP:

  • Pro Razor Stránky vytvořte více metod obslužné rutiny. Slouží OnGet například ke zpracování výjimek GET a zpracování OnPost výjimek POST.
  • Pro MVC použijte atributy příkazů HTTP na více akcí. Slouží [HttpGet] například ke zpracování výjimek GET a zpracování [HttpPost] výjimek POST.

Pokud chcete umožnit neověřeným uživatelům zobrazit vlastní stránku pro zpracování chyb, ujistěte se, že podporuje anonymní přístup.

Přístup k výjimce

Slouží IExceptionHandlerPathFeature k přístupu k výjimce a původní cestě požadavku v obslužné rutině chyby. Následující příklad používá IExceptionHandlerPathFeature k získání dalších informací o výjimce, která byla vyvolán:

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

Upozorňující

Neservírujte klientům citlivé informace o chybách. Obsluha chyb představuje bezpečnostní riziko.

Lambda obslužné rutiny výjimek

Alternativou k vlastní stránce obslužné rutiny výjimky je poskytnutí lambda UseExceptionHandler. Použití lambda umožňuje přístup k chybě před vrácením odpovědi.

Následující kód používá lambda pro zpracování výjimek:

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

Upozorňující

Neservírujte klientům citlivé informace o chybách. Obsluha chyb představuje bezpečnostní riziko.

IExceptionHandler

IExceptionHandler je rozhraní, které vývojářům poskytuje zpětné volání pro zpracování známých výjimek v centrálním umístění.

IExceptionHandler implementace jsou registrovány voláním IServiceCollection.AddExceptionHandler<T>. Životnost IExceptionHandler instance je singleton. Je možné přidat více implementací a volají se v zaregistrované objednávce.

Pokud obslužná rutina výjimky zpracovává požadavek, může se vrátit true a zastavit zpracování. Pokud žádná obslužná rutina výjimky nezpracuje výjimku, ovládací prvek se vrátí do výchozího chování a možností z middlewaru. Pro zpracovávané a neošetřené výjimky se vygenerují různé metriky a protokoly.

Následující příklad ukazuje implementaci IExceptionHandler :

using Microsoft.AspNetCore.Diagnostics;

namespace ErrorHandlingSample
{
    public class CustomExceptionHandler : IExceptionHandler
    {
        private readonly ILogger<CustomExceptionHandler> logger;
        public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
        {
            this.logger = logger;
        }
        public ValueTask<bool> TryHandleAsync(
            HttpContext httpContext,
            Exception exception,
            CancellationToken cancellationToken)
        {
            var exceptionMessage = exception.Message;
            logger.LogError(
                "Error Message: {exceptionMessage}, Time of occurrence {time}",
                exceptionMessage, DateTime.UtcNow);
            // Return false to continue with the default behavior
            // - or - return true to signal that this exception is handled
            return ValueTask.FromResult(false);
        }
    }
}

Následující příklad ukazuje, jak zaregistrovat IExceptionHandler implementaci pro injektáž závislostí:

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

Při spuštění předchozího kódu ve vývojovém prostředí:

  • Volá se CustomExceptionHandler jako první pro zpracování výjimky.
  • Po protokolování výjimky TryHandleException metoda vrátí false, takže se zobrazí stránka výjimky vývojáře.

V jiných prostředích:

  • Volá se CustomExceptionHandler jako první pro zpracování výjimky.
  • Po protokolování výjimky TryHandleException metoda vrátí false, takže /Error stránka je zobrazena.

UseStatusCodePages

Ve výchozím nastavení aplikace ASP.NET Core neposkytuje stavovou znakovou stránku pro stavové kódy chyb HTTP, například 404 – Nenalezena. Když aplikace nastaví stavový kód chyby HTTP 400-599, který nemá tělo, vrátí stavový kód a prázdný text odpovědi. Pokud chcete povolit výchozí obslužné rutiny jen pro text pro běžné stavové kódy chyb, zavolejte do UseStatusCodePagesProgram.cs:

var app = builder.Build();

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

app.UseStatusCodePages();

Před zpracováním middlewaru žádostí zavolejte UseStatusCodePages . Například zavolejte UseStatusCodePages před middlewarem statického souboru a middlewarem koncových bodů.

Pokud UseStatusCodePages se nepoužívá, přechod na adresu URL bez koncového bodu vrátí chybovou zprávu závislá na prohlížeči, která značí, že se koncový bod nenašel. Při UseStatusCodePages zavolání vrátí prohlížeč následující odpověď:

Status Code: 404; Not Found

UseStatusCodePages se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

Poznámka:

Middleware stavových kódů nezachytává výjimky. Pokud chcete poskytnout vlastní stránku zpracování chyb, použijte stránku obslužné rutiny výjimky.

UseStatusCodePages s formátovacím řetězcem

Pokud chcete přizpůsobit typ obsahu odpovědi a text, použijte přetížení UseStatusCodePages , které přebírá typ obsahu a formátovací řetězec:

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

V předchozím kódu {0} je zástupný symbol pro kód chyby.

UseStatusCodePages formátovací řetězec se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

PoužitíStatusCodePages s lambda

Pokud chcete zadat vlastní zpracování chyb a kód pro zápis odpovědí, použijte přetížení UseStatusCodePages , které přebírá výraz lambda:

var app = builder.Build();

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

app.UseStatusCodePages(async statusCodeContext =>
{
    // using static System.Net.Mime.MediaTypeNames;
    statusCodeContext.HttpContext.Response.ContentType = Text.Plain;

    await statusCodeContext.HttpContext.Response.WriteAsync(
        $"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});

UseStatusCodePages s lambda se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

UseStatusCodePagesWithRedirects

Metoda UseStatusCodePagesWithRedirects rozšíření:

  • Odešle klientovi stavový kód 302 .
  • Přesměruje klienta na koncový bod zpracování chyb zadaný v šabloně adresy URL. Koncový bod zpracování chyb obvykle zobrazuje informace o chybě a vrací http 200.
var app = builder.Build();

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

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

Šablona adresy URL může obsahovat {0} zástupný symbol pro stavový kód, jak je znázorněno v předchozím kódu. Pokud šablona adresy URL začíná vlnovkou ~ , ~ nahradí se aplikací PathBase. Při zadávání koncového bodu v aplikaci vytvořte zobrazení nebo Razor stránku MVC pro koncový bod.

Tato metoda se běžně používá v případě, že aplikace:

  • Měl by přesměrovat klienta na jiný koncový bod, obvykle v případech, kdy jiná aplikace zpracovává chybu. U webových aplikací se adresní řádek prohlížeče klienta odráží přesměrovaný koncový bod.
  • Nemělo by se zachovat a vrátit původní stavový kód s počáteční odpovědí přesměrování.

UseStatusCodePagesWithReExecute

Metoda UseStatusCodePagesWithReExecute rozšíření:

  • Vygeneruje tělo odpovědi opětovným spuštěním kanálu požadavku pomocí alternativní cesty.
  • Nezmění stavový kód před opětovným spuštěním kanálu ani po jeho opětovném spuštění.

Nové spuštění kanálu může změnit stavový kód odpovědi, protože nový kanál má plnou kontrolu nad stavovým kódem. Pokud nový kanál nezmění stavový kód, původní stavový kód se odešle klientovi.

var app = builder.Build();

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

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

Pokud je zadaný koncový bod v aplikaci, vytvořte zobrazení nebo Razor stránku MVC pro koncový bod.

Tato metoda se běžně používá, když by aplikace měla:

  • Zpracujte požadavek bez přesměrování na jiný koncový bod. U webových aplikací se adresní řádek prohlížeče klienta odráží původně požadovaný koncový bod.
  • Zachování a vrácení původního stavového kódu s odpovědí

Šablona adresy URL musí začínat / a může obsahovat zástupný symbol {0} pro stavový kód. Chcete-li předat stavový kód jako parametr řetězce dotazu, předejte druhý argument do UseStatusCodePagesWithReExecute. Příklad:

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

Koncový bod, který chybu zpracuje, může získat původní adresu URL, která chybu vygenerovala, jak je znázorněno v následujícím příkladu:

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

Vzhledem k tomu, že tento middleware může kanál požadavku znovu spustit:

  • Middleware musí zpracovávat opakování se stejným požadavkem. Obvykle to znamená, že po zavolání _next nebo uložení zpracování do mezipaměti HttpContext na zařízení, aby se zabránilo jeho opětovnému vytvoření. Při práci s textem požadavku to znamená ukládání do vyrovnávací paměti nebo ukládání výsledků do mezipaměti, jako je čtečka formulářů.
  • Služby s vymezeným oborem zůstávají stejné.

Zakázat stavové kódové stránky

Chcete-li zakázat stránky stavového kódu pro kontroler MVC nebo metodu akce, použijte atribut [SkipStatusCodePages].

Chcete-li zakázat stavové kódové stránky pro konkrétní požadavky v Razor metodě obslužné rutiny stránky nebo v kontroleru MVC, použijte IStatusCodePagesFeature:

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

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

Kód pro zpracování výjimek

Kód na stránkách zpracování výjimek může také vyvolat výjimky. Produkční chybové stránky by se měly důkladně testovat a pečlivě se postarat, aby nedocházelo k výjimkám jejich vlastních.

Hlavičky odpovědi

Po odeslání hlaviček odpovědi:

  • Aplikace nemůže změnit stavový kód odpovědi.
  • Všechny stránky výjimek nebo obslužné rutiny se nedají spustit. Odpověď musí být dokončena nebo přerušeno připojení.

Zpracování výjimek serveru

Kromě logiky zpracování výjimek v aplikaci může implementace serveru HTTP zpracovávat některé výjimky. Pokud server zachytí výjimku před odesláním hlaviček odpovědi, server odešle 500 - Internal Server Error odpověď bez textu odpovědi. Pokud server zachytí výjimku po odeslání hlaviček odpovědi, server připojení zavře. Požadavky, které aplikace nezpracuje, zpracovává server. Všechny výjimky, ke kterým dochází, když server zpracovává požadavek, se zpracovává zpracováním výjimek serveru. Vlastní chybové stránky aplikace, middleware pro zpracování výjimek a filtry toto chování neovlivní.

Ošetření výjimek při spuštění

Pouze vrstva hostování může zpracovávat výjimky, které probíhají při spuštění aplikace. Hostitel lze nakonfigurovat tak, aby zaznamenával chyby při spuštění a zaznamenával podrobné chyby.

Vrstva hostování může zobrazit chybovou stránku zachycené chyby při spuštění pouze v případě, že k chybě dojde po vazbě adresy hostitele nebo portu. Pokud se vazba nezdaří:

  • Vrstva hostování zaznamená kritickou výjimku.
  • Proces dotnet se chybově ukončí.
  • Pokud je Kestrelserver HTTP, nezobrazí se žádná chybová stránka.

Pokud je proces spuštěný ve službě IIS (nebo ve službě Aplikace Azure) nebo IIS Express, vrátí modul ASP.NET Core chybu procesu 502.5. Další informace najdete v tématu Řešení potíží ASP.NET Core ve službě Aplikace Azure a službě IIS.

Chybová stránka databáze

Filtr AddDatabaseDeveloperPageExceptionFilter výjimek stránky pro vývojáře databáze zachycuje výjimky související s databází, které je možné vyřešit pomocí migrací Entity Framework Core. Pokud dojde k těmto výjimkám, vygeneruje se odpověď HTML s podrobnostmi o možných akcích k vyřešení problému. Tato stránka je povolená pouze ve vývojovém prostředí. Následující kód přidá filtr výjimek stránky pro vývojáře databáze:

var builder = WebApplication.CreateBuilder(args);

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

Filtry výjimek

V aplikacích MVC je možné nakonfigurovat filtry výjimek globálně nebo podle kontroleru nebo podle akce. V Razor aplikacích Pages je možné je nakonfigurovat globálně nebo podle modelu stránky. Tyto filtry zpracovávají všechny neošetřené výjimky, ke kterým dochází během provádění akce kontroleru nebo jiného filtru. Další informace najdete v tématu Filtry v ASP.NET Core.

Filtry výjimek jsou užitečné pro zachycení výjimek, ke kterým dochází v rámci akcí MVC, ale nejsou tak flexibilní jako integrovaný middlewareUseExceptionHandler pro zpracování výjimek. Doporučujeme použít UseExceptionHandler, pokud nepotřebujete provádět zpracování chyb odlišně podle toho, jakou akci MVC zvolíte.

Chyby stavu modelu

Informace o tom, jak zpracovávat chyby stavu modelu, naleznete v tématu Vazby modelu a Ověření modelu.

Podrobnosti o problému

Podrobnosti o problému nejsou jediným formátem odpovědi, který popisuje chybu rozhraní HTTP API, ale běžně se používají k hlášení chyb pro rozhraní HTTP API.

Služba podrobností problému implementuje IProblemDetailsService rozhraní, které podporuje vytváření podrobností o problému v ASP.NET Core. Metoda AddProblemDetails rozšíření při IServiceCollection registraci výchozí IProblemDetailsService implementace.

V aplikacích ASP.NET Core generuje následující middleware při zavolání podrobnosti o problému odpovědi AddProblemDetails HTTP, s výjimkou případů, kdy Accept hlavička HTTP požadavku neobsahuje jeden z typů obsahu podporovaných zaregistrovaným IProblemDetailsWriter (výchozí: application/json):

Následující kód nakonfiguruje aplikaci tak, aby vygenerovala odpověď na podrobnosti o problému pro všechny odpovědi na chyby klienta HTTP a serveru, které ještě nemají základní obsah:

builder.Services.AddProblemDetails();

var app = builder.Build();        

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

app.UseStatusCodePages();

V další části se dozvíte, jak přizpůsobit text odpovědi s podrobnostmi o problému.

Přizpůsobení podrobností o problému

Automatické vytváření ProblemDetails můžete přizpůsobit pomocí některé z následujících možností:

  1. Použití ProblemDetailsOptions.CustomizeProblemDetails
  2. Použití vlastního IProblemDetailsWriter
  3. IProblemDetailsService Volání v middlewaru

CustomizeProblemDetails Operace

Vygenerované podrobnosti o problému lze přizpůsobit pomocí CustomizeProblemDetailsa vlastní nastavení se použijí na všechny automaticky generované podrobnosti o problému.

Následující kód používá ProblemDetailsOptions k nastavení 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();

Výsledek koncového HTTP Status 400 Bad Request bodu například vytvoří následující text odpovědi s podrobnostmi o problému:

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

Vlastní IProblemDetailsWriter

Pro IProblemDetailsWriter pokročilá přizpůsobení je možné vytvořit implementaci.

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

Poznámka: Při použití vlastní IProblemDetailsWriter, vlastní IProblemDetailsWriter musí být registrován před voláním AddRazorPages, AddControllers, , AddControllersWithViewsnebo AddMvc:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<IProblemDetailsWriter, SampleProblemDetailsWriter>();

var app = builder.Build();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsWriter>() is
            { } problemDetailsService)
        {

            if (problemDetailsService.CanWrite(new ProblemDetailsContext() { HttpContext = context }))
            {
                (string Detail, string Type) details = mathErrorFeature.MathError switch
                {
                    MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                        "https://en.wikipedia.org/wiki/Division_by_zero"),
                    _ => ("Negative or complex numbers are not valid input.",
                        "https://en.wikipedia.org/wiki/Square_root")
                };

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

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

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

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

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

app.Run();

Podrobnosti o problému z middlewaru

Alternativním přístupem k použití s ProblemDetailsOptionsCustomizeProblemDetails je nastavení v middlewaru ProblemDetails . Odpověď s podrobnostmi o problému může být napsána voláním 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();

V předchozím kódu minimální koncové body /divide rozhraní API a /squareroot vrátí očekávanou vlastní odpověď na problém při zadávání chyb.

Koncové body kontroleru rozhraní API vrací výchozí odpověď na problém při zadávání chyb, nikoli vlastní odpověď na problém. Výchozí odpověď na problém je vrácena, protože kontroler rozhraní API zapisoval do streamu odpovědi, podrobnosti o problému pro stavové kódy chyb, před IProblemDetailsService.WriteAsync voláním a odpověď se znovu nezapíše .

Následující ValuesController vrátí hodnotu BadRequestResult, která zapisuje do streamu odpovědi, a proto zabraňuje vrácení vlastní odpovědi na problém.

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

}

Následující Values3Controller vrátí ControllerBase.Problem výsledek očekávaného vlastního problému:

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

}

Vytvoření datové části ProblemDetails pro výjimky

Zvažte následující aplikaci:

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

Pokud dojde k výjimce v prostředích, která nejsou vývojová, jedná se o standardní odpověď ProblemDetails, která se vrátí klientovi:

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

U většiny aplikací je předchozí kód vše, co je potřeba pro výjimky. Následující část ale ukazuje, jak získat podrobnější odpovědi na problémy.

Alternativou k vlastní stránce obslužné rutiny výjimky je poskytnutí lambda UseExceptionHandler. Použití lambda umožňuje přístup k chybě a napsání odpovědi s podrobnostmi o problému pomocí 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();

Upozorňující

Neservírujte klientům citlivé informace o chybách. Obsluha chyb představuje bezpečnostní riziko.

Alternativním přístupem k vygenerování podrobností o problému je použití balíčku NuGet jiného výrobce Hellang.Middleware.ProblemDetails , který lze použít k mapování výjimek a chyb klienta na podrobnosti o problému.

Další materiály

Od Tom Dykstra

Tento článek popisuje běžné přístupy ke zpracování chyb ve webových aplikacích ASP.NET Core. Podívejte se také na zpracování chyb ve webových rozhraních API ASP.NET Core a zpracování chyb v aplikacích s minimálním rozhraním API.

Stránka výjimky pro vývojáře

Na stránce výjimka pro vývojáře se zobrazují podrobné informace o neošetřených výjimkách požadavků. ASP.NET aplikace Core ve výchozím nastavení povolí stránku výjimky pro vývojáře, pokud jsou obě:

Stránka výjimky vývojáře se spouští v rané fázi kanálu middlewaru, aby bylo možné zachytit neošetřené výjimky vyvolané v middlewaru, které následuje.

Podrobné informace o výjimce by se neměly zobrazovat veřejně při spuštění aplikace v produkčním prostředí. Další informace o konfiguraci prostředí najdete v tématu Použití více prostředí v ASP.NET Core.

Stránka výjimky pro vývojáře může obsahovat následující informace o výjimce a požadavku:

  • Trasování zásobníku
  • Parametry řetězce dotazu, pokud nějaké
  • Cookies, pokud existuje
  • Hlavičky

Na stránce výjimky pro vývojáře není zaručeno, že poskytnete žádné informace. Protokolování slouží k úplným informacím o chybě.

Stránka obslužné rutiny výjimky

Chcete-li nakonfigurovat vlastní stránku zpracování chyb pro produkční prostředí, zavolejte UseExceptionHandler. Tento middleware pro zpracování výjimek:

  • Zachytává a zaznamenává neošetřené výjimky.
  • Znovu spustí požadavek v alternativním kanálu pomocí uvedené cesty. Pokud se odpověď spustila, požadavek se znovu nespustí. Vygenerovaný kód šablony znovu spustí požadavek pomocí /Error cesty.

Upozorňující

Pokud alternativní kanál vyvolá výjimku vlastní, middleware zpracování výjimek znovu zvětšuje původní výjimku.

Vzhledem k tomu, že tento middleware může kanál požadavku znovu spustit:

  • Middleware musí zpracovávat opakování se stejným požadavkem. Obvykle to znamená, že po zavolání _next nebo uložení zpracování do mezipaměti HttpContext na zařízení, aby se zabránilo jeho opětovnému vytvoření. Při práci s textem požadavku to znamená ukládání do vyrovnávací paměti nebo ukládání výsledků do mezipaměti, jako je čtečka formulářů.
  • UseExceptionHandler(IApplicationBuilder, String) Pro přetížení, které se používá v šablonách, je změněna pouze cesta požadavku a směrovací data jsou vymazána. Všechna data požadavku, jako jsou hlavičky, metoda a položky, se znovu používají tak, jak jsou.
  • Služby s vymezeným oborem zůstávají stejné.

V následujícím příkladu UseExceptionHandler přidá middleware pro zpracování výjimek v prostředích, která nejsou vývojová:

var app = builder.Build();

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

Šablona Razor aplikace Pages poskytuje ve složce Pages chybovou stránku (.cshtml) a PageModel třídu (ErrorModel). V případě aplikace MVC obsahuje šablona projektu metodu Error akce a zobrazení Chyba kontroleru Home .

Middleware zpracování výjimek znovu spustí požadavek pomocí původní metody HTTP. Pokud je koncový bod obslužné rutiny chyby omezen na konkrétní sadu metod HTTP, spustí se pouze pro tyto metody HTTP. Například akce kontroleru MVC, která používá [HttpGet] atribut, se spouští pouze pro požadavky GET. Abyste zajistili, že všechny požadavky dosáhnou vlastní stránky zpracování chyb, neomezovat je na konkrétní sadu metod HTTP.

Zpracování výjimek odlišně na základě původní metody HTTP:

  • Pro Razor Stránky vytvořte více metod obslužné rutiny. Slouží OnGet například ke zpracování výjimek GET a zpracování OnPost výjimek POST.
  • Pro MVC použijte atributy příkazů HTTP na více akcí. Slouží [HttpGet] například ke zpracování výjimek GET a zpracování [HttpPost] výjimek POST.

Pokud chcete umožnit neověřeným uživatelům zobrazit vlastní stránku pro zpracování chyb, ujistěte se, že podporuje anonymní přístup.

Přístup k výjimce

Slouží IExceptionHandlerPathFeature k přístupu k výjimce a původní cestě požadavku v obslužné rutině chyby. Následující příklad používá IExceptionHandlerPathFeature k získání dalších informací o výjimce, která byla vyvolán:

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

Upozorňující

Neservírujte klientům citlivé informace o chybách. Obsluha chyb představuje bezpečnostní riziko.

Lambda obslužné rutiny výjimek

Alternativou k vlastní stránce obslužné rutiny výjimky je poskytnutí lambda UseExceptionHandler. Použití lambda umožňuje přístup k chybě před vrácením odpovědi.

Následující kód používá lambda pro zpracování výjimek:

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

Upozorňující

Neservírujte klientům citlivé informace o chybách. Obsluha chyb představuje bezpečnostní riziko.

UseStatusCodePages

Ve výchozím nastavení aplikace ASP.NET Core neposkytuje stavovou znakovou stránku pro stavové kódy chyb HTTP, například 404 – Nenalezena. Když aplikace nastaví stavový kód chyby HTTP 400-599, který nemá tělo, vrátí stavový kód a prázdný text odpovědi. Pokud chcete povolit výchozí obslužné rutiny jen pro text pro běžné stavové kódy chyb, zavolejte do UseStatusCodePagesProgram.cs:

var app = builder.Build();

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

app.UseStatusCodePages();

Před zpracováním middlewaru žádostí zavolejte UseStatusCodePages . Například zavolejte UseStatusCodePages před middlewarem statického souboru a middlewarem koncových bodů.

Pokud UseStatusCodePages se nepoužívá, přechod na adresu URL bez koncového bodu vrátí chybovou zprávu závislá na prohlížeči, která značí, že se koncový bod nenašel. Při UseStatusCodePages zavolání vrátí prohlížeč následující odpověď:

Status Code: 404; Not Found

UseStatusCodePages se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

Poznámka:

Middleware stavových kódů nezachytává výjimky. Pokud chcete poskytnout vlastní stránku zpracování chyb, použijte stránku obslužné rutiny výjimky.

UseStatusCodePages s formátovacím řetězcem

Pokud chcete přizpůsobit typ obsahu odpovědi a text, použijte přetížení UseStatusCodePages , které přebírá typ obsahu a formátovací řetězec:

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

V předchozím kódu {0} je zástupný symbol pro kód chyby.

UseStatusCodePages formátovací řetězec se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

PoužitíStatusCodePages s lambda

Pokud chcete zadat vlastní zpracování chyb a kód pro zápis odpovědí, použijte přetížení UseStatusCodePages , které přebírá výraz lambda:

var app = builder.Build();

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

app.UseStatusCodePages(async statusCodeContext =>
{
    // using static System.Net.Mime.MediaTypeNames;
    statusCodeContext.HttpContext.Response.ContentType = Text.Plain;

    await statusCodeContext.HttpContext.Response.WriteAsync(
        $"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});

UseStatusCodePages s lambda se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

UseStatusCodePagesWithRedirects

Metoda UseStatusCodePagesWithRedirects rozšíření:

  • Odešle klientovi stavový kód 302 .
  • Přesměruje klienta na koncový bod zpracování chyb zadaný v šabloně adresy URL. Koncový bod zpracování chyb obvykle zobrazuje informace o chybě a vrací http 200.
var app = builder.Build();

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

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

Šablona adresy URL může obsahovat {0} zástupný symbol pro stavový kód, jak je znázorněno v předchozím kódu. Pokud šablona adresy URL začíná vlnovkou ~ , ~ nahradí se aplikací PathBase. Při zadávání koncového bodu v aplikaci vytvořte zobrazení nebo Razor stránku MVC pro koncový bod.

Tato metoda se běžně používá v případě, že aplikace:

  • Měl by přesměrovat klienta na jiný koncový bod, obvykle v případech, kdy jiná aplikace zpracovává chybu. U webových aplikací se adresní řádek prohlížeče klienta odráží přesměrovaný koncový bod.
  • Nemělo by se zachovat a vrátit původní stavový kód s počáteční odpovědí přesměrování.

UseStatusCodePagesWithReExecute

Metoda UseStatusCodePagesWithReExecute rozšíření:

  • Vygeneruje tělo odpovědi opětovným spuštěním kanálu požadavku pomocí alternativní cesty.
  • Nezmění stavový kód před opětovným spuštěním kanálu ani po jeho opětovném spuštění.

Nové spuštění kanálu může změnit stavový kód odpovědi, protože nový kanál má plnou kontrolu nad stavovým kódem. Pokud nový kanál nezmění stavový kód, původní stavový kód se odešle klientovi.

var app = builder.Build();

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

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

Pokud je zadaný koncový bod v aplikaci, vytvořte zobrazení nebo Razor stránku MVC pro koncový bod.

Tato metoda se běžně používá, když by aplikace měla:

  • Zpracujte požadavek bez přesměrování na jiný koncový bod. U webových aplikací se adresní řádek prohlížeče klienta odráží původně požadovaný koncový bod.
  • Zachování a vrácení původního stavového kódu s odpovědí

Šablona adresy URL musí začínat / a může obsahovat zástupný symbol {0} pro stavový kód. Chcete-li předat stavový kód jako parametr řetězce dotazu, předejte druhý argument do UseStatusCodePagesWithReExecute. Příklad:

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

Koncový bod, který chybu zpracuje, může získat původní adresu URL, která chybu vygenerovala, jak je znázorněno v následujícím příkladu:

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

Vzhledem k tomu, že tento middleware může kanál požadavku znovu spustit:

  • Middleware musí zpracovávat opakování se stejným požadavkem. Obvykle to znamená, že po zavolání _next nebo uložení zpracování do mezipaměti HttpContext na zařízení, aby se zabránilo jeho opětovnému vytvoření. Při práci s textem požadavku to znamená ukládání do vyrovnávací paměti nebo ukládání výsledků do mezipaměti, jako je čtečka formulářů.
  • Služby s vymezeným oborem zůstávají stejné.

Zakázat stavové kódové stránky

Chcete-li zakázat stránky stavového kódu pro kontroler MVC nebo metodu akce, použijte atribut [SkipStatusCodePages].

Chcete-li zakázat stavové kódové stránky pro konkrétní požadavky v Razor metodě obslužné rutiny stránky nebo v kontroleru MVC, použijte IStatusCodePagesFeature:

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

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

Kód pro zpracování výjimek

Kód na stránkách zpracování výjimek může také vyvolat výjimky. Produkční chybové stránky by se měly důkladně testovat a pečlivě se postarat, aby nedocházelo k výjimkám jejich vlastních.

Hlavičky odpovědi

Po odeslání hlaviček odpovědi:

  • Aplikace nemůže změnit stavový kód odpovědi.
  • Všechny stránky výjimek nebo obslužné rutiny se nedají spustit. Odpověď musí být dokončena nebo přerušeno připojení.

Zpracování výjimek serveru

Kromě logiky zpracování výjimek v aplikaci může implementace serveru HTTP zpracovávat některé výjimky. Pokud server zachytí výjimku před odesláním hlaviček odpovědi, server odešle 500 - Internal Server Error odpověď bez textu odpovědi. Pokud server zachytí výjimku po odeslání hlaviček odpovědi, server připojení zavře. Požadavky, které aplikace nezpracuje, zpracovává server. Všechny výjimky, ke kterým dochází, když server zpracovává požadavek, se zpracovává zpracováním výjimek serveru. Vlastní chybové stránky aplikace, middleware pro zpracování výjimek a filtry toto chování neovlivní.

Ošetření výjimek při spuštění

Pouze vrstva hostování může zpracovávat výjimky, které probíhají při spuštění aplikace. Hostitel lze nakonfigurovat tak, aby zaznamenával chyby při spuštění a zaznamenával podrobné chyby.

Vrstva hostování může zobrazit chybovou stránku zachycené chyby při spuštění pouze v případě, že k chybě dojde po vazbě adresy hostitele nebo portu. Pokud se vazba nezdaří:

  • Vrstva hostování zaznamená kritickou výjimku.
  • Proces dotnet se chybově ukončí.
  • Pokud je Kestrelserver HTTP, nezobrazí se žádná chybová stránka.

Pokud je proces spuštěný ve službě IIS (nebo ve službě Aplikace Azure) nebo IIS Express, vrátí modul ASP.NET Core chybu procesu 502.5. Další informace najdete v tématu Řešení potíží ASP.NET Core ve službě Aplikace Azure a službě IIS.

Chybová stránka databáze

Filtr AddDatabaseDeveloperPageExceptionFilter výjimek stránky pro vývojáře databáze zachycuje výjimky související s databází, které je možné vyřešit pomocí migrací Entity Framework Core. Pokud dojde k těmto výjimkám, vygeneruje se odpověď HTML s podrobnostmi o možných akcích k vyřešení problému. Tato stránka je povolená pouze ve vývojovém prostředí. Následující kód přidá filtr výjimek stránky pro vývojáře databáze:

var builder = WebApplication.CreateBuilder(args);

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

Filtry výjimek

V aplikacích MVC je možné nakonfigurovat filtry výjimek globálně nebo podle kontroleru nebo podle akce. V Razor aplikacích Pages je možné je nakonfigurovat globálně nebo podle modelu stránky. Tyto filtry zpracovávají všechny neošetřené výjimky, ke kterým dochází během provádění akce kontroleru nebo jiného filtru. Další informace najdete v tématu Filtry v ASP.NET Core.

Filtry výjimek jsou užitečné pro zachycení výjimek, ke kterým dochází v rámci akcí MVC, ale nejsou tak flexibilní jako integrovaný middlewareUseExceptionHandler pro zpracování výjimek. Doporučujeme použít UseExceptionHandler, pokud nepotřebujete provádět zpracování chyb odlišně podle toho, jakou akci MVC zvolíte.

Chyby stavu modelu

Informace o tom, jak zpracovávat chyby stavu modelu, naleznete v tématu Vazby modelu a Ověření modelu.

Podrobnosti o problému

Podrobnosti o problému nejsou jediným formátem odpovědi, který popisuje chybu rozhraní HTTP API, ale běžně se používají k hlášení chyb pro rozhraní HTTP API.

Služba podrobností problému implementuje IProblemDetailsService rozhraní, které podporuje vytváření podrobností o problému v ASP.NET Core. Metoda AddProblemDetails rozšíření při IServiceCollection registraci výchozí IProblemDetailsService implementace.

V aplikacích ASP.NET Core generuje následující middleware při zavolání podrobnosti o problému odpovědi AddProblemDetails HTTP, s výjimkou případů, kdy Accept hlavička HTTP požadavku neobsahuje jeden z typů obsahu podporovaných zaregistrovaným IProblemDetailsWriter (výchozí: application/json):

Následující kód nakonfiguruje aplikaci tak, aby vygenerovala odpověď na podrobnosti o problému pro všechny odpovědi na chyby klienta HTTP a serveru, které ještě nemají základní obsah:

builder.Services.AddProblemDetails();

var app = builder.Build();        

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

app.UseStatusCodePages();

V další části se dozvíte, jak přizpůsobit text odpovědi s podrobnostmi o problému.

Přizpůsobení podrobností o problému

Automatické vytváření ProblemDetails můžete přizpůsobit pomocí některé z následujících možností:

  1. Použití ProblemDetailsOptions.CustomizeProblemDetails
  2. Použití vlastního IProblemDetailsWriter
  3. IProblemDetailsService Volání v middlewaru

CustomizeProblemDetails Operace

Vygenerované podrobnosti o problému lze přizpůsobit pomocí CustomizeProblemDetailsa vlastní nastavení se použijí na všechny automaticky generované podrobnosti o problému.

Následující kód používá ProblemDetailsOptions k nastavení 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();

Výsledek koncového HTTP Status 400 Bad Request bodu například vytvoří následující text odpovědi s podrobnostmi o problému:

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

Vlastní IProblemDetailsWriter

Pro IProblemDetailsWriter pokročilá přizpůsobení je možné vytvořit implementaci.

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

Poznámka: Při použití vlastní IProblemDetailsWriter, vlastní IProblemDetailsWriter musí být registrován před voláním AddRazorPages, AddControllers, , AddControllersWithViewsnebo AddMvc:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<IProblemDetailsWriter, SampleProblemDetailsWriter>();

var app = builder.Build();

// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
    await next(context);
    var mathErrorFeature = context.Features.Get<MathErrorFeature>();
    if (mathErrorFeature is not null)
    {
        if (context.RequestServices.GetService<IProblemDetailsWriter>() is
            { } problemDetailsService)
        {

            if (problemDetailsService.CanWrite(new ProblemDetailsContext() { HttpContext = context }))
            {
                (string Detail, string Type) details = mathErrorFeature.MathError switch
                {
                    MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
                        "https://en.wikipedia.org/wiki/Division_by_zero"),
                    _ => ("Negative or complex numbers are not valid input.",
                        "https://en.wikipedia.org/wiki/Square_root")
                };

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

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

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

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

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

app.Run();

Podrobnosti o problému z middlewaru

Alternativním přístupem k použití s ProblemDetailsOptionsCustomizeProblemDetails je nastavení v middlewaru ProblemDetails . Odpověď s podrobnostmi o problému může být napsána voláním 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();

V předchozím kódu minimální koncové body /divide rozhraní API a /squareroot vrátí očekávanou vlastní odpověď na problém při zadávání chyb.

Koncové body kontroleru rozhraní API vrací výchozí odpověď na problém při zadávání chyb, nikoli vlastní odpověď na problém. Výchozí odpověď na problém je vrácena, protože kontroler rozhraní API zapisoval do streamu odpovědi, podrobnosti o problému pro stavové kódy chyb, před IProblemDetailsService.WriteAsync voláním a odpověď se znovu nezapíše .

Následující ValuesController vrátí hodnotu BadRequestResult, která zapisuje do streamu odpovědi, a proto zabraňuje vrácení vlastní odpovědi na problém.

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

}

Následující Values3Controller vrátí ControllerBase.Problem výsledek očekávaného vlastního problému:

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

}

Vytvoření datové části ProblemDetails pro výjimky

Zvažte následující aplikaci:

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

Pokud dojde k výjimce v prostředích, která nejsou vývojová, jedná se o standardní odpověď ProblemDetails, která se vrátí klientovi:

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

U většiny aplikací je předchozí kód vše, co je potřeba pro výjimky. Následující část ale ukazuje, jak získat podrobnější odpovědi na problémy.

Alternativou k vlastní stránce obslužné rutiny výjimky je poskytnutí lambda UseExceptionHandler. Použití lambda umožňuje přístup k chybě a napsání odpovědi s podrobnostmi o problému pomocí 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();

Upozorňující

Neservírujte klientům citlivé informace o chybách. Obsluha chyb představuje bezpečnostní riziko.

Alternativním přístupem k vygenerování podrobností o problému je použití balíčku NuGet jiného výrobce Hellang.Middleware.ProblemDetails , který lze použít k mapování výjimek a chyb klienta na podrobnosti o problému.

Další materiály

Od Tom Dykstra

Tento článek popisuje běžné přístupy ke zpracování chyb ve webových aplikacích ASP.NET Core. Viz Zpracování chyb ve webových rozhraních API ASP.NET Core pro webová rozhraní API.

Stránka výjimky pro vývojáře

Na stránce výjimka pro vývojáře se zobrazují podrobné informace o neošetřených výjimkách požadavků. ASP.NET aplikace Core ve výchozím nastavení povolí stránku výjimky pro vývojáře, pokud jsou obě:

Stránka výjimky vývojáře se spouští v rané fázi kanálu middlewaru, aby bylo možné zachytit neošetřené výjimky vyvolané v middlewaru, které následuje.

Podrobné informace o výjimce by se neměly zobrazovat veřejně při spuštění aplikace v produkčním prostředí. Další informace o konfiguraci prostředí najdete v tématu Použití více prostředí v ASP.NET Core.

Stránka výjimky pro vývojáře může obsahovat následující informace o výjimce a požadavku:

  • Trasování zásobníku
  • Parametry řetězce dotazu, pokud nějaké
  • Cookies, pokud existuje
  • Hlavičky

Na stránce výjimky pro vývojáře není zaručeno, že poskytnete žádné informace. Protokolování slouží k úplným informacím o chybě.

Stránka obslužné rutiny výjimky

Chcete-li nakonfigurovat vlastní stránku zpracování chyb pro produkční prostředí, zavolejte UseExceptionHandler. Tento middleware pro zpracování výjimek:

  • Zachytává a zaznamenává neošetřené výjimky.
  • Znovu spustí požadavek v alternativním kanálu pomocí uvedené cesty. Pokud se odpověď spustila, požadavek se znovu nespustí. Vygenerovaný kód šablony znovu spustí požadavek pomocí /Error cesty.

Upozorňující

Pokud alternativní kanál vyvolá výjimku vlastní, middleware zpracování výjimek znovu zvětšuje původní výjimku.

V následujícím příkladu UseExceptionHandler přidá middleware pro zpracování výjimek v prostředích, která nejsou vývojová:

var app = builder.Build();

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

Šablona Razor aplikace Pages poskytuje ve složce Pages chybovou stránku (.cshtml) a PageModel třídu (ErrorModel). V případě aplikace MVC obsahuje šablona projektu metodu Error akce a zobrazení Chyba kontroleru Home .

Middleware zpracování výjimek znovu spustí požadavek pomocí původní metody HTTP. Pokud je koncový bod obslužné rutiny chyby omezen na konkrétní sadu metod HTTP, spustí se pouze pro tyto metody HTTP. Například akce kontroleru MVC, která používá [HttpGet] atribut, se spouští pouze pro požadavky GET. Abyste zajistili, že všechny požadavky dosáhnou vlastní stránky zpracování chyb, neomezovat je na konkrétní sadu metod HTTP.

Zpracování výjimek odlišně na základě původní metody HTTP:

  • Pro Razor Stránky vytvořte více metod obslužné rutiny. Slouží OnGet například ke zpracování výjimek GET a zpracování OnPost výjimek POST.
  • Pro MVC použijte atributy příkazů HTTP na více akcí. Slouží [HttpGet] například ke zpracování výjimek GET a zpracování [HttpPost] výjimek POST.

Pokud chcete umožnit neověřeným uživatelům zobrazit vlastní stránku pro zpracování chyb, ujistěte se, že podporuje anonymní přístup.

Přístup k výjimce

Slouží IExceptionHandlerPathFeature k přístupu k výjimce a původní cestě požadavku v obslužné rutině chyby. Následující příklad používá IExceptionHandlerPathFeature k získání dalších informací o výjimce, která byla vyvolán:

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

Upozorňující

Neservírujte klientům citlivé informace o chybách. Obsluha chyb představuje bezpečnostní riziko.

Lambda obslužné rutiny výjimek

Alternativou k vlastní stránce obslužné rutiny výjimky je poskytnutí lambda UseExceptionHandler. Použití lambda umožňuje přístup k chybě před vrácením odpovědi.

Následující kód používá lambda pro zpracování výjimek:

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

Upozorňující

Neservírujte klientům citlivé informace o chybách. Obsluha chyb představuje bezpečnostní riziko.

UseStatusCodePages

Ve výchozím nastavení aplikace ASP.NET Core neposkytuje stavovou znakovou stránku pro stavové kódy chyb HTTP, například 404 – Nenalezena. Když aplikace nastaví stavový kód chyby HTTP 400-599, který nemá tělo, vrátí stavový kód a prázdný text odpovědi. Pokud chcete povolit výchozí obslužné rutiny jen pro text pro běžné stavové kódy chyb, zavolejte do UseStatusCodePagesProgram.cs:

var app = builder.Build();

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

app.UseStatusCodePages();

Před zpracováním middlewaru žádostí zavolejte UseStatusCodePages . Například zavolejte UseStatusCodePages před middlewarem statického souboru a middlewarem koncových bodů.

Pokud UseStatusCodePages se nepoužívá, přechod na adresu URL bez koncového bodu vrátí chybovou zprávu závislá na prohlížeči, která značí, že se koncový bod nenašel. Při UseStatusCodePages zavolání vrátí prohlížeč následující odpověď:

Status Code: 404; Not Found

UseStatusCodePages se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

Poznámka:

Middleware stavových kódů nezachytává výjimky. Pokud chcete poskytnout vlastní stránku zpracování chyb, použijte stránku obslužné rutiny výjimky.

UseStatusCodePages s formátovacím řetězcem

Pokud chcete přizpůsobit typ obsahu odpovědi a text, použijte přetížení UseStatusCodePages , které přebírá typ obsahu a formátovací řetězec:

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

V předchozím kódu {0} je zástupný symbol pro kód chyby.

UseStatusCodePages formátovací řetězec se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

PoužitíStatusCodePages s lambda

Pokud chcete zadat vlastní zpracování chyb a kód pro zápis odpovědí, použijte přetížení UseStatusCodePages , které přebírá výraz lambda:

var app = builder.Build();

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

app.UseStatusCodePages(async statusCodeContext =>
{
    // using static System.Net.Mime.MediaTypeNames;
    statusCodeContext.HttpContext.Response.ContentType = Text.Plain;

    await statusCodeContext.HttpContext.Response.WriteAsync(
        $"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});

UseStatusCodePages s lambda se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

UseStatusCodePagesWithRedirects

Metoda UseStatusCodePagesWithRedirects rozšíření:

  • Odešle klientovi stavový kód 302 .
  • Přesměruje klienta na koncový bod zpracování chyb zadaný v šabloně adresy URL. Koncový bod zpracování chyb obvykle zobrazuje informace o chybě a vrací http 200.
var app = builder.Build();

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

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

Šablona adresy URL může obsahovat {0} zástupný symbol pro stavový kód, jak je znázorněno v předchozím kódu. Pokud šablona adresy URL začíná vlnovkou ~ , ~ nahradí se aplikací PathBase. Při zadávání koncového bodu v aplikaci vytvořte zobrazení nebo Razor stránku MVC pro koncový bod.

Tato metoda se běžně používá v případě, že aplikace:

  • Měl by přesměrovat klienta na jiný koncový bod, obvykle v případech, kdy jiná aplikace zpracovává chybu. U webových aplikací se adresní řádek prohlížeče klienta odráží přesměrovaný koncový bod.
  • Nemělo by se zachovat a vrátit původní stavový kód s počáteční odpovědí přesměrování.

UseStatusCodePagesWithReExecute

Metoda UseStatusCodePagesWithReExecute rozšíření:

  • Vrátí původní stavový kód klientovi.
  • Vygeneruje tělo odpovědi opětovným spuštěním kanálu požadavku pomocí alternativní cesty.
var app = builder.Build();

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

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

Pokud je zadaný koncový bod v aplikaci, vytvořte zobrazení nebo Razor stránku MVC pro koncový bod.

Tato metoda se běžně používá, když by aplikace měla:

  • Zpracujte požadavek bez přesměrování na jiný koncový bod. U webových aplikací se adresní řádek prohlížeče klienta odráží původně požadovaný koncový bod.
  • Zachování a vrácení původního stavového kódu s odpovědí

Šablona adresy URL musí začínat / a může obsahovat zástupný symbol {0} pro stavový kód. Chcete-li předat stavový kód jako parametr řetězce dotazu, předejte druhý argument do UseStatusCodePagesWithReExecute. Příklad:

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

Koncový bod, který chybu zpracuje, může získat původní adresu URL, která chybu vygenerovala, jak je znázorněno v následujícím příkladu:

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

Zakázat stavové kódové stránky

Chcete-li zakázat stránky stavového kódu pro kontroler MVC nebo metodu akce, použijte atribut [SkipStatusCodePages].

Chcete-li zakázat stavové kódové stránky pro konkrétní požadavky v Razor metodě obslužné rutiny stránky nebo v kontroleru MVC, použijte IStatusCodePagesFeature:

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

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

Kód pro zpracování výjimek

Kód na stránkách zpracování výjimek může také vyvolat výjimky. Produkční chybové stránky by se měly důkladně testovat a pečlivě se postarat, aby nedocházelo k výjimkám jejich vlastních.

Hlavičky odpovědi

Po odeslání hlaviček odpovědi:

  • Aplikace nemůže změnit stavový kód odpovědi.
  • Všechny stránky výjimek nebo obslužné rutiny se nedají spustit. Odpověď musí být dokončena nebo přerušeno připojení.

Zpracování výjimek serveru

Kromě logiky zpracování výjimek v aplikaci může implementace serveru HTTP zpracovávat některé výjimky. Pokud server zachytí výjimku před odesláním hlaviček odpovědi, server odešle 500 - Internal Server Error odpověď bez textu odpovědi. Pokud server zachytí výjimku po odeslání hlaviček odpovědi, server připojení zavře. Požadavky, které aplikace nezpracuje, zpracovává server. Všechny výjimky, ke kterým dochází, když server zpracovává požadavek, se zpracovává zpracováním výjimek serveru. Vlastní chybové stránky aplikace, middleware pro zpracování výjimek a filtry toto chování neovlivní.

Ošetření výjimek při spuštění

Pouze vrstva hostování může zpracovávat výjimky, které probíhají při spuštění aplikace. Hostitel lze nakonfigurovat tak, aby zaznamenával chyby při spuštění a zaznamenával podrobné chyby.

Vrstva hostování může zobrazit chybovou stránku zachycené chyby při spuštění pouze v případě, že k chybě dojde po vazbě adresy hostitele nebo portu. Pokud se vazba nezdaří:

  • Vrstva hostování zaznamená kritickou výjimku.
  • Proces dotnet se chybově ukončí.
  • Pokud je Kestrelserver HTTP, nezobrazí se žádná chybová stránka.

Pokud je proces spuštěný ve službě IIS (nebo ve službě Aplikace Azure) nebo IIS Express, vrátí modul ASP.NET Core chybu procesu 502.5. Další informace najdete v tématu Řešení potíží ASP.NET Core ve službě Aplikace Azure a službě IIS.

Chybová stránka databáze

Filtr AddDatabaseDeveloperPageExceptionFilter výjimek stránky pro vývojáře databáze zachycuje výjimky související s databází, které je možné vyřešit pomocí migrací Entity Framework Core. Pokud dojde k těmto výjimkám, vygeneruje se odpověď HTML s podrobnostmi o možných akcích k vyřešení problému. Tato stránka je povolená pouze ve vývojovém prostředí. Následující kód přidá filtr výjimek stránky pro vývojáře databáze:

var builder = WebApplication.CreateBuilder(args);

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

Filtry výjimek

V aplikacích MVC je možné nakonfigurovat filtry výjimek globálně nebo podle kontroleru nebo podle akce. V Razor aplikacích Pages je možné je nakonfigurovat globálně nebo podle modelu stránky. Tyto filtry zpracovávají všechny neošetřené výjimky, ke kterým dochází během provádění akce kontroleru nebo jiného filtru. Další informace najdete v tématu Filtry v ASP.NET Core.

Filtry výjimek jsou užitečné pro zachycení výjimek, ke kterým dochází v rámci akcí MVC, ale nejsou tak flexibilní jako integrovaný middlewareUseExceptionHandler pro zpracování výjimek. Doporučujeme použít UseExceptionHandler, pokud nepotřebujete provádět zpracování chyb odlišně podle toho, jakou akci MVC zvolíte.

Chyby stavu modelu

Informace o tom, jak zpracovávat chyby stavu modelu, naleznete v tématu Vazby modelu a Ověření modelu.

Další materiály

Kirk Larkin, Tom Dykstra a Steve Smith

Tento článek popisuje běžné přístupy ke zpracování chyb ve webových aplikacích ASP.NET Core. Viz Zpracování chyb ve webových rozhraních API ASP.NET Core pro webová rozhraní API.

Zobrazení nebo stažení vzorového kódu (Jak stáhnout.) Karta Síť na vývojářských nástrojích prohlížeče F12 je užitečná při testování ukázkové aplikace.

Stránka výjimky pro vývojáře

Na stránce výjimka pro vývojáře se zobrazují podrobné informace o neošetřených výjimkách požadavků. Šablony ASP.NET Core generují následující kód:

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

Předchozí zvýrazněný kód povolí stránku výjimky vývojáře, když je aplikace spuštěná ve vývojovém prostředí.

Šablony jsou v UseDeveloperExceptionPage rané fázi kanálu middlewaru, aby mohly zachytit neošetřené výjimky vyvolané v middlewaru, které následuje.

Předchozí kód povolí stránku výjimky vývojáře pouze v době, kdy aplikace běží ve vývojovém prostředí. Podrobné informace o výjimce by se neměly zobrazovat veřejně při spuštění aplikace v produkčním prostředí. Další informace o konfiguraci prostředí najdete v tématu Použití více prostředí v ASP.NET Core.

Stránka výjimky pro vývojáře může obsahovat následující informace o výjimce a požadavku:

  • Trasování zásobníku
  • Parametry řetězce dotazu, pokud nějaké
  • Cookies if any
  • Hlavičky

Na stránce výjimky pro vývojáře není zaručeno, že poskytnete žádné informace. Protokolování slouží k úplným informacím o chybě.

Stránka obslužné rutiny výjimky

Chcete-li nakonfigurovat vlastní stránku zpracování chyb pro produkční prostředí, zavolejte UseExceptionHandler. Tento middleware pro zpracování výjimek:

  • Zachytává a zaznamenává neošetřené výjimky.
  • Znovu spustí požadavek v alternativním kanálu pomocí uvedené cesty. Pokud se odpověď spustila, požadavek se znovu nespustí. Vygenerovaný kód šablony znovu spustí požadavek pomocí /Error cesty.

Upozorňující

Pokud alternativní kanál vyvolá výjimku vlastní, middleware zpracování výjimek znovu zvětšuje původní výjimku.

V následujícím příkladu UseExceptionHandler přidá middleware pro zpracování výjimek v prostředích, která nejsou vývojová:

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

Šablona Razor aplikace Pages poskytuje ve složce Pages chybovou stránku (.cshtml) a PageModel třídu (ErrorModel). V případě aplikace MVC obsahuje šablona projektu metodu Error akce a zobrazení Chyba kontroleru Home .

Middleware zpracování výjimek znovu spustí požadavek pomocí původní metody HTTP. Pokud je koncový bod obslužné rutiny chyby omezen na konkrétní sadu metod HTTP, spustí se pouze pro tyto metody HTTP. Například akce kontroleru MVC, která používá [HttpGet] atribut, se spouští pouze pro požadavky GET. Abyste zajistili, že všechny požadavky dosáhnou vlastní stránky zpracování chyb, neomezovat je na konkrétní sadu metod HTTP.

Zpracování výjimek odlišně na základě původní metody HTTP:

  • Pro Razor Stránky vytvořte více metod obslužné rutiny. Slouží OnGet například ke zpracování výjimek GET a zpracování OnPost výjimek POST.
  • Pro MVC použijte atributy příkazů HTTP na více akcí. Slouží [HttpGet] například ke zpracování výjimek GET a zpracování [HttpPost] výjimek POST.

Pokud chcete umožnit neověřeným uživatelům zobrazit vlastní stránku pro zpracování chyb, ujistěte se, že podporuje anonymní přístup.

Přístup k výjimce

Slouží IExceptionHandlerPathFeature k přístupu k výjimce a původní cestě požadavku v obslužné rutině chyby. Následující kód se přidá ExceptionMessage do výchozího vygenerovaného Pages/Error.cshtml.cs šablonami ASP.NET Core:

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

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

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

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

Upozorňující

Neservírujte klientům citlivé informace o chybách. Obsluha chyb představuje bezpečnostní riziko.

Otestování výjimky v ukázkové aplikaci:

  • Nastavte prostředí na produkční.
  • Odeberte komentáře ze webBuilder.UseStartup<Startup>(); souboru .Program.cs
  • Na domovské stránce vyberte Aktivovat výjimku .

Lambda obslužné rutiny výjimek

Alternativou k vlastní stránce obslužné rutiny výjimky je poskytnutí lambda UseExceptionHandler. Použití lambda umožňuje přístup k chybě před vrácením odpovědi.

Následující kód používá lambda pro zpracování výjimek:

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

Upozorňující

Neslouží citlivým informacím o chybách od IExceptionHandlerFeature klientů nebo IExceptionHandlerPathFeature klientům. Obsluha chyb představuje bezpečnostní riziko.

Otestování výrazu lambda zpracování výjimek v ukázkové aplikaci:

  • Nastavte prostředí na produkční.
  • Odeberte komentáře ze webBuilder.UseStartup<StartupLambda>(); souboru .Program.cs
  • Na domovské stránce vyberte Aktivovat výjimku .

UseStatusCodePages

Ve výchozím nastavení aplikace ASP.NET Core neposkytuje stavovou znakovou stránku pro stavové kódy chyb HTTP, například 404 – Nenalezena. Když aplikace nastaví stavový kód chyby HTTP 400-599, který nemá tělo, vrátí stavový kód a prázdný text odpovědi. Pokud chcete zadat stavové kódové stránky, použijte middleware stavových stránek kódu. Pokud chcete povolit výchozí obslužné rutiny jen pro text pro běžné stavové kódy chyb, zavolejte UseStatusCodePages v Startup.Configure metodě:

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

Před zpracováním middlewaru žádostí zavolejte UseStatusCodePages . Například zavolejte UseStatusCodePages před middlewarem statického souboru a middlewarem koncových bodů.

Pokud UseStatusCodePages se nepoužívá, přechod na adresu URL bez koncového bodu vrátí chybovou zprávu závislá na prohlížeči, která značí, že se koncový bod nenašel. Například přechod na Home/Privacy2. Při UseStatusCodePages zavolání prohlížeč vrátí:

Status Code: 404; Not Found

UseStatusCodePages se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

UseStatusCodePages Testování v ukázkové aplikaci:

  • Nastavte prostředí na produkční.
  • Odeberte komentáře ze webBuilder.UseStartup<StartupUseStatusCodePages>(); souboru .Program.cs
  • Vyberte odkazy na domovské stránce na domovské stránce.

Poznámka:

Middleware stavových kódů nezachytává výjimky. Pokud chcete poskytnout vlastní stránku zpracování chyb, použijte stránku obslužné rutiny výjimky.

UseStatusCodePages s formátovacím řetězcem

Pokud chcete přizpůsobit typ obsahu odpovědi a text, použijte přetížení UseStatusCodePages , které přebírá typ obsahu a formátovací řetězec:

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

V předchozím kódu {0} je zástupný symbol pro kód chyby.

UseStatusCodePages formátovací řetězec se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

Pokud chcete testovat UseStatusCodePagesv ukázkové aplikaci, odeberte komentáře z webBuilder.UseStartup<StartupFormat>();Program.csaplikace .

PoužitíStatusCodePages s lambda

Pokud chcete zadat vlastní zpracování chyb a kód pro zápis odpovědí, použijte přetížení UseStatusCodePages , které přebírá výraz lambda:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePages(async context =>
    {
        context.HttpContext.Response.ContentType = "text/plain";

        await context.HttpContext.Response.WriteAsync(
            "Status code page, status code: " +
            context.HttpContext.Response.StatusCode);
    });

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

    app.UseRouting();

    app.UseAuthorization();

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

UseStatusCodePages s lambda se obvykle nepoužívá v produkčním prostředí, protože vrací zprávu, která není pro uživatele užitečná.

Pokud chcete testovat UseStatusCodePagesv ukázkové aplikaci, odeberte komentáře z webBuilder.UseStartup<StartupStatusLambda>();Program.csaplikace .

UseStatusCodePagesWithRedirects

Metoda UseStatusCodePagesWithRedirects rozšíření:

  • Odešle klientovi stavový kód 302 .
  • Přesměruje klienta na koncový bod zpracování chyb zadaný v šabloně adresy URL. Koncový bod zpracování chyb obvykle zobrazuje informace o chybě a vrací 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();
    });
}

Šablona adresy URL může obsahovat {0} zástupný symbol pro stavový kód, jak je znázorněno v předchozím kódu. Pokud šablona adresy URL začíná vlnovkou ~ , ~ nahradí se aplikací PathBase. Při zadávání koncového bodu v aplikaci vytvořte zobrazení nebo Razor stránku MVC pro koncový bod. Razor Příklad stránky naleznete v části Pages/MyStatusCode.cshtml v ukázkové aplikaci.

Tato metoda se běžně používá v případě, že aplikace:

  • Měl by přesměrovat klienta na jiný koncový bod, obvykle v případech, kdy jiná aplikace zpracovává chybu. U webových aplikací se adresní řádek prohlížeče klienta odráží přesměrovaný koncový bod.
  • Nemělo by se zachovat a vrátit původní stavový kód s počáteční odpovědí přesměrování.

Pokud chcete testovat UseStatusCodePagesv ukázkové aplikaci, odeberte komentáře z webBuilder.UseStartup<StartupSCredirect>();Program.csaplikace .

UseStatusCodePagesWithReExecute

Metoda UseStatusCodePagesWithReExecute rozšíření:

  • Vrátí původní stavový kód klientovi.
  • Vygeneruje tělo odpovědi opětovným spuštěním kanálu požadavku pomocí alternativní cesty.
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();
    });
}

Pokud je zadaný koncový bod v aplikaci, vytvořte zobrazení nebo Razor stránku MVC pro koncový bod. Ujistěte se UseStatusCodePagesWithReExecute , že je před umístěním UseRouting požadavku možné směrovat na stavovou stránku. Razor Příklad stránky naleznete v části Pages/MyStatusCode2.cshtml v ukázkové aplikaci.

Tato metoda se běžně používá, když by aplikace měla:

  • Zpracujte požadavek bez přesměrování na jiný koncový bod. U webových aplikací se adresní řádek prohlížeče klienta odráží původně požadovaný koncový bod.
  • Zachování a vrácení původního stavového kódu s odpovědí

Šablony adres URL a řetězce dotazu můžou obsahovat zástupný symbol {0} stavového kódu. Šablona adresy URL musí začínat /na .

Koncový bod, který chybu zpracuje, může získat původní adresu URL, která chybu vygenerovala, jak je znázorněno v následujícím příkladu:

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

Razor Příklad stránky naleznete v části Pages/MyStatusCode2.cshtml v ukázkové aplikaci.

Pokud chcete testovat UseStatusCodePagesv ukázkové aplikaci, odeberte komentáře z webBuilder.UseStartup<StartupSCreX>();Program.csaplikace .

Zakázat stavové kódové stránky

Chcete-li zakázat stránky stavového kódu pro kontroler MVC nebo metodu akce, použijte atribut [SkipStatusCodePages].

Chcete-li zakázat stavové kódové stránky pro konkrétní požadavky v Razor metodě obslužné rutiny stránky nebo v kontroleru MVC, použijte IStatusCodePagesFeature:

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

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

Kód pro zpracování výjimek

Kód na stránkách zpracování výjimek může také vyvolat výjimky. Produkční chybové stránky by se měly důkladně testovat a pečlivě se postarat, aby nedocházelo k výjimkám jejich vlastních.

Hlavičky odpovědi

Po odeslání hlaviček odpovědi:

  • Aplikace nemůže změnit stavový kód odpovědi.
  • Všechny stránky výjimek nebo obslužné rutiny se nedají spustit. Odpověď musí být dokončena nebo přerušeno připojení.

Zpracování výjimek serveru

Kromě logiky zpracování výjimek v aplikaci může implementace serveru HTTP zpracovávat některé výjimky. Pokud server zachytí výjimku před odesláním hlaviček odpovědi, server odešle 500 - Internal Server Error odpověď bez textu odpovědi. Pokud server zachytí výjimku po odeslání hlaviček odpovědi, server připojení zavře. Požadavky, které aplikace nezpracuje, zpracovává server. Všechny výjimky, ke kterým dochází, když server zpracovává požadavek, se zpracovává zpracováním výjimek serveru. Vlastní chybové stránky aplikace, middleware pro zpracování výjimek a filtry toto chování neovlivní.

Ošetření výjimek při spuštění

Pouze vrstva hostování může zpracovávat výjimky, které probíhají při spuštění aplikace. Hostitel lze nakonfigurovat tak, aby zaznamenával chyby při spuštění a zaznamenával podrobné chyby.

Vrstva hostování může zobrazit chybovou stránku zachycené chyby při spuštění pouze v případě, že k chybě dojde po vazbě adresy hostitele nebo portu. Pokud se vazba nezdaří:

  • Vrstva hostování zaznamená kritickou výjimku.
  • Proces dotnet se chybově ukončí.
  • Pokud je Kestrelserver HTTP, nezobrazí se žádná chybová stránka.

Pokud je proces spuštěný ve službě IIS (nebo ve službě Aplikace Azure) nebo IIS Express, vrátí modul ASP.NET Core chybu procesu 502.5. Další informace najdete v tématu Řešení potíží ASP.NET Core ve službě Aplikace Azure a službě IIS.

Chybová stránka databáze

Filtr AddDatabaseDeveloperPageExceptionFilter výjimek stránky pro vývojáře databáze zachycuje výjimky související s databází, které je možné vyřešit pomocí migrací Entity Framework Core. Pokud dojde k těmto výjimkám, vygeneruje se odpověď HTML s podrobnostmi o možných akcích k vyřešení problému. Tato stránka je povolená pouze ve vývojovém prostředí. Následující kód vygeneroval šablony ASP.NET Core Razor Pages při zadání jednotlivých uživatelských účtů:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDatabaseDeveloperPageExceptionFilter();
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();
}

Filtry výjimek

V aplikacích MVC je možné nakonfigurovat filtry výjimek globálně nebo podle kontroleru nebo podle akce. V Razor aplikacích Pages je možné je nakonfigurovat globálně nebo podle modelu stránky. Tyto filtry zpracovávají všechny neošetřené výjimky, ke kterým dochází během provádění akce kontroleru nebo jiného filtru. Další informace najdete v tématu Filtry v ASP.NET Core.

Filtry výjimek jsou užitečné pro zachycení výjimek, ke kterým dochází v rámci akcí MVC, ale nejsou tak flexibilní jako integrovaný middlewareUseExceptionHandler pro zpracování výjimek. Doporučujeme použít UseExceptionHandler, pokud nepotřebujete provádět zpracování chyb odlišně podle toho, jakou akci MVC zvolíte.

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

Chyby stavu modelu

Informace o tom, jak zpracovávat chyby stavu modelu, naleznete v tématu Vazby modelu a Ověření modelu.

Další materiály

Tom Dykstra a Steve Smith

Tento článek popisuje běžné přístupy ke zpracování chyb ve webových aplikacích ASP.NET Core. Viz Zpracování chyb ve webových rozhraních API ASP.NET Core pro webová rozhraní API.

Zobrazení nebo stažení vzorového kódu (Jak stáhnout.)

Stránka výjimky pro vývojáře

Na stránce výjimka pro vývojáře se zobrazují podrobné informace o výjimkách požadavků. Šablony ASP.NET Core generují následující kód:

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

Předchozí kód povolí stránku výjimky vývojáře, když je aplikace spuštěná ve vývojovém prostředí.

Šablony jsou před jakýmkoli middlewarem, UseDeveloperExceptionPage takže výjimky jsou zachyceny v middlewaru, který následuje.

Předchozí kód povolí stránku výjimky vývojáře pouze v případech, kdy je aplikace spuštěná ve vývojovém prostředí. Podrobné informace o výjimce by se neměly zobrazovat veřejně při spuštění aplikace v produkčním prostředí. Další informace o konfiguraci prostředí najdete v tématu Použití více prostředí v ASP.NET Core.

Stránka výjimky pro vývojáře obsahuje následující informace o výjimce a požadavku:

  • Trasování zásobníku
  • Parametry řetězce dotazu, pokud nějaké
  • Cookies if any
  • Hlavičky

Stránka obslužné rutiny výjimky

Ke konfiguraci vlastní stránky zpracování chyb pro produkční prostředí použijte middleware zpracování výjimek. Middleware:

  • Zachytává a protokoluje výjimky.
  • Znovu spustí požadavek v alternativním kanálu pro stránku nebo kontroler označený. Pokud se odpověď spustila, požadavek se znovu nespustí. Šablona vygenerovaný kód znovu spustí požadavek na /Error.

V následujícím příkladu přidá middleware zpracování výjimek v prostředích, UseExceptionHandler která nejsou vývojová:

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

Šablona Razor aplikace Pages poskytuje ve složce Pages chybovou stránku (.cshtml) a PageModel třídu (ErrorModel). V případě aplikace MVC obsahuje šablona projektu metodu akce Chyba a zobrazení Chyba v Home kontroleru.

Neoznačí metodu akce obslužné rutiny chyby s atributy metody HTTP, například HttpGet. Explicitní příkazy brání některým požadavkům v dosažení metody. Pokud by se neověřeným uživatelům mělo zobrazit zobrazení chyb, povolte anonymní přístup k metodě.

Přístup k výjimce

Slouží IExceptionHandlerPathFeature k přístupu k výjimce a původní cestě požadavku v kontroleru nebo stránce obslužné rutiny chyby:

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

Upozorňující

Neservírujte klientům citlivé informace o chybách. Obsluha chyb představuje bezpečnostní riziko.

Pokud chcete aktivovat předchozí stránku zpracování výjimek, nastavte prostředí na produkční prostředí a vynuťte výjimku.

Lambda obslužné rutiny výjimek

Alternativou k vlastní stránce obslužné rutiny výjimky je poskytnutí lambda UseExceptionHandler. Použití lambda umožňuje přístup k chybě před vrácením odpovědi.

Tady je příklad použití lambda pro zpracování výjimek:

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

V předchozím kódu se přidá, await context.Response.WriteAsync(new string(' ', 512)); aby prohlížeč Internet Explorer zobrazoval chybovou zprávu místo chybové zprávy IE. Další informace najdete u tohoto problému na GitHubu.

Upozorňující

Neslouží citlivým informacím o chybách od IExceptionHandlerFeature klientů nebo IExceptionHandlerPathFeature klientům. Obsluha chyb představuje bezpečnostní riziko.

Pokud chcete zobrazit výsledek zpracování výjimek lambda v ukázkové aplikaci, použijte ProdEnvironment direktivy preprocesoru a ErrorHandlerLambda vyberte Na domovské stránce aktivovat výjimku .

UseStatusCodePages

Ve výchozím nastavení aplikace ASP.NET Core neposkytuje stavovou stránku stavových kódů HTTP, například 404 – Nenalezena. Aplikace vrátí stavový kód a prázdný text odpovědi. Pokud chcete zadat stavové kódové stránky, použijte middleware Stavové stránky kódu.

Middleware je zpřístupněn balíčkem Microsoft.AspNetCore.Diagnostics .

Pokud chcete povolit výchozí obslužné rutiny jen pro text pro běžné stavové kódy chyb, zavolejte UseStatusCodePages v Startup.Configure metodě:

app.UseStatusCodePages();

Před zpracováním požadavků volejte UseStatusCodePages middleware (například Middleware statického souboru a middleware MVC).

Pokud UseStatusCodePages se nepoužívá, přechod na adresu URL bez koncového bodu vrátí chybovou zprávu závislé na prohlížeči, která značí, že se koncový bod nenašel. Například přechod na Home/Privacy2. Při UseStatusCodePages zavolání prohlížeč vrátí:

Status Code: 404; Not Found

UseStatusCodePages s formátovacím řetězcem

Pokud chcete přizpůsobit typ obsahu odpovědi a text, použijte přetížení UseStatusCodePages , které přebírá typ obsahu a formátovací řetězec:

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

PoužitíStatusCodePages s lambda

Pokud chcete zadat vlastní zpracování chyb a kód pro zápis odpovědí, použijte přetížení UseStatusCodePages , které přebírá výraz lambda:

app.UseStatusCodePages(async context =>
{
    context.HttpContext.Response.ContentType = "text/plain";

    await context.HttpContext.Response.WriteAsync(
        "Status code page, status code: " + 
        context.HttpContext.Response.StatusCode);
});

UseStatusCodePagesWithRedirects

Metoda UseStatusCodePagesWithRedirects rozšíření:

  • Odešle klientovi stavový kód 302 .
  • Přesměruje klienta do umístění zadaného v šabloně adresy URL.
app.UseStatusCodePagesWithRedirects("/StatusCode?code={0}");

Šablona adresy URL může obsahovat {0} zástupný symbol pro stavový kód, jak je znázorněno v příkladu. Pokud šablona adresy URL začíná vlnovkou ~ , ~ nahradí se aplikací PathBase. Pokud v aplikaci nasměrujete na koncový bod, vytvořte pro něj zobrazení nebo Razor stránku MVC. Razor Příklad stránky najdete Pages/StatusCode.cshtml v ukázkové aplikaci.

Tato metoda se běžně používá v případě, že aplikace:

  • Měl by přesměrovat klienta na jiný koncový bod, obvykle v případech, kdy jiná aplikace zpracovává chybu. U webových aplikací se adresní řádek prohlížeče klienta odráží přesměrovaný koncový bod.
  • Nemělo by se zachovat a vrátit původní stavový kód s počáteční odpovědí přesměrování.

UseStatusCodePagesWithReExecute

Metoda UseStatusCodePagesWithReExecute rozšíření:

  • Vrátí původní stavový kód klientovi.
  • Vygeneruje tělo odpovědi opětovným spuštěním kanálu požadavku pomocí alternativní cesty.
app.UseStatusCodePagesWithReExecute("/StatusCode","?code={0}");

Pokud v aplikaci nasměrujete na koncový bod, vytvořte pro něj zobrazení nebo Razor stránku MVC. Ujistěte se UseStatusCodePagesWithReExecute , že je před umístěním UseRouting požadavku možné směrovat na stavovou stránku. Razor Příklad stránky najdete Pages/StatusCode.cshtml v ukázkové aplikaci.

Tato metoda se běžně používá, když by aplikace měla:

  • Zpracujte požadavek bez přesměrování na jiný koncový bod. U webových aplikací se adresní řádek prohlížeče klienta odráží původně požadovaný koncový bod.
  • Zachování a vrácení původního stavového kódu s odpovědí

Šablony adres URL a řetězce dotazu můžou obsahovat zástupný symbol ({0}) pro stavový kód. Šablona adresy URL musí začínat lomítkem (/). Při použití zástupného symbolu v cestě ověřte, že koncový bod (stránka nebo kontroler) může segment cesty zpracovat. Například Razor stránka pro chyby by měla přijmout volitelnou hodnotu segmentu cesty s direktivou @page :

@page "{code?}"

Koncový bod, který chybu zpracuje, může získat původní adresu URL, která chybu vygenerovala, jak je znázorněno v následujícím příkladu:

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

Neoznačí metodu akce obslužné rutiny chyby s atributy metody HTTP, například HttpGet. Explicitní příkazy brání některým požadavkům v dosažení metody. Pokud by se neověřeným uživatelům mělo zobrazit zobrazení chyb, povolte anonymní přístup k metodě.

Zakázat stavové kódové stránky

Chcete-li zakázat stránky stavového kódu pro kontroler MVC nebo metodu [SkipStatusCodePages] akce, použijte atribut.

Chcete-li zakázat stavové kódové stránky pro konkrétní požadavky v Razor metodě obslužné rutiny stránky nebo v kontroleru MVC, použijte IStatusCodePagesFeature:

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

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

Kód pro zpracování výjimek

Kód na stránkách zpracování výjimek může vyvolat výjimky. Často je vhodné, aby se produkční chybové stránky skládají z čistě statického obsahu.

Hlavičky odpovědi

Po odeslání hlaviček odpovědi:

  • Aplikace nemůže změnit stavový kód odpovědi.
  • Všechny stránky výjimek nebo obslužné rutiny se nedají spustit. Odpověď musí být dokončena nebo přerušeno připojení.

Zpracování výjimek serveru

Kromě logiky zpracování výjimek ve vaší aplikaci může implementace serveru HTTP zpracovávat některé výjimky. Pokud server zachytí výjimku před odesláním hlaviček odpovědí, server odešle odpověď 500 – Vnitřní chyba serveru bez textu odpovědi. Pokud server zachytí výjimku po odeslání hlaviček odpovědi, server připojení zavře. Požadavky, které vaše aplikace nezpracuje, zpracovává server. Všechny výjimky, ke kterým dochází, když server zpracovává požadavek, se zpracovává zpracováním výjimek serveru. Vlastní chybové stránky aplikace, middleware pro zpracování výjimek a filtry toto chování neovlivní.

Ošetření výjimek při spuštění

Pouze vrstva hostování může zpracovávat výjimky, které probíhají při spuštění aplikace. Hostitel lze nakonfigurovat tak, aby zaznamenával chyby při spuštění a zaznamenával podrobné chyby.

Vrstva hostování může zobrazit chybovou stránku zachycené chyby při spuštění pouze v případě, že k chybě dojde po vazbě adresy hostitele nebo portu. Pokud se vazba nezdaří:

  • Vrstva hostování zaznamená kritickou výjimku.
  • Proces dotnet se chybově ukončí.
  • Pokud je Kestrelserver HTTP, nezobrazí se žádná chybová stránka.

Pokud je proces spuštěný ve službě IIS (nebo ve službě Aplikace Azure) nebo IIS Express, vrátí modul ASP.NET Core chybu procesu 502.5. Další informace najdete v tématu Řešení potíží ASP.NET Core ve službě Aplikace Azure a službě IIS.

Chybová stránka databáze

Middleware chybové stránky databáze zachycuje výjimky související s databází, které je možné vyřešit pomocí migrací entity Framework. Pokud dojde k těmto výjimkám, vygeneruje se odpověď HTML s podrobnostmi o možných akcích k vyřešení problému. Tato stránka by měla být povolená pouze ve vývojovém prostředí. Povolte stránku přidáním kódu do Startup.Configure:

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

UseDatabaseErrorPagevyžaduje balíček NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.

Filtry výjimek

V aplikacích MVC je možné nakonfigurovat filtry výjimek globálně nebo podle kontroleru nebo podle akce. V Razor aplikacích Pages je možné je nakonfigurovat globálně nebo podle modelu stránky. Tyto filtry zpracovávají všechny neošetřené výjimky, ke kterým dochází během provádění akce kontroleru nebo jiného filtru. Další informace najdete v tématu Filtry v ASP.NET Core.

Tip

Filtry výjimek jsou užitečné pro zachycení výjimek, ke kterým dochází v rámci akcí MVC, ale nejsou tak flexibilní jako middleware zpracování výjimek. Doporučujeme použít middleware. Filtry použijte pouze v případě, že potřebujete provádět zpracování chyb odlišně podle toho, jakou akci MVC zvolíte.

Chyby stavu modelu

Informace o tom, jak zpracovávat chyby stavu modelu, naleznete v tématu Vazby modelu a Ověření modelu.

Další materiály