Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Poznámka:
Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 10 tohoto článku.
Varování
Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Pro aktuální vydání se podívejte na verzi .NET 9 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 šablon projektů Blazor používají atribut data-nosnippet k signalizaci prohlížečům, aby neukládaly do mezipaměti obsah chybového uživatelského rozhraní, ale všechny verze dokumentace Blazor tento atribut používají.
V Blazor Web App přizpůsobte nastavení v komponentě MainLayout. Protože Tag Helper pro prostředí (například <environment include="Production">...</environment>) není v Razor komponentách podporovaný, následující příklad vloží IHostEnvironment pro konfiguraci chybových zpráv pro různá prostředí.
V horní části: MainLayout.razor
@inject IHostEnvironment HostEnvironment
Vytvořte nebo upravte Blazor označení 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>
V aplikaci Blazor Server upravte zážitky v souboru Pages/_Host.cshtml. Následující příklad používá Environment Tag Helper ke konfiguraci chybových zpráv pro různá prostředí.
V aplikaci Blazor Server upravte zážitky v souboru Pages/_Layout.cshtml. Následující příklad používá Environment Tag Helper ke konfiguraci chybových zpráv pro různá prostředí.
V aplikaci Blazor Server upravte zážitky v souboru Pages/_Host.cshtml. Následující příklad používá Environment Tag Helper ke konfiguraci chybových zpráv pro různá prostředí.
Vytvořte nebo upravte Blazor označení 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ě, framework aplikuje 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ě, framework aplikuje display: block na prvek.
Podrobné chyby elektrického okruhu
Tato část se vztahuje na Blazor Web Appzařízení fungující na okruhu.
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 kritického obvodu zpřístupněny klientovi povolením podrobných hlášení 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"
}
}
}
Klíč konfigurace DetailedErrors lze nastavit na true pomocí proměnné prostředí ASPNETCORE_DETAILEDERRORS s hodnotou true na serverech prostředí Development/Staging nebo na vašem místním systému.
Varování
Vždy se vyhněte zveřejnění informací o chybách klientům na internetu, což je bezpečnostní riziko.
Podrobné chyby při vykreslování komponent na straně serveru
Tato část se týká Blazor Web Apps.
Tuto možnost RazorComponentsServiceOptions.DetailedErrors použijte k řízení tvorby podrobných informací o chybách pro Razor vykreslování komponent na straně serveru. Výchozí hodnota je false.
Následující příklad umožňuje zobrazit podrobné chyby.
builder.Services.AddRazorComponents(options =>
options.DetailedErrors = builder.Environment.IsDevelopment());
Varování
Povolte podrobné chyby jenom v prostředí 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 nastaveno na true hodnotu. Tento přístup není bezpečný, protože je možné hostovat produkční aplikaci na veřejném serveru v prostředí Development.
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 obvody
Tato část se týká aplikací na straně serveru, které fungují přes okruh.
Razor komponenty se zapnutou serverovou interaktivitou jsou stavové na serveru. 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ů pro zpracování událostí, které mohou být spuště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 ty ukončené, tzn. okruhy pro jiné uživatele nebo jiné karty prohlížeče, nejsou ovlivněny. Tento scénář je podobný desktopové aplikaci, která spadne. Spadená aplikace musí být restartována, ale ostatní aplikace nejsou ovlivněny.
Rámec ukončí okruh, pokud dojde k neošetřené výjimce z následujících důvodů:
- Neošetřená výjimka často zanechává 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
Přístupy k globálnímu zpracování výjimek najdete v následujících částech:
- Hranice chyb: Platí pro všechny Blazor aplikace.
- Alternativní globální zpracování výjimek: Platí pro Blazor Server, Blazor WebAssemblya Blazor Web Apps (8.0 nebo novější), které přijímají globální interaktivní režim vykreslování.
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, pokud je neošetřená výjimka vyvolána kteroukoli komponentou v rámci oblasti chyby.
Chcete-li definovat hranici chyby, použijte komponentu ErrorBoundary k zabalení jedné nebo více dalších komponent. Hranice chyby spravuje nezachycené výjimky vyvolané komponentami, které obaluje.
<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>
V Blazor Web App s chybovou hranicí použitou pouze pro statickou MainLayout komponentu je hranice aktivní pouze během statického serverového vykreslování (static SSR). Hranice se neaktivuje, protože komponenta dále v hierarchii komponent je interaktivní.
Interaktivní režim vykreslení nelze u MainLayout komponenty použít, protože parametr komponenty Body je RenderFragment delegát, což je libovolný kód a nedá se serializovat. Aby byla interaktivita pro komponentu MainLayout a zbývající komponenty níže v hierarchii komponent, musí aplikace přijmout globální interaktivní režim vykreslování tím, že použije interaktivní režim vykreslování na instance komponent HeadOutlet a Routes v hlavní komponentě aplikace, kterou je obvykle komponenta App. Následující příklad přijímá globálně režim vykreslování Interactive Server (InteractiveServer).
V Components/App.razor:
<HeadOutlet @rendermode="InteractiveServer" />
...
<Routes @rendermode="InteractiveServer" />
Pokud nechcete povolit globální interaktivitu, umístěte hranici chyby dále dolů v hierarchii komponent. 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 komponenta, kde je umístěna hranice chyby, není interaktivní, je hranice chyby schopna se aktivovat pouze na serveru během statického Server Side Renderingu (SSR). Hranice se například může aktivovat, když dojde k chybě v metodě životního cyklu komponenty, ale ne pro událost aktivovanou interaktivitou uživatele v rámci komponenty, například chybu vyvolanou obslužnou rutinou kliknutí na tlačítko.
- Pokud je komponenta, ve které je umístěna hranice chyby, interaktivní, je hranice chyby schopna se aktivovat pro interaktivní komponenty, které obaluje.
Poznámka:
Předchozí aspekty nejsou relevantní pro samostatné Blazor WebAssembly aplikace, protože vykreslování aplikace na straně klienta (CSR) Blazor WebAssembly je zcela interaktivní.
Podívejte se na následující příklad, kde výjimka vyvolaná vloženou komponentou čítače je zachycena ohraničením chyb v komponentě Home, která používá interaktivní režim vykreslování.
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>
Podívejte se na následující příklad, kde je výjimka vyvolaná vloženou komponentou čítače zachycena hranicí chyby v komponentě Home.
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>
Pokud je neošetřená výjimka vyvolána pro currentCount více než pět případů:
- Chyba se protokoluje normálně (
System.InvalidOperationException: Current count is too big!). - Výjimku zpracovává hranice chyby.
- Výchozí uživatelské rozhraní chyby se vykreslí podle hranice chyby.
Komponenta ErrorBoundary vykresluje prázdný <div> prvek pomocí blazor-error-boundary třídy CSS, která se používá pro jeho chybový obsah. Barvy, text a ikona výchozího uživatelského rozhraní jsou definované v šabloně stylů aplikace ve složce wwwroot, takže uživatelské rozhraní pro chyby můžete přizpůsobit.
Změna výchozího obsahu chyby:
- Zabalte součásti okraje chyby do vlastnosti ChildContent.
- Nastavte vlastnost ErrorContent na obsah chyby.
Následující příklad zabalí komponentu EmbeddedCounter a dodává vlastní obsah chyb:
<ErrorBoundary>
<ChildContent>
<EmbeddedCounter />
</ChildContent>
<ErrorContent>
<p class="errorUI">😈 A rotten gremlin got us. Sorry!</p>
</ErrorContent>
</ErrorBoundary>
V předchozím příkladu pravděpodobně šablona stylů aplikace obsahuje errorUI třídu CSS pro styl obsahu. Obsah chyby se vykreslí z vlastnosti ErrorContent bez blokového elementu. Prvek na úrovni bloku, například divize (<div>) nebo prvek odstavce (<p>), může obalovat označení chybného obsahu, ale není to povinné.
Volitelně můžete k získání chybových @context dat použít kontext (ErrorContent):
<ErrorContent>
@context.HelpLink
</ErrorContent>
ErrorContent může také pojmenovat kontext. V následujícím příkladu je kontext pojmenován exception:
<ErrorContent Context="exception">
@exception.HelpLink
</ErrorContent>
Varování
Vždy se vyhněte zveřejnění informací o chybách klientům na internetu, což je bezpečnostní riziko.
Pokud je hranice chyby definovaná v rozložení aplikace, 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 nastavíte hranici chyby široce, můžete ji resetovat do bezchybového stavu při následných událostech navigace na stránce voláním metody hranice chyby Recover.
V MainLayout.razor:
- Přidejte pole pro ErrorBoundaryzachycení odkazu na něj pomocí direktivy atributu
@ref. -
OnParameterSetživotního cyklu můžete aktivovat obnovu v hraniční oblasti chyb pomocí , aby se vymazala chyba, když uživatel přejde na jinou komponentu.
...
<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 znovu vykreslí komponentu, která chybu znovu vyvolá, nevolejte Recover v rámci logiky vykreslování. Zavolat Recover pouze tehdy:
- 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, která se provádí, také vymaže výjimku. Když je komponenta znovu vykreslena, chyba se znovu nevyskytne.
Následující příklad uživateli umožňuje obnovit výjimku pomocí tlačítka:
<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;
}
Můžete také vytvořit podtřídu ErrorBoundary pro vlastní zpracování tak, že přepíšete OnErrorAsync. Následující příklad pouze zaprotokoluje chybu, ale můžete implementovat libovolný kód pro zpracování chyb, jaký si přejete. Řádek, který vrací CompletedTask , můžete odebrat, pokud váš kód čeká na asynchronní úlohu.
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;
}
}
Předchozí příklad lze také implementovat jako třídu.
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;
}
}
Některou z předchozích implementací použitých v komponentě:
<CustomErrorBoundary>
...
</CustomErrorBoundary>
Alternativní globální zpracování výjimek
Přístup popsaný v této části platí pro Blazor Server, Blazor WebAssemblya Blazor Web Apps, které přijímají globální interaktivní režim vykreslování (InteractiveServer, InteractiveWebAssemblynebo InteractiveAuto). Tento přístup nefunguje s Blazor Web App režimy vykreslování podle jednotlivých stránek nebo komponent a také se statickým vykreslováním na straně serveru (statické SSR), protože přístup spoléhá na CascadingValue/CascadingParameter, které nefungují přes hranice režimu vykreslování ani s komponentami, které přijímají statické SSR.
Alternativou k použití hranic chyb (ErrorBoundary) je předat vlastní chybovou komponentu jako CascadingValue parametr podřízeným komponentám. 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í ProcessError 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.
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();
}
}
Poznámka:
Další informace o RenderFragment, naleznete v části Komponenty Razor ASP.NET Core.
CascadingValue<TValue>.IsFixed slouží k označení, že se kaskádový parametr po inicializaci nezmění.
Při použití tohoto přístupu v Blazor Web App, otevřete komponentu Routes a zabalte komponentu Router (<Router>...</Router>) komponentou ProcessError. To umožňuje komponentě ProcessError předávat kaskádovitě do libovolné komponenty aplikace, kam je komponenta ProcessError přijata jako CascadingParameter.
V Routes.razor:
<ProcessError>
<Router ...>
...
</Router>
</ProcessError>
Otevřete komponentu Blazor Server, při použití tohoto přístupu v aplikaci Blazor WebAssembly nebo App, a obalte komponentu Router (<Router>...</Router>) komponentou ProcessError. To umožňuje komponentě ProcessError předávat kaskádovitě do libovolné komponenty aplikace, kam je komponenta ProcessError přijata jako CascadingParameter.
V App.razor:
<ProcessError>
<Router ...>
...
</Router>
</ProcessError>
Zpracování chyb v komponentě:
Označte komponentu
ProcessErrorjakoCascadingParametersoučást v@codebloku. Do ukázkovéCounterkomponenty v aplikaci založené na Blazor šabloně projektu přidejte následujícíProcessErrorvlastnost:[CascadingParameter] private ProcessError? ProcessError { get; set; }Volejte metodu zpracování chyb v libovolném
catchbloku s odpovídajícím typem výjimky. UkázkováProcessErrorkomponenta nabízí pouze jednuLogErrormetodu, 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. NásledujícíCounterpříklad bloku komponenty@codeobsahujeProcessErrorkaskádový parametr a zachytí výjimku pro účely protokolování, pokud je počet větší než pě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); } } }
Zaprotokolovaná chyba:
fail: {COMPONENT NAMESPACE}.ProcessError[0]
ProcessError.LogError: System.InvalidOperationException Message: Current count is over five!
Pokud se LogError 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 LogError 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 obvod fatální. 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í ProcessError komponenta se předává jako CascadingValue svým podřízeným komponentám. 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ě.
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);
}
}
Poznámka:
Další informace o RenderFragment, naleznete v části Komponenty Razor ASP.NET Core.
CascadingValue<TValue>.IsFixed slouží k označení, že se kaskádový parametr po inicializaci nezmění.
V komponentě App vložte komponentu Router do komponenty ProcessError. To umožňuje komponentě ProcessError předávat kaskádovitě do libovolné komponenty aplikace, kam je komponenta ProcessError přijata jako CascadingParameter.
App.razor:
<ProcessError>
<Router ...>
...
</Router>
</ProcessError>
Zpracování chyb v komponentě:
Označte komponentu
ProcessErrorjakoCascadingParametersoučást v@codebloku:[CascadingParameter] private ProcessError ProcessError { get; set; }Volejte metodu zpracování chyb v libovolném
catchbloku s odpovídajícím typem výjimky. UkázkováProcessErrorkomponenta nabízí pouze jednuLogErrormetodu, 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) { ProcessError.LogError(ex); }
Pomocí předchozí ukázkové ProcessError komponenty a LogError metody označuje konzola vývojářských nástrojů prohlížeče zachycenou zaprotokolovanou chybu:
fail: {COMPONENT NAMESPACE}.Shared.ProcessError[0]
ProcessError.LogError: System.NullReferenceException Message: Object reference not set to an instance of an object.
Pokud se LogError 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 LogError 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 Blazor mezi klientem a serverem není přerušeno, SignalR 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, výjimka se zaprotokoluje do instancí nakonfigurovaných v kontejneru služby. Blazor Aplikace zapisují výstup konzoly pomocí zprostředkovatele protokolování konzoly. Zvažte protokolování do umístění na serveru (nebo backendovém webovém rozhraní API pro aplikace na straně klienta) s poskytovatelem, který spravuje velikost protokolu a rotaci protokolů. Alternativně může aplikace využít službu Application Performance Management (APM), jako je například Azure Application Insights (Azure Monitor).
Poznámka:
Nativní funkce Application Insights , 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 App Insights na Blazor straně klienta WASM (microsoft/ApplicationInsights-dotnet #2143) a webové analýzy a diagnostika (včetně odkazů na komunitní implementace) (dotnet/aspnetcore #5461). Mezitím může aplikace na straně klienta používat sadu Application Insights JavaScript SDK s JS interoperabilitou k protokolování chyb přímo do Application Insights z aplikace na straně klienta.
Během vývoje aplikace Blazor, která pracuje přes síť, aplikace obvykle odesílá úplné podrobnosti o výjimkách do konzoly prohlížeče k usnadnění 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ů. Nepřátelští 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 k záznamu.
Další informace najdete v následujících článcích:
- Protokolování ASP.NET Core Blazor
- Zpracování chyb v ASP.NET Core}
- Vytváření webových rozhraní API pomocí ASP.NET Core
•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. Klientské aplikace můžou zachytit a odesílat informace o chybách na klientovi do webového rozhraní API, které protokoluje informace o chybách do trvalého poskytovatele protokolování.
Pokud dojde k neošetřené výjimce, výjimka se zaprotokoluje do instancí nakonfigurovaných v kontejneru služby. Blazor Aplikace zapisují výstup konzoly pomocí zprostředkovatele protokolování konzoly. Zvažte protokolování do trvalejšího umístění na serveru pomocí odesílání informací o chybách do webového rozhraní API backendu, které používá zprostředkovatele protokolování se správou velikosti protokolu a rotací protokolů. Případně může backendová webová aplikace API používat službu Application Performance Management (APM), jako je Azure Application Insights (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ů. Nepřátelští 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 k záznamu.
Další informace najdete v následujících článcích:
- Protokolování ASP.NET Core Blazor
- Zpracování chyb v ASP.NET Core}
- Vytváření webových rozhraní API pomocí ASP.NET Core
†Nativní funkce Application Insights , které podporují aplikace na straně klienta a nativní Blazor podporu rozhraní pro Google Analytics , mohou být dostupné v budoucích verzích těchto technologií. Další informace najdete v tématu Podpora App Insights na Blazor straně klienta WASM (microsoft/ApplicationInsights-dotnet #2143) a webové analýzy a diagnostika (včetně odkazů na komunitní implementace) (dotnet/aspnetcore #5461). Mezitím může aplikace na straně klienta používat sadu Application Insights JavaScript SDK s JS interoperabilitou k protokolování chyb přímo do Application Insights 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 zachytávají a odesílají informace o chybách do webového API, které protokoluje informace o chybě do trvalého úložiště logů.
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
@injectdirektivy 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 funguje na obvodu, obvod selže. Pokud může logika konstruktoru vyvolat výjimky, měla by je aplikace zachytit pomocí try-catch příkazu se 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.GetProductByIdAsyncmetodě zpracovávátry-catchpříkaz. - Při
catchspuštění bloku:-
loadFailedje nastavena natruehodnotu , která slouží k zobrazení chybové zprávy uživateli. - Chyba byla zaznamenána do protokolu.
-
@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);
}
}
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, když je @someObject.PropertyName vyhodnoceno, 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á.
Aby se zabránilo NullReferenceException v logice vykreslování, zkontrolujte null objekt před přístupem k jeho členům. V následujícím příkladu nejsou vlastnosti přístupné, pokud je person.Addressperson.Address: 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 person zaručeno, že existuje, protože person se vytvoří při instanciaci 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:
- Rámec 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 tato výjimka pro okruh aplikace fatální.
Pokud logika odstranění může vyvolat výjimky, měla by aplikace výjimky zachytit pomocí příkazu try-catch se zpracováním chyb a protokolováním.
Další informace o likvidaci komponentů naleznete v tématu ASP.NET Core Razor komponenty.
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í InvokeAsync selže 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á běží nad okruhem Blazor, je výsledná výjimka pro okruh aplikace fatální.
- Pokud volání na InvokeAsync selže v asynchronním režimu, selže také rozhraní .NET Task. Volání InvokeAsync může selhat, například proto, že kód na straně JS vyvolá výjimku nebo vrátí
Promise, které bylo dokončeno jakorejected. Vývojářský kód musí zachytit výjimku. Pokud používáteawaitoperátor, zvažte zabalení volání metody dotry-catchpří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. - Volání na InvokeAsync musí být dokončena během určité doby, jinak dojde k vypršení časového limitu. 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. Zachytit a zpracovat výjimku s protokolováním.
JS Podobně může kód inicializovat volání metod .NET označených atributem[JSInvokable]. Pokud tyto metody .NET vyvolají neošetřenou výjimku:
- Blazor V aplikaci, která pracuje přes okruh, není výjimka považována za závažnou pro okruh aplikace.
- Strana JS-side
Promiseje 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:
- Volání funkcí JavaScriptu z metod .NET v ASP.NET Core Blazor
- Volání metod .NET z funkcí JavaScriptu v ASP.NET Core Blazor
Předběžné vykreslování
Razor komponenty jsou ve výchozím nastavení předem vykresleny tak, aby se jejich vykreslený HTML kód vrátil jako součást počátečního požadavku HTTP uživatele.
V aplikaci, která pracuje na okruhu, funguje prerendering takto:
- Vytvoření nového obvodu pro všechny vykreslené předem součásti, které jsou součástí stejné stránky.
- Generuje se počáteční kód HTML.
- Považujte okruh za
disconnected, dokud 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ředirenderovaných komponent na straně klienta funguje předirenderová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 během předběžného vykreslování vyvolá neošetřenou výjimku, například během metody životního cyklu nebo v logice vykreslování:
- Blazor V aplikaci, která funguje na okruhu, je výjimka pro okruh fatální. U převykreslených komponent na straně klienta výjimka zabraňuje vykreslení komponentu.
- Výjimka vyvolá zásobník volání z objektu ComponentTagHelper.
Za normálních okolností, když se předběžné vykreslování nezdaří, nemá smysl nadále sestavovat a vykreslovat komponentu, protože nelze vykreslit funkční komponentu.
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í ComponentTagHelper do příkazu try-catch umístěte logiku zpracování chyb do komponenty vykreslené pomocí ComponentTagHelper.
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ž potomci zahrnují jeho samotného.
- Nevytvávejte řetězec rozložení, který obsahuje cyklus. Například nevytvořte rozložení, které se skládá samo ze sebe.
- 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 Blazor selže 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 komponentové soubory (.razor) a jsou zkompilovány frameworkem k vytvoření logiky, která pracuje na RenderTreeBuilder k vykreslení 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 Core Blazor pokročilé scénáře (vytváření stromové struktury vykreslování).
Varování
Použití logiky pro manuální vytváření vykreslovacího stromu se považuje za pokročilý a nebezpečný scénář, který se nedoporučuje 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í na OpenElement a CloseElement jsou správně vyvážená.
- Atributy se přidají pouze na správných místech.
Nesprávná logika ručního tvůrce stromu vykreslování může způsobit libovolné nedefinované chování, včetně pádů, nezareagování aplikace nebo serveru a zranitelnosti zabezpečení.
Zvažte ruční logiku pro vytváření stromu na stejné úrovni složitosti a se stejnou úrovní nebezpečí, jako je ruční psaní assemblerového kódu nebo pokynů jazyka Microsoft Intermediate Language (MSIL).
Další materiály
†Platí pro back-end aplikace webového rozhraní API ASP.NET Core, které aplikace na straně klienta používají k zaznamenávání.