Sdílet prostřednictvím


Zpracování chyb v aplikacích ASP.NET Core Blazor

Poznámka:

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

Důležité

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

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

Tento článek popisuje, jak spravovat neošetřené výjimky a jak Blazor vyvíjet aplikace, které detekují a zpracovávají chyby.

Podrobné chyby během vývoje

Blazor Pokud aplikace během vývoje nefunguje správně, pomůže vám při řešení potíží a opravě problému získat podrobné informace o chybách. Když dojde k chybě, Blazor aplikace zobrazí světle žlutý pruh v dolní části obrazovky:

  • Během vývoje vás panel přesměruje na konzolu prohlížeče, kde se zobrazí výjimka.
  • V produkčním prostředí panel upozorní uživatele, že došlo k chybě, a doporučí aktualizaci prohlížeče.

Uživatelské rozhraní pro toto zpracování chyb je součástí Blazor šablon projektů. Ne všechny verze Blazor šablon projektů používají data-nosnippet atribut k signálu prohlížečům, aby neukázely obsah uživatelského rozhraní chyby, ale všechny verze Blazor dokumentace tento atribut používají.

Blazor Ve webové aplikaci přizpůsobte prostředí komponentyMainLayout. Protože pomocné rutiny značek prostředí (například<environment include="Production">...</environment>) nejsou v Razor komponentách podporované, následující příklad vloží IHostEnvironment do konfigurace chybových zpráv pro různá prostředí.

V horní části :MainLayout.razor

@inject IHostEnvironment HostEnvironment

Vytvořte nebo upravte Blazor kód uživatelského rozhraní chyby:

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

Blazor Server V aplikaci upravte prostředí v Pages/_Host.cshtml souboru. Následující příklad používá pomocníka značky prostředí ke konfiguraci chybových zpráv pro různá prostředí.

Blazor Server V aplikaci upravte prostředí v Pages/_Layout.cshtml souboru. Následující příklad používá pomocníka značky prostředí ke konfiguraci chybových zpráv pro různá prostředí.

Blazor Server V aplikaci upravte prostředí v Pages/_Host.cshtml souboru. Následující příklad používá pomocníka značky prostředí ke konfiguraci chybových zpráv pro různá prostředí.

Vytvořte nebo upravte Blazor kód uživatelského rozhraní chyby:

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

Blazor WebAssembly V aplikaci přizpůsobte prostředí v wwwroot/index.html souboru:

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

Element blazor-error-ui je obvykle skrytý z důvodu přítomnosti display: none stylu blazor-error-ui třídy CSS v automaticky generované šabloně stylů aplikace. Pokud dojde k chybě, architektura se vztahuje display: block na prvek.

Element blazor-error-ui je obvykle skrytý kvůli přítomnosti display: none stylu blazor-error-ui třídy CSS v šabloně stylů webu ve wwwroot/css složce. Pokud dojde k chybě, architektura se vztahuje display: block na prvek.

Podrobné chyby okruhu

Tato část se vztahuje na Blazor webové aplikace provozující přes okruh.

Tato část se týká Blazor Server aplikací.

Chyby na straně klienta nezahrnují zásobník volání a neposkytují podrobnosti o příčině chyby, ale protokoly serveru tyto informace obsahují. Pro účely vývoje mohou být informace o chybách citlivého okruhu zpřístupněny klientovi povolením podrobných chyb.

Nastavte CircuitOptions.DetailedErrors na hodnotu true. Další informace a příklad najdete v ASP.NET BlazorSignalR základních doprovodných materiálech.

Alternativou k nastavení CircuitOptions.DetailedErrors je nastavení konfiguračního DetailedErrors klíče v true souboru nastavení prostředí aplikace Development (appsettings.Development.json). Kromě toho nastavte SignalR protokolování na straně serveru (Microsoft.AspNetCore.SignalR) na ladění nebo trasování pro podrobné SignalR protokolování.

appsettings.Development.json:

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

Konfigurační DetailedErrors klíč lze také nastavit tak, aby true používal ASPNETCORE_DETAILEDERRORS proměnnou prostředí s hodnotou true na Development/Staging serverech prostředí nebo v místním systému.

Upozorňující

Vždy se vyhněte zveřejnění informací o chybách klientům na internetu, což je bezpečnostní riziko.

Podrobné chyby pro Razor vykreslování na straně serveru komponent

Tato část platí pro Blazor Web Apps.

RazorComponentsServiceOptions.DetailedErrors Pomocí možnosti můžete řídit vytváření podrobných informací o chybách při Razor vykreslování na straně serveru. Výchozí hodnota je false.

Následující příklad umožňuje podrobné chyby:

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

Upozorňující

Povolte v prostředí jenom podrobné chyby Development . Podrobné chyby můžou obsahovat citlivé informace o aplikaci, kterou můžou uživatelé se zlými úmysly použít při útoku.

Předchozí příklad poskytuje stupeň bezpečnosti nastavením hodnoty DetailedErrors na základě hodnoty vrácené IsDevelopment. Když je aplikace v Development prostředí, DetailedErrors je nastavená na truehodnotu . Tento přístup není hloupý, protože je možné hostovat produkční aplikaci na veřejném serveru v Development prostředí.

Správa neošetřených výjimek v kódu vývojáře

Aby aplikace pokračovala po chybě, musí mít aplikace logiku zpracování chyb. Další části tohoto článku popisují potenciální zdroje neošetřených výjimek.

V produkčním prostředí nevykreslujte zprávy o výjimce architektury ani trasování zásobníku v uživatelském rozhraní. Vykreslování zpráv výjimek nebo trasování zásobníku může:

  • Zveřejnit citlivé informace koncovým uživatelům.
  • Pomozte uživateli se zlými úmysly zjistit slabá místa v aplikaci, která může ohrozit zabezpečení aplikace, serveru nebo sítě.

Neošetřené výjimky pro okruhy

Tato část se týká aplikací na straně serveru, které fungují přes okruh.

Razor komponenty s povolenou interaktivitou serveru jsou na serveru stavové. Zatímco uživatelé pracují s komponentou na serveru, udržují připojení k serveru známému jako okruh. Okruh obsahuje aktivní instance komponent a mnoho dalších aspektů stavu, například:

  • Nejnovější vykreslený výstup komponent.
  • Aktuální sada delegátů zpracování událostí, které by mohly být aktivovány událostmi na straně klienta.

Pokud uživatel aplikaci otevře na několika kartách prohlížeče, uživatel vytvoří více nezávislých okruhů.

Blazor považuje většinu neošetřených výjimek za závažnou pro okruh, ve kterém k nim dochází. Pokud se okruh ukončí kvůli neošetřené výjimce, uživatel může s aplikací dál pracovat, a to tak, že stránku znovu načte a vytvoří nový okruh. Okruhy mimo okruhy ukončené, což jsou okruhy pro jiné uživatele nebo jiné karty prohlížeče, to neovlivní. Tento scénář je podobný desktopové aplikaci, která se chybově ukončí. Aplikace s chybovým ukončením se musí restartovat, ale jiné aplikace to neovlivní.

Architektura ukončí okruh, pokud dojde k neošetřené výjimce z následujících důvodů:

  • Neošetřená výjimka často opouští okruh v nedefinovaném stavu.
  • Normální provoz aplikace není možné zaručit po neošetřené výjimce.
  • Pokud okruh pokračuje v nedefinovaném stavu, můžou se v aplikaci objevit ohrožení zabezpečení.

Globální zpracování výjimek

Globální zpracování výjimek najdete v následujících částech:

Hranice chyb

Hranice chyb poskytují pohodlný přístup pro zpracování výjimek. Komponenta ErrorBoundary :

  • Vykreslí podřízený obsah v případě, že nedošlo k chybě.
  • Vykreslí uživatelské rozhraní chyby při vyvolání neošetřené výjimky.

K definování hranice chyby použijte komponentu k zabalení existujícího ErrorBoundary obsahu. Aplikace funguje normálně, ale hranice chyb zpracovává neošetřené výjimky.

<ErrorBoundary>
    ...
</ErrorBoundary>

Pokud chcete implementovat hranici chyb v globálním měřítku, přidejte hranici kolem základního obsahu hlavního rozložení aplikace.

V MainLayout.razor:

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

Ve Blazor službě Web Apps s chybovou hranicí použitou pouze pro statickou MainLayout komponentu je hranice aktivní pouze během fáze statického vykreslování na straně serveru (statická služba SSR). Hranice se neaktivuje, protože komponenta dále v hierarchii komponent je interaktivní. Pokud chcete pro komponentu MainLayout a zbytek komponent dál v hierarchii komponent povolit interaktivitu, povolte interaktivní vykreslování instancí HeadOutletRoutes komponent v App komponentě (Components/App.razor). Následující příklad přijímá režim vykreslování Interactive Server (InteractiveServer):

<HeadOutlet @rendermode="InteractiveServer" />

...

<Routes @rendermode="InteractiveServer" />

Pokud nechcete povolit interaktivitu serveru v celé aplikaci z Routes komponenty, umístěte hranici chyby dále dolů do hierarchie komponent. Například umístěte hranici chyby kolem značek v jednotlivých komponentách, které umožňují interaktivitu, ne v hlavním rozložení aplikace. Důležité koncepty, které je potřeba mít na paměti, jsou, že všude, kde je umístěna hranice chyby:

  • Pokud hranice chyby není interaktivní, je schopná aktivovat pouze na serveru během statického vykreslování. Hranice se například může aktivovat, když dojde k chybě v metodě životního cyklu součásti.
  • Pokud je hranice chyby interaktivní, je schopná aktivovat interaktivní součásti vykreslené interaktivním serverem, které zabalí.

Podívejte se na následující příklad, kde Counter komponenta vyvolá výjimku, pokud počet zvýší po pěti.

V Counter.razor:

private void IncrementCount()
{
    currentCount++;

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

Pokud je neošetřená výjimka vyvolán pro currentCount více než pět:

  • Chyba se protokoluje normálně (System.InvalidOperationException: Current count is too big!).
  • Výjimku zpracovává hranice chyby.
  • Uživatelské rozhraní chyby je vykresleno hranicí chyby s následující výchozí chybovou zprávou: An error has occurred.

Ve výchozím nastavení komponenta ErrorBoundary vykresluje prázdný <div> prvek s blazor-error-boundary třídou CSS pro svůj chybový obsah. Barvy, text a ikona výchozího uživatelského rozhraní se definují pomocí šablon stylů CSS v šabloně stylů aplikace ve wwwroot složce, takže můžete uživatelské rozhraní chyby přizpůsobit.

Změňte výchozí obsah chyby nastavením ErrorContent vlastnosti:

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

Vzhledem k tomu, že hranice chyby je definována v rozložení v předchozích příkladech, uživatelské rozhraní chyby se zobrazí bez ohledu na to, na kterou stránku uživatel přejde po chybě. Ve většině scénářů doporučujeme zúžit hranice chyb. Pokud v širokém rozsahu rozsahu hranice chyby, můžete ho resetovat do stavu, který není chybový u následných událostí navigace na stránce volání metody hranice Recover chyby.

V MainLayout.razor:

...

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

...

@code {
    private ErrorBoundary? errorBoundary;

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

Abyste se vyhnuli nekonečné smyčce, kdy obnovení pouze rerenderuje komponentu, která chybu znovu vyvolá, nezavolejte Recover z logiky vykreslování. Zavolat Recover pouze v případech:

  • Uživatel provede gesto uživatelského rozhraní, například výběrem tlačítka, které označuje, že chce zopakovat proceduru nebo když uživatel přejde na novou komponentu.
  • Další logika také vymaže výjimku. Pokud je komponenta znovu vysunutá, chyba se znovu nezobrazí.

Alternativní globální zpracování výjimek

Alternativou k použití hranic chyb (ErrorBoundary) je předání vlastní chybové komponenty jako podřízeným komponentám CascadingValue . Výhodou použití komponenty oproti použití vložené služby nebo vlastní implementace protokolovacího modulu je, že kaskádová komponenta může vykreslit obsah a použít styly CSS v případě, že dojde k chybě.

Následující Error příklad komponenty pouze protokoluje chyby, ale metody komponenty mohou zpracovávat chyby jakýmkoli způsobem vyžadovaným aplikací, včetně použití více metod zpracování chyb.

Error.razor:

@inject ILogger<Error> Logger

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

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

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

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

Poznámka:

Další informace o RenderFragmentkomponentách ASP.NET CoreRazor.

V komponentě Routes zabalte komponentu Router (<Router>...</Router>) s komponentou Error . To umožňuje komponentě Error kaskádovitě dolů na libovolnou komponentu aplikace, kde Error je komponenta přijata jako CascadingParameter.

V Routes.razor:

<Error>
    <Router ...>
        ...
    </Router>
</Error>

V komponentě App zabalte komponentu Router (<Router>...</Router>) s komponentou Error . To umožňuje komponentě Error kaskádovitě dolů na libovolnou komponentu aplikace, kde Error je komponenta přijata jako CascadingParameter.

V App.razor:

<Error>
    <Router ...>
        ...
    </Router>
</Error>

Zpracování chyb v komponentě:

  • Označte komponentu Error jako CascadingParameter součást v @code bloku. Do ukázkové Counter komponenty v aplikaci založené na Blazor šabloně projektu přidejte následující Error vlastnost:

    [CascadingParameter]
    public Error? Error { get; set; }
    
  • Volejte metodu zpracování chyb v libovolném catch bloku s odpovídajícím typem výjimky. Ukázková Error komponenta nabízí pouze jednu ProcessError metodu, ale komponenta zpracování chyb může poskytnout libovolný počet metod zpracování chyb, které řeší alternativní požadavky na zpracování chyb v celé aplikaci. V následujícím Counter příkladu komponenty je vyvolána výjimka a zachycena, pokud je počet větší než pět:

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

Pokud použijete předchozí Error komponentu s předchozími změnami provedenými v komponentě Counter , konzola vývojářských nástrojů prohlížeče indikuje zachytěnou a protokolovanou chybu:

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

Pokud se ProcessError metoda přímo účastní vykreslování, například zobrazení vlastního panelu chybových zpráv nebo změna stylů CSS vykreslených prvků, volání StateHasChanged na konci ProcessErrors metody pro opětovné vykreslení uživatelského rozhraní.

Protože přístupy v této části zpracovávají chyby pomocí try-catch příkazu, připojení aplikace SignalR mezi klientem a serverem není přerušeno, když dojde k chybě a okruh zůstane aktivní. Ostatní neošetřené výjimky zůstávají pro okruh závažnou. Další informace najdete v části o tom, jak okruh reaguje na neošetřené výjimky.

Aplikace může použít komponentu zpracování chyb jako kaskádovou hodnotu ke zpracování chyb centralizovaným způsobem.

Následující Error komponenta se předává jako podřízené CascadingValue komponenty. Následující příklad pouze protokoluje chybu, ale metody komponenty mohou zpracovávat chyby jakýmkoli způsobem vyžadovaným aplikací, včetně použití více metod zpracování chyb. Výhodou použití komponenty oproti použití vložené služby nebo vlastní implementace protokolovacího modulu je, že kaskádová komponenta může vykreslit obsah a použít styly CSS v případě, že dojde k chybě.

Error.razor:

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

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

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

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

Poznámka:

Další informace o RenderFragmentkomponentách ASP.NET CoreRazor.

V této komponentě App zabalte Router komponentu s komponentou Error . To umožňuje komponentě Error kaskádovitě dolů na libovolnou komponentu aplikace, kde Error je komponenta přijata jako CascadingParameter.

App.razor:

<Error>
    <Router ...>
        ...
    </Router>
</Error>

Zpracování chyb v komponentě:

  • Označte komponentu Error jako CascadingParameter součást v @code bloku:

    [CascadingParameter]
    public Error Error { get; set; }
    
  • Volejte metodu zpracování chyb v libovolném catch bloku s odpovídajícím typem výjimky. Ukázková Error komponenta nabízí pouze jednu ProcessError metodu, ale komponenta zpracování chyb může poskytnout libovolný počet metod zpracování chyb, které řeší alternativní požadavky na zpracování chyb v celé aplikaci.

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

Pomocí předchozí ukázkové Error komponenty a ProcessError metody označuje konzola vývojářských nástrojů prohlížeče zachycenou zaprotokolovanou chybu:

selhání: BlazorSample.Shared.Error[0] Error:ProcessError – Typ: System.NullReferenceException Message: Odkaz na objekt není nastaven na instanci objektu.

Pokud se ProcessError metoda přímo účastní vykreslování, například zobrazení vlastního panelu chybových zpráv nebo změna stylů CSS vykreslených prvků, volání StateHasChanged na konci ProcessErrors metody pro opětovné vykreslení uživatelského rozhraní.

Protože přístupy v této části zpracovávají chyby pomocí try-catch příkazu, připojení aplikace SignalR mezi klientem a serverem není přerušeno, Blazor když dojde k chybě a okruh zůstane aktivní. Jakákoli neošetřená výjimka je pro okruh závažná. Další informace najdete v části o tom, jak okruh reaguje na neošetřené výjimky.

Chyby protokolu s trvalým zprostředkovatelem

Pokud dojde k neošetřené výjimce, zaprotokoluje ILogger se do instancí nakonfigurovaných v kontejneru služby. Ve výchozím nastavení Blazor se aplikace protokolují do výstupu konzoly pomocí zprostředkovatele protokolování konzoly. Zvažte protokolování do umístění na serveru (nebo back-endovém webovém rozhraní API pro aplikace na straně klienta) s poskytovatelem, který spravuje velikost protokolu a obměnu protokolů. Aplikace může také používat službu Application Performance Management (APM), například Aplikace Azure Přehledy (Azure Monitor).

Poznámka:

Nativní aplikace Přehledy funkce, které podporují aplikace na straně klienta a nativní Blazor podporu rozhraní pro Google Analytics, můžou být dostupné v budoucích verzích těchto technologií. Další informace najdete v tématu Podpora Přehledy aplikace na Blazor straně klienta WASM (microsoft/application Přehledy-dotnet #2143) a webové analýzy a diagnostiky (včetně odkazů na komunitní implementace) (dotnet/aspnetcore #5461). Mezitím může aplikace na straně klienta používat sadu Application Přehledy JavaScript SDK s JS interoperabilitou k protokolování chyb přímo do aplikace Přehledy aplikace z aplikace na straně klienta.

Během vývoje aplikace Blazor , která pracuje přes okruh, aplikace obvykle odesílá úplné podrobnosti o výjimkách do konzoly prohlížeče, aby pomohla při ladění. V produkčním prostředí se podrobné chyby neodesílají klientům, ale úplné podrobnosti o výjimce se protokolují na serveru.

Musíte se rozhodnout, které incidenty se mají protokolovat, a úroveň závažnosti protokolovaných incidentů. Nehostinní uživatelé můžou záměrně aktivovat chyby. Například nezaznamujte incident z chyby, kdy je v adrese URL komponenty, která zobrazuje podrobnosti o produktu, zadaná neznámá ProductId . Ne všechny chyby by se měly považovat za incidenty pro protokolování.

Další informace najdete v následujících článcích:

•Platí pro aplikace na straně Blazor serveru a další aplikace na straně serveru ASP.NET Core, které jsou back-endové aplikace webového rozhraní API pro Blazor. Aplikace na straně klienta můžou vystihot a odesílat informace o chybách klienta do webového rozhraní API, které protokoluje informace o chybách do trvalého zprostředkovatele protokolování.

Pokud dojde k neošetřené výjimce, zaprotokoluje ILogger se do instancí nakonfigurovaných v kontejneru služby. Ve výchozím nastavení Blazor se aplikace protokolují do výstupu konzoly pomocí zprostředkovatele protokolování konzoly. Zvažte protokolování do trvalejšího umístění na serveru odesláním informací o chybách do back-endového webového rozhraní API, které používá zprostředkovatele protokolování se správou velikostí protokolu a obměnou protokoly. Případně může back-endová webová aplikace API používat službu Application Performance Management (APM), jako je Aplikace Azure Přehledy (Azure Monitor)†, k zaznamenávání informací o chybách, které obdrží od klientů.

Musíte se rozhodnout, které incidenty se mají protokolovat, a úroveň závažnosti protokolovaných incidentů. Nehostinní uživatelé můžou záměrně aktivovat chyby. Například nezaznamujte incident z chyby, kdy je v adrese URL komponenty, která zobrazuje podrobnosti o produktu, zadaná neznámá ProductId . Ne všechny chyby by se měly považovat za incidenty pro protokolování.

Další informace najdete v následujících článcích:

†Native Application Přehledy features to support client-side apps and native Blazor framework support for Google Analytics might be available in future release of these technologies. Další informace najdete v tématu Podpora Přehledy aplikace na Blazor straně klienta WASM (microsoft/application Přehledy-dotnet #2143) a webové analýzy a diagnostiky (včetně odkazů na komunitní implementace) (dotnet/aspnetcore #5461). Mezitím může aplikace na straně klienta používat sadu Application Přehledy JavaScript SDK s JS interoperabilitou k protokolování chyb přímo do aplikace Přehledy aplikace z aplikace na straně klienta.

•Platí pro aplikace na straně serveru ASP.NET Core, které jsou back-endové aplikace webového rozhraní API pro Blazor aplikace. Aplikace na straně klienta soutisknou a odesílají informace o chybách do webového rozhraní API, které protokoluje informace o chybě do trvalého zprostředkovatele protokolování.

Místa, kde mohou nastat chyby

Kód architektury a aplikace může aktivovat neošetřené výjimky v některém z následujících umístění, které jsou popsány dále v následujících částech tohoto článku:

Vytváření instancí komponent

Při Blazor vytváření instance komponenty:

  • Vyvolá se konstruktor komponenty.
  • Konstruktory služeb DI poskytované konstruktoru komponenty prostřednictvím @inject direktivy nebo atributu [Inject] jsou vyvolány.

Chyba v spuštěném konstruktoru nebo setter pro libovolnou [Inject] vlastnost má za následek neošetřenou výjimku a zastaví architekturu v vytvoření instance komponenty. Pokud aplikace pracuje přes okruh, okruh selže. Pokud logika konstruktoru může vyvolat výjimky, měla by aplikace výjimku spojit pomocí try-catch příkazu s zpracováním chyb a protokolováním.

Metody životního cyklu

Během životnosti komponenty Blazor vyvolá metody životního cyklu. Pokud nějaká metoda životního cyklu vyvolá výjimku, synchronně nebo asynchronně, je výjimka pro okruh závažná. Aby komponenty řešily chyby v metodách životního cyklu, přidejte logiku zpracování chyb.

V následujícím příkladu, kde OnParametersSetAsync volá metodu pro získání produktu:

  • Výjimku vyvolanou v ProductRepository.GetProductByIdAsync metodě zpracovává try-catch příkaz.
  • catch Při spuštění bloku:
    • loadFailed je nastavena na truehodnotu , která slouží k zobrazení chybové zprávy uživateli.
    • Chyba se zaprotokoluje.
@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);
    }
}

Logika vykreslování

Deklarativní kód v Razor souboru komponenty (.razor) je zkompilován do metody jazyka C# volaný BuildRenderTree. Když se komponenta vykreslí, BuildRenderTree spustí a sestaví datovou strukturu popisující prvky, text a podřízené součásti vykreslené komponenty.

Logika vykreslování může vyvolat výjimku. Příkladem tohoto scénáře je @someObject.PropertyName vyhodnocení, ale @someObject je null. U Blazor aplikací, které pracují přes okruh, je neošetřená výjimka vyvolaná logikou vykreslování pro okruh aplikace závažná.

Pokud chcete zabránit NullReferenceException v logice vykreslování, zkontrolujte null objekt před přístupem ke svým členům. V následujícím příkladu nejsou vlastnosti přístupné, person.Address pokud person.Address je null:

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

Předchozí kód předpokládá, že person není null. Struktura kódu často zaručuje, že objekt existuje v době vykreslení komponenty. V takových případech není nutné zkontrolovat null logiku vykreslování. V předchozím příkladu může být zaručeno, že existuje, person protože person se vytvoří při vytvoření instance komponenty, jak ukazuje následující příklad:

@code {
    private Person person = new();

    ...
}

Obslužné rutiny událostí

Kód na straně klienta aktivuje vyvolání kódu V# při vytváření obslužných rutin událostí pomocí:

  • @onclick
  • @onchange
  • Další @on... atributy
  • @bind

Kód obslužné rutiny události může v těchto scénářích vyvolat neošetřenou výjimku.

Pokud aplikace volá kód, který by mohl selhat z externích důvodů, vyvolá výjimku pomocí try-catch příkazu s zpracováním chyb a protokolováním.

Pokud obslužná rutina události vyvolá neošetřenou výjimku (například databázový dotaz selže), která není zachycená a zpracována kódem vývojáře:

  • Architektura zaznamená výjimku.
  • Blazor V aplikaci, která pracuje přes okruh, je výjimka pro okruh aplikace závažná.

Vyřazení součástí

Součást může být například odebrána z uživatelského rozhraní, protože uživatel přešel na jinou stránku. Pokud je komponenta, která implementuje System.IDisposable , odebrána z uživatelského rozhraní, volá rozhraní metodu Dispose komponenty.

Pokud metoda komponenty Dispose vyvolá neošetřenou výjimku v Blazor aplikaci, která pracuje přes okruh, je pro okruh aplikace závažná.

Pokud logika odstranění může vyvolat výjimky, měla by aplikace výjimku spojit pomocí try-catch příkazu s zpracováním chyb a protokolováním.

Další informace o odstranění součástí najdete v tématu ASP.NET životní Razor cyklus základních komponent.

Interoperabilita JavaScriptu

IJSRuntime je registrován v Blazor rámci. IJSRuntime.InvokeAsync umožňuje kódu .NET provádět asynchronní volání modulu runtime JavaScriptu (JS) v prohlížeči uživatele.

Následující podmínky platí pro zpracování chyb s InvokeAsync:

  • Pokud volání selže InvokeAsync synchronně, dojde k výjimce .NET. Volání InvokeAsync může například selhat, protože zadané argumenty nelze serializovat. Vývojářský kód musí zachytit výjimku. Pokud kód aplikace v obslužné rutině události nebo metodě životního cyklu komponent nezpracuje výjimku v aplikaci, která Blazor pracuje přes okruh, je výsledná výjimka pro okruh aplikace závažná.
  • Pokud volání selže InvokeAsync asynchronně, rozhraní .NET Task selže. Volání může selhat InvokeAsync , například proto, že JSkód -side vyvolá výjimku nebo vrátí dokončené Promise jako rejected. Vývojářský kód musí zachytit výjimku. Pokud používáte await operátor, zvažte zabalení volání metody do try-catch příkazu s zpracováním chyb a protokolováním. Jinak v Blazor aplikaci, která pracuje přes okruh, vede chybný kód k neošetřené výjimce, která je závažná pro okruh aplikace.
  • Ve výchozím nastavení musí volání InvokeAsync dokončit během určitého období nebo jinak vyprší časový limit volání. Výchozí časový limit je jedna minuta. Časový limit chrání kód před ztrátou síťového připojení nebo JS kódu, který nikdy neodesílá zprávu o dokončení. Pokud vyprší časový limit volání, výsledek System.Threading.Tasks selže s chybou OperationCanceledException. Soutisk a zpracování výjimky s protokolováním

JS Podobně může kód inicializovat volání metod .NET označených atributem[JSInvokable]. Pokud tyto metody .NET vyvolá neošetřenou výjimku:

  • V aplikaci, která Blazor pracuje přes okruh, není výjimka považována za závažnou pro okruh aplikace.
  • Strana JS-side Promise je odmítnuta.

Máte možnost použít kód zpracování chyb na straně .NET nebo JS na straně volání metody.

Další informace najdete v následujících článcích:

Předkreslování

Razor komponenty jsou ve výchozím nastavení předem vytyčovány tak, aby se jejich vykreslené kódy HTML vrátily jako součást počátečního požadavku HTTP uživatele.

Blazor V aplikaci, která pracuje přes okruh, funguje prerendering podle:

  • Vytvoření nového okruhu pro všechny předem připravené součásti, které jsou součástí stejné stránky.
  • Generuje se počáteční kód HTML.
  • S okruhem zacházíte tak, dokud disconnected prohlížeč uživatele nenaváže SignalR připojení zpět ke stejnému serveru. Po navázání připojení se obnoví interaktivita okruhu a aktualizuje se kód HTML komponent.

U předem vytyčovaných komponent na straně klienta funguje předkreslování:

  • Generování počátečního kódu HTML na serveru pro všechny předsdílené komponenty, které jsou součástí stejné stránky.
  • Vytvoření interaktivní komponenty na klientovi po načtení zkompilovaného kódu aplikace a modulu runtime .NET (pokud ještě není načteno) na pozadí.

Pokud komponenta při předřazení vyvolá neošetřenou výjimku, například během metody životního cyklu nebo v logice vykreslování:

  • Blazor V aplikaci, která pracuje přes okruh, je výjimka pro okruh závažná. U předsekvenovaných komponent na straně klienta výjimka zabraňuje vykreslení komponenty.
  • Výjimka vyvolá zásobník volání z objektu ComponentTagHelper.

Za normálních okolností, když se předdekretování nezdaří, nebude nadále sestavovat a vykreslovat komponentu, protože pracovní komponentu nelze vykreslit.

Aby bylo možné tolerovat chyby, ke kterým může dojít během předkreslování, musí být logika zpracování chyb umístěna uvnitř komponenty, která může vyvolat výjimky. Používejte try-catch příkazy se zpracováním chyb a protokolováním. Místo zabalení příkazu ComponentTagHelpertry-catch do příkazu umístěte logiku zpracování chyb v komponentě vykreslené ComponentTagHelpersadou .

Pokročilé scénáře

Rekurzivní vykreslování

Komponenty se dají vnořit rekurzivně. To je užitečné pro reprezentaci rekurzivních datových struktur. Například komponenta TreeNode může vykreslit více TreeNode komponent pro každou podřízenou položku uzlu.

Při rekurzivním vykreslování se vyhněte vzorům kódování, které vedou k nekonečné rekurzi:

  • Nevykreslujte rekurzivně datovou strukturu, která obsahuje cyklus. Například nevykreslujte uzel stromu, jehož podřízené položky se zahrnují.
  • Nevytvávejte řetězec rozložení, která obsahují cyklus. Například nevytvořte rozložení, jehož rozložení je samotné.
  • Nepovolujte koncovému uživateli porušení invariantů rekurze (pravidel) prostřednictvím škodlivých datových zadávání nebo volání interop JavaScriptu.

Nekonečné smyčky během vykreslování:

  • Způsobí, že proces vykreslování bude pokračovat navždy.
  • Je ekvivalentní vytvoření neukončené smyčky.

V těchto scénářích se Blazor selhání a obvykle se pokouší:

  • Spotřebovávejte tolik času procesoru, kolik povoluje operační systém, neomezeně dlouho.
  • Spotřebovávat neomezené množství paměti. Využívání neomezené paměti odpovídá scénáři, kdy neukončená smyčka přidává položky do kolekce při každé iteraci.

Abyste se vyhnuli nekonečným rekurzivním vzorcům, ujistěte se, že rekurzivní kód vykreslování obsahuje vhodné podmínky zastavení.

Vlastní logika stromu vykreslování

Většina Razor komponent se implementuje jako Razor soubory komponent (.razor) a jsou zkompilovány architekturou za účelem vytvoření logiky, která pracuje s vykreslením RenderTreeBuilder jejich výstupu. Vývojář však může ručně implementovat RenderTreeBuilder logiku pomocí procedurálního kódu jazyka C#. Další informace najdete v tématu ASP.NET Blazor pokročilých scénářích (vytváření stromové struktury vykreslování).

Upozorňující

Použití logiky ručního tvůrce stromové struktury vykreslování se považuje za pokročilý a nebezpečný scénář, nedoporučuje se pro obecný vývoj komponent.

Pokud RenderTreeBuilder je kód napsaný, vývojář musí zaručit správnost kódu. Vývojář například musí zajistit, aby:

  • Volání a OpenElementCloseElement jsou správně vyvážená.
  • Atributy se přidají pouze na správných místech.

Nesprávná logika ručního tvůrce stromové struktury může způsobit libovolné nedefinované chování, včetně chybových ukončení, zablokování aplikace nebo serveru a ohrožení zabezpečení.

Zvažte ruční logiku tvůrce stromové struktury na stejné úrovni složitosti a se stejnou úrovní nebezpečí jako ruční psaní kódu sestavení nebo pokynů jazyka MSIL (Microsoft Intermediate Language).

Další materiály

†Applies k back-endu ASP.NET aplikacím core webového rozhraní API, které aplikace na straně Blazor klienta používají k protokolování.