Megosztás a következőn keresztül:


Hibák kezelése a ASP.NET Core-ban

Megjegyzés:

Ez nem a cikk legújabb verziója. A jelen cikk .NET 9-es verzióját lásd az aktuális kiadásért .

Figyelmeztetés

A ASP.NET Core ezen verziója már nem támogatott. További információ: .NET és .NET Core támogatási szabályzat. A jelen cikk .NET 9-es verzióját lásd az aktuális kiadásért .

Fontos

Ezek az információk egy olyan előzetes termékre vonatkoznak, amelyet a kereskedelmi forgalomba kerülés előtt jelentősen módosíthatnak. A Microsoft nem vállal kifejezett vagy hallgatólagos szavatosságot az itt megadott információkra vonatkozóan.

A jelen cikk .NET 9-es verzióját lásd az aktuális kiadásért .

Készítette: Tom Dykstra

Ez a cikk az ASP.NET Core-webalkalmazásokban előforduló hibák kezelésének gyakori megközelítéseit ismerteti. Lásd még: ASP.NET vezérlőalapú webes API-k hibáinak kezelése és a hibák kezelése minimális API-kban.

A Blazor jelen cikkben szereplő útmutatást kiegészítő vagy felülíró hibakezelési útmutatásért lásd: ASP.NET Core-alkalmazások Blazor hibáinak kezelése.

Fejlesztői kivételoldal

A Fejlesztői kivétel lap részletes információkat jelenít meg a nem kezelt kérelmek kivételeiről. A HTTP-folyamat szinkron és aszinkron kivételeinek rögzítésére és hibaválaszok létrehozására használja DeveloperExceptionPageMiddleware . A fejlesztői kivételoldal a köztes szoftver folyamatának korai szakaszában fut, hogy a következő köztes szoftverben megjelenő kezeletlen kivételeket észlelhesse.

ASP.NET Core-alkalmazások alapértelmezés szerint engedélyezik a fejlesztői kivételoldalt, ha mindkettő:

A korábbi sablonokkal, azaz a WebHost.CreateDefaultBuilder sablon használatával létrehozott alkalmazások a app.UseDeveloperExceptionPage függvény hívásával engedélyezhetik a fejlesztői kivételoldalt.

Figyelmeztetés

Csak akkor engedélyezze a fejlesztői kivételoldalt, ha az alkalmazás a fejlesztői környezetben fut. Ne ossza meg nyilvánosan a részletes kivételadatokat, ha az alkalmazás éles környezetben fut. További információ a környezetek konfigurálásáról: Több környezet használata a ASP.NET Core-ban.

A Fejlesztői kivétel lap a következő információkat tartalmazhatja a kivételről és a kérésről:

  • Verem nyomkövetése
  • Lekérdezési sztringparaméterek, ha vannak ilyenek
  • Cookie-k, ha vannak ilyenek
  • Fejlécek
  • Végpont metaadatai, ha vannak ilyenek

A fejlesztői kivételoldal nem garantáltan biztosít semmilyen információt. Használja a naplózást a teljes hibainformációkhoz.

Az alábbi képen egy minta fejlesztői kivétellap látható, amelyen animáció látható a lapok és a megjelenített információk megjelenítéséhez:

A fejlesztői kivétellap animáltan jelenik meg az egyes kijelölt lapok megjelenítéséhez.

Egy fejlécet tartalmazó Accept: text/plain kérésre válaszul a Fejlesztői kivételoldal a HTML helyett egyszerű szöveget ad vissza. Például:

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

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

Kivételkezelő lap

Ha egyéni hibakezelő lapot szeretne konfigurálni az éles környezethez, hívja meg a következőt UseExceptionHandler: . Ez a kivételkezelés köztes szoftver:

  • Naplózza a kezeletlen kivételeket.
  • A kérést újra végrehajtja egy másik folyamatban a megadott elérési út használatával. A rendszer nem hajtja végre újra a kérést, ha a válasz elindult. A sablon által létrehozott kód újra végrehajtja a kérést az /Error elérési út használatával.

Figyelmeztetés

Ha az alternatív csővezeték saját kivételt dob, a Kivételkezelő köztes réteg újradobja az eredeti kivételt.

Mivel ez a köztes szoftver újra tudja futtatni a kérési folyamatot:

  • A köztes rétegeknek kezelniük kell a reentranciát ugyanazzal a kéréssel. Ez általában azt jelenti, hogy a hívás _next után megtisztítják az állapotukat, vagy a feldolgozásukat HttpContext gyorsítótárazzák, hogy elkerüljék az újbóli elvégzést. A kérelem törzsének kezelésekor ez az eredmények pufferelését vagy gyorsítótárazását jelenti, ahogyan azt az Űrlapolvasó is teszi.
  • UseExceptionHandler(IApplicationBuilder, String) A sablonokban használt túlterhelés esetén csak a kérelem útvonala módosul, és az útvonaladatok törlődnek. A kérelemadatok, például a fejlécek, a metódusok és az elemek mind újra felhasználhatók as-is.
  • A hatókörön belüli szolgáltatások változatlanok maradnak.

Az alábbi példában a UseExceptionHandler hozzáadja a kivételkezelő middleware-t a fejlesztési környezeteken kívüli környezetekben.

var app = builder.Build();

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

A Razor Lapok alkalmazássablon egy Hibalapot (.cshtml) és PageModel osztályt (ErrorModel) biztosít a Lapok mappában. MVC-alkalmazások esetén a projektsablon tartalmaz egy Error műveletmetódust és egy hibanézetet a Home vezérlőhöz.

A köztes szoftver kivételkezelése újra végrehajtja a kérést az eredeti HTTP-módszerrel. Ha egy hibakezelő végpont a HTTP-metódusok egy meghatározott halmazára korlátozódik, az csak az adott HTTP-metódusokhoz fut. Az attribútumot használó [HttpGet] MVC-vezérlőművelet például csak GET-kérelmek esetén fut. Annak érdekében, hogy minden kérés elérje az egyéni hibakezelő lapot, ne korlátozza azokat a HTTP-metódusok meghatározott készletére.

A kivételek eltérő kezelése az eredeti HTTP-módszer alapján:

  • A Razor Pages esetében hozzon létre több kezelő metódusokat. Például a OnGet a GET-kivételek kezelésére, míg a OnPost a POST-kivételek kezelésére használható.
  • MVC esetén http-igeattribútumokat alkalmazhat több műveletre. Például a [HttpGet] a GET-kivételek kezelésére használható, és a [HttpPost] a POST-kivételek kezelésére.

Ha engedélyezni szeretné, hogy a hitelesítés nélküli felhasználók megtekinthessék az egyéni hibakezelő lapot, győződjön meg arról, hogy támogatja a névtelen hozzáférést.

A kivétel elérése

Használja a IExceptionHandlerPathFeature elemet a kivétel és az eredeti kérési útvonal elérésére egy hibakezelőben. Az alábbi példa IExceptionHandlerPathFeature a kidobott kivételről nyújt további információt:

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

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

Kivételkezelő lambda

Az egyéni kivételkezelő lap alternatívájaként lambda adható meg a UseExceptionHandler-nek. A lambda használata lehetővé teszi a hiba elérését a válasz visszaadása előtt.

A következő kód egy lambdát használ a kivételkezeléshez:

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

A lambda használatának másik módja, ha az állapotkódot a kivétel típusa alapján állítja be, ahogyan az alábbi példában is látható:

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

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

IExceptionHandler

Az IExceptionHandler egy olyan felület, amely visszahívást biztosít a fejlesztőnek az ismert kivételek központi helyen való kezelésére.

IExceptionHandler implementációkat a IServiceCollection.AddExceptionHandler<T>meghívásával regisztrálják. Az IExceptionHandler példány élettartama singleton. Több implementáció is hozzáadható, és a rendszer a regisztrált sorrendben hívja meg őket.

Ha egy kivételkezelő kezel egy kérést, true adhat vissza a feldolgozás leállításához. Ha egy kivételt egyetlen kivételkezelő sem kezel, akkor a vezérlés visszaesik a köztes szoftver alapértelmezett viselkedésére és beállításaira. Különböző metrikákat és naplókat bocsát ki a rendszer a kezelt és kezeletlen kivételek esetén.

Az alábbi példa egy implementációt IExceptionHandler mutat be:

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

Az alábbi példa bemutatja, hogyan regisztrálhat implementációt IExceptionHandler függőséginjektáláshoz:

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

Ha az előző kód a fejlesztési környezetben fut:

  • A CustomExceptionHandler először meghívódik a kivétel kezelésére.
  • A kivétel naplózása után a TryHandleAsync metódus visszatér false, így megjelenik a fejlesztői kivétellap.

Más környezetekben:

  • CustomExceptionHandler-t először hívják meg a kivétel kezelésére.
  • A kivétel naplózása után a TryHandleAsync metódus visszatér, így megjelenik falseaz /Error oldal .

Állapotkód-oldalak használata

Alapértelmezés szerint egy ASP.NET Core-alkalmazás nem biztosít állapotkódlapot a HTTP-hibaállapot-kódokhoz, például 404 – Nem található. Amikor az alkalmazás beállít egy HTTP 400-599 hibaállapot-kódot, amely nem tartalmaz törzset, az állapotkódot és egy üres választörzset ad vissza. Ha engedélyezni szeretné az alapértelmezett csak szöveges kezelőket a gyakori hibaállapot-kódokhoz, hívja meg UseStatusCodePages a Program.cs-ben:

var app = builder.Build();

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

app.UseStatusCodePages();

Hívás UseStatusCodePages a köztes szoftverrel kapcsolatos kérések kezelése előtt. Hívja például a UseStatusCodePages parancsot a statikus fájlok köztes rétege és az Endpoints Middleware előtt.

Ha UseStatusCodePages nincs használatban, a végpont nélküli URL-címre való navigálás egy böngészőfüggő hibaüzenetet ad vissza, amely jelzi, hogy a végpont nem található. Amikor UseStatusCodePages a rendszer meghívja, a böngésző a következő választ adja vissza:

Status Code: 404; Not Found

UseStatusCodePages általában nem használják éles környezetben, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

Megjegyzés:

Az állapotkódlapok köztes szoftverei nem észlelnek kivételeket. Egyéni hibakezelő lap megadásához használja a kivételkezelő lapot.

UseStatusCodePages formátum sztring alkalmazásával

A válasz tartalomtípusának és szövegének testreszabásához használja azt a UseStatusCodePages túlterhelést, amely tartalomtípust és formázási sztringet vesz fel.

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

Az előző kódban a {0} egy helyőrző a hibakód számára.

UseStatusCodePages a formátumsztringet általában nem használják éles környezetben, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

UseStatusCodePages a lambda kifejezéssel

Az egyéni hibakezelési és válaszírási kód megadásához használja az UseStatusCodePages azon túlterhelését, amely elfogad egy lambda kifejezést:

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

A lambdát általában nem használják termelési környezetben, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

Állapotkód-oldalak használata átirányítással (UseStatusCodePagesWithRedirects)

A UseStatusCodePagesWithRedirects bővítmény metódusa:

  • 302 – Talált állapotkódot küld az ügyfélnek.
  • Átirányítja az ügyfelet az URL-sablonban megadott hibakezelési végpontra. A hibakezelő végpont általában hibainformációkat jelenít meg, és HTTP 200-et ad vissza.
var app = builder.Build();

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

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

Az URL-sablon tartalmazhat helyőrzőt {0} az állapotkódhoz, ahogyan az az előző kódban is látható. Ha az URL-sablon ~ (tilde) jellel kezdődik ~, akkor az ~ helyébe az alkalmazás PathBase kerül. Amikor végpontot ad meg az alkalmazásban, hozzon létre egy MVC-nézetet vagy Razor lapot a végponthoz.

Ezt a módszert gyakran használják az alkalmazás esetében:

  • Az ügyfelet egy másik végpontra kell átirányítani, általában olyan esetekben, amikor egy másik alkalmazás feldolgozza a hibát. Webalkalmazások esetén az ügyfél böngészőcímsávja az átirányított végpontot tükrözi.
  • Ne őrizze meg és ne adja vissza az eredeti állapotkódot az első átirányítási válasznál.

UseStatusCodePagesWithReExecute

A UseStatusCodePagesWithReExecute bővítmény metódusa:

  • A válasz törzsét úgy hozza létre, hogy egy másik elérési út használatával újra végrehajtja a kérelemfolyamatot.
  • Nem módosítja az állapotkódot a folyamat ismételt végrehajtása előtt vagy után.

Az új folyamat végrehajtása megváltoztathatja a válasz állapotkódját, mivel az új folyamat teljes mértékben szabályozza az állapotkódot. Ha az új folyamat nem módosítja az állapotkódot, a rendszer elküldi az eredeti állapotkódot az ügyfélnek.

var app = builder.Build();

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

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

Ha az alkalmazásban egy végpont van megadva, hozzon létre egy MVC-nézetet vagy Razor lapot a végponthoz.

Ezt a módszert gyakran használják, amikor az alkalmazásnak a következőt kell használnia:

  • A kérés feldolgozása egy másik végpontra való átirányítás nélkül. Webalkalmazások esetén az ügyfél böngészőcímsávja az eredetileg kért végpontot tükrözi.
  • Őrizze meg és adja vissza az eredeti állapotkódot a válaszsal együtt.

Az URL-sablonnak kezdődnie kell, / és tartalmaznia kell egy helyőrzőt {0} az állapotkódhoz. Ha az állapotkódot lekérdezési sztring paraméterként szeretné átadni, adjon át egy második argumentumot a következőbe UseStatusCodePagesWithReExecute: Például:

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

A hibát feldolgozó végpont lekérheti a hibát létrehozó eredeti URL-címet, ahogyan az az alábbi példában látható:

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

    public string? OriginalPathAndQuery { get; set; }

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

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

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

        }
    }
}

Mivel ez a köztes szoftver újra tudja futtatni a kérési folyamatot:

  • A middleware-eknek kell kezelniük a reentranciát ugyanazzal a kéréssel. Ez általában azt jelenti, hogy vagy a hívás _next után tisztítják meg az állapotukat, vagy a HttpContext-en gyorsítótárazzák a feldolgozást, hogy elkerüljék annak újbóli elvégzését. A kérelem törzsének kezelésekor ez azt jelenti, hogy az eredményeket vagy pufferelni, vagy gyorsítótárazni kell, mint az űrlapolvasó esetében.
  • A hatókörön belüli szolgáltatások változatlanok maradnak.

Állapotkódlapok letiltása

MVC-vezérlő vagy műveleti metódus állapotkódlapjainak letiltásához használja a [SkipStatusCodePages] attribútumot.

Ha le szeretné tiltani az állapotkódlapokat a Razor Pages-kezelő metódusban vagy egy MVC-vezérlőben megadott kérésekhez, használja a következőt IStatusCodePagesFeature:

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

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

Kivételkezelési kód

A kivételkezelő lapok kódjai kivételeket is okozhatnak. A gyártási hibaoldalakat alaposan tesztelni kell, és fokozottan ügyelni kell arra, hogy elkerüljük a sajátjaik által okozott kivételeket.

Válaszfejlécek

A válasz fejléceinek elküldése után:

  • Az alkalmazás nem tudja módosítani a válasz állapotkódját.
  • A kivételoldalak vagy -kezelők nem futtathatók. A választ be kell fejezni, vagy megszakadt a kapcsolat.

Kiszolgálói kivételkezelés

Az alkalmazások kivételkezelési logikája mellett a HTTP-kiszolgáló implementációja is képes kezelni néhány kivételt. Ha a kiszolgáló kivételt kap a válaszfejlécek elküldése előtt, a kiszolgáló választörzs nélkül küld 500 - Internal Server Error választ. Ha a kiszolgáló kivételt kap a válaszfejlécek elküldése után, a kiszolgáló bezárja a kapcsolatot. Az alkalmazás által nem kezelt kérelmeket a kiszolgáló kezeli. Minden kivételt, amely akkor fordul elő, amikor a kiszolgáló kezeli a kérést, a kiszolgáló kivételkezelése kezeli. Az alkalmazás egyéni hibaoldalai, a köztes szoftver kivételkezelése és a szűrők nem befolyásolják ezt a viselkedést.

Indítási kivétel kezelése

Csak az üzemeltetési réteg képes kezelni az alkalmazás indításakor előforduló kivételeket. A gazdagép konfigurálható az indítási hibák és a részletes hibák rögzítésére.

A fogadó réteg csak akkor jeleníthet meg hibalapot egy észlelt indítási hiba esetén, ha a hiba a gazdacím/port kötése után következik be. Ha a kötés sikertelen:

  • Az üzemeltetési réteg kritikus kivételt naplóz.
  • A dotnet-folyamat összeomlik.
  • HTTP-kiszolgáló Kestrel állapotában nem jelenik meg hibaoldal.

Ha IIS-en (vagy Azure App Service-en) vagy IIS Expressen fut, az ASP.NET core modul502.5-ös folyamathibát ad vissza, ha a folyamat nem indítható el. További információ: ASP.NET Core hibaelhárítása az Azure App Service-ben és az IIS-ben.

Adatbázis hibaoldala

Az adatbázis fejlesztői lapjának kivételszűrője AddDatabaseDeveloperPageExceptionFilter rögzíti az adatbázissal kapcsolatos kivételeket, amelyek az Entity Framework Core migrálásával oldhatók meg. Ha ezek a kivételek előfordulnak, a rendszer HTML-választ hoz létre a probléma megoldásához szükséges lehetséges műveletek részleteivel. Ez a lap csak a fejlesztési környezetben engedélyezett. A következő kód hozzáadja az Adatbázis fejlesztői oldal kivételszűrőjét:

var builder = WebApplication.CreateBuilder(args);

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

Kivételszűrők

Az MVC-alkalmazásokban a kivételszűrők globálisan, vezérlőnként vagy műveletenként konfigurálhatók. A Razor Pages-alkalmazásokban globálisan vagy oldalmodellenként konfigurálhatók. Ezek a szűrők kezelik a vezérlőművelet vagy egy másik szűrő végrehajtása során előforduló nem kezelt kivételeket. További információ: Szűrők a ASP.NET Core-ban.

A kivételszűrők hasznosak az MVC-műveletekben előforduló kivételek csapdába ejtéséhez, de nem olyan rugalmasak, mint a beépített kivételkezelő köztes szoftver, UseExceptionHandler. Javasoljuk, hogy használja UseExceptionHandler, kivéve, ha a hibakezelést a választott MVC-művelet alapján eltérő módon kell elvégeznie.

Modellállapot-hibák

A modellállapot-hibák kezelésével kapcsolatos információkért tekintse meg a modellkötést és a modellérvényesítést ismertető témakört.

Probléma részletei

A probléma részletei nem az egyetlen válaszformátum a HTTP API-hibák leírásához, azonban gyakran használják őket a HTTP API-k hibáinak jelentésére.

A probléma részletei szolgáltatás implementálja a felületet, amely támogatja a IProblemDetailsService probléma részleteinek létrehozását a ASP.NET Core-ban. A AddProblemDetails(IServiceCollection) bővítménymetódus a IServiceCollection regisztrálja az alapértelmezett IProblemDetailsService implementációt.

A ASP.NET Core-alkalmazásokban a következő köztes szoftver generálja a probléma részleteit HTTP-válaszok meghívásakorAddProblemDetails, kivéve, ha a Accept kérelem HTTP-fejléce nem tartalmazza a regisztrált IProblemDetailsWriter (alapértelmezett) által támogatott tartalomtípusokat: application/json

  • ExceptionHandlerMiddleware: Probléma részleteit tartalmazó választ hoz létre, ha nincs definiálva egyéni kezelő.
  • StatusCodePagesMiddleware: Alapértelmezés szerint létrehoz egy probléma részleteit tartalmazó választ.
  • DeveloperExceptionPageMiddleware: Probléma részleteit tartalmazó választ hoz létre a fejlesztési környezetben, amikor a kérelem HTTP-fejléce nem tartalmazza a text/html értéket.

Az alábbi kód úgy konfigurálja az alkalmazást, hogy probléma részleteit adja meg az összes olyan HTTP-ügyfél- és kiszolgálóhiba-válaszhoz, amely még nem rendelkezik törzstartalommal:

builder.Services.AddProblemDetails();

var app = builder.Build();        

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

app.UseStatusCodePages();

A következő szakasz bemutatja, hogyan szabhatja testre a probléma részleteit tartalmazó választörzset.

A probléma részleteinek testreszabása

Az ProblemDetails automatikus létrehozása az alábbi lehetőségek bármelyikével testre szabható.

  1. Használja a ProblemDetailsOptions.CustomizeProblemDetails-t
  2. Egyéni IProblemDetailsWriter használata
  3. Hívja a IProblemDetailsService köztes szoftvert

CustomizeProblemDetails művelet

A létrehozott probléma részletei testre szabhatók a használatával CustomizeProblemDetails, és a testreszabások az automatikusan létrehozott probléma részleteire lesznek alkalmazva.

A beállításhoz CustomizeProblemDetailsa következő kód szolgálProblemDetailsOptions:

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

Például egy HTTP Status 400 Bad Request végpont eredménye a következő probléma részletei választ ad vissza:

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

Egyéni IProblemDetailsWriter

A IProblemDetailsWriter implementáció létrehozható a speciális testreszabásokhoz.

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

Jegyzet: Egyéni IProblemDetailsWriter használata esetén a hívás AddRazorPages előtt regisztrálni kell az egyénit IProblemDetailsWriter, AddControllers, AddControllersWithViews, vagy 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();

A Middleware-ből származó probléma részletei

Az ProblemDetailsOptions és CustomizeProblemDetails használatának egyik alternatív módszere, hogy a ProblemDetails-t köztes rétegben állítjuk be. A probléma részleteire adott választ a következő hívással IProblemDetailsService.WriteAsynclehet megírni:

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

Az előző kódban a minimális API-végpontok /divide , és /squareroot a várt egyéni hibaválaszt adja vissza a hibabemenethez.

Az API-vezérlő végpontjai az alapértelmezett hibaválaszt adják vissza a hibabemenetkor, nem pedig az egyéni problémamegoldást. A rendszer az alapértelmezett problémaválaszt adja vissza, mert az API-vezérlő a válaszstreambe írt a hibaállapotkódokkal kapcsolatos problémák részleteit, mielőtt IProblemDetailsService.WriteAsync meghívták, és a válasz nem íródik újra.

Az alábbi ValuesController visszaadja BadRequestResult, ami a válaszfolyamba ír, és így megakadályozza az egyéni probléma válaszának visszatérését.

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

}

A következő Values3Controller a ControllerBase.Problem eredményt adja vissza, így a várt egyéni probléma eredménye lesz:

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

}

Hozzon létre egy ProblemDetails adatcsomagot a kivételekhez

Fontolja meg a következő alkalmazást:

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

Nem fejlesztési környezetekben, ha kivétel történik, a következő egy szabványos ProblemDetails-válasz , amelyet a rendszer visszaad az ügyfélnek:

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

A legtöbb alkalmazás esetében csak az előző kód szükséges a kivételekhez. Az alábbi szakasz azonban bemutatja, hogyan kaphat részletesebb választ a problémákra.

Az alternatíva az egyéni kivételkezelő lappal szemben az, hogy lambdát adjon meg a UseExceptionHandler. A lambda használata lehetővé teszi a hiba elérését és a probléma részleteit tartalmazó válasz megírását a következővel 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();

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

A probléma részleteinek létrehozására másik módszer a külső NuGet-csomag, a Hellang.Middleware.ProblemDetails használata, amely a kivételek és az ügyfélhibák leképezésére használható a probléma részleteire.

További erőforrások

Készítette: Tom Dykstra

Ez a cikk az ASP.NET Core-webalkalmazásokban előforduló hibák kezelésének gyakori megközelítéseit ismerteti. Lásd még: ASP.NET vezérlőalapú webes API-k hibáinak kezelése és a hibák kezelése minimális API-kban.

Fejlesztői kivételoldal

A Fejlesztői kivétel lap részletes információkat jelenít meg a nem kezelt kérelmek kivételeiről. ASP.NET Core-alkalmazások alapértelmezés szerint engedélyezik a fejlesztői kivételoldalt, ha mindkettő:

A fejlesztői kivételoldal a köztes szoftver folyamatának korai szakaszában fut, hogy a következő köztes szoftverben megjelenő kezeletlen kivételeket észlelhesse.

A részletes kivételadatokat nem szabad nyilvánosan megjeleníteni, amikor az alkalmazás éles környezetben fut. További információ a környezetek konfigurálásáról: Több környezet használata a ASP.NET Core-ban.

A Fejlesztői kivétel lap a következő információkat tartalmazhatja a kivételről és a kérésről:

  • Verem nyomkövetése
  • Lekérdezési sztringparaméterek, ha vannak ilyenek
  • Cookie-k, ha vannak ilyenek
  • Fejlécek

A fejlesztői kivételoldal nem garantáltan biztosít semmilyen információt. Teljes hibainformációk megszerzéséhez használja a naplózást.

Kivételkezelő lap

Ha egyéni hibakezelő lapot szeretne konfigurálni az éles környezethez, használja a következőt UseExceptionHandler. Ez a kivételkezelés köztes szoftver:

  • Elkapja és naplózza a kezeletlen kivételeket.
  • A kérést újra végrehajtja egy másik folyamatban a megadott elérési út használatával. A rendszer nem hajtja végre újra a kérést, ha a válasz elindult. A sablon által létrehozott kód újra végrehajtja a kérést az /Error elérési út használatával.

Figyelmeztetés

Ha a másik folyamat saját magának új kivételt ad ki, a Kivételkezelő köztes szoftver újra dobja az eredeti kivételt.

Mivel ez a köztes szoftver újra tudja futtatni a kérési folyamatot:

  • A köztes szoftvereknek képesnek kell lenniük a ki-be lépés kezelésére ugyanazzal a kéréssel. Ez általában azt jelenti, hogy vagy a hívás _next után megtisztítják az állapotukat, vagy a feldolgozásukat a HttpContext gyorsítótárazásával elkerülik annak újbóli elvégzését. A kérés törzsének kezelésekor ez az eredmények pufferelését vagy gyorsítótárazását jelenti, hasonlóan ahhoz, ahogy az Űrlapolvasó működik.
  • UseExceptionHandler(IApplicationBuilder, String) A sablonokban használt túlterhelés esetén csak a kérelem útvonala módosul, és az útvonaladatok törlődnek. A kérelemadatok, például a fejlécek, a metódusok és az elemek mind újra felhasználhatók as-is.
  • A hatókörön belüli szolgáltatások változatlanok maradnak.

Az alábbi példában UseExceptionHandler nem fejlesztői környezetben hozzáadja a kivételkezelő köztes szoftvert.

var app = builder.Build();

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

A Razor Lapok alkalmazássablon egy Hibalapot (.cshtml) és PageModel osztályt (ErrorModel) biztosít a Lapok mappában. MVC-alkalmazások esetén a projektsablon tartalmaz egy Error műveletmetódust és egy hibanézetet a Home vezérlőhöz.

A köztes szoftver kivételkezelése újra végrehajtja a kérést az eredeti HTTP-módszerrel. Ha egy hibakezelő végpont a HTTP-metódusok egy meghatározott halmazára korlátozódik, az csak az adott HTTP-metódusokhoz fut. Az attribútumot használó [HttpGet] MVC-vezérlőművelet például csak GET-kérelmek esetén fut. Annak érdekében, hogy minden kérés elérje az egyéni hibakezelő lapot, ne korlátozza azokat a HTTP-metódusok meghatározott készletére.

A kivételek eltérő kezelése az eredeti HTTP-módszer alapján:

  • A Razor Pages esetében hozzon létre több kezelő metódust. Például a OnGet a GET-kivételek kezelésére, míg a OnPost a POST-kivételek kezelésére használható.
  • MVC esetén http-igeattribútumokat alkalmazhat több műveletre. Például használja a [HttpGet]-t a GET-kivételek kezelésére, és a [HttpPost]-t a POST-kivételek kezelésére.

Ha engedélyezni szeretné, hogy a hitelesítés nélküli felhasználók megtekinthessék az egyéni hibakezelő lapot, győződjön meg arról, hogy támogatja a névtelen hozzáférést.

A kivétel elérése

A kivételt és az eredeti kérés elérési útját hibakezelőben a IExceptionHandlerPathFeature használatával érheti el. Az alábbi példa IExceptionHandlerPathFeature a kidobott kivételről nyújt további információt:

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

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

Kivételkezelő lambda

A egyéni kivételkezelő lap alternatívája, ha egy lambdát biztosít a UseExceptionHandler számára. A lambda használata lehetővé teszi a hiba elérését a válasz visszaadása előtt.

A következő kód egy lambdát használ a kivételkezeléshez:

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

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

IExceptionHandler

IExceptionHandler egy olyan felület, amely visszahívást biztosít a fejlesztőnek egy központi helyen lévő ismert kivételek kezelésére.

IExceptionHandler az implementációk regisztrálásához a IServiceCollection.AddExceptionHandler<T> használják: [IServiceCollection.AddExceptionHandler<T>]. Az IExceptionHandler példány élettartama singleton. Több implementáció is hozzáadható, és a rendszer a regisztrált sorrendben hívja meg őket.

Ha egy kivételkezelő kezel egy kérést, true adhat vissza a feldolgozás leállításához. Ha egy kivételt egyetlen kivételkezelő sem kezel, akkor a vezérlés visszaesik a köztes szoftver alapértelmezett viselkedésére és beállításaira. Különböző metrikákat és naplókat bocsát ki a rendszer a kezelt és kezeletlen kivételek esetén.

Az alábbi példa egy implementációt IExceptionHandler mutat be:

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

Az alábbi példa bemutatja, hogyan regisztrálhat implementációt IExceptionHandler függőséginjektáláshoz:

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

Ha az előző kód a fejlesztési környezetben fut:

  • A CustomExceptionHandler-t először a kivétel kezelésére hívják meg.
  • A kivétel naplózása után a TryHandleException metódus visszatér false, így megjelenik a fejlesztői kivétel oldal.

Más környezetekben:

  • Először a CustomExceptionHandler hívódik meg a kivétel kezelésére.
  • A kivétel naplózása után a TryHandleException metódus visszatér, így megjelenik falseaz /Error oldal .

StatusCodePages használata

Alapértelmezés szerint egy ASP.NET Core-alkalmazás nem biztosít állapotkódlapot a HTTP-hibaállapot-kódokhoz, például 404 – Nem található. Amikor az alkalmazás beállít egy HTTP 400-599 hibaállapot-kódot, amely nem tartalmaz törzset, az állapotkódot és egy üres választörzset ad vissza. Ha engedélyezni szeretné az alapértelmezett csak szöveges kezelőket a gyakori hibaállapot-kódokhoz, hívja meg a következőt UseStatusCodePagesProgram.cs:

var app = builder.Build();

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

app.UseStatusCodePages();

Hívás UseStatusCodePages a köztes szoftverrel kapcsolatos kérések kezelése előtt. Hívja meg például a UseStatusCodePages a statikus fájl közbenső réteg és az végpontok közbenső réteg előtt.

Ha UseStatusCodePages nincs használatban, a végpont nélküli URL-címre való navigálás egy böngészőfüggő hibaüzenetet ad vissza, amely jelzi, hogy a végpont nem található. Amikor UseStatusCodePages a rendszer meghívja, a böngésző a következő választ adja vissza:

Status Code: 404; Not Found

UseStatusCodePages általában nem használják éles környezetben, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

Megjegyzés:

Az állapotkódlapok köztes szoftverei nem észlelnek kivételeket. Egyéni hibakezelő lap megadásához használja a kivételkezelő lapot.

UseStatusCodePages formátum szöveggel

A válasz tartalomtípusának és szövegének testreszabásához használja a UseStatusCodePages metódusut, amely tartalomtípust és formázási sztringet veszi figyelembe.

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

Az előző kódban a {0} a hibakód helyőrzőjeként szerepel.

UseStatusCodePages a formátumsztringet általában nem használják éles környezetben, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

UseStatusCodePages lambda segítségével

Egyéni hibakezelési és válaszírási kód megadásához használja a lambda kifejezést fogadó UseStatusCodePages túlterhelést:

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 a lambdát általában nem használják éles környezetben, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

Használja az állapotkód-oldalakat átirányításokkal

A UseStatusCodePagesWithRedirects bővítmény metódusa:

  • 302 – Talált állapotkódot küld az ügyfélnek.
  • Átirányítja az ügyfelet az URL-sablonban megadott hibakezelési végpontra. A hibakezelő végpont általában hibainformációkat jelenít meg, és HTTP 200-et ad vissza.
var app = builder.Build();

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

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

Az URL-sablon tartalmazhat helyőrzőt {0} az állapotkódhoz, ahogyan az az előző kódban is látható. Ha az URL-sablon ~ (tilde) karakterrel kezdődik, akkor az ~ helyére az alkalmazás PathBase lép. Amikor végpontot ad meg az alkalmazásban, hozzon létre egy MVC-nézetet vagy Razor lapot a végponthoz.

Ezt a módszert gyakran használják az alkalmazás esetében:

  • Az ügyfelet egy másik végpontra kell átirányítani, általában olyan esetekben, amikor egy másik alkalmazás feldolgozza a hibát. Webalkalmazások esetén az ügyfél böngészőcímsávja az átirányított végpontot tükrözi.
  • Nem szabad megőrizni és visszaadni az eredeti állapotkódot a kezdeti átirányítási válaszban.

UseStatusCodePagesWithReExecute

A UseStatusCodePagesWithReExecute bővítmény metódusa:

  • A válasz törzsét úgy hozza létre, hogy egy másik elérési út használatával újra végrehajtja a kérelemfolyamatot.
  • Nem módosítja az állapotkódot a folyamat ismételt végrehajtása előtt vagy után.

Az új folyamat végrehajtása megváltoztathatja a válasz állapotkódját, mivel az új folyamat teljes mértékben szabályozza az állapotkódot. Ha az új folyamat nem módosítja az állapotkódot, a rendszer elküldi az eredeti állapotkódot az ügyfélnek.

var app = builder.Build();

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

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

Ha az alkalmazásban egy végpont van megadva, hozzon létre egy MVC-nézetet vagy Razor lapot a végponthoz.

Ezt a módszert gyakran használják, amikor az alkalmazásnak a következőt kell használnia:

  • A kérés feldolgozása egy másik végpontra való átirányítás nélkül. Webalkalmazások esetén az ügyfél böngészőcímsávja az eredetileg kért végpontot tükrözi.
  • Őrizze meg és adja vissza az eredeti állapotkódot a válaszsal együtt.

Az URL-sablonnak kezdődnie kell, / és tartalmaznia kell egy helyőrzőt {0} az állapotkódhoz. Ha az állapotkódot lekérdezési sztring paraméterként szeretné átadni, adjon át egy második argumentumot a következőbe UseStatusCodePagesWithReExecute: Például:

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

A hibát feldolgozó végpont lekérheti a hibát létrehozó eredeti URL-címet, ahogyan az az alábbi példában látható:

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

    public string? OriginalPathAndQuery { get; set; }

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

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

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

        }
    }
}

Mivel ez a köztes szoftver újra tudja futtatni a kérési folyamatot:

  • A middlewareeket kezelni kell újrabehatolással ugyanazon kérés esetén. Ez általában azt jelenti, hogy vagy megtisztítják az állapotukat a _next hívása után, vagy a feldolgozásukat a HttpContext gyorsítótárazásán keresztül elvégezve elkerülik az újbóli feldolgozást. A kérelem törzsének kezelésekor ez az eredmények pufferelését vagy gyorsítótárazását jelenti, az űrlap olvasójához hasonlóan.
  • A hatókörön belüli szolgáltatások változatlanok maradnak.

Állapotkódlapok letiltása

MVC-vezérlő vagy műveleti metódus állapotkódlapjainak letiltásához használja a [SkipStatusCodePages] attribútumot.

Ha le szeretné tiltani az állapotkódlapokat a Razor Pages-kezelő metódusban vagy egy MVC-vezérlőben megadott kérésekhez, használja a következőt IStatusCodePagesFeature:

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

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

Kivételkezelési kód

A kivételkezelő lapok kódjai kivételeket is okozhatnak. A gyártási hibaoldalakat alaposan tesztelni kell, és fokozottan ügyelni kell arra, hogy elkerüljék a saját maguk által okozott kivételeket.

Válaszfejlécek

A válasz fejléceinek elküldése után:

  • Az alkalmazás nem tudja módosítani a válasz állapotkódját.
  • A kivételoldalak vagy -kezelők nem futtathatók. A választ be kell fejezni, vagy megszakadt a kapcsolat.

Kiszolgálói kivételkezelés

Az alkalmazások kivételkezelési logikája mellett a HTTP-kiszolgáló implementációja is képes kezelni néhány kivételt. Ha a kiszolgáló kivételt kap a válaszfejlécek elküldése előtt, a kiszolgáló választörzs nélkül küld 500 - Internal Server Error választ. Ha a kiszolgáló kivételt kap a válaszfejlécek elküldése után, a kiszolgáló bezárja a kapcsolatot. Az alkalmazás által nem kezelt kérelmeket a kiszolgáló kezeli. Minden kivételt, amely akkor fordul elő, amikor a kiszolgáló kezeli a kérést, a kiszolgáló kivételkezelése kezeli. Az alkalmazás egyéni hibaoldalai, a köztes szoftver kivételkezelése és a szűrők nem befolyásolják ezt a viselkedést.

Indítási kivétel kezelése

Csak az üzemeltetési réteg képes kezelni az alkalmazás indításakor előforduló kivételeket. A gazdagép konfigurálható az indítási hibák rögzítésére és a részletes hibák rögzítésére.

Az üzemeltetési réteg csak akkor jeleníthet meg hibalapot egy rögzített indítási hibához, ha a hiba a gazdagép címe/portkötése után következik be. Ha a kötés sikertelen:

  • Az üzemeltetési réteg kritikus kivételt naplóz.
  • A dotnet-folyamat összeomlik.
  • Nem jelenik meg hibaoldal, amikor a HTTP-kiszolgáló Kestrel.

Ha IIS-en (vagy Azure App Service-en) vagy IIS Expressen fut, az ASP.NET core modul502.5-ös folyamathibát ad vissza, ha a folyamat nem indítható el. További információ: ASP.NET Core hibaelhárítása az Azure App Service-ben és az IIS-ben.

Adatbázis hibaoldala

Az adatbázis fejlesztői lapjának kivételszűrője AddDatabaseDeveloperPageExceptionFilter rögzíti az adatbázissal kapcsolatos kivételeket, amelyek az Entity Framework Core migrálásával oldhatók meg. Ha ezek a kivételek előfordulnak, a rendszer HTML-választ hoz létre a probléma megoldásához szükséges lehetséges műveletek részleteivel. Ez a lap csak a fejlesztési környezetben engedélyezett. A következő kód hozzáadja az Adatbázis fejlesztői oldal kivételszűrőjét:

var builder = WebApplication.CreateBuilder(args);

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

Kivételszűrők

Az MVC-alkalmazásokban a kivételszűrők globálisan, vezérlőnként vagy műveletenként konfigurálhatók. A Razor Pages-alkalmazásokban globálisan vagy oldalmodellenként konfigurálhatók. Ezek a szűrők kezelik a vezérlőművelet vagy egy másik szűrő végrehajtása során előforduló nem kezelt kivételeket. További információ: Szűrők a ASP.NET Core-ban.

A kivételszűrők hasznosak az MVC-műveletekben előforduló kivételek elfogására, de nem olyan rugalmasak, mint a beépített kivételkezelő middleware, UseExceptionHandler. Javasoljuk, hogy használja UseExceptionHandler, kivéve, ha a hibakezelést a választott MVC-művelet alapján eltérő módon kell elvégeznie.

Modellállapot-hibák

A modellállapot-hibák kezelésével kapcsolatos információkért tekintse meg a modellkötést és a modellérvényesítést ismertető témakört.

Probléma részletei

A probléma részletei nem az egyetlen válaszformátum a HTTP API-hibák leírásához, azonban gyakran használják őket a HTTP API-k hibáinak jelentésére.

A probléma részletei szolgáltatás implementálja a felületet, amely támogatja a IProblemDetailsService probléma részleteinek létrehozását a ASP.NET Core-ban. A AddProblemDetails(IServiceCollection) bővítménymetódus IServiceCollection regisztrálja az alapértelmezett IProblemDetailsService implementációt.

Az ASP.NET Core-alkalmazásokban a következő köztes szoftver HTTP-válaszokat generál a probléma részleteivel, amikor meghívják AddProblemDetails, kivéve, ha a Accept kérelem HTTP-fejléce nem tartalmazza a regisztrált IProblemDetailsWriter által támogatott tartalomtípusok egyikét (alapértelmezett: application/json).

Az alábbi kód úgy konfigurálja az alkalmazást, hogy probléma részleteit adja meg az összes olyan HTTP-ügyfél- és kiszolgálóhiba-válaszhoz, amely még nem rendelkezik törzstartalommal:

builder.Services.AddProblemDetails();

var app = builder.Build();        

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

app.UseStatusCodePages();

A következő szakasz bemutatja, hogyan szabhatja testre a probléma részleteit tartalmazó választörzset.

A probléma részleteinek testreszabása

Az ProblemDetails automatikus létrehozása az alábbi lehetőségek bármelyikével testre szabható.

  1. Használja a ProblemDetailsOptions.CustomizeProblemDetails-t
  2. Egyéni IProblemDetailsWriter használata
  3. IProblemDetailsService Köztes szoftver meghívása

CustomizeProblemDetails művelet

A létrehozott probléma részletei testre szabhatók a használatával CustomizeProblemDetails, és a testreszabások az automatikusan létrehozott probléma részleteire lesznek alkalmazva.

A következő kód ProblemDetailsOptions használ CustomizeProblemDetails beállításához.

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

Például egy végpont eredménye a következő probléma részleteit tartalmazó választörzset ad vissza:

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

Egyedi IProblemDetailsWriter

Egy IProblemDetailsWriter implementáció hozható létre speciális testreszabásokhoz.

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

Megjegyzés: Ha egyéni IProblemDetailsWriter-t használ, akkor az egyéni IProblemDetailsWriter-t regisztrálnia kell a AddRazorPages, AddControllers, AddControllersWithViews vagy AddMvc hívása előtt.

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

A Middleware-ből származó probléma részletei

Egy alternatív módja a ProblemDetailsOptions és a CustomizeProblemDetails használatának, hogy a ProblemDetails-t köztes szoftverben állítjuk be. A probléma részleteire adott választ a következő hívással IProblemDetailsService.WriteAsynclehet megírni:

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

Az előző kódban a minimális API-végpontok /divide , és /squareroot a várt egyéni hibaválaszt adja vissza a hibabemenethez.

Az API-vezérlő végpontjai az alapértelmezett hibaválaszt adják vissza a hibabemenetkor, nem pedig az egyéni problémamegoldást. A rendszer az alapértelmezett problémamegoldást adja vissza, mert az API-vezérlő a válaszstreambe írt, a hibaállapotkódokkal kapcsolatos problémák részleteit, mielőtt IProblemDetailsService.WriteAsync meghívták, és a válasz nem lesz újra megírva.

Az alábbi ValuesController visszaad egy értéket BadRequestResult, amely a válaszfolyamba ír, és így megakadályozza az egyéni probléma válaszának visszaadását.

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

}

A következő Values3Controller visszaadja a ControllerBase.Problem értéket, így a várt egyéni probléma eredményét kapjuk meg.

[Route("api/[controller]/[action]")]
[ApiController]
public class Values3Controller : ControllerBase
{
    // /api/values3/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Divison by zero is not defined.",
                type: "https://en.wikipedia.org/wiki/Division_by_zero",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values3/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return Problem(
                title: "Bad Input",
                detail: "Negative or complex numbers are not valid input.",
                type: "https://en.wikipedia.org/wiki/Square_root",
                statusCode: StatusCodes.Status400BadRequest
                );
        }

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

}

ProblemDetails adatcsomag létrehozása kivételekhez

Fontolja meg a következő alkalmazást:

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

Nem fejlesztési környezetekben, ha kivétel történik, a következő egy szabványos ProblemDetails-válasz , amelyet a rendszer visszaad az ügyfélnek:

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

A legtöbb alkalmazás esetében csak az előző kód szükséges a kivételekhez. Az alábbi szakasz azonban bemutatja, hogyan kaphat részletesebb választ a problémákra.

Egy másik megoldás az egyéni kivételkezelő lap helyett, ha lambdát biztosítunk a UseExceptionHandler-hez. A lambda használata lehetővé teszi a hiba elérését és a probléma részleteit tartalmazó válasz megírását a következővel 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();

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

A probléma részleteinek létrehozására másik módszer a külső NuGet-csomag, a Hellang.Middleware.ProblemDetails használata, amely a kivételek és az ügyfélhibák leképezésére használható a probléma részleteire.

További erőforrások

Készítette: Tom Dykstra

Ez a cikk az ASP.NET Core-webalkalmazásokban előforduló hibák kezelésének gyakori megközelítéseit ismerteti. Lásd még: ASP.NET vezérlőalapú webes API-k hibáinak kezelése és a hibák kezelése minimális API-kban.

Fejlesztői kivételoldal

A Fejlesztői kivétel lap részletes információkat jelenít meg a nem kezelt kérelmek kivételeiről. ASP.NET Core-alkalmazások alapértelmezés szerint engedélyezik a fejlesztői kivételoldalt, ha mindkettő:

A fejlesztői kivételoldal a köztes szoftver folyamatának korai szakaszában fut, hogy a következő köztes szoftverben megjelenő kezeletlen kivételeket észlelhesse.

A részletes kivételadatokat nem szabad nyilvánosan megjeleníteni, amikor az alkalmazás éles környezetben fut. További információ a környezetek konfigurálásáról: Több környezet használata a ASP.NET Core-ban.

A Fejlesztői kivétel lap a következő információkat tartalmazhatja a kivételről és a kérésről:

  • Verem nyomkövetése
  • Lekérdezési sztringparaméterek, ha vannak ilyenek
  • Cookie-k, ha vannak ilyenek
  • Fejlécek

A fejlesztői kivételoldal nem garantáltan biztosít semmilyen információt. A teljes hibainformációk naplózása .

Kivételkezelő lap

Ha egyéni hibakezelő lapot szeretne konfigurálni az éles környezethez, hívja meg a következőt UseExceptionHandler: . Ez a kivételkezelés köztes szoftver:

  • Kezeli és naplózza a kezeletlen kivételeket.
  • A kérést újra végrehajtja egy másik folyamatban a megadott elérési út használatával. A rendszer nem hajtja végre újra a kérést, ha a válasz elindult. A sablon által létrehozott kód újra végrehajtja a kérést az /Error elérési út használatával.

Figyelmeztetés

Ha az alternatív csővezeték kivételt dob, a kivételkezelő köztes szoftver újradobja az eredeti kivételt.

Mivel ez a köztes szoftver újra tudja futtatni a kérési folyamatot:

  • A köztes szoftvereknek kezelniük kell az újbóli belépést ugyanazzal a kéréssel. Ez általában azt jelenti, hogy vagy megtisztítják az állapotukat a _next hívása után, vagy a HttpContext gyorsítótárazzák a feldolgozásukat, hogy elkerüljék annak újbóli elvégzését. A kérelem törzsének kezelésekor ez az eredmények pufferelését vagy gyorsítótárazását jelenti, hasonlóan az űrlapolvasóhoz.
  • UseExceptionHandler(IApplicationBuilder, String) A sablonokban használt túlterhelés esetén csak a kérelem útvonala módosul, és az útvonaladatok törlődnek. A kérelemadatok, például a fejlécek, a metódusok és az elemek mind újra felhasználhatók as-is.
  • A hatókörön belüli szolgáltatások változatlanok maradnak.

Az alábbi példában a UseExceptionHandler middleware hozzáadja a kivételkezelést nem fejlesztési környezetekben.

var app = builder.Build();

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

A Razor Lapok alkalmazássablon egy Hibalapot (.cshtml) és PageModel osztályt (ErrorModel) biztosít a Lapok mappában. MVC-alkalmazások esetén a projektsablon tartalmaz egy Error műveletmetódust és egy hibanézetet a Home vezérlőhöz.

A köztes szoftver kivételkezelése újra végrehajtja a kérést az eredeti HTTP-módszerrel. Ha egy hibakezelő végpont a HTTP-metódusok egy meghatározott halmazára korlátozódik, az csak az adott HTTP-metódusokhoz fut. Az attribútumot használó [HttpGet] MVC-vezérlőművelet például csak GET-kérelmek esetén fut. Annak érdekében, hogy minden kérés elérje az egyéni hibakezelő lapot, ne korlátozza azokat a HTTP-metódusok meghatározott készletére.

A kivételek eltérő kezelése az eredeti HTTP-módszer alapján:

  • A Razor Pageshez hozzon létre több kezelő metódust. Például, a OnGet a GET-kivételek kezelésére, a OnPost pedig a POST-kivételek kezelésére használható.
  • MVC esetén http-igeattribútumokat alkalmazhat több műveletre. Például a [HttpGet] a GET-kivételek kezelésére és a [HttpPost] a POST-kivételek kezelésére használható.

Ha engedélyezni szeretné, hogy a hitelesítés nélküli felhasználók megtekinthessék az egyéni hibakezelő lapot, győződjön meg arról, hogy támogatja a névtelen hozzáférést.

A kivétel elérése

Használja a IExceptionHandlerPathFeature-t a kivétel és az eredeti kérelem elérési útjának eléréséhez egy hibakezelőben. Az alábbi példa IExceptionHandlerPathFeature a kidobott kivételről nyújt további információt:

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

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

Kivételkezelő lambda

Egy alternatíva az egyéni kivételkezelő lap helyett, ha egy lambda-t biztosít UseExceptionHandler. A lambda használata lehetővé teszi a hiba elérését a válasz visszaadása előtt.

A következő kód egy lambdát használ a kivételkezeléshez:

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

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

UseStatusCodePages

Alapértelmezés szerint egy ASP.NET Core-alkalmazás nem biztosít állapotkódlapot a HTTP-hibaállapot-kódokhoz, például 404 – Nem található. Amikor az alkalmazás beállít egy HTTP 400-599 hibaállapot-kódot, amely nem tartalmaz törzset, az állapotkódot és egy üres választörzset ad vissza. Ha engedélyezni szeretné az alapértelmezett csak szöveges kezelőket a gyakori hibaállapot-kódokhoz, hívja meg a következőt UseStatusCodePagesProgram.cs:

var app = builder.Build();

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

app.UseStatusCodePages();

Hívja a UseStatusCodePages-t, mielőtt a köztes szoftver kéréseinek feldolgozását megkezdené. Például a UseStatusCodePages-t hívja meg a Statikus fájl Middleware és az Endpoints Middleware előtt.

Ha UseStatusCodePages nincs használatban, a végpont nélküli URL-címre való navigálás egy böngészőfüggő hibaüzenetet ad vissza, amely jelzi, hogy a végpont nem található. Amikor UseStatusCodePages a rendszer meghívja, a böngésző a következő választ adja vissza:

Status Code: 404; Not Found

UseStatusCodePages általában nem használják éles környezetben, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

Megjegyzés:

Az állapotkódlapok köztes szoftverei nem észlelnek kivételeket. Egyéni hibakezelő lap megadásához használja a kivételkezelő lapot.

UseStatusCodePages formátumsztring használatával

A válasz tartalomtípusának és szövegének testreszabásához használja a UseStatusCodePages túlterhelést, amely tartalomtípust és formátum sztringet vesz fel.

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

Az előző kódban a {0} a hibakód helyőrzője.

UseStatusCodePages a formátumsztringet általában nem használják éles környezetben, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

A StatusCode oldalakat lambda funkcióval használja

Az egyéni hibakezelési és válaszírási kód megadásához használja a UseStatusCodePages azon túlterhelését, amely lambda kifejezést fogad:

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 a lambda kifejezést általában nem használják termelési környezetben, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

UseStatusCodePagesWithRedirects

A UseStatusCodePagesWithRedirects bővítmény metódusa:

  • 302 – Talált állapotkódot küld az ügyfélnek.
  • Átirányítja az ügyfelet az URL-sablonban megadott hibakezelési végpontra. A hibakezelő végpont általában hibainformációkat jelenít meg, és HTTP 200-et ad vissza.
var app = builder.Build();

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

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

Az URL-sablon tartalmazhat helyőrzőt {0} az állapotkódhoz, ahogyan az az előző kódban is látható. Ha az URL-sablon ~ (tilde) jellel kezdődik, az ~ részt az alkalmazás PathBase-ra cseréli. Amikor végpontot ad meg az alkalmazásban, hozzon létre egy MVC-nézetet vagy Razor lapot a végponthoz.

Ezt a módszert gyakran használják az alkalmazás esetében:

  • Az ügyfelet egy másik végpontra kell átirányítani, általában olyan esetekben, amikor egy másik alkalmazás feldolgozza a hibát. Webalkalmazások esetén az ügyfél böngészőcímsávja az átirányított végpontot tükrözi.
  • Nem szabad megőriznie és visszaadnia az eredeti állapotkódot a kezdő átirányítási válaszban.

Használja az állapotkódos oldalakat újra végrehajtással

A UseStatusCodePagesWithReExecute bővítmény metódusa:

  • A válasz törzsét úgy hozza létre, hogy egy másik elérési út használatával újra végrehajtja a kérelemfolyamatot.
  • Nem módosítja az állapotkódot a folyamat ismételt végrehajtása előtt vagy után.

Az új folyamat végrehajtása megváltoztathatja a válasz állapotkódját, mivel az új folyamat teljes mértékben szabályozza az állapotkódot. Ha az új folyamat nem módosítja az állapotkódot, a rendszer elküldi az eredeti állapotkódot az ügyfélnek.

var app = builder.Build();

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

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

Ha az alkalmazásban egy végpont van megadva, hozzon létre egy MVC-nézetet vagy Razor lapot a végponthoz.

Ezt a módszert gyakran használják, amikor az alkalmazásnak a következőt kell használnia:

  • A kérés feldolgozása egy másik végpontra való átirányítás nélkül. Webalkalmazások esetén az ügyfél böngészőcímsávja az eredetileg kért végpontot tükrözi.
  • Őrizze meg és adja vissza az eredeti állapotkódot a válaszsal együtt.

Az URL-sablonnak kezdődnie kell, / és tartalmaznia kell egy helyőrzőt {0} az állapotkódhoz. Ha az állapotkódot lekérdezési sztring paraméterként szeretné átadni, adjon át egy második argumentumot a következőbe UseStatusCodePagesWithReExecute: Például:

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

A hibát feldolgozó végpont lekérheti a hibát létrehozó eredeti URL-címet, ahogyan az az alábbi példában látható:

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

    public string? OriginalPathAndQuery { get; set; }

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

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

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

        }
    }
}

Mivel ez a köztes szoftver újra tudja futtatni a kérési folyamatot:

  • Az middleware-eknek kezelniük kell az újrabelépést ugyanazon kéréssel. Ez általában azt jelenti, hogy vagy a _next hívása után megtisztítják az állapotukat, vagy a HttpContext-en végzett feldolgozásukat gyorsítótárazzák, hogy elkerüljék ennek újbóli elvégzését. A kérelemtörzs kezelésekor ez az eredmények pufferelését vagy gyorsítótárazását jelenti, mint például az Űrlapolvasónál.
  • A hatókörön belüli szolgáltatások változatlanok maradnak.

Állapotkódlapok letiltása

MVC-vezérlő vagy műveleti metódus állapotkódlapjainak letiltásához használja a [SkipStatusCodePages] attribútumot.

Ha le szeretné tiltani az állapotkódlapokat a Razor Pages-kezelő metódusban vagy egy MVC-vezérlőben megadott kérésekhez, használja a következőt IStatusCodePagesFeature:

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

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

Kivételkezelési kód

A kivételkezelő lapok kódjai kivételeket is okozhatnak. A gyártási fázisban lévő hibaoldalakat alaposan tesztelni kell, és fokozottan ügyelni kell arra, hogy elkerüljük az általuk okozott kivételeket.

Válaszfejlécek

A válasz fejléceinek elküldése után:

  • Az alkalmazás nem tudja módosítani a válasz állapotkódját.
  • A kivételoldalak vagy -kezelők nem futtathatók. A választ be kell fejezni, vagy megszakadt a kapcsolat.

Kiszolgálói kivételkezelés

Az alkalmazások kivételkezelési logikája mellett a HTTP-kiszolgáló implementációja is képes kezelni néhány kivételt. Ha a kiszolgáló kivételt kap a válaszfejlécek elküldése előtt, a kiszolgáló választörzs nélkül küld 500 - Internal Server Error választ. Ha a kiszolgáló kivételt kap a válaszfejlécek elküldése után, a kiszolgáló bezárja a kapcsolatot. Az alkalmazás által nem kezelt kérelmeket a kiszolgáló kezeli. Minden kivételt, amely akkor fordul elő, amikor a kiszolgáló kezeli a kérést, a kiszolgáló kivételkezelése kezeli. Az alkalmazás egyéni hibaoldalai, a köztes szoftver kivételkezelése és a szűrők nem befolyásolják ezt a viselkedést.

Indítási kivétel kezelése

Csak az üzemeltetési réteg képes kezelni az alkalmazás indításakor előforduló kivételeket. A gazdagép beállítható, hogy indítási hibákat rögzítsen és részletes hibákat rögzítsen.

A kiszolgálói réteg csak akkor jeleníthet meg hibaoldalt egy elfogott indítási hiba esetén, ha a hiba a gazdagép címének/portjának kötése után következik be. Ha a kötés sikertelen:

  • Az üzemeltetési réteg kritikus kivételt naplóz.
  • A dotnet-folyamat összeomlik.
  • Nem jelenik meg hibalap, amikor az HTTP-kiszolgáló Kestrel.

Ha IIS-en (vagy Azure App Service-en) vagy IIS Expressen fut, az ASP.NET core modul502.5-ös folyamathibát ad vissza, ha a folyamat nem indítható el. További információ: ASP.NET Core hibaelhárítása az Azure App Service-ben és az IIS-ben.

Adatbázis hibaoldala

Az adatbázis fejlesztői lapjának kivételszűrője AddDatabaseDeveloperPageExceptionFilter rögzíti az adatbázissal kapcsolatos kivételeket, amelyek az Entity Framework Core migrálásával oldhatók meg. Ha ezek a kivételek előfordulnak, a rendszer HTML-választ hoz létre a probléma megoldásához szükséges lehetséges műveletek részleteivel. Ez a lap csak a fejlesztési környezetben engedélyezett. A következő kód hozzáadja az Adatbázis fejlesztői oldal kivételszűrőjét:

var builder = WebApplication.CreateBuilder(args);

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

Kivételszűrők

Az MVC-alkalmazásokban a kivételszűrők globálisan, vezérlőnként vagy műveletenként konfigurálhatók. A Razor Pages-alkalmazásokban globálisan vagy oldalmodellenként konfigurálhatók. Ezek a szűrők kezelik a vezérlőművelet vagy egy másik szűrő végrehajtása során előforduló nem kezelt kivételeket. További információ: Szűrők a ASP.NET Core-ban.

A kivételszűrők hasznosak az MVC-műveletek során előforduló kivételek elkapásához, de nem olyan rugalmasak, mint a beépített kivételkezelő köztes szoftver. UseExceptionHandler Javasoljuk, hogy használja UseExceptionHandler, kivéve, ha a hibakezelést a választott MVC-művelet alapján eltérő módon kell elvégeznie.

Modellállapot-hibák

A modellállapot-hibák kezelésével kapcsolatos információkért tekintse meg a modellkötést és a modellérvényesítést ismertető témakört.

Probléma részletei

A probléma részletei nem az egyetlen válaszformátum a HTTP API-hibák leírásához, azonban gyakran használják őket a HTTP API-k hibáinak jelentésére.

A probléma részletei szolgáltatás implementálja a felületet, amely támogatja a IProblemDetailsService probléma részleteinek létrehozását a ASP.NET Core-ban. A AddProblemDetails(IServiceCollection) bővítménymetódus IServiceCollection regisztrálja az alapértelmezett IProblemDetailsService implementációt.

Az ASP.NET Core-alkalmazásokban a következő köztes szoftver generál HTTP-válaszokat, amelyek problémarészleteket tartalmaznak, amikor a AddProblemDetails hívást kezdeményezik, kivéve, ha a Accept kérelmi HTTP-fejlécek nem tartalmazzák a regisztrált IProblemDetailsWriter által támogatott tartalomtípusok egyikét (alapértelmezett: application/json).

Az alábbi kód úgy konfigurálja az alkalmazást, hogy probléma részleteit adja meg az összes olyan HTTP-ügyfél- és kiszolgálóhiba-válaszhoz, amely még nem rendelkezik törzstartalommal:

builder.Services.AddProblemDetails();

var app = builder.Build();        

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

app.UseStatusCodePages();

A következő szakasz bemutatja, hogyan szabhatja testre a probléma részleteit tartalmazó választörzset.

A probléma részleteinek testreszabása

Az ProblemDetails automatikus létrehozása az alábbi lehetőségek bármelyikével testre szabható.

  1. Használja a ProblemDetailsOptions.CustomizeProblemDetails-t
  2. Egyéni IProblemDetailsWriter használata
  3. IProblemDetailsService Hívja meg egy köztes szoftverben

CustomizeProblemDetails művelet

A létrehozott probléma részletei testre szabhatók a használatával CustomizeProblemDetails, és a testreszabások az automatikusan létrehozott probléma részleteire lesznek alkalmazva.

A következő kód ProblemDetailsOptions használja a CustomizeProblemDetails beállításához:

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

Például egy HTTP Status 400 Bad Request végpont eredménye a következő probléma részleteit tartalmazó választörzset hoz létre.

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

Egyéni IProblemDetailsWriter

Speciális testreszabásokhoz implementáció hozható létre IProblemDetailsWriter.

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

Jegyzet: Egyéni IProblemDetailsWriter használata esetén az egyéni IProblemDetailsWriter-et előbb regisztrálni kell, mielőtt a AddRazorPages, AddControllers, AddControllersWithViews vagy AddMvc hívás történik.

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

A Middleware-ből származó probléma részletei

Alternatív megközelítés abban, hogy a ProblemDetailsOptions-t használjuk CustomizeProblemDetails-vel, az, hogy a ProblemDetails-t middleware-ben állítjuk be. A probléma részleteire adott választ a következő hívással IProblemDetailsService.WriteAsynclehet megírni:

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

Az előző kódban a minimális API-végpontok /divide , és /squareroot a várt egyéni hibaválaszt adja vissza a hibabemenethez.

Az API-vezérlő végpontjai az alapértelmezett hibaválaszt adják vissza a hibabemenetkor, nem pedig az egyéni problémamegoldást. A rendszer az alapértelmezett problémaválaszt adja vissza, mert az API-vezérlő a válaszstreambe írt a hibaállapotkódokkal kapcsolatos problémák részleteiről azelőtt, hogy a IProblemDetailsService.WriteAsync meghívásra kerülne, és a válasz nem lesz újra megírva.

Az alábbi ValuesController eredmény BadRequestResulta válaszfolyamba ír, és így megakadályozza az egyéni probléma válaszának visszaadását.

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

}

A következő Values3Controller így adja vissza a ControllerBase.Problem eredményt, tehát a várt egyéni probléma eredménye is visszaadódik.

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

}

Hozzon létre egy ProblemDetails-payloadot kivételekhez.

Fontolja meg a következő alkalmazást:

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

Nem fejlesztési környezetekben, ha kivétel történik, a következő egy szabványos ProblemDetails-válasz , amelyet a rendszer visszaad az ügyfélnek:

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

A legtöbb alkalmazás esetében csak az előző kód szükséges a kivételekhez. Az alábbi szakasz azonban bemutatja, hogyan kaphat részletesebb választ a problémákra.

Az egyéni kivételkezelő oldal alternatívája, ha egy lambdát ad meg UseExceptionHandler-nek. A lambda használata lehetővé teszi a hiba elérését és a probléma részleteit tartalmazó válasz megírását a következővel 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();

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

A probléma részleteinek létrehozására másik módszer a külső NuGet-csomag, a Hellang.Middleware.ProblemDetails használata, amely a kivételek és az ügyfélhibák leképezésére használható a probléma részleteire.

További erőforrások

Készítette: Tom Dykstra

Ez a cikk az ASP.NET Core-webalkalmazásokban előforduló hibák kezelésének gyakori megközelítéseit ismerteti. Lásd: Hibák kezelése ASP.NET Core vezérlőalapú webes API-kban a webes API-khoz.

Fejlesztői kivételoldal

A Fejlesztői kivétel lap részletes információkat jelenít meg a nem kezelt kérelmek kivételeiről. ASP.NET Core-alkalmazások alapértelmezés szerint engedélyezik a fejlesztői kivételoldalt, ha mindkettő:

A fejlesztői kivételoldal a köztes szoftver folyamatának korai szakaszában fut, hogy a következő köztes szoftverben megjelenő kezeletlen kivételeket észlelhesse.

A részletes kivételadatokat nem szabad nyilvánosan megjeleníteni, amikor az alkalmazás éles környezetben fut. További információ a környezetek konfigurálásáról: Több környezet használata a ASP.NET Core-ban.

A Fejlesztői kivétel lap a következő információkat tartalmazhatja a kivételről és a kérésről:

  • Verem nyomkövetése
  • Lekérdezési sztringparaméterek, ha vannak ilyenek
  • Cookie-k, ha vannak ilyenek
  • Fejlécek

A fejlesztői kivételoldal nem garantáltan biztosít semmilyen információt. Használja a naplózást a teljes hibainformációért.

Kivételkezelő lap

Ha egyéni hibakezelő lapot szeretne konfigurálni az éles környezethez, hívja meg a következőt UseExceptionHandler: . Ez a kivételkezelő köztes szoftver:

  • Elfogja és naplózza a kezeletlen kivételeket.
  • A kérést újra végrehajtja egy másik folyamatban a megadott elérési út használatával. A rendszer nem hajtja végre újra a kérést, ha a válasz elindult. A sablon által létrehozott kód újra végrehajtja a kérést az /Error elérési út használatával.

Figyelmeztetés

Ha az alternatív csatorna saját kivételt dob, a Kivételkezelő közvetítő szoftver újra dobja az eredeti kivételt.

Az alábbi példában a UseExceptionHandler a kivételkezelő köztes réteget nem fejlesztési környezetekben hozzáadja.

var app = builder.Build();

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

A Razor Lapok alkalmazássablon egy Hibalapot (.cshtml) és PageModel osztályt (ErrorModel) biztosít a Lapok mappában. MVC-alkalmazások esetén a projektsablon tartalmaz egy Error műveletmetódust és egy hibanézetet a Home vezérlőhöz.

A köztes szoftver kivételkezelése újra végrehajtja a kérést az eredeti HTTP-módszerrel. Ha egy hibakezelő végpont a HTTP-metódusok egy meghatározott halmazára korlátozódik, az csak az adott HTTP-metódusokhoz fut. Az attribútumot használó [HttpGet] MVC-vezérlőművelet például csak GET-kérelmek esetén fut. Annak érdekében, hogy minden kérés elérje az egyéni hibakezelő lapot, ne korlátozza azokat a HTTP-metódusok meghatározott készletére.

A kivételek eltérő kezelése az eredeti HTTP-módszer alapján:

  • A Razor Pages esetében hozzon létre több kezelő metódust. Például használja a OnGet kódot a GET-kivételek kezelésére, és a OnPost kódot a POST-kivételek kezelésére.
  • MVC esetén http-igeattribútumokat alkalmazhat több műveletre. Például használja a [HttpGet] a GET-kivételek kezelésére, és használja a [HttpPost] a POST-kivételek kezelésére.

Ha engedélyezni szeretné, hogy a hitelesítés nélküli felhasználók megtekinthessék az egyéni hibakezelő lapot, győződjön meg arról, hogy támogatja a névtelen hozzáférést.

A kivétel elérése

A hibakezelőben a kivétel és az eredeti kérelem elérési útjának eléréséhez használja a IExceptionHandlerPathFeature-t. Az alábbi példa IExceptionHandlerPathFeature a kidobott kivételről nyújt további információt:

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

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

Kivételkezelő lambda

Az egyéni kivételkezelő oldal alternatívája egy lambda biztosítása UseExceptionHandler. A lambda használata lehetővé teszi a hiba elérését a válasz visszaadása előtt.

A következő kód egy lambdát használ a kivételkezeléshez:

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

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

Használja a státuszkódoldalakat

Alapértelmezés szerint egy ASP.NET Core-alkalmazás nem biztosít állapotkódlapot a HTTP-hibaállapot-kódokhoz, például 404 – Nem található. Amikor az alkalmazás beállít egy HTTP 400-599 hibaállapot-kódot, amely nem tartalmaz törzset, az állapotkódot és egy üres választörzset ad vissza. Ha engedélyezni szeretné az alapértelmezett csak szöveges kezelőket a gyakori hibaállapot-kódokhoz, hívja meg a következőt UseStatusCodePagesProgram.cs:

var app = builder.Build();

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

app.UseStatusCodePages();

Hívja meg UseStatusCodePages, mielőtt a köztes szoftver kezeli a kéréseket. Például, a UseStatusCodePages hívása a Statikus fájl Middleware és az Endpoints Middleware előtt történjen.

Ha UseStatusCodePages nincs használatban, a végpont nélküli URL-címre való navigálás egy böngészőfüggő hibaüzenetet ad vissza, amely jelzi, hogy a végpont nem található. Amikor UseStatusCodePages a rendszer meghívja, a böngésző a következő választ adja vissza:

Status Code: 404; Not Found

UseStatusCodePages általában nem használják éles környezetben, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

Megjegyzés:

Az állapotkódlapok köztes szoftverei nem észlelnek kivételeket. Egyéni hibakezelő lap megadásához használja a kivételkezelő lapot.

UseStatusCodePages formátum karakterlánccal

A válasz tartalomtípusának és szövegének testreszabásához használja a túlterhelést UseStatusCodePages, ami tartalomtípust és formázási sztringet fogad el:

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

A megelőző kódban a {0} egy helyőrző a hibakód számára.

UseStatusCodePages a formátumsztringet általában nem használják éles környezetben, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

A UseStatusCodePages függvény lambda kifejezéssel

Egyéni hibakezelési és válaszírási kód megadásához használja a UseStatusCodePages lambda kifejezés által lehetővé tett túlterhelést:

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 a lambdát általában nem használják éles környezetben, mert olyan üzenetet küld vissza, amely nem hasznos a felhasználók számára.

Állapotkód-oldalak használata átirányításokkal

A UseStatusCodePagesWithRedirects bővítmény metódusa:

  • 302 – Talált állapotkódot küld az ügyfélnek.
  • Átirányítja az ügyfelet az URL-sablonban megadott hibakezelési végpontra. A hibakezelő végpont általában hibainformációkat jelenít meg, és HTTP 200-et ad vissza.
var app = builder.Build();

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

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

Az URL-sablon tartalmazhat helyőrzőt {0} az állapotkódhoz, ahogyan az az előző kódban is látható. Ha az URL-sablon ~ (tilde) karakterrel kezdődik, az ~ helyére az alkalmazás PathBase kerül. Amikor végpontot ad meg az alkalmazásban, hozzon létre egy MVC-nézetet vagy Razor lapot a végponthoz.

Ezt a módszert gyakran használják az alkalmazás esetében:

  • Az ügyfelet egy másik végpontra kell átirányítani, általában olyan esetekben, amikor egy másik alkalmazás feldolgozza a hibát. Webalkalmazások esetén az ügyfél böngészőcímsávja az átirányított végpontot tükrözi.
  • Nem szabad megőrizni és visszaadni az eredeti állapotkódot az első átirányítási válaszban.

UseStatusCodePagesWithReExecute

A UseStatusCodePagesWithReExecute bővítmény metódusa:

  • Az eredeti állapotkódot adja vissza az ügyfélnek.
  • A válasz törzsét úgy hozza létre, hogy egy másik elérési út használatával újra végrehajtja a kérelemfolyamatot.
var app = builder.Build();

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

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

Ha az alkalmazásban egy végpont van megadva, hozzon létre egy MVC-nézetet vagy Razor lapot a végponthoz.

Ezt a módszert gyakran használják, amikor az alkalmazásnak a következőt kell használnia:

  • A kérés feldolgozása egy másik végpontra való átirányítás nélkül. Webalkalmazások esetén az ügyfél böngészőcímsávja az eredetileg kért végpontot tükrözi.
  • Őrizze meg és adja vissza az eredeti állapotkódot a válaszsal együtt.

Az URL-sablonnak kezdődnie kell, / és tartalmaznia kell egy helyőrzőt {0} az állapotkódhoz. Ha az állapotkódot lekérdezési sztring paraméterként szeretné átadni, adjon át egy második argumentumot a következőbe UseStatusCodePagesWithReExecute: Például:

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

A hibát feldolgozó végpont lekérheti a hibát létrehozó eredeti URL-címet, ahogyan az az alábbi példában látható:

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

Állapotkódlapok letiltása

MVC-vezérlő vagy műveleti metódus állapotkódlapjainak letiltásához használja a [SkipStatusCodePages] attribútumot.

Ha le szeretné tiltani az állapotkódlapokat a Razor Pages-kezelő metódusban vagy egy MVC-vezérlőben megadott kérésekhez, használja a következőt IStatusCodePagesFeature:

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

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

Kivételkezelési kód

A kivételkezelő lapok kódjai kivételeket is okozhatnak. Az éles hibaoldalakat alaposan tesztelni kell, és különösen ügyelni kell arra, hogy saját maguk ne dobjanak ki kivételeket.

Válaszfejlécek

A válasz fejléceinek elküldése után:

  • Az alkalmazás nem tudja módosítani a válasz állapotkódját.
  • A kivételoldalak vagy -kezelők nem futtathatók. A választ be kell fejezni, vagy megszakadt a kapcsolat.

Kiszolgálói kivételkezelés

Az alkalmazások kivételkezelési logikája mellett a HTTP-kiszolgáló implementációja is képes kezelni néhány kivételt. Ha a kiszolgáló kivételt kap a válaszfejlécek elküldése előtt, a kiszolgáló választörzs nélkül küld 500 - Internal Server Error választ. Ha a kiszolgáló kivételt kap a válaszfejlécek elküldése után, a kiszolgáló bezárja a kapcsolatot. Az alkalmazás által nem kezelt kérelmeket a kiszolgáló kezeli. Minden kivételt, amely akkor fordul elő, amikor a kiszolgáló kezeli a kérést, a kiszolgáló kivételkezelése kezeli. Az alkalmazás egyéni hibaoldalai, a köztes szoftver kivételkezelése és a szűrők nem befolyásolják ezt a viselkedést.

Indítási kivétel kezelése

Csak az üzemeltetési réteg képes kezelni az alkalmazás indításakor előforduló kivételeket. Konfigurálható a kiszolgáló, hogy indítási hibákat rögzítsen és részletes hibákat rögzítsen.

Az hosztoló réteg csak akkor jeleníthet meg egy hibaüzenet oldalt egy rögzített indítási hibához, ha a hiba a host cím/port kötése után következik be. Ha a kötés sikertelen:

  • Az üzemeltetési réteg kritikus kivételt naplóz.
  • A dotnet-folyamat összeomlik.
  • A HTTP-kiszolgáló Kestrel használatakor nem jelenik meg hibaüzenet oldal.

Ha IIS-en (vagy Azure App Service-en) vagy IIS Expressen fut, az ASP.NET core modul502.5-ös folyamathibát ad vissza, ha a folyamat nem indítható el. További információ: ASP.NET Core hibaelhárítása az Azure App Service-ben és az IIS-ben.

Adatbázis hibaoldala

Az adatbázis fejlesztői lapjának kivételszűrője AddDatabaseDeveloperPageExceptionFilter rögzíti az adatbázissal kapcsolatos kivételeket, amelyek az Entity Framework Core migrálásával oldhatók meg. Ha ezek a kivételek előfordulnak, a rendszer HTML-választ hoz létre a probléma megoldásához szükséges lehetséges műveletek részleteivel. Ez a lap csak a fejlesztési környezetben engedélyezett. A következő kód hozzáadja az Adatbázis fejlesztői oldal kivételszűrőjét:

var builder = WebApplication.CreateBuilder(args);

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

Kivételszűrők

Az MVC-alkalmazásokban a kivételszűrők globálisan, vezérlőnként vagy műveletenként konfigurálhatók. A Razor Pages-alkalmazásokban globálisan vagy oldalmodellenként konfigurálhatók. Ezek a szűrők kezelik a vezérlőművelet vagy egy másik szűrő végrehajtása során előforduló nem kezelt kivételeket. További információ: Szűrők a ASP.NET Core-ban.

A kivételszűrők hasznosak az MVC-műveletekben előforduló kivételek elkapására, de nem olyan rugalmasak, mint a beépített kivételkezelő middleware. UseExceptionHandler Javasoljuk, hogy használja UseExceptionHandler, kivéve, ha a hibakezelést a választott MVC-művelet alapján eltérő módon kell elvégeznie.

Modellállapot-hibák

A modellállapot-hibák kezelésével kapcsolatos információkért tekintse meg a modellkötést és a modellérvényesítést ismertető témakört.

További erőforrások

Kirk Larkin, Tom Dykstra és Steve Smith

Ez a cikk az ASP.NET Core-webalkalmazásokban előforduló hibák kezelésének gyakori megközelítéseit ismerteti. Lásd: Hibák kezelése ASP.NET Core vezérlőalapú webes API-kban a webes API-khoz.

Mintakód megtekintése vagy letöltése. (A letöltés menete.) Az F12 böngésző fejlesztői eszközeinek hálózati lapja hasznos a mintaalkalmazás tesztelése során.

Fejlesztői kivétel oldal

A Fejlesztői kivétel lap részletes információkat jelenít meg a nem kezelt kérelmek kivételeiről. A ASP.NET Core-sablonok a következő kódot generálják:

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

Az előző kiemelt kód lehetővé teszi a fejlesztői kivételoldalt, ha az alkalmazás a fejlesztői környezetben fut.

A sablonok a köztes szoftver folyamatának korai szakaszában vannak elhelyezve UseDeveloperExceptionPage , hogy elkaphassák a következő köztes szoftverben kidobott kezeletlen kivételeket.

Az előző kód csak akkor engedélyezi a fejlesztői kivételoldalt, ha az alkalmazás a fejlesztői környezetben fut. A részletes kivételadatokat nem szabad nyilvánosan megjeleníteni, amikor az alkalmazás éles környezetben fut. További információ a környezetek konfigurálásáról: Több környezet használata a ASP.NET Core-ban.

A Fejlesztői kivétel lap a következő információkat tartalmazhatja a kivételről és a kérésről:

  • Stack nyomvonal
  • Sztringparaméterek lekérdezése, ha vannak ilyenek
  • Cookie-k, ha vannak ilyenek
  • Fejlécek

A fejlesztői kivételoldal nem garantáltan biztosít semmilyen információt. Használja a naplózást a teljes hibainformáció megszerzésére.

Kivételkezelő lap

Ha egyéni hibakezelő lapot szeretne konfigurálni az éles környezethez, hívja meg a következőt UseExceptionHandler: . Ez a kivételkezelés köztes szoftver:

  • Elfogja és naplózza a kezeletlen kivételeket.
  • A kérést újra végrehajtja egy másik folyamatban a megadott elérési út használatával. A rendszer nem hajtja végre újra a kérést, ha a válasz elindult. A sablon által létrehozott kód újra végrehajtja a kérést az /Error elérési út használatával.

Figyelmeztetés

Ha a másik csővezeték saját maga dob kivételt, a Kivételkezelő köztes réteg újra eldobja az eredeti kivételt.

Az alábbi példában a UseExceptionHandler a kivételkezelő köztes szoftvert adja hozzá a nem fejlesztési környezetekhez.

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

A Razor Lapok alkalmazássablon egy Hibalapot (.cshtml) és PageModel osztályt (ErrorModel) biztosít a Lapok mappában. MVC-alkalmazások esetén a projektsablon tartalmaz egy Error műveletmetódust és egy hibanézetet a Home vezérlőhöz.

A köztes szoftver kivételkezelése újra végrehajtja a kérést az eredeti HTTP-módszerrel. Ha egy hibakezelő végpont a HTTP-metódusok egy meghatározott halmazára korlátozódik, az csak az adott HTTP-metódusokhoz fut. Az attribútumot használó [HttpGet] MVC-vezérlőművelet például csak GET-kérelmek esetén fut. Annak érdekében, hogy minden kérés elérje az egyéni hibakezelő lapot, ne korlátozza azokat a HTTP-metódusok meghatározott készletére.

A kivételek eltérő kezelése az eredeti HTTP-módszer alapján:

  • A Razor Pages esetében hozzon létre több kezelési metódust. Például a OnGet használható a GET-kivételek kezelésére, és a OnPost használható a POST-kivételek kezelésére.
  • MVC esetén http-igeattribútumokat alkalmazhat több műveletre. Például a [HttpGet] a GET-kivételek kezelésére, míg a [HttpPost] a POST-kivételek kezelésére használható.

Ha engedélyezni szeretné, hogy a hitelesítés nélküli felhasználók megtekinthessék az egyéni hibakezelő lapot, győződjön meg arról, hogy támogatja a névtelen hozzáférést.

A kivétel elérése

Hibakezelőben használja a IExceptionHandlerPathFeature-t a kivételhez és az eredeti kérelem elérési útjához való hozzáféréshez. A következő kód hozzáadja ExceptionMessage az ASP.NET Core-sablonok által létrehozott alapértelmezett Pages/Error.cshtml.cs-hoz.

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

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

A kivétel tesztelése a mintaalkalmazásban:

  • Állítsa be a környezetet élesre.
  • Távolítsa el a megjegyzéseket a fájlból webBuilder.UseStartup<Startup>();Program.cs.
  • Válassza a Kezdőlapon a Kivétel aktiválása lehetőséget.

Kivételkezelő lambda

Alternatívája az egyéni kivételkezelő lapnak, ha lambdát ad a UseExceptionHandler-nak. A lambda használata lehetővé teszi a hiba elérését a válasz visszaadása előtt.

A következő kód egy lambdát használ a kivételkezeléshez:

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

Figyelmeztetés

Ne osszon meg bizalmas hibainformációkat IExceptionHandlerFeature vagy IExceptionHandlerPathFeature az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

A lambda kivételkezelésének tesztelése a mintaalkalmazásban:

  • Állítsa be a környezetet élesre.
  • Távolítsa el a megjegyzéseket a fájlból webBuilder.UseStartup<StartupLambda>();Program.cs.
  • Válassza a Kezdőlapon a Kivétel aktiválása lehetőséget.

Állapotkód oldalak használata

Alapértelmezés szerint egy ASP.NET Core-alkalmazás nem biztosít állapotkódlapot a HTTP-hibaállapot-kódokhoz, például 404 – Nem található. Amikor az alkalmazás beállít egy HTTP 400-599 hibaállapot-kódot, amely nem tartalmaz törzset, az állapotkódot és egy üres választörzset ad vissza. Az állapotkódlapok megadásához használja az állapotkódlapok köztes szoftverét. Ha engedélyezni szeretné az alapértelmezett csak szöveges kezelőket a gyakori hibaállapot-kódokhoz, hívja meg UseStatusCodePages a Startup.Configure metódust:

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

Hívás UseStatusCodePages a köztes szoftverrel kapcsolatos kérések kezelése előtt. Hívás például UseStatusCodePages a Statikus fájl köztes szoftver és az Endpoints Middleware előtt.

Ha UseStatusCodePages nincs használatban, a végpont nélküli URL-címre való navigálás egy böngészőfüggő hibaüzenetet ad vissza, amely jelzi, hogy a végpont nem található. Például a következőre navigálva: Home/Privacy2. Amikor UseStatusCodePages meghívják, a böngésző a következőt adja vissza:

Status Code: 404; Not Found

UseStatusCodePages általában nem használják éles környezetben, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

Tesztelés UseStatusCodePages a mintaalkalmazásban:

  • Állítsa be a környezetet élesre.
  • Távolítsa el a megjegyzéseket a webBuilder.UseStartup<StartupUseStatusCodePages>(); objektumból Program.cs objektumon belül.
  • Válassza ki a kezdőlap kezdőlapján található hivatkozásokat.

Megjegyzés:

Az állapotkódlapok köztes szoftverei nem észlelnek kivételeket. Egyéni hibakezelő lap megadásához használja a kivételkezelő lapot.

A UseStatusCodePages funkció formátumsztring használatával

A válasz tartalomtípusának és szövegének testreszabásához használja azt a UseStatusCodePages túlterhelést, amely tartalomtípust és formázási karakterláncot vesz.

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

Az előzőleg említett kódban a {0} a hibakód helyőrzője.

UseStatusCodePages a formátumsztringet általában nem használják éles környezetben, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

A mintaalkalmazásban való teszteléshez távolítsa el a megjegyzéseket a(z) webBuilder.UseStartup<StartupFormat>();Program.cs-ból/ből.

Használja az állapotkódoldalakat lambdával

Az egyéni hibakezelési és válaszírási kód megadásához használja a UseStatusCodePages olyan változatát, amely lambda kifejezést vesz át:

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 lambdával általában nem használják gyakorlati alkalmazásban, mert olyan üzenetet ad vissza, amely nem hasznos a felhasználók számára.

A UseStatusCodePages teszteléséhez a mintaalkalmazásban, távolítsa el a megjegyzéseket webBuilder.UseStartup<StartupStatusLambda>(); a Program.cs részében.

UseStatusCodePagesWithRedirects

A UseStatusCodePagesWithRedirects bővítmény metódusa:

  • 302 – Talált állapotkódot küld az ügyfélnek.
  • Átirányítja az ügyfelet az URL-sablonban megadott hibakezelési végpontra. A hibakezelő végpont általában hibainformációkat jelenít meg, és HTTP 200-et ad vissza.
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();
    });
}

Az URL-sablon tartalmazhat helyőrzőt {0} az állapotkódhoz, ahogyan az az előző kódban is látható. Ha az URL-sablon ~ (tilde) jellel kezdődik, akkor az ~ helyére az alkalmazás PathBase kerül. Amikor végpontot ad meg az alkalmazásban, hozzon létre egy MVC-nézetet vagy Razor lapot a végponthoz. A Razor Pages példáért lásd a Pages/MyStatusCode.cshtml fájlt a mintaalkalmazásban.

Ezt a módszert gyakran használják az alkalmazás esetében:

  • Az ügyfelet egy másik végpontra kell átirányítani, általában olyan esetekben, amikor egy másik alkalmazás feldolgozza a hibát. Webalkalmazások esetén az ügyfél böngészőcímsávja az átirányított végpontot tükrözi.
  • Ne tartsa meg és ne adja vissza az eredeti állapotkódot a kezdeti átirányítási válaszban.

A mintaalkalmazás teszteléséhez távolítsa el a megjegyzéseket a webBuilder.UseStartup<StartupSCredirect>();-ből Program.cs-ben.

UseStatusCodePagesWithReExecute

A UseStatusCodePagesWithReExecute bővítmény metódusa:

  • Az eredeti állapotkódot adja vissza az ügyfélnek.
  • A válasz törzsét úgy hozza létre, hogy egy másik elérési út használatával újra végrehajtja a kérelemfolyamatot.
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();
    });
}

Ha az alkalmazásban egy végpont van megadva, hozzon létre egy MVC-nézetet vagy Razor lapot a végponthoz. Győződjön meg arról, hogy UseStatusCodePagesWithReExecuteUseRouting elé kerüljön, így a kérelmet át lehet irányítani az állapotlapra. Egy Razor Pages példáért, lásd a Pages/MyStatusCode2.cshtml fájlt a mintapéldában.

Ezt a módszert gyakran használják, amikor az alkalmazásnak a következőt kell használnia:

  • A kérés feldolgozása egy másik végpontra való átirányítás nélkül. Webalkalmazások esetén az ügyfél böngészőcímsávja az eredetileg kért végpontot tükrözi.
  • Őrizze meg és adja vissza az eredeti állapotkódot a válaszsal együtt.

Az URL- és lekérdezési sztringsablonok tartalmazhatnak helyőrzőt {0} az állapotkódhoz. Az URL-sablonnak a következővel /kell kezdődnie: .

A hibát feldolgozó végpont lekérheti a hibát létrehozó eredeti URL-címet, ahogyan az az alábbi példában látható:

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

A Razor Pages mintát lásd a Pages/MyStatusCode2.cshtml fájlban a mintaalkalmazásban.

A mintaalkalmazásban való teszteléshez UseStatusCodePages távolítsa el a megjegyzéseket a következőből webBuilder.UseStartup<StartupSCreX>();Program.cs: .

Állapotkódlapok letiltása

MVC-vezérlő vagy műveleti metódus állapotkódlapjainak letiltásához használja a [SkipStatusCodePages] attribútumot.

Ha le szeretné tiltani az állapotkódlapokat a Razor Pages-kezelő metódusban vagy egy MVC-vezérlőben megadott kérésekhez, használja a következőt IStatusCodePagesFeature:

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

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

Kivételkezelési kód

A kivételkezelő lapok kódjai kivételeket is okozhatnak. Az éles hibaoldalakat alaposan tesztelni kell, és különösen ügyelni kell arra, hogy azok ne okozzanak saját kivételeket.

Válaszfejlécek

A válasz fejléceinek elküldése után:

  • Az alkalmazás nem tudja módosítani a válasz állapotkódját.
  • A kivételoldalak vagy -kezelők nem futtathatók. A választ be kell fejezni, vagy megszakadt a kapcsolat.

Kiszolgálói kivételkezelés

Az alkalmazások kivételkezelési logikája mellett a HTTP-kiszolgáló implementációja is képes kezelni néhány kivételt. Ha a kiszolgáló kivételt kap a válaszfejlécek elküldése előtt, a kiszolgáló választörzs nélkül küld 500 - Internal Server Error választ. Ha a kiszolgáló kivételt kap a válaszfejlécek elküldése után, a kiszolgáló bezárja a kapcsolatot. Az alkalmazás által nem kezelt kérelmeket a kiszolgáló kezeli. Minden kivételt, amely akkor fordul elő, amikor a kiszolgáló kezeli a kérést, a kiszolgáló kivételkezelése kezeli. Az alkalmazás egyéni hibaoldalai, a köztes szoftver kivételkezelése és a szűrők nem befolyásolják ezt a viselkedést.

Indítási kivétel kezelése

Csak az üzemeltetési réteg képes kezelni az alkalmazás indításakor előforduló kivételeket. A gazdagép konfigurálható az indítási hibák és a részletes hibák rögzítésére.

Az üzemeltetési réteg csak akkor jeleníthet meg egy hibaoldalt egy rögzített indítási hiba esetén, ha a hiba a gazdagép címének/portjának kötése után következik be. Ha a kötés sikertelen:

  • Az üzemeltetési réteg kritikus kivételt naplóz.
  • A dotnet-folyamat összeomlik.
  • A HTTP-kiszolgáló Kestrelhasználatakor nem jelenik meg hibalap.

Ha IIS-en (vagy Azure App Service-en) vagy IIS Expressen fut, az ASP.NET core modul502.5-ös folyamathibát ad vissza, ha a folyamat nem indítható el. További információ: ASP.NET Core hibaelhárítása az Azure App Service-ben és az IIS-ben.

Adatbázis hibaoldala

Az adatbázis fejlesztői lapjának kivételszűrője AddDatabaseDeveloperPageExceptionFilter rögzíti az adatbázissal kapcsolatos kivételeket, amelyek az Entity Framework Core migrálásával oldhatók meg. Ha ezek a kivételek előfordulnak, a rendszer HTML-választ hoz létre a probléma megoldásához szükséges lehetséges műveletek részleteivel. Ez a lap csak a fejlesztési környezetben engedélyezett. Az egyes felhasználói fiókok megadásakor a ASP.NET Core Razor Pages-sablonok a következő kódot hozták létre:

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

Kivételszűrők

Az MVC-alkalmazásokban a kivételszűrők globálisan, vezérlőnként vagy műveletenként konfigurálhatók. A Razor Pages-alkalmazásokban globálisan vagy oldalmodellenként konfigurálhatók. Ezek a szűrők kezelik a vezérlőművelet vagy egy másik szűrő végrehajtása során előforduló nem kezelt kivételeket. További információ: Szűrők a ASP.NET Core-ban.

A kivételszűrők hasznosak az MVC-műveletekben előforduló kivételek kezelésére, de nem olyan rugalmasak, mint a beépített kivételkezelő köztes szoftver. UseExceptionHandler Javasoljuk, hogy használja UseExceptionHandler, kivéve, ha a hibakezelést a választott MVC-művelet alapján eltérő módon kell elvégeznie.

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

Modellállapot-hibák

A modellállapot-hibák kezelésével kapcsolatos információkért tekintse meg a modellkötést és a modellérvényesítést ismertető témakört.

További erőforrások

Tom Dykstra és Steve Smith

Ez a cikk az ASP.NET Core-webalkalmazásokban előforduló hibák kezelésének gyakori megközelítéseit ismerteti. Lásd: Hibák kezelése ASP.NET Core vezérlőalapú webes API-kban webes API-k esetén.

Mintakód megtekintése vagy letöltése. (A letöltés menete.)

Fejlesztői kivétel lap

A Fejlesztői kivétel lap részletes információkat jelenít meg a kérelemkivételekről. A ASP.NET Core-sablonok a következő kódot generálják:

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

Az előző kód lehetővé teszi a fejlesztői kivételoldalt, ha az alkalmazás a fejlesztői környezetben fut.

A sablonok a UseDeveloperExceptionPage elemet minden köztes szoftver elé helyezik, így a következő köztes szoftverben fogják elkapni a kivételeket.

Az előző kód csak akkor engedélyezi a fejlesztői kivételoldalt , ha az alkalmazás a fejlesztői környezetben fut. A részletes kivételadatok nem jeleníthetők meg nyilvánosan, amikor az alkalmazás éles környezetben fut. További információ a környezetek konfigurálásáról: Több környezet használata a ASP.NET Core-ban.

A Fejlesztői kivétel lap a következő információkat tartalmazza a kivételről és a kérésről:

  • Verem nyomkövetése
  • Sztringparaméterek lekérdezése, ha vannak ilyenek
  • Cookie-k, ha vannak ilyenek
  • Fejlécek

Kivételkezelő lap

Ha egyéni hibakezelő oldalt szeretne konfigurálni az éles környezethez, használja az Exception Handling Middleware-t. A köztes szoftver:

  • Fogások és naplók kivételei.
  • Újra végrehajtja a kérést egy másik folyamatban a megjelölt lap vagy vezérlő számára. A rendszer nem hajtja végre újra a kérést, ha a válasz elindult. A sablon által létrehozott kód újravégrehajtja a kérést a következőre: /Error.

Az alábbi példában, UseExceptionHandler hozzáadja a Kivételkezelési köztes szoftvert a fejlesztési környezeten kívüli esetekben.

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

A Razor Lapok alkalmazássablon egy Hibalapot (.cshtml) és PageModel osztályt (ErrorModel) biztosít a Lapok mappában. MVC-alkalmazások esetén a projektsablon tartalmaz egy hibaműveleti módszert és egy hibanézetet a Home vezérlőben.

Ne jelölje meg a hibakezelő műveleti metódust HTTP-metódusattribútumokkal, például HttpGet. Az explicit igék megakadályozzák, hogy egyes kérések elérjék a metódust. Engedélyezze a névtelen hozzáférést a metódushoz, ha a hitelesítés nélküli felhasználóknak látniuk kell a hibanézetet.

A kivétel elérése

A hibakezelő vezérlőben vagy lapon használja a IExceptionHandlerPathFeature kódot a kivétel és az eredeti kérés útvonalának eléréséhez.

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

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

A kivételkezelési oldal aktiválásához állítsa a környezetet éles módra, és idézze elő a kivételt.

Kivételkezelő lambda

A kivételek kezelésére szolgáló egyéni oldal alternatívája az, ha lambda kifejezést biztosít az UseExceptionHandler számára. A lambda használata lehetővé teszi a hiba elérését a válasz visszaadása előtt.

Íme egy példa a lambda kivételkezelésre való használatára:

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

Az előző kódban hozzáadásra kerül a await context.Response.WriteAsync(new string(' ', 512));, hogy az Internet Explorer böngésző ne egy IE-hibaüzenetet, hanem maga a hibaüzenetet jelenítse meg. További információkért tekintse meg ezt a GitHub-problémát.

Figyelmeztetés

Ne szolgáljon ki bizalmas hibainformációkat IExceptionHandlerFeature vagy IExceptionHandlerPathFeature az ügyfeleknek. A hibák kiszolgálása biztonsági kockázatot jelent.

Ha meg szeretné tekinteni a lambda kivételkezelésének eredményét a mintaalkalmazásban, használja az és ErrorHandlerLambda az ProdEnvironment előfeldolgozási irányelveket, és válassza a Kezdőlapon a Kivétel aktiválása lehetőséget.

Állapotkód Oldalak Használata

Alapértelmezés szerint egy ASP.NET Core-alkalmazás nem biztosít állapotkódlapot a HTTP-állapotkódokhoz, például 404 – Nem található. Az alkalmazás egy állapotkódot és egy üres választörzset ad vissza. Állapotkódlapok megadásához használja az Állapotkódlapok köztes szoftvert.

A köztes szoftvert a Microsoft.AspNetCore.Diagnostics csomag teszi elérhetővé.

Ha engedélyezni szeretné az alapértelmezett csak szöveges kezelőket a gyakori hibaállapot-kódokhoz, hívja meg UseStatusCodePages a Startup.Configure metódust:

app.UseStatusCodePages();

Hívja meg a UseStatusCodePages igénykezelő köztes szoftvert (például a statikus fájl köztes szoftvert és az MVC köztes szoftvert) kezelése előtt.

Ha UseStatusCodePages nincs használatban, a végpont nélküli URL-címre való navigálás egy böngészőfüggő hibaüzenetet ad vissza, amely jelzi, hogy a végpont nem található. Például navigálás a következőre Home/Privacy2. Amikor UseStatusCodePages meghívják, a böngésző a következőt adja vissza:

Status Code: 404; Not Found

UseStatusCodePages kódoldalakat formátum karakterlánccal

A válasz tartalomtípusának és szövegének testreszabásához használja a UseStatusCodePages metódust, amely tartalomtípust és formázási sztringet fogad.

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

Használja a StatusCodePages-t lambda kifejezéssel

Egyéni hibakezelési és válaszírási kód megadásához használja a lambda kifejezést elfogadó túlterhelést UseStatusCodePages:

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

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

Használja az Állapotkód Oldalakat Átirányításokkal

A UseStatusCodePagesWithRedirects bővítmény metódusa:

  • 302 – Talált állapotkódot küld az ügyfélnek.
  • Átirányítja az ügyfelet az URL-sablonban megadott helyre.
app.UseStatusCodePagesWithRedirects("/StatusCode?code={0}");

Az URL-sablon tartalmazhat helyőrzőt {0} az állapotkódhoz, ahogyan az a példában is látható. Ha az URL-sablon ~ (tilde) jellel kezdődik, akkor az ~ helyére az alkalmazás PathBase kerül. Ha az alkalmazásban egy végpontra mutat, hozzon létre egy MVC nézetet vagy Razor lapot a végponthoz. Razor Példaként a Pages/StatusCode.cshtml oldalt lásd a mintaalkalmazásban.

Ezt a módszert gyakran használják az alkalmazás esetében:

  • Az ügyfelet egy másik végpontra kell átirányítani, általában olyan esetekben, amikor egy másik alkalmazás feldolgozza a hibát. Webalkalmazások esetén az ügyfél böngészőcímsávja az átirányított végpontot tükrözi.
  • Nem szabad megőrizni és visszaadni az eredeti állapotkódot a kezdeti átirányítási válasszal.

UseStatusCodePagesWithReExecute

A UseStatusCodePagesWithReExecute bővítmény metódusa:

  • Az eredeti állapotkódot adja vissza az ügyfélnek.
  • A válasz törzsét úgy hozza létre, hogy egy másik elérési út használatával újra végrehajtja a kérelemfolyamatot.
app.UseStatusCodePagesWithReExecute("/StatusCode","?code={0}");

Ha az alkalmazásban egy végpontra mutat, hozzon létre egy MVC nézetet vagy Razor lapot a végponthoz. Győződjön meg arról, hogy UseStatusCodePagesWithReExecute a UseRouting elé van helyezve, hogy a kérést át lehessen irányítani az állapotlapra. Razor A Példák lapjaira a mintaalkalmazásban talál Pages/StatusCode.cshtml példát.

Ezt a módszert gyakran használják, amikor az alkalmazásnak a következőt kell használnia:

  • A kérés feldolgozása egy másik végpontra való átirányítás nélkül. Webalkalmazások esetén az ügyfél böngészőcímsávja az eredetileg kért végpontot tükrözi.
  • Őrizze meg és adja vissza az eredeti állapotkódot a válaszsal együtt.

Az URL- és lekérdezési sztringsablonok tartalmazhatnak helyőrzőt ({0}) az állapotkódhoz. Az URL-sablonnak perjellel (/perjellel) kell kezdődnie. Ha helyőrzőt használ az elérési úton, győződjön meg arról, hogy a végpont (oldal vagy vezérlő) feldolgozhatja az elérési út szegmensét. Például egy hibaoldalnak támogatnia kell az opcionális útvonal szegmens értékét az @page irányelvvel:

@page "{code?}"

A hibát feldolgozó végpont lekérheti a hibát létrehozó eredeti URL-címet, ahogyan az az alábbi példában látható:

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

Ne jelölje meg a hibakezelő műveleti metódust HTTP-metódusattribútumokkal, például HttpGet. Az explicit igék megakadályozzák, hogy egyes kérések elérjék a metódust. Engedélyezze a névtelen hozzáférést a metódushoz, ha a hitelesítés nélküli felhasználóknak látniuk kell a hibanézetet.

Állapotkódlapok letiltása

Ha le szeretné tiltani egy MVC-vezérlő vagy műveleti módszer állapotkódlapjait, használja az [SkipStatusCodePages] attribútumot.

Ha le szeretné tiltani az állapotkódlapokat a Razor Pages-kezelő metódusban vagy egy MVC-vezérlőben megadott kérésekhez, használja a következőt IStatusCodePagesFeature:

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

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

Kivételkezelési kód

A kivételkezelő lapok kódjai kivételeket okozhatnak. Gyakran jó ötlet, ha az éles hibaoldalak csak statikus tartalomból állnak.

Válaszfejlécek

A válasz fejléceinek elküldése után:

  • Az alkalmazás nem tudja módosítani a válasz állapotkódját.
  • A kivételoldalak vagy -kezelők nem futtathatók. A választ be kell fejezni, vagy megszakadt a kapcsolat.

Kiszolgálói kivételkezelés

Az alkalmazás kivételkezelési logikája mellett a HTTP-kiszolgáló implementációja is képes kezelni néhány kivételt. Ha a kiszolgáló kivételt kap a válaszfejlécek elküldése előtt, a kiszolgáló választörzs nélkül küld egy 500 – belső kiszolgálói hibaválaszt . Ha a kiszolgáló kivételt kap a válaszfejlécek elküldése után, a kiszolgáló bezárja a kapcsolatot. Az alkalmazás által nem kezelt kérelmeket a kiszolgáló kezeli. Minden kivételt, amely akkor fordul elő, amikor a kiszolgáló kezeli a kérést, a kiszolgáló kivételkezelése kezeli. Az alkalmazás egyéni hibaoldalai, a köztes szoftver kivételkezelése és a szűrők nem befolyásolják ezt a viselkedést.

Indítási kivétel kezelése

Csak az üzemeltetési réteg képes kezelni az alkalmazás indításakor előforduló kivételeket. A gazdagép konfigurálható az indítási hibák és részletes hibák rögzítésére.

A tárhelyréteg hibalapot csak akkor tud megjeleníteni egy rögzített indítási hibához, ha a hiba a gazdagép címének/portjának kötése után következik be. Ha a kötés sikertelen:

  • Az üzemeltetési réteg kritikus kivételt naplóz.
  • A dotnet-folyamat összeomlik.
  • HTTP-kiszolgáló Kestrelhasználatakor nem jelenik meg hibaoldal.

Ha IIS-en (vagy Azure App Service-en) vagy IIS Expressen fut, az ASP.NET core modul502.5-ös folyamathibát ad vissza, ha a folyamat nem indítható el. További információ: ASP.NET Core hibaelhárítása az Azure App Service-ben és az IIS-ben.

Adatbázis hibaoldala

Az Adatbázis Hibalap köztes szoftver rögzíti az adatbázissal kapcsolatos kivételeket, amelyeket az Entity Framework migráció alkalmazásával lehet megoldani. Amikor ilyen kivételek lépnek fel, a rendszer html-választ ad a probléma megoldásához szükséges lehetséges műveletek részleteivel. Ezt a lapot csak a fejlesztési környezetben szabad engedélyezni. Engedélyezze a lapot úgy, hogy kódot ad hozzá a következőhöz Startup.Configure:

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

UseDatabaseErrorPage A Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet csomag szükséges.

Kivételszűrők

Az MVC-alkalmazásokban a kivételszűrők globálisan, vezérlőnként vagy műveletenként konfigurálhatók. A Razor Pages-alkalmazásokban globálisan vagy oldalmodellenként konfigurálhatók. Ezek a szűrők kezelik azokat a kezeletlen kivételeket, amelyek egy vezérlőművelet vagy egy másik szűrő végrehajtása során fordulnak elő. További információ: Szűrők a ASP.NET Core-ban.

Jótanács

A kivétel-szűrők hasznosak az MVC-műveletekben előforduló kivételek kezelésére, de nem olyan rugalmasak, mint a kivételkezelő köztes szoftver. A köztes szoftver használatát javasoljuk. Csak akkor használjon szűrőket, ha a hibakezelést a választott MVC-művelet alapján eltérő módon kell elvégeznie.

Modellállapot-hibák

A modellállapot-hibák kezelésével kapcsolatos információkért tekintse meg a modellkötést és a modellérvényesítést ismertető témakört.

További erőforrások