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


ASP.NET Core Blazor-alkalmazások hibáinak kezelése

Jegyzet

Ez nem a cikk legújabb verziója. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.

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. Az aktuális kiadást a jelen cikk .NET 9-es verziójában.

Ez a cikk bemutatja, hogyan kezeli a Blazor a kezeletlen kivételeket, és hogyan fejleszthet hibákat észlelő és kezelő alkalmazásokat.

Részletes hibák a fejlesztés során

Ha egy Blazor alkalmazás nem működik megfelelően a fejlesztés során, az alkalmazás részletes hibainformációinak fogadása segít a hiba elhárításában és javításában. Hiba esetén Blazor alkalmazások egy világos sárga sávot jelenítenek meg a képernyő alján:

  • A fejlesztés során a sáv a böngészőkonzolra irányítja, ahol láthatja a kivételt.
  • Éles környezetben a sáv értesíti a felhasználót, hogy hiba történt, és javasolja a böngésző frissítését.

A hibakezelési felület felhasználói felülete a Blazor projektsablonokrésze. A Blazor projektsablonok nem minden verziója használja a data-nosnippet attribútumot annak jelzésére, hogy a böngészők ne gyorsítótárazják a hiba felhasználói felületének tartalmát, de a Blazor dokumentáció minden verziója alkalmazza az attribútumot.

Egy Blazor Web App-ban, testre szabhatja a MainLayout komponenst. Mivel az környezeti címke segéd (például <environment include="Production">...</environment>) nem támogatott Razor összetevőkben, az alábbi példa IHostEnvironment alkalmaz a különféle környezetekhez kapcsolódó hibaüzenetek konfigurálásához.

A MainLayout.razortetején:

@inject IHostEnvironment HostEnvironment

Hozzon létre vagy módosítsa a Blazor hiba UI jelölését.

<div id="blazor-error-ui" data-nosnippet>
    @if (HostEnvironment.IsProduction())
    {
        <span>An error has occurred.</span>
    }
    else
    {
        <span>An unhandled exception occurred.</span>
    }
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

Egy Blazor Server alkalmazásban testre szabhatja a Pages/_Host.cshtml fájl felhasználói felületét. Az alábbi példa a környezeti címke súgójának használatával konfigurálja a különböző környezetekhez tartozó hibaüzeneteket.

Egy Blazor Server alkalmazásban testre szabhatja a Pages/_Layout.cshtml fájl felhasználói felületét. Az alábbi példa a környezeti címke súgójának használatával konfigurálja a különböző környezetekhez tartozó hibaüzeneteket.

Egy Blazor Server alkalmazásban testre szabhatja a Pages/_Host.cshtml fájl felhasználói felületét. Az alábbi példa a környezeti címke súgójának használatával konfigurálja a különböző környezetekhez tartozó hibaüzeneteket.

Hozzon létre vagy módosítsa a Blazor hiba UI jelölését.

<div id="blazor-error-ui" data-nosnippet>
    <environment include="Staging,Production">
        An error has occurred.
    </environment>
    <environment include="Development">
        An unhandled exception occurred.
    </environment>
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

Egy Blazor WebAssembly alkalmazásban testre szabhatja a felhasználói élményt a wwwroot/index.html fájlban:

<div id="blazor-error-ui" data-nosnippet>
    An unhandled error has occurred.
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

A blazor-error-ui elem általában rejtve van az alkalmazás automatikusan létrehozott stíluslapján található display: none CSS-osztály blazor-error-ui stílusa miatt. Hiba esetén a keretrendszer display: block alkalmaz az elemre.

A blazor-error-ui elem általában rejtve van, mivel a display: none CSS-osztály blazor-error-ui stílusa szerepel a webhely stíluslapján a wwwroot/css mappában. Hiba esetén a keretrendszer display: block alkalmaz az elemre.

Részletes áramköri hibák

Ez a szakasz a körön keresztül működő Blazor Web App-re vonatkozik.

Ez a szakasz Blazor Server alkalmazásokra vonatkozik.

Az ügyféloldali hibák nem tartalmazzák a hívásvermet, és nem adnak részletes információt a hiba okáról, de a kiszolgálónaplók tartalmazzák ezeket az információkat. Fejlesztési célokból az érzékeny áramköri hibainformációk részletes hibák megjelenítésének engedélyezésével elérhetővé tehetők az ügyfél számára.

Állítsa CircuitOptions.DetailedErrorstrueértékre. További információ és példa: ASP.NET Core BlazorSignalR útmutató.

A CircuitOptions.DetailedErrors beállításának alternatívája, hogy az DetailedErrors konfigurációs kulcsot true-re állítjuk az alkalmazás Development környezetbeállítási fájljában (appsettings.Development.json). Emellett a SignalR kiszolgáló oldali naplózás (Microsoft.AspNetCore.SignalR) állítsa hibakereső vagy nyomkövető módba a részletes SignalR naplózáshoz.

appsettings.Development.json:

{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.AspNetCore.SignalR": "Debug"
    }
  }
}

A DetailedErrors konfigurációs kulcs úgy is beállítható, hogy true a ASPNETCORE_DETAILEDERRORS környezeti változóval, true értékkel Development/Staging környezeti kiszolgálókon vagy a helyi rendszeren.

Figyelmeztetés

Mindig kerülje a hibainformációk internetes felfedését, ami biztonsági kockázatot jelent.

Részletes hibák Razor összetevő kiszolgálóoldali rendereléséhez

Ez a szakasz alkalmazandó Blazor Web Apps-es esetekre.

A RazorComponentsServiceOptions.DetailedErrors beállítással szabályozhatja a Razor összetevő kiszolgálóoldali renderelésével kapcsolatos hibák részletes információinak előállítását. Az alapértelmezett érték a false.

Az alábbi példa részletes hibákat tesz lehetővé:

builder.Services.AddRazorComponents(options => 
    options.DetailedErrors = builder.Environment.IsDevelopment());

Figyelmeztetés

Csak a részletes hibákat engedélyezze a Development környezetben. A részletes hibák bizalmas információkat tartalmazhatnak arról az alkalmazásról, amelyet a rosszindulatú felhasználók használhatnak a támadás során.

Az előző példa bizonyos fokú biztonságot nyújt azáltal, hogy a DetailedErrors értékét a IsDevelopmentáltal visszaadott érték alapján állítja be. Ha az alkalmazás a Development környezetben van, DetailedErrorstrueértékre van állítva. Ez a megközelítés nincs garantálva, mert lehetséges egy éles alkalmazást nyilvános kiszolgálón üzemeltetni a Development környezetben.

Kezeletlen kivételek kezelése a fejlesztői kódban

Ahhoz, hogy egy alkalmazás hiba után is folytatódjon, az alkalmazásnak hibakezelési logikával kell rendelkeznie. A cikk későbbi szakaszai a kezeletlen kivételek lehetséges forrásait ismertetik.

Éles környezetben ne renderelje a keretrendszer kivételüzeneteit vagy veremkövetkezményeit a felhasználói felületen. A kivételüzenetek vagy a veremkövetések renderelése a következők valamelyikét eredményezheti:

  • Bizalmas információk közzététele a végfelhasználók számára.
  • Segítsen a rosszindulatú felhasználóknak felderíteni az alkalmazások gyengeségeit, amelyek veszélyeztethetik az alkalmazás, a kiszolgáló vagy a hálózat biztonságát.

Nem kezelt kivételek áramkörökre

Ez a szakasz a kapcsolatcsoporton keresztül működő kiszolgálóoldali alkalmazásokra vonatkozik.

Razor kiszolgáló interaktivitását engedélyező összetevők állapotalapúak a kiszolgálón. Miközben a felhasználók a kiszolgálón lévő összetevővel kommunikálnak, fenntartják a kapcsolatot az kapcsolatcsoportnéven ismert kiszolgálóval. Az áramkör aktív komponenspéldányokat, valamint számos egyéb állapotot is tartalmaz, például:

  • Az összetevők legutóbbi renderelt kimenete.
  • Az ügyféloldali események által aktiválható eseménykezelő meghatalmazottak aktuális készlete.

Ha egy felhasználó több böngészőlapon nyitja meg az alkalmazást, a felhasználó több független kapcsolatcsoportot hoz létre.

Blazor a legtöbb kezeletlen kivételt végzetesnek kezeli azon az áramkörben, ahol előfordulnak. Ha egy kapcsolatcsoport kezeletlen kivétel miatt leáll, a felhasználó csak úgy folytathatja az interakciót az alkalmazással, ha újra betölti a lapot egy új kapcsolatcsoport létrehozásához. Azon körökre, amelyek nem érintettek a megszakított vonal által, mint például más felhasználók vagy más böngészőlapok körei, nincs hatással. Ez a forgatókönyv hasonló egy összeomló asztali alkalmazáshoz. Az összeomlott alkalmazást újra kell indítani, de más alkalmazásokat nem érint.

A keretrendszer leállítja a kapcsolatcsoportot, ha a következő okokból nem kezelt kivétel történik:

  • A kezeletlen kivétel gyakran nem definiált állapotban hagyja a logikai áramkört.
  • Az alkalmazás normál működése nem garantálható kezeletlen kivétel után.
  • Biztonsági rések jelenhetnek meg az alkalmazásban, ha a kapcsolatcsoport nem definiált állapotban folytatódik.

Globális kivételkezelés

A kivételek globális kezelésére vonatkozó megközelítéseket a következő szakaszokban találja:

Hibahatárok

hibahatárok kényelmes megoldást nyújtanak a kivételek kezelésére. A ErrorBoundary összetevő:

  • Megjeleníti a gyermek tartalmát, ha nem történt hiba.
  • Megjeleníti a hibaüzenetet, ha a hiba határán belül bármely összetevő nem kezelt kivételt vet fel.

Hibahatár meghatározásához használja a ErrorBoundary összetevőt egy vagy több más összetevő körbefuttatásához. A hibahatár-kezelő kezeli az általa körülvett összetevők által kidobott kezeletlen kivételeket.

<ErrorBoundary>
    ...
</ErrorBoundary>

Ha globálisan szeretné megvalósítani a hibahatárt, adja hozzá a határt az alkalmazás fő elrendezésének törzstartalma köré.

A MainLayout.razor:

<article class="content px-4">
    <ErrorBoundary>
        @Body
    </ErrorBoundary>
</article>

Azon Blazor Web Apps esetében, amelynél a hibahatár csak statikus MainLayout összetevőre van alkalmazva, a határ csak statikus kiszolgálóoldali renderelés (statikus SSR) során aktív. A határ nem aktiválódik csak azért, mert egy összetevő az összetevő hierarchiájában tovább interaktív.

Az interaktív renderelési mód nem alkalmazható az MainLayout összetevőre, mert az összetevő Body paramétere egy RenderFragment delegált, amely tetszőleges kód, és nem szerializálható. Ahhoz, hogy a MainLayout összetevő és a többi összetevő általánosan engedélyezhesse az interaktivitást, az alkalmazásnak globális interaktív renderelési módot kell alkalmaznia az alkalmazás gyökérösszetevőjében található HeadOutlet és Routes összetevőpéldányokra, amely általában az App összetevő. Az alábbi példa globálisan bevezeti az Interaktív kiszolgáló (InteractiveServer) renderelési módját.

A Components/App.razor:

<HeadOutlet @rendermode="InteractiveServer" />

...

<Routes @rendermode="InteractiveServer" />

Ha nem szeretné engedélyezni a globális interaktivitást, helyezze a hibahatárt távolabb az összetevőhierarchiától. A fontos fogalmak, amelyeket szem előtt kell tartani, az, hogy a hibahatár bárhol elhelyezve legyen:

  • Ha a hibahatárt tartalmazó összetevő nem interaktív, a hibahatár csak a statikus SSR során aktiválható a kiszolgálón. A határ például aktiválható, ha hiba jelenik meg egy összetevő életciklus-metódusában, de nem az összetevőn belüli felhasználói interaktivitás által kiváltott esemény esetén, például egy gombkattintás-kezelő által kiváltott hiba esetén.
  • Ha a hibahatárt tartalmazó összetevő interaktív, a hibahatár képes aktiválni azokat az interaktív összetevőket, amelyeket körbefuttat.

Jegyzet

Az előző szempontok nem relevánsak az önálló Blazor WebAssembly-alkalmazások esetében, mivel egy Blazor WebAssembly alkalmazás ügyféloldali renderelése (CSR) teljesen interaktív.

Vegyük az alábbi példát, ahol egy beágyazott számláló összetevő által okozott kivételt a Home összetevő hibahatára észlel, amely interaktív renderelési módot alkalmaz.

EmbeddedCounter.razor:

<h1>Embedded Counter</h1>

<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;

        if (currentCount > 5)
        {
            throw new InvalidOperationException("Current count is too big!");
        }
    }
}

Home.razor:

@page "/"
@rendermode InteractiveServer

<PageTitle>Home</PageTitle>

<h1>Home</h1>

<ErrorBoundary>
    <EmbeddedCounter />
</ErrorBoundary>

Vegye figyelembe az alábbi példát, amelyben egy beágyazott számlálóösszetevő által okozott kivételt a Home összetevő hibahatára elkapja.

EmbeddedCounter.razor:

<h1>Embedded Counter</h1>

<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;

        if (currentCount > 5)
        {
            throw new InvalidOperationException("Current count is too big!");
        }
    }
}

Home.razor:

@page "/"

<PageTitle>Home</PageTitle>

<h1>Home</h1>

<ErrorBoundary>
    <EmbeddedCounter />
</ErrorBoundary>

Ha a kezeletlen kivétel ötnél több currentCount esetén van kiírva:

  • A hiba naplózása normál módon történik (System.InvalidOperationException: Current count is too big!).
  • A kivételt a hibahatár kezeli.
  • Az alapértelmezett hiba felhasználói felületét a hibahatár jeleníti meg.

A ErrorBoundary összetevő egy üres <div> elemet jelenít meg a blazor-error-boundary CSS-osztály használatával a hibatartalma miatt. Az alapértelmezett felhasználói felület színeit, szövegét és ikonját az alkalmazás stíluslapja határozza meg a wwwroot mappában, így szabadon testre szabhatja a hiba felhasználói felületét.

Az hibahatár-felület által renderelt alapértelmezett hibaképernyő, amely piros háttérrel, a

Az alapértelmezett hibatartalom módosítása:

  • Csomagold be a hiba határoló összetevőit a ChildContent tulajdonságba.
  • Állítsa a ErrorContent tulajdonságot a hibatartalomra.

Az alábbi példa becsomagolja a EmbeddedCounter összetevőt, és egyéni hibatartalmat biztosít:

<ErrorBoundary>
    <ChildContent>
        <EmbeddedCounter />
    </ChildContent>
    <ErrorContent>
        <p class="errorUI">😈 A rotten gremlin got us. Sorry!</p>
    </ErrorContent>
</ErrorBoundary>

Az előző példában az alkalmazás stíluslapja feltehetően tartalmaz egy errorUI CSS-osztályt a tartalom stílusához. A hibatartalom blokkszintű elem nélkül jelenik meg a ErrorContent tulajdonságból. Egy blokkszintű elem, például egy osztás (<div>) vagy egy bekezdés (<p>) elem körülveheti a hibatartalom jelölését, de nem kötelező.

Ha szeretné, használja a @context környezetét (ErrorContent) a hibaadatok lekéréséhez:

<ErrorContent>
    @context.HelpLink
</ErrorContent>

A ErrorContent a környezetnek is nevet adhat. Az alábbi példában a környezet neve exception:

<ErrorContent Context="exception">
    @exception.HelpLink
</ErrorContent>

Figyelmeztetés

Mindig kerülje a hibainformációk internetes felfedését, ami biztonsági kockázatot jelent.

Ha a hibahatár az alkalmazás elrendezésében van meghatározva, a hiba felhasználói felülete attól függetlenül jelenik meg, hogy a felhasználó melyik lapra navigál a hiba bekövetkezése után. A legtöbb forgatókönyvben javasoljuk a hibahatárok szűk hatókörének meghatározását. Ha széles körben hatókörbe rendez egy hibahatárt, a hibahatár Recover metódusának meghívásával visszaállíthatja azt hibamentes állapotba a későbbi oldalnavigációs eseményeken.

A MainLayout.razor:

...

<ErrorBoundary @ref="errorBoundary">
    @Body
</ErrorBoundary>

...

@code {
    private ErrorBoundary? errorBoundary;

    protected override void OnParametersSet()
    {
        errorBoundary?.Recover();
    }
}

A végtelen hurok elkerülése érdekében, ahol a helyreállítás csupán egy olyan összetevőt rendez vissza, amely ismét hibát okoz, ne hívja meg a Recover a renderelési logikából. Csak akkor hívja Recover, ha:

  • A felhasználó egy felhasználói felületi kézmozdulatot hajt végre, például kiválaszt egy gombot, amely jelzi, hogy újra meg szeretné próbálkozni az eljárást, vagy amikor a felhasználó egy új összetevőre navigál.
  • A további végrehajtott logika is törli a kivételt. Az összetevő átrendezésekor a hiba nem jelentkezik újra.

Az alábbi példa lehetővé teszi, hogy a felhasználó egy gombbal helyreállítsa a kivételt:

<ErrorBoundary @ref="errorBoundary">
    <ChildContent>
        <EmbeddedCounter />
    </ChildContent>
    <ErrorContent>
        <div class="alert alert-danger" role="alert">
            <p class="fs-3 fw-bold">😈 A rotten gremlin got us. Sorry!</p>
            <p>@context.HelpLink</p>
            <button class="btn btn-info" @onclick="_ => errorBoundary?.Recover()">
                Clear
            </button>
        </div>
    </ErrorContent>
</ErrorBoundary>

@code {
    private ErrorBoundary? errorBoundary;
}

A ErrorBoundary osztályt alosztályozhatja egyéni feldolgozáshoz a OnErrorAsyncfelülírásával. Az alábbi példa csupán naplózza a hibát, de bármilyen hibakezelési kódot implementálhat. Eltávolíthatja a CompletedTask visszaadó sort, ha a kód aszinkron feladatra vár.

CustomErrorBoundary.razor:

@inherits ErrorBoundary
@inject ILogger<CustomErrorBoundary> Logger

@if (CurrentException is null)
{
    @ChildContent
}
else if (ErrorContent is not null)
{
    @ErrorContent(CurrentException)
}

@code {
    protected override Task OnErrorAsync(Exception ex)
    {
        Logger.LogError(ex, "😈 A rotten gremlin got us. Sorry!");
        return Task.CompletedTask;
    }
}

Az előző példa osztályként is implementálható.

CustomErrorBoundary.cs:

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;

namespace BlazorSample;

public class CustomErrorBoundary : ErrorBoundary
{
    [Inject]
    ILogger<CustomErrorBoundary> Logger {  get; set; } = default!;

    protected override Task OnErrorAsync(Exception ex)
    {
        Logger.LogError(ex, "😈 A rotten gremlin got us. Sorry!");
        return Task.CompletedTask;
    }
}

Az összetevőben használt előző implementációk bármelyike:

<CustomErrorBoundary>
    ...
</CustomErrorBoundary>

Alternatív globális kivételkezelés

Az ebben a szakaszban ismertetett megközelítés a globális interaktív renderelési módot (Blazor Server, Blazor WebAssemblyvagy Blazor Web App) alkalmazó InteractiveServer, InteractiveWebAssemblyés InteractiveAutos-ekre vonatkozik. A megközelítés nem működik az oldal/összetevő renderelési módot vagy statikus kiszolgálóoldali renderelést (statikus SSR) alkalmazó Blazor Web Appesetén, mert a megközelítés egy CascadingValue/CascadingParameter-ra épül, amely nem működik a renderelési módok közötti határokon vagy statikus SSR-t alkalmazó összetevőkkel.

A hibahatárok (ErrorBoundary) használatának másik lehetősége, ha egy egyéni hiba komponenst CascadingValue ad át a gyermek komponenseknek. Az egyik előnye annak, ha egy összetevőt használunk a injektált szolgáltatással vagy egy egyéni naplózó implementációval szemben, hogy egy kaszkádolt összetevő képes tartalmat megjeleníteni és CSS-stílusokat alkalmazni hiba esetén.

Az alábbi ProcessError összetevő-példa csupán naplózza a hibákat, de az összetevő metódusai az alkalmazás által megkövetelt módon feldolgozhatják a hibákat, beleértve több hibafeldolgozási módszer használatát is.

ProcessError.razor:

@inject ILogger<ProcessError> Logger

<CascadingValue Value="this" IsFixed="true">
    @ChildContent
</CascadingValue>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    public void LogError(Exception ex)
    {
        Logger.LogError("ProcessError.LogError: {Type} Message: {Message}", 
            ex.GetType(), ex.Message);

        // Call StateHasChanged if LogError directly participates in 
        // rendering. If LogError only logs or records the error,
        // there's no need to call StateHasChanged.
        //StateHasChanged();
    }
}

Jegyzet

További információkért a RenderFragmentösszetevőkről tekintse meg a ASP.NET Core Razor összetevőket.

CascadingValue<TValue>.IsFixed használják arra, hogy jelezze, hogy egy kaszkádolt paraméter nem változik az inicializálás után.

Ha ezt a megközelítést alkalmazza egy Blazor Web App-ban, először nyissa meg a Routes összetevőt, majd a Router összetevőt (<Router>...</Router>) csomagolja be a ProcessError összetevővel. Ez lehetővé teszi, hogy a ProcessError összetevő az alkalmazás bármely olyan összetevőjére kaszkádolt legyen, ahol a ProcessError összetevő CascadingParameterérkezik.

A Routes.razor:

<ProcessError>
    <Router ...>
        ...
    </Router>
</ProcessError>

Ha ezt a megközelítést egy Blazor Server vagy Blazor WebAssembly alkalmazásban használja, nyissa meg a App összetevőt, csomagolja a Router összetevőt (<Router>...</Router>) a ProcessError összetevővel. Ez lehetővé teszi, hogy a ProcessError összetevő az alkalmazás bármely olyan összetevőjére kaszkádolt legyen, ahol a ProcessError összetevő CascadingParameterérkezik.

A App.razor:

<ProcessError>
    <Router ...>
        ...
    </Router>
</ProcessError>

Összetevők hibáinak feldolgozása:

  • Jelölje ki a ProcessError összetevőt CascadingParameter-ként a @code blokkban. Egy Counter összetevő példa egy Blazor projektsablonon alapuló alkalmazásban, adja hozzá a következő ProcessError tulajdonságot:

    [CascadingParameter]
    private ProcessError? ProcessError { get; set; }
    
  • Hibafeldolgozási módszer meghívása bármely catch blokkban megfelelő kivételtípussal. A példa ProcessError összetevő csak egyetlen LogError metódust kínál, de a hibafeldolgozó összetevő tetszőleges számú hibafeldolgozási módszert biztosíthat az alkalmazás alternatív hibafeldolgozási követelményeinek kezeléséhez. A következő Counter összetevő @code blokkpéldája tartalmazza a ProcessError kaszkádolt paramétert, és a naplózás kivételét zárja le, ha a szám meghaladja az ötöt:

    @code {
        private int currentCount = 0;
    
        [CascadingParameter]
        private ProcessError? ProcessError { get; set; }
    
        private void IncrementCount()
        {
            try
            {
                currentCount++;
    
                if (currentCount > 5)
                {
                    throw new InvalidOperationException("Current count is over five!");
                }
            }
            catch (Exception ex)
            {
                ProcessError?.LogError(ex);
            }
        }
    }
    

A naplózott hiba:

fail: {COMPONENT NAMESPACE}.ProcessError[0]
ProcessError.LogError: System.InvalidOperationException Message: Current count is over five!

Ha a LogError metódus közvetlenül részt vesz a renderelésben, például egyéni hibaüzenetsávot jelenít meg, vagy módosítja a renderelt elemek CSS-stílusait, hívja meg a StateHasChanged a LogError metódus végén a felhasználói felület újrarendezéséhez.

Mivel az ebben a szakaszban szereplő módszerek egy try-catch utasítással kezelik a hibákat, az alkalmazás SignalR kapcsolat az ügyfél és a kiszolgáló között nem megszakad, ha hiba történik, és a kapcsolatcsoport életben marad. Más kezeletlen kivételek továbbra is végzetesek maradnak egy áramkör számára. További információért lásd az szakaszt, amely azt tárgyalja, hogyan reagál az áramkör a kezeletlen kivételekre.

Az alkalmazások kaszkádolt értékként használhatnak hibafeldolgozó összetevőt a hibák központosított feldolgozásához.

A következő ProcessError összetevő CascadingValue-ként azonosítja magát a gyermekösszetevők számára. Az alábbi példa csupán naplózza a hibát, de az összetevő metódusai az alkalmazás által megkövetelt módon feldolgozhatják a hibákat, beleértve több hibafeldolgozási módszer használatát is. Az egyik előnye annak, ha egy összetevőt használunk a injektált szolgáltatással vagy egy egyéni naplózó implementációval szemben, hogy egy kaszkádolt összetevő képes tartalmat megjeleníteni és CSS-stílusokat alkalmazni hiba esetén.

ProcessError.razor:

@using Microsoft.Extensions.Logging
@inject ILogger<ProcessError> Logger

<CascadingValue Value="this" IsFixed="true">
    @ChildContent
</CascadingValue>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public void LogError(Exception ex)
    {
        Logger.LogError("ProcessError.LogError: {Type} Message: {Message}", 
            ex.GetType(), ex.Message);
    }
}

Jegyzet

További információkért a RenderFragmentösszetevőkről tekintse meg a ASP.NET Core Razor összetevőket.

CascadingValue<TValue>.IsFixed használják arra, hogy jelezze, hogy egy kaszkádolt paraméter nem változik az inicializálás után.

A App összetevőben csomagolja be a Router összetevőt a ProcessError összetevővel. Ez lehetővé teszi, hogy a ProcessError összetevő az alkalmazás bármely olyan összetevőjére kaszkádolt legyen, ahol a ProcessError összetevő CascadingParameterérkezik.

App.razor:

<ProcessError>
    <Router ...>
        ...
    </Router>
</ProcessError>

Összetevők hibáinak feldolgozása:

  • Jelölje ki a ProcessError összetevőt CascadingParameter-ként a @code blokkban:

    [CascadingParameter]
    private ProcessError ProcessError { get; set; }
    
  • Hibafeldolgozási módszer meghívása bármely catch blokkban megfelelő kivételtípussal. A példa ProcessError összetevő csak egyetlen LogError metódust kínál, de a hibafeldolgozó összetevő tetszőleges számú hibafeldolgozási módszert biztosíthat az alkalmazás alternatív hibafeldolgozási követelményeinek kezeléséhez.

    try
    {
        ...
    }
    catch (Exception ex)
    {
        ProcessError.LogError(ex);
    }
    

Az előző példa ProcessError összetevő és LogError metódus használatával a böngésző fejlesztői eszközeinek konzolja a beszorult, naplózott hibát jelzi:

fail: {COMPONENT NAMESPACE}.Shared.ProcessError[0]
ProcessError.LogError: System.NullReferenceException Message: Object reference not set to an instance of an object.

Ha a LogError metódus közvetlenül részt vesz a renderelésben, például egyéni hibaüzenetsávot jelenít meg, vagy módosítja a renderelt elemek CSS-stílusait, hívja meg a StateHasChanged a LogError metódus végén a felhasználói felület újrarendezéséhez.

Mivel az ebben a szakaszban szereplő módszerek egy try-catch utasítással kezelik a hibákat, a Blazor alkalmazás SignalR kapcsolata az ügyfél és a kiszolgáló között nem megszakad, ha hiba történik, és a kapcsolatcsoport életben marad. Egy kezeletlen kivétel végzetes egy áramkör számára. További információért lásd az szakaszt, amely azt tárgyalja, hogyan reagál az áramkör a kezeletlen kivételekre.

Naplóhibák állandó szolgáltatóval

Ha nem kezelt kivétel történik, a rendszer naplózza a kivételt ILogger szolgáltatástárolóban konfigurált példányokra. Blazor alkalmazások naplókonzoljának kimenetét a konzolnapló-szolgáltatóval. Fontolja meg, hogy egy olyan helyre naplózza a kiszolgálón (vagy az ügyféloldali alkalmazások háttér webes API-jában), ahol a naplóméretet és a naplóforgatást egy szolgáltató kezeli. Másik lehetőségként az alkalmazás egy Alkalmazásteljesítmény-kezelési (APM) szolgáltatást is használhat, például Azure Application Insights (Azure Monitor).

Jegyzet

A natív Application Insights szolgáltatások, amelyek az ügyféloldali alkalmazások támogatására szolgálnak, valamint a natív Blazor keretrendszer támogatása a Google Analytics-hoz a technológiák jövőbeli kiadásaiban válhatnak elérhetővé. További információ: Support App Insights in Blazor WASM Client Side (microsoft/ApplicationInsights-dotnet #2143) és webelemzés és diagnosztika (a közösségi implementációkra mutató hivatkozásokat tartalmaz) (dotnet/aspnetcore #5461). Addig is az ügyféloldali alkalmazások használhatják a Application Insights JavaScript SDKJS interop segítségével, hogy közvetlenül az Application Insightsba naplózzanak hibákat egy ügyféloldali alkalmazásból.

A kapcsolatcsoporton keresztül működő Blazor-alkalmazások fejlesztése során az alkalmazás általában elküldi a kivételek teljes részleteit a böngésző konzoljára, hogy segítsen a hibakeresésben. Éles környezetben a rendszer nem küld részletes hibákat az ügyfeleknek, de a kivétel teljes adatait naplózza a rendszer a kiszolgálón.

El kell döntenie, hogy mely incidenseket és a naplózott incidensek súlyosságát kell megadnia. Előfordulhat, hogy az ellenséges felhasználók szándékosan okozhatnak hibákat. Ne naplózza például az incidenst olyan hibából, amelynél ismeretlen ProductId jelenik meg egy termékadatokat megjelenítő összetevő URL-címében. Nem minden hibát kell incidensként kezelni a naplózáshoz.

További információkért lásd a következő cikkeket:

‡A kiszolgálóoldali Blazor alkalmazásokra és más kiszolgálóoldali ASP.NET Core-alkalmazásokra vonatkozik, amelyek webes API-háttéralkalmazások Blazor. Az ügyféloldali alkalmazások képesek az ügyfélre vonatkozó hibainformációk letöltésére és küldésére egy webes API-nak, amely naplózza a hibainformációkat egy állandó naplózási szolgáltatónak.

Ha nem kezelt kivétel történik, a rendszer naplózza a kivételt ILogger szolgáltatástárolóban konfigurált példányokra. Blazor alkalmazások naplókonzoljának kimenetét a konzolnapló-szolgáltatóval. Fontolja meg a kiszolgáló állandóbb helyére való naplózást, ha hibainformációkat küld egy háttérbeli webes API-nak, amely naplóméret-kezeléssel és naplóforgatással rendelkező naplózási szolgáltatót használ. Másik lehetőségként a háttérbeli webes API-alkalmazás egy Alkalmazásteljesítmény-kezelési (APM) szolgáltatást is használhat, például Azure Application Insights (Azure Monitor)†, hogy rögzítse az ügyfelektől kapott hibainformációkat.

El kell döntenie, hogy mely incidenseket és a naplózott incidensek súlyosságát kell megadnia. Előfordulhat, hogy az ellenséges felhasználók szándékosan okozhatnak hibákat. Ne naplózza például az incidenst olyan hibából, amelynél ismeretlen ProductId jelenik meg egy termékadatokat megjelenítő összetevő URL-címében. Nem minden hibát kell incidensként kezelni a naplózáshoz.

További információkért lásd a következő cikkeket:

A natív Application Insights funkciók az ügyféloldali alkalmazások támogatása, valamint a natív Blazor keretrendszer támogatás a Google Analytics számára a technológiák jövőbeli kiadásaiban elérhetővé válhatnak. További információ: Support App Insights in Blazor WASM Client Side (microsoft/ApplicationInsights-dotnet #2143) és webelemzés és diagnosztika (a közösségi implementációkra mutató hivatkozásokat tartalmaz) (dotnet/aspnetcore #5461). Addig is az ügyféloldali alkalmazások használhatják a Application Insights JavaScript SDKJS interop segítségével, hogy közvetlenül az Application Insightsba naplózzanak hibákat egy ügyféloldali alkalmazásból.

‡Olyan kiszolgálóoldali ASP.NET Core-alkalmazásokra vonatkozik, amelyek webes API-háttéralkalmazások Blazor alkalmazásokhoz. Az ügyféloldali alkalmazások csapdába ejtik és elküldik a hibainformációkat egy webes API-nak, amely naplózza a hibainformációkat egy állandó naplózási szolgáltatónak.

Helyek, ahol hibák léphetnek fel

A keretrendszer és az alkalmazáskód kezeletlen kivételeket válthat ki az alábbi helyek bármelyikén, amelyeket a cikk következő szakaszai részletesen ismertetnek:

Komponens példányosítás

Amikor Blazor létrehoz egy összetevő egy példányát:

  • A rendszer meghívja az összetevő konstruktorát.
  • A rendszer meghívja az összetevő konstruktorának a @inject irányelv vagy a [Inject] attribútum keresztül nyújtott DI-szolgáltatások konstruktorait.

Egy végrehajtott konstruktorban vagy egy [Inject] tulajdonság beállításában megjelenő hiba kezeletlen kivételt eredményez, és megakadályozza, hogy a keretrendszer példányosítsa az összetevőt. Ha az alkalmazás egy kapcsolatcsoporton keresztül működik, a kapcsolatcsoport meghibásodik. Ha a konstruktorlogika kivételeket okozhat, az alkalmazásnak egy try-catch utasítással kell csapdába ejtenie a kivételeket hibakezeléssel és naplózással.

Életciklus-módszerek

Egy összetevő élettartama alatt Blazor meghívja életciklus-metódusokat. Ha egy életciklus-metódus kivételt okoz szinkron vagy aszinkron módon, a kivétel végzetes a kapcsolatcsoport számára. Annak érdekében, hogy az összetevők kezelni tudják az életciklus-módszerek hibáit, adja hozzá a hibakezelési logikát.

A következő példában, ahol OnParametersSetAsync egy termék beszerzésére szolgáló metódust hív meg:

  • A ProductRepository.GetProductByIdAsync metódusban bevezetett kivételeket egy try-catch utasítás kezeli.
  • A catch blokk végrehajtásakor:
    • loadFailed trueértékre van állítva, amely a felhasználónak szóló hibaüzenet megjelenítésére szolgál.
    • A hiba naplózva van.
@page "/product-details/{ProductId:int?}"
@inject ILogger<ProductDetails> Logger
@inject IProductRepository Product

<PageTitle>Product Details</PageTitle>

<h1>Product Details Example</h1>

@if (details != null)
{
    <h2>@details.ProductName</h2>
    <p>
        @details.Description
        <a href="@details.Url">Company Link</a>
    </p>
    
}
else if (loadFailed)
{
    <h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
    <h1>Loading...</h1>
}

@code {
    private ProductDetail? details;
    private bool loadFailed;

    [Parameter]
    public int ProductId { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        try
        {
            loadFailed = false;

            // Reset details to null to display the loading indicator
            details = null;

            details = await Product.GetProductByIdAsync(ProductId);
        }
        catch (Exception ex)
        {
            loadFailed = true;
            Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
        }
    }

    public class ProductDetail
    {
        public string? ProductName { get; set; }
        public string? Description { get; set; }
        public string? Url { get; set; }
    }

    /*
    * Register the service in Program.cs:
    * using static BlazorSample.Components.Pages.ProductDetails;
    * builder.Services.AddScoped<IProductRepository, ProductRepository>();
    */

    public interface IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id);
    }

    public class ProductRepository : IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id)
        {
            return Task.FromResult(
                new ProductDetail()
                {
                    ProductName = "Flowbee ",
                    Description = "The Revolutionary Haircutting System You've Come to Love!",
                    Url = "https://flowbee.com/"
                });
        }
    }
}
@page "/product-details/{ProductId:int?}"
@inject ILogger<ProductDetails> Logger
@inject IProductRepository Product

<PageTitle>Product Details</PageTitle>

<h1>Product Details Example</h1>

@if (details != null)
{
    <h2>@details.ProductName</h2>
    <p>
        @details.Description
        <a href="@details.Url">Company Link</a>
    </p>
    
}
else if (loadFailed)
{
    <h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
    <h1>Loading...</h1>
}

@code {
    private ProductDetail? details;
    private bool loadFailed;

    [Parameter]
    public int ProductId { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        try
        {
            loadFailed = false;

            // Reset details to null to display the loading indicator
            details = null;

            details = await Product.GetProductByIdAsync(ProductId);
        }
        catch (Exception ex)
        {
            loadFailed = true;
            Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
        }
    }

    public class ProductDetail
    {
        public string? ProductName { get; set; }
        public string? Description { get; set; }
        public string? Url { get; set; }
    }

    /*
    * Register the service in Program.cs:
    * using static BlazorSample.Components.Pages.ProductDetails;
    * builder.Services.AddScoped<IProductRepository, ProductRepository>();
    */

    public interface IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id);
    }

    public class ProductRepository : IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id)
        {
            return Task.FromResult(
                new ProductDetail()
                {
                    ProductName = "Flowbee ",
                    Description = "The Revolutionary Haircutting System You've Come to Love!",
                    Url = "https://flowbee.com/"
                });
        }
    }
}
@page "/product-details/{ProductId:int}"
@using Microsoft.Extensions.Logging
@inject ILogger<ProductDetails> Logger
@inject IProductRepository ProductRepository

@if (details != null)
{
    <h1>@details.ProductName</h1>
    <p>@details.Description</p>
}
else if (loadFailed)
{
    <h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
    <h1>Loading...</h1>
}

@code {
    private ProductDetail? details;
    private bool loadFailed;

    [Parameter]
    public int ProductId { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        try
        {
            loadFailed = false;

            // Reset details to null to display the loading indicator
            details = null;

            details = await ProductRepository.GetProductByIdAsync(ProductId);
        }
        catch (Exception ex)
        {
            loadFailed = true;
            Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
        }
    }

    public class ProductDetail
    {
        public string? ProductName { get; set; }
        public string? Description { get; set; }
    }

    public interface IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id);
    }
}
@page "/product-details/{ProductId:int}"
@using Microsoft.Extensions.Logging
@inject ILogger<ProductDetails> Logger
@inject IProductRepository ProductRepository

@if (details != null)
{
    <h1>@details.ProductName</h1>
    <p>@details.Description</p>
}
else if (loadFailed)
{
    <h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
    <h1>Loading...</h1>
}

@code {
    private ProductDetail? details;
    private bool loadFailed;

    [Parameter]
    public int ProductId { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        try
        {
            loadFailed = false;

            // Reset details to null to display the loading indicator
            details = null;

            details = await ProductRepository.GetProductByIdAsync(ProductId);
        }
        catch (Exception ex)
        {
            loadFailed = true;
            Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
        }
    }

    public class ProductDetail
    {
        public string? ProductName { get; set; }
        public string? Description { get; set; }
    }

    public interface IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id);
    }
}
@page "/product-details/{ProductId:int}"
@using Microsoft.Extensions.Logging
@inject ILogger<ProductDetails> Logger
@inject IProductRepository ProductRepository

@if (details != null)
{
    <h1>@details.ProductName</h1>
    <p>@details.Description</p>
}
else if (loadFailed)
{
    <h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
    <h1>Loading...</h1>
}

@code {
    private ProductDetail details;
    private bool loadFailed;

    [Parameter]
    public int ProductId { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        try
        {
            loadFailed = false;

            // Reset details to null to display the loading indicator
            details = null;

            details = await ProductRepository.GetProductByIdAsync(ProductId);
        }
        catch (Exception ex)
        {
            loadFailed = true;
            Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
        }
    }

    public class ProductDetail
    {
        public string ProductName { get; set; }
        public string Description { get; set; }
    }

    public interface IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id);
    }
}
@page "/product-details/{ProductId:int}"
@using Microsoft.Extensions.Logging
@inject ILogger<ProductDetails> Logger
@inject IProductRepository ProductRepository

@if (details != null)
{
    <h1>@details.ProductName</h1>
    <p>@details.Description</p>
}
else if (loadFailed)
{
    <h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
    <h1>Loading...</h1>
}

@code {
    private ProductDetail details;
    private bool loadFailed;

    [Parameter]
    public int ProductId { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        try
        {
            loadFailed = false;

            // Reset details to null to display the loading indicator
            details = null;

            details = await ProductRepository.GetProductByIdAsync(ProductId);
        }
        catch (Exception ex)
        {
            loadFailed = true;
            Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
        }
    }

    public class ProductDetail
    {
        public string ProductName { get; set; }
        public string Description { get; set; }
    }

    public interface IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id);
    }
}

Renderelési logika

A deklaratív korrektúra egy Razor összetevőfájlban (.razor) egy BuildRenderTreenevű C#-metódusba lesz lefordítva. Amikor egy összetevő renderel, BuildRenderTree végrehajt és létrehoz egy adatstruktúrát, amely leírja a renderelt összetevő elemeit, szövegét és gyermekösszetevőit.

A renderelési logika kivételt okozhat. Erre a forgatókönyvre példa az, amikor @someObject.PropertyName ki van értékelve, és @someObjectnull. A kapcsolatcsoporton keresztül működő Blazor alkalmazások esetében a renderelési logika által okozott kezeletlen kivétel végzetes az alkalmazás kapcsolatcsoportja számára.

Annak érdekében, hogy megelőzze a NullReferenceException hibát a megjelenítési logikában, ellenőrizze, hogy létezik-e null objektum, mielőtt hozzáfér a tagjaihoz. Az alábbi példában person.Address tulajdonságok nem érhetők el, ha person.Addressnull:

@if (person.Address != null)
{
    <div>@person.Address.Line1</div>
    <div>@person.Address.Line2</div>
    <div>@person.Address.City</div>
    <div>@person.Address.Country</div>
}

Az előző kód feltételezi, hogy person nem null. A kód szerkezete gyakran garantálja, hogy egy objektum az összetevő renderelésének időpontjában létezik. A renderelési logikában ezekben az esetekben nem szükséges ellenőrizni a null-t. Az előző példában person garantáltan létezik, mert person jön létre az összetevő példányosításakor, ahogy az alábbi példa mutatja:

@code {
    private Person person = new();

    ...
}

Eseménykezelők

A kliensoldali kód C# kód meghívását váltja ki, amikor eseménykezelők jönnek létre a következő módon:

  • @onclick
  • @onchange
  • Egyéb @on... attribútumok
  • @bind

Az eseménykezelő kódja nem kezelt kivételt dobhat ezekben az esetekben.

Ha az alkalmazás olyan kódot hív meg, amely külső okokból meghiúsulhat, a kivételeket a try-catch utasítással fogja el, amely hibakezelést és naplózást tartalmaz.

Ha egy eseménykezelő kezeletlen kivételt dob (például, ha egy adatbázis-lekérdezés meghiúsul), és azt nem fogja fel és kezeli a fejlesztői kód:

  • A keretrendszer naplózza a kivételt.
  • Egy áramkörön keresztül működő Blazor alkalmazásban a kivétel végzetes az alkalmazás áramkörére nézve.

Összetevők ártalmatlanítása

Előfordulhat például, hogy egy összetevő el lesz távolítva a felhasználói felületről, mert a felhasználó egy másik oldalra navigált. Ha egy System.IDisposable megvalósító összetevő el lesz távolítva a felhasználói felületről, a keretrendszer meghívja az összetevő Dispose metódusát.

Ha az összetevő Dispose metódusa kezeletlen kivételt eredményez egy kapcsolatcsoporton futó Blazor-alkalmazásban, a kivétel végzetes az alkalmazás kapcsolatcsoportja számára.

Ha az ártalmatlanítási logika kivételeket okozhat, az alkalmazásnak egy try-catch utasítással kell csapdába ejtenie a kivételeket hibakezeléssel és naplózással.

További információt az összetevők ártalmatlanításáról az ASP.NET Core Razor összetevők ártalmatlanításarészben találhat.

JavaScript interop

A IJSRuntime-t a Blazor keretrendszer regisztrálja. IJSRuntime.InvokeAsync lehetővé teszi, hogy a .NET-kód aszinkron hívásokat kezdeményezhessen a JavaScript (JS) futtatókörnyezetbe a felhasználó böngészőjében.

A InvokeAsynchibakezelésére a következő feltételek vonatkoznak:

  • Ha a InvokeAsync hívása szinkron módon meghiúsul, .NET-kivétel lép fel. Előfordulhat például, hogy a InvokeAsync hívása meghiúsul, mert a megadott argumentumok nem szerializálhatók. A fejlesztői kódnak el kell kapnia a kivételt. Ha egy eseménykezelő vagy összetevő életciklus-metódusának alkalmazáskódja nem kezeli a kapcsolatcsoporton keresztül működő Blazor-alkalmazások kivételeit, az ebből eredő kivétel végzetes az alkalmazás kapcsolatcsoportja számára.
  • Ha a InvokeAsync hívása aszinkron módon meghiúsul, a .NET Task meghiúsul. Előfordulhat, hogy a InvokeAsync hívása meghiúsul, például, mert a JSoldali kód kivételt dob, vagy visszaad egy Promise-t, amely rejected-ként fejeződött be. A fejlesztői kódnak el kell kapnia a kivételt. Ha a await operátort használja, fontolja meg a metódushívás try-catch utasításba való befuttatását hibakezeléssel és naplózással. Ellenkező esetben egy kapcsolatcsoporton keresztül működő Blazor-alkalmazásokban a hibás kód kezeletlen kivételt eredményez, amely végzetes az alkalmazás kapcsolatcsoportja számára.
  • A InvokeAsync hívásainak egy bizonyos időszakon belül kell befejeződniük, különben a hívás túllépi az időkorlátot. Az alapértelmezett időtúllépési időszak egy perc. Az időkorlát védi a kódot a hálózati kapcsolat megszakadása ellen, vagy az olyan JS kód ellen, amely soha nem küld vissza befejezési üzenetet. Ha a hívás túllépi az időkorlátot, akkor az eredményként kapott System.Threading.TasksOperationCanceledExceptionhibával meghiúsul. A kivétel naplózással való elkapása és feldolgozása.

Hasonlóképpen JS kód kezdeményezhet hívásokat a .NET metódusokhoz, amelyeket a [JSInvokable] attribútumjelöl. Ha ezek a .NET-metódusok kezeletlen kivételt eredményeznek:

  • Egy kapcsolatcsoporton keresztül működő Blazor-alkalmazásokban a kivétel nem minősül halálos kimenetelűnek az alkalmazás kapcsolatcsoportja számára.
  • A JS-side Promise elutasításra kerül.

Lehetősége van hibakezelő kód használatára a .NET oldalon vagy a metódushívás JS oldalán.

További információkért lásd a következő cikkeket:

Előrendezés

A Razor összetevői alapértelmezés szerint előre renderelve vannak, így a renderelt HTML-jelölés a felhasználó kezdeti HTTP-kérésének részeként van visszaadva.

Egy áramkör felett működő Blazor alkalmazásban a prerenderelés a következő módon működik:

  • Új áramkör létrehozása az ugyanazon lap részét képező összes előre renderelt összetevő számára.
  • A kezdeti HTML létrehozása.
  • Az áramkört disconnected-ként kezeljük, amíg a felhasználó böngészője létre nem hozza a SignalR kapcsolatot ugyanahhoz a kiszolgálóhoz. A kapcsolat létrejötte után az áramkör interaktivitása folytatódik, és az összetevők HTML jelölése frissül.

Előrerendelt ügyféloldali összetevők esetén az előrendelés a következőkkel működik:

  • Kezdeti HTML-fájl létrehozása a kiszolgálón az ugyanazon lap részét képező összes előre beállított összetevőhöz.
  • Az összetevő interaktívvá tétele a kliensoldalon, amikor a böngésző betöltötte a háttérben az alkalmazás lefordított kódját és a .NET-futtatókörnyezetet (ha még nem töltődött be).

Ha egy összetevő nem kezelt kivételt jelez az előrendelés során, például életciklus-módszer vagy renderelési logika során:

  • Egy kapcsolatcsoporton keresztül működő Blazor-alkalmazásokban a kivétel végzetes a kapcsolatcsoport számára. Az előre beállított ügyféloldali összetevők esetében a kivétel megakadályozza az összetevő renderelését.
  • A kivétel feljebb kerül a hívásveremen a ComponentTagHelper-tól.

Normál körülmények között, amikor az előrendelés meghiúsul, az összetevő továbbépítése és renderelése nem logikus, mert egy működő összetevő nem jeleníthető meg.

Az előrendelés során előforduló hibák elviseléséhez a hibakezelési logikát egy olyan összetevőbe kell helyezni, amely kivételeket okozhat. Hibakezeléssel és naplózással használja a try-catch utasításokat. Ahelyett, hogy egy ComponentTagHelper utasításba csomagolná a try-catch, helyezze el a hibakezelési logikát a ComponentTagHelperáltal renderelt összetevőben.

Speciális forgatókönyvek

Rekurzív renderelés

Az összetevők rekurzív módon ágyazhatók be. Ez rekurzív adatstruktúrák ábrázolásához hasznos. Egy TreeNode összetevő például több TreeNode összetevőt jeleníthet meg a csomópont egyes gyermekei számára.

Rekurzív rendereléskor kerülje a végtelen rekurziót eredményező kódolási mintákat:

  • Ne jelenítsen meg rekurzív módon egy ciklust tartalmazó adatstruktúrát. Például ne jelenítsen meg olyan facsomópontot, amelynek a gyermekei magukra is kiterjednek.
  • Ne hozzon létre ciklust tartalmazó elrendezésláncot. Például ne hozzon létre olyan elrendezést, amely saját magát ábrázolja.
  • Ne engedélyezze a végfelhasználók számára a rekurziós invariánsok (szabályok) megsértését rosszindulatú adatbevitel vagy JavaScript-interop hívások révén.

Végtelen hurkok a renderelés során:

  • A renderelési folyamat örökre folytatódik.
  • Egyenértékű egy befejezetlen hurok létrehozásával.

Ezekben a forgatókönyvekben a Blazor meghiúsul, és általában a következőkre próbál meg kísérletet tenni:

  • Használj fel annyi processzoridőt, amennyit az operációs rendszer megenged, határozatlan ideig.
  • Korlátlan mennyiségű memóriát használ. A korlátlan memóriahasználat egyenértékű azzal a forgatókönyvvel, amikor egy nem felügyelt ciklus minden iterációban bejegyzéseket ad hozzá egy gyűjteményhez.

A végtelen rekurziós minták elkerülése érdekében győződjön meg arról, hogy a rekurzív renderelési kód megfelelő leállási feltételeket tartalmaz.

Egyéni render fa logika

A legtöbb Razor összetevő Razor összetevőfájlként (.razor) van implementálva, és a keretrendszer fordítja le a logika előállítása érdekében, amely a RenderTreeBuilder-on működik a kimenet megjelenítésére. A fejlesztők azonban manuálisan implementálhatják RenderTreeBuilder logikát eljárási C# kóddal. További információ: ASP.NET Core Blazor speciális forgatókönyvek (renderelési fa felépítése).

Figyelmeztetés

A manuális renderelési faszerkesztő logika használata speciális és nem biztonságos forgatókönyvnek számít, általános összetevő-fejlesztéshez nem ajánlott.

Ha RenderTreeBuilder kód meg van írva, a fejlesztőnek garantálnia kell a kód helyességét. A fejlesztőnek például biztosítania kell, hogy:

  • A OpenElement és CloseElement hívásai megfelelően kiegyensúlyozottak.
  • Az attribútumok csak a megfelelő helyeken vannak hozzáadva.

A helytelen manuális renderelési faszerkesztői logika tetszőleges, nem definiált viselkedést okozhat, beleértve az összeomlásokat, az alkalmazás vagy a kiszolgáló válaszának leállítását, valamint a biztonsági réseket.

Vegye figyelembe a manuális renderelés faszerkesztő logikáját ugyanazon az összetettségi szinten, és ugyanolyan szintű veszélyekkel, mint a szerelvénykód vagy Microsoft Köztes nyelv (MSIL) utasítások kézzel történő megírása.

További erőforrások

†A háttérrendszerbeli ASP.NET Core web API-alkalmazásokra vonatkozik, amelyeket az ügyféloldali Blazor alkalmazások a naplózáshoz használnak.