Obsługa błędów w aplikacjach ASP.NET Core Blazor
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
W tym artykule opisano sposób Blazor zarządzania nieobsługiwanymi wyjątkami oraz tworzenia aplikacji, które wykrywają błędy i obsługują je.
Szczegółowe błędy podczas programowania
Blazor Gdy aplikacja nie działa prawidłowo podczas programowania, otrzymuje szczegółowe informacje o błędzie z aplikacji, pomagając w rozwiązywaniu problemów i rozwiązywaniu problemu. Gdy wystąpi błąd, Blazor aplikacje wyświetlają jasnożółty pasek w dolnej części ekranu:
- Podczas programowania pasek kieruje Cię do konsoli przeglądarki, gdzie można zobaczyć wyjątek.
- W środowisku produkcyjnym pasek powiadamia użytkownika o wystąpieniu błędu i zaleca odświeżenie przeglądarki.
Interfejs użytkownika dla tego środowiska obsługi błędów jest częścią Blazor szablonów projektów. Nie wszystkie wersje Blazor szablonów projektów używają atrybutu data-nosnippet
do sygnalizatora przeglądarek, aby nie buforować zawartości interfejsu użytkownika błędu, ale wszystkie wersje Blazor dokumentacji stosują atrybut.
W elemencie Blazor Web Appdostosuj środowisko w składniku MainLayout
. Ponieważ pomocnik tagów środowiska (na przykład <environment include="Production">...</environment>
) nie jest obsługiwany w Razor składnikach, poniższy przykład wprowadza w celu skonfigurowania komunikatów IHostEnvironment o błędach dla różnych środowisk.
W górnej części elementu MainLayout.razor
:
@inject IHostEnvironment HostEnvironment
Utwórz lub zmodyfikuj znaczniki interfejsu użytkownika błędu Blazor :
<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 W aplikacji dostosuj środowisko w Pages/_Host.cshtml
pliku. W poniższym przykładzie użyto pomocnika tagów środowiska do skonfigurowania komunikatów o błędach dla różnych środowisk.
Blazor Server W aplikacji dostosuj środowisko w Pages/_Layout.cshtml
pliku. W poniższym przykładzie użyto pomocnika tagów środowiska do skonfigurowania komunikatów o błędach dla różnych środowisk.
Blazor Server W aplikacji dostosuj środowisko w Pages/_Host.cshtml
pliku. W poniższym przykładzie użyto pomocnika tagów środowiska do skonfigurowania komunikatów o błędach dla różnych środowisk.
Utwórz lub zmodyfikuj znaczniki interfejsu użytkownika błędu Blazor :
<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 W aplikacji dostosuj środowisko w wwwroot/index.html
pliku:
<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
jest zwykle ukryty ze względu na obecność display: none
stylu blazor-error-ui
klasy CSS w automatycznie wygenerowanym arkuszu stylów aplikacji. W przypadku wystąpienia błędu struktura ma zastosowanie display: block
do elementu .
Element blazor-error-ui
jest zwykle ukryty ze względu na obecność display: none
stylu blazor-error-ui
klasy CSS w arkuszu stylów witryny w folderze wwwroot/css
. W przypadku wystąpienia błędu struktura ma zastosowanie display: block
do elementu .
Szczegółowe błędy obwodu
Ta sekcja dotyczy Blazor Web Appoperacji na obwodzie.
Ta sekcja dotyczy Blazor Server aplikacji.
Błędy po stronie klienta nie zawierają stosu wywołań i nie zawierają szczegółowych informacji na temat przyczyny błędu, ale dzienniki serwera zawierają takie informacje. W celach programistycznych poufne informacje o błędach obwodu można udostępnić klientowi, włączając szczegółowe błędy.
Ustaw wartość opcji CircuitOptions.DetailedErrors na true
. Aby uzyskać więcej informacji i przykład, zobacz wskazówki dotyczące platformy ASP.NET CoreBlazorSignalR.
Alternatywą dla ustawienia CircuitOptions.DetailedErrors jest ustawienie DetailedErrors
klucza konfiguracji na true
wartość w pliku ustawień środowiska aplikacji Development
(appsettings.Development.json
). Ponadto ustaw SignalR rejestrowanie po stronie serwera (Microsoft.AspNetCore.SignalR
) na debugowanie lub śledzenie w celu uzyskania szczegółowego SignalR rejestrowania.
appsettings.Development.json
:
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.SignalR": "Debug"
}
}
}
DetailedErrors Klucz konfiguracji można również ustawić na true
użycie zmiennej środowiskowej ASPNETCORE_DETAILEDERRORS
z wartością true
na Development
/Staging
serwerach środowiskowych lub w systemie lokalnym.
Ostrzeżenie
Należy zawsze unikać uwidaczniania informacji o błędach klientom w Internecie, co jest zagrożeniem bezpieczeństwa.
Szczegółowe błędy renderowania Razor po stronie serwera składnika
Ta sekcja dotyczy s Blazor Web App.
RazorComponentsServiceOptions.DetailedErrors Użyj opcji , aby kontrolować tworzenie szczegółowych informacji na temat błędów Razor renderowania po stronie serwera składników. Domyślna wartość to false
.
Poniższy przykład umożliwia szczegółowe błędy:
builder.Services.AddRazorComponents(options =>
options.DetailedErrors = builder.Environment.IsDevelopment());
Ostrzeżenie
Włącz tylko szczegółowe błędy w Development
środowisku. Szczegółowe błędy mogą zawierać poufne informacje o aplikacji, których złośliwi użytkownicy mogą używać w ataku.
Powyższy przykład zapewnia stopień bezpieczeństwa, ustawiając wartość na podstawie wartości DetailedErrors zwracanej przez IsDevelopment. Gdy aplikacja znajduje się w Development
środowisku, DetailedErrors jest ustawiona na true
wartość . Takie podejście nie jest niezawodne, ponieważ istnieje możliwość hostowania aplikacji produkcyjnej na serwerze publicznym w Development
środowisku.
Zarządzanie nieobsługiwanymi wyjątkami w kodzie dewelopera
Aby aplikacja mogła kontynuować działanie po błędzie, aplikacja musi mieć logikę obsługi błędów. W kolejnych sekcjach tego artykułu opisano potencjalne źródła nieobsługiwane wyjątki.
W środowisku produkcyjnym nie renderuj komunikatów wyjątków struktury ani śladów stosu w interfejsie użytkownika. Renderowanie komunikatów wyjątków lub śladów stosu może:
- Ujawnianie poufnych informacji użytkownikom końcowym.
- Pomóż złośliwemu użytkownikowi wykryć słabe strony w aplikacji, która może naruszyć bezpieczeństwo aplikacji, serwera lub sieci.
Nieobsługiwane wyjątki dla obwodów
Ta sekcja dotyczy aplikacji po stronie serwera działających w obwodzie.
Razor składniki z włączonym interakcyjnością serwera są stanowe na serwerze. Podczas gdy użytkownicy wchodzą w interakcję z składnikiem na serwerze, utrzymują połączenie z serwerem znanym jako obwód. Obwód przechowuje aktywne wystąpienia składników, a także wiele innych aspektów stanu, takich jak:
- Najnowsze renderowane dane wyjściowe składników.
- Bieżący zestaw delegatów obsługi zdarzeń, które mogą być wyzwalane przez zdarzenia po stronie klienta.
Jeśli użytkownik otworzy aplikację na wielu kartach przeglądarki, użytkownik utworzy wiele niezależnych obwodów.
Blazor traktuje najbardziej nieobsługiwane wyjątki jako krytyczne dla obwodu, w którym występują. Jeśli obwód zostanie przerwany z powodu nieobsługiwanego wyjątku, użytkownik może nadal wchodzić w interakcję z aplikacją, ładując ponownie stronę w celu utworzenia nowego obwodu. Obwody poza tym, które zostały zakończone, które są obwodami dla innych użytkowników lub innych kart przeglądarki, nie mają wpływu. Ten scenariusz jest podobny do aplikacji klasycznej, która ulega awarii. Awaria aplikacji musi zostać ponownie uruchomiona, ale nie ma to wpływu na inne aplikacje.
Struktura kończy obwód, gdy wystąpi nieobsługiwany wyjątek z następujących powodów:
- Nieobsługiwany wyjątek często pozostawia obwód w stanie niezdefiniowanym.
- Normalne działanie aplikacji nie może być gwarantowane po nieobsługiwanym wyjątku.
- Luki w zabezpieczeniach mogą pojawić się w aplikacji, jeśli obwód będzie nadal w stanie niezdefiniowanym.
Globalna obsługa wyjątków
Aby zapoznać się z podejściami do globalnego obsługi wyjątków, zobacz następujące sekcje:
- Granice błędów: dotyczy wszystkich Blazor aplikacji.
- Alternatywna globalna obsługa wyjątków: dotyczy Blazor Serverelementów , Blazor WebAssemblyi Blazor Web App(8.0 lub nowszych), które przyjmują globalny tryb renderowania interaktywnego.
Granice błędów
Granice błędów zapewniają wygodne podejście do obsługi wyjątków. Składnik ErrorBoundary :
- Renderuje jego zawartość podrzędną, gdy wystąpił błąd.
- Renderuje interfejs użytkownika błędu, gdy nieobsługiwany wyjątek jest zgłaszany przez dowolny składnik w granicach błędu.
Aby zdefiniować granicę błędu, należy użyć ErrorBoundary składnika do opakowania co najmniej jednego innego składnika. Granica błędu zarządza nieobsługiwanych wyjątków zgłaszanych przez składniki, które opakowuje.
<ErrorBoundary>
...
</ErrorBoundary>
Aby zaimplementować granicę błędu w sposób globalny, dodaj granicę wokół zawartości treści głównego układu aplikacji.
W pliku MainLayout.razor
:
<article class="content px-4">
<ErrorBoundary>
@Body
</ErrorBoundary>
</article>
W Blazor Web Appprogramie s z granicą błędu zastosowaną tylko do składnika statycznego MainLayout
granica jest aktywna tylko podczas renderowania statycznego po stronie serwera (statyczny przewodnik SSR). Granica nie aktywuje się tylko dlatego, że składnik dalej w hierarchii składników jest interaktywny.
Nie można zastosować trybu renderowania interakcyjnego MainLayout
do składnika, ponieważ parametr składnika Body
jest delegatem RenderFragment , który jest dowolnym kodem i nie można go serializować. Aby umożliwić szeroko interakcyjność dla MainLayout
składnika i rest składników w dalszej części hierarchii składników, aplikacja musi przyjąć globalny tryb renderowania interaktywnego, stosując tryb renderowania interaktywnego do HeadOutlet
wystąpień składników i Routes
w głównym składniku aplikacji, który jest zazwyczaj składnikiem App
. Poniższy przykład stosuje tryb renderowania interactive server (InteractiveServer
) globalnie.
W pliku Components/App.razor
:
<HeadOutlet @rendermode="InteractiveServer" />
...
<Routes @rendermode="InteractiveServer" />
Jeśli nie chcesz włączać globalnej interakcyjności, umieść granicę błędu w dół hierarchii składników. Ważne pojęcia, które należy wziąć pod uwagę, to to, że wszędzie tam, gdzie znajduje się granica błędu:
- Jeśli składnik, w którym znajduje się granica błędu, nie jest interakcyjny, granica błędu jest w stanie uaktywnić się tylko na serwerze podczas statycznego routingu SSR. Na przykład granica może uaktywnić się po wystąpieniu błędu w metodzie cyklu życia składnika, ale nie w przypadku zdarzenia wyzwalanego przez interakcyjność użytkownika w składniku, na przykład błąd zgłaszany przez procedurę obsługi kliknięć przycisku.
- Jeśli składnik, w którym znajduje się granica błędu, jest interakcyjny, granica błędu może aktywować dla składników interaktywnych, które opakowuje.
Uwaga
Powyższe zagadnienia nie są istotne dla aplikacji autonomicznych Blazor WebAssembly , ponieważ renderowanie po stronie klienta (CSR) Blazor WebAssembly aplikacji jest całkowicie interaktywne.
Rozważmy poniższy przykład, w którym wyjątek zgłaszany przez osadzony składnik licznika jest przechwycony przez granicę błędu w składniku Home
, który przyjmuje tryb renderowania interaktywnego.
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>
Rozważmy poniższy przykład, w którym wyjątek zgłaszany przez osadzony składnik licznika jest przechwycony przez granicę błędu w składniku 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>
Jeśli nieobsługiwany wyjątek zostanie zgłoszony dla currentCount
ponad pięciu:
- Błąd jest rejestrowany normalnie (
System.InvalidOperationException: Current count is too big!
). - Wyjątek jest obsługiwany przez granicę błędu.
- Domyślny interfejs użytkownika błędu jest renderowany przez granicę błędu.
Składnik ErrorBoundary renderuje pusty <div>
element przy użyciu blazor-error-boundary
klasy CSS dla zawartości błędu. Kolory, tekst i ikona domyślnego interfejsu użytkownika są definiowane w arkuszu stylów aplikacji w folderze wwwroot
, więc możesz dostosować interfejs użytkownika błędu.
Aby zmienić domyślną zawartość błędu:
- Zawijaj składniki granicy błędu we ChildContent właściwości .
- ErrorContent Ustaw właściwość na zawartość błędu.
Poniższy przykład opakowuje składnik i dostarcza niestandardową EmbeddedCounter
zawartość błędu:
<ErrorBoundary>
<ChildContent>
<EmbeddedCounter />
</ChildContent>
<ErrorContent>
<p class="errorUI">😈 A rotten gremlin got us. Sorry!</p>
</ErrorContent>
</ErrorBoundary>
W poprzednim przykładzie arkusz stylów aplikacji prawdopodobnie zawiera klasę errorUI
CSS do stylu zawartości. Zawartość błędu jest renderowana z ErrorContent właściwości bez elementu na poziomie bloku. Element na poziomie bloku, taki jak dzielenie (<div>
) lub element akapitu (<p>
), może opakowować znacznik zawartości błędu, ale nie jest wymagany.
Opcjonalnie użyj kontekstu () ErrorContent elementu ,@context
aby uzyskać dane o błędach:
<ErrorContent>
@context.HelpLink
</ErrorContent>
Element ErrorContent może również nazwać kontekst. W poniższym przykładzie kontekst ma nazwę exception
:
<ErrorContent Context="exception">
@exception.HelpLink
</ErrorContent>
Ostrzeżenie
Należy zawsze unikać uwidaczniania informacji o błędach klientom w Internecie, co jest zagrożeniem bezpieczeństwa.
Jeśli granica błędu jest zdefiniowana w układzie aplikacji, interfejs użytkownika błędu jest widoczny niezależnie od strony, do której przechodzi użytkownik po wystąpieniu błędu. W większości scenariuszy zalecamy określenie zakresu granic błędów. Jeśli w szerokim zakresie granica błędu, możesz zresetować ją do stanu nieumyślnego w kolejnych zdarzeniach nawigacji stron, wywołując metodę granicy Recover błędu.
W pliku MainLayout.razor
:
- Dodaj pole , ErrorBoundary aby przechwycić odwołanie do niego za pomocą dyrektywy atrybutu
@ref
. - W metodzie
OnParameterSet
cyklu życia można wyzwolić odzyskiwanie na granicy błędu za pomocą Recover polecenia , aby wyczyścić błąd, gdy użytkownik przejdzie do innego składnika.
...
<ErrorBoundary @ref="errorBoundary">
@Body
</ErrorBoundary>
...
@code {
private ErrorBoundary? errorBoundary;
protected override void OnParametersSet()
{
errorBoundary?.Recover();
}
}
Aby uniknąć nieskończonej pętli, w której odzyskiwanie tylko rerenders składnika, który ponownie zgłasza błąd, nie należy wywoływać Recover z logiki renderowania. Wywołaj tylko Recover wtedy, gdy:
- Użytkownik wykonuje gest interfejsu użytkownika, taki jak wybranie przycisku, aby wskazać, że chce ponowić próbę wykonania procedury lub gdy użytkownik przejdzie do nowego składnika.
- Dodatkowa logika wykonywana również usuwa wyjątek. Gdy składnik jest rerendered, błąd nie jest powtarzany.
Poniższy przykład umożliwia użytkownikowi odzyskanie sprawności po wyjątku za pomocą przycisku:
<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;
}
Można również podklasę ErrorBoundary do przetwarzania niestandardowego, zastępując element OnErrorAsync. Poniższy przykład rejestruje tylko błąd, ale można zaimplementować dowolny kod obsługi błędów. Możesz usunąć wiersz, który zwraca CompletedTask wartość , jeśli kod oczekuje na zadanie asynchroniczne.
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;
}
}
Powyższy przykład można również zaimplementować jako klasę.
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;
}
}
Jedną z poprzednich implementacji używanych w składniku:
<CustomErrorBoundary>
...
</CustomErrorBoundary>
Alternatywna globalna obsługa wyjątków
Podejście opisane w tej sekcji dotyczy Blazor Serverelementów , Blazor WebAssemblyi Blazor Web App, które przyjmują globalny interaktywny tryb renderowania (InteractiveServer
, InteractiveWebAssembly
, lub InteractiveAuto
). Takie podejście nie działa z modelami Blazor Web App, które przyjmują tryby renderowania poszczególnych stron/składników ani statyczne renderowanie po stronie serwera (statyczny przewodnik SSR), ponieważ podejście opiera się na metodzie CascadingValue
/CascadingParameter
, która nie działa w granicach trybu renderowania ani ze składnikami, które przyjmują statyczny przewodnik SSR.
Alternatywą dla używania granic błędów (ErrorBoundary) jest przekazanie niestandardowego składnika błędu jako CascadingValue
składników podrzędnych. Zaletą korzystania ze składnika za pomocą wprowadzonej usługi lub niestandardowej implementacji rejestratora jest to, że składnik kaskadowy może renderować zawartość i stosować style CSS w przypadku wystąpienia błędu.
Poniższy ProcessError
przykład składnika jedynie rejestruje błędy, ale metody składnika mogą przetwarzać błędy w dowolny sposób wymagany przez aplikację, w tym za pomocą wielu metod przetwarzania błędów.
ProcessError.razor
:
@inject ILogger<ProcessError> Logger
<CascadingValue Value="this">
@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();
}
}
Uwaga
Aby uzyskać więcej informacji na temat RenderFragmentprogramu , zobacz składniki ASP.NET CoreRazor.
W przypadku korzystania z tego podejścia w elemencie Blazor Web Appotwórz Routes
składnik i opakuj Router składnik (<Router>...</Router>
) ze składnikiem ProcessError
. ProcessError
Umożliwia to składnikowi kaskadowe działanie dowolnej aplikacji, w której ProcessError
składnik jest odbierany jako CascadingParameter
.
W pliku Routes.razor
:
<ProcessError>
<Router ...>
...
</Router>
</ProcessError>
W przypadku korzystania z tego podejścia w aplikacji Blazor Server lub Blazor WebAssembly otwórz App
składnik, opakuj Router składnik (<Router>...</Router>
) za ProcessError
pomocą składnika . ProcessError
Umożliwia to składnikowi kaskadowe działanie dowolnej aplikacji, w której ProcessError
składnik jest odbierany jako CascadingParameter
.
W pliku App.razor
:
<ProcessError>
<Router ...>
...
</Router>
</ProcessError>
Aby przetworzyć błędy w składniku:
Wyznaczanie
ProcessError
składnika jako elementuCascadingParameter
w@code
bloku. W przykładowymCounter
składniku aplikacji na Blazor podstawie szablonu projektu dodaj następującąProcessError
właściwość:[CascadingParameter] public ProcessError? ProcessError { get; set; }
Wywołaj metodę przetwarzania błędów w dowolnym
catch
bloku z odpowiednim typem wyjątku. PrzykładowyProcessError
składnik oferuje tylko jednąLogError
metodę, ale składnik przetwarzania błędów może zapewnić dowolną liczbę metod przetwarzania błędów w celu rozwiązania alternatywnych wymagań dotyczących przetwarzania błędów w całej aplikacji.Counter
Poniższy przykład bloku składników@code
zawieraProcessError
parametr kaskadowy i pułapki wyjątek rejestrowania, gdy liczba jest większa niż pięć:@code { private int currentCount = 0; [CascadingParameter] public 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); } } }
Zarejestrowany błąd:
fail: {COMPONENT NAMESPACE}.ProcessError[0]
ProcessError.LogError: System.InvalidOperationException Message: Current count is over five!
LogError
Jeśli metoda bezpośrednio uczestniczy w renderowaniu, takich jak wyświetlanie niestandardowego paska komunikatu o błędzie lub zmiana stylów CSS renderowanych elementów, wywołaj StateHasChanged
na końcu LogError
metody , aby rerender interfejsu użytkownika.
Ponieważ metody w tej sekcji obsługują błędy z instrukcją try-catch
, połączenie aplikacji SignalR między klientem a serwerem nie jest uszkodzone, gdy wystąpi błąd, a obwód pozostaje aktywny. Inne nieobsługiwane wyjątki pozostają krytyczne dla obwodu. Aby uzyskać więcej informacji, zobacz sekcję dotyczącą reagowania obwodu na nieobsługiwane wyjątki.
Aplikacja może używać składnika przetwarzania błędów jako wartości kaskadowej do przetwarzania błędów w scentralizowany sposób.
Następujący ProcessError
składnik przekazuje się jako CascadingValue
składnik podrzędny. Poniższy przykład jedynie rejestruje błąd, ale metody składnika mogą przetwarzać błędy w dowolny sposób wymagany przez aplikację, w tym za pomocą wielu metod przetwarzania błędów. Zaletą korzystania ze składnika za pomocą wprowadzonej usługi lub niestandardowej implementacji rejestratora jest to, że składnik kaskadowy może renderować zawartość i stosować style CSS w przypadku wystąpienia błędu.
ProcessError.razor
:
@using Microsoft.Extensions.Logging
@inject ILogger<ProcessError> Logger
<CascadingValue Value="this">
@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);
}
}
Uwaga
Aby uzyskać więcej informacji na temat RenderFragmentprogramu , zobacz składniki ASP.NET CoreRazor.
W składniku App
opakuj Router składnik za pomocą ProcessError
składnika. ProcessError
Umożliwia to składnikowi kaskadowe działanie dowolnej aplikacji, w której ProcessError
składnik jest odbierany jako CascadingParameter
.
App.razor
:
<ProcessError>
<Router ...>
...
</Router>
</ProcessError>
Aby przetworzyć błędy w składniku:
Wyznaczanie
ProcessError
składnika jako elementuCascadingParameter
w@code
bloku:[CascadingParameter] public ProcessError ProcessError { get; set; }
Wywołaj metodę przetwarzania błędów w dowolnym
catch
bloku z odpowiednim typem wyjątku. PrzykładowyProcessError
składnik oferuje tylko jednąLogError
metodę, ale składnik przetwarzania błędów może zapewnić dowolną liczbę metod przetwarzania błędów w celu rozwiązania alternatywnych wymagań dotyczących przetwarzania błędów w całej aplikacji.try { ... } catch (Exception ex) { ProcessError.LogError(ex); }
Korzystając z poprzedniego przykładowego ProcessError
składnika i LogError
metody, konsola narzędzi deweloperskich przeglądarki wskazuje uwięziony, zarejestrowany błąd:
fail: {COMPONENT NAMESPACE}.Shared.ProcessError[0]
ProcessError.LogError: System.NullReferenceException Message: Object reference not set to an instance of an object.
LogError
Jeśli metoda bezpośrednio uczestniczy w renderowaniu, takich jak wyświetlanie niestandardowego paska komunikatu o błędzie lub zmiana stylów CSS renderowanych elementów, wywołaj StateHasChanged
na końcu LogError
metody , aby rerender interfejsu użytkownika.
Ponieważ metody w tej sekcji obsługują błędy z instrukcją try-catch
, Blazor połączenie aplikacji SignalR między klientem a serwerem nie jest uszkodzone, gdy wystąpi błąd, a obwód pozostaje aktywny. Każdy nieobsługiwany wyjątek jest krytyczny dla obwodu. Aby uzyskać więcej informacji, zobacz sekcję dotyczącą reagowania obwodu na nieobsługiwane wyjątki.
Rejestrowanie błędów z trwałym dostawcą
Jeśli wystąpi nieobsługiwany wyjątek, wyjątek jest rejestrowany w ILogger wystąpieniach skonfigurowanych w kontenerze usługi. Blazor dane wyjściowe konsoli dzienników aplikacji za pomocą dostawcy rejestrowania konsoli. Rozważ rejestrowanie w lokalizacji na serwerze (lub internetowym interfejsie API zaplecza dla aplikacji po stronie klienta) z dostawcą, który zarządza rozmiarem dziennika i rotacją dzienników. Alternatywnie aplikacja może używać usługi zarządzania wydajnością aplikacji (APM), takiej jak aplikacja systemu Azure Insights (Azure Monitor).
Uwaga
Natywne funkcje usługi Application Insights do obsługi aplikacji po stronie klienta i natywnej Blazor obsługi platformy dla usługi Google Analytics mogą stać się dostępne w przyszłych wersjach tych technologii. Aby uzyskać więcej informacji, zobacz Support App Insights in Blazor WASM Client Side (microsoft/ApplicationInsights-dotnet #2143) i Web Analytics and diagnostics (zawiera linki do implementacji społeczności) (dotnet/aspnetcore #5461). W międzyczasie aplikacja po stronie klienta może używać zestawu SDK języka JavaScript usługi Application Insights z JS międzyoperacji , aby rejestrować błędy bezpośrednio w usłudze Application Insights z poziomu aplikacji po stronie klienta.
Podczas programowania w aplikacji działającej Blazor w obwodzie aplikacja zwykle wysyła pełne szczegóły wyjątków do konsoli przeglądarki, aby ułatwić debugowanie. W środowisku produkcyjnym szczegółowe błędy nie są wysyłane do klientów, ale pełne szczegóły wyjątku są rejestrowane na serwerze.
Musisz zdecydować, które zdarzenia mają być rejestrowane, oraz poziom ważności zarejestrowanych zdarzeń. Wrogi użytkownicy mogą celowo wyzwalać błędy. Na przykład nie rejestruj zdarzenia z błędu, w którym nieznany ProductId
jest podany w adresie URL składnika, który wyświetla szczegóły produktu. Nie wszystkie błędy powinny być traktowane jako zdarzenia rejestrowania.
Aby uzyskać więcej informacji, zobacz następujące artykuły:
- Rejestrowanie Blazor platformy ASP.NET Core
- Obsługa błędów w programie ASP.NET Core**
- Tworzenie internetowych interfejsów API za pomocą platformy ASP.NET Core
‹Dotyczy aplikacji po stronie Blazor serwera i innych aplikacji po stronie serwera ASP.NET Core, które są aplikacjami zaplecza internetowego interfejsu API dla programu Blazor. Aplikacje po stronie klienta mogą wychwytować i wysyłać informacje o błędach na kliencie do internetowego interfejsu API, który rejestruje informacje o błędzie u dostawcy trwałego rejestrowania.
Jeśli wystąpi nieobsługiwany wyjątek, wyjątek jest rejestrowany w ILogger wystąpieniach skonfigurowanych w kontenerze usługi. Blazor dane wyjściowe konsoli dzienników aplikacji za pomocą dostawcy rejestrowania konsoli. Rozważ rejestrowanie w bardziej trwałej lokalizacji na serwerze, wysyłając informacje o błędach do internetowego interfejsu API zaplecza, który używa dostawcy rejestrowania z zarządzaniem rozmiarem dziennika i rotacją dzienników. Alternatywnie aplikacja internetowego interfejsu API zaplecza może używać usługi Application Performance Management (APM), takiej jak aplikacja systemu Azure Insights (Azure Monitor)†, do rejestrowania informacji o błędach odbieranych od klientów.
Musisz zdecydować, które zdarzenia mają być rejestrowane, oraz poziom ważności zarejestrowanych zdarzeń. Wrogi użytkownicy mogą celowo wyzwalać błędy. Na przykład nie rejestruj zdarzenia z błędu, w którym nieznany ProductId
jest podany w adresie URL składnika, który wyświetla szczegóły produktu. Nie wszystkie błędy powinny być traktowane jako zdarzenia rejestrowania.
Aby uzyskać więcej informacji, zobacz następujące artykuły:
- Rejestrowanie Blazor platformy ASP.NET Core
- Obsługa błędów w programie ASP.NET Core**
- Tworzenie internetowych interfejsów API za pomocą platformy ASP.NET Core
†Native Application Insights funkcje do obsługi aplikacji po stronie klienta i natywnej Blazor obsługi platformy google Analytics mogą stać się dostępne w przyszłych wersjach tych technologii. Aby uzyskać więcej informacji, zobacz Support App Insights in Blazor WASM Client Side (microsoft/ApplicationInsights-dotnet #2143) i Web Analytics and diagnostics (zawiera linki do implementacji społeczności) (dotnet/aspnetcore #5461). W międzyczasie aplikacja po stronie klienta może używać zestawu SDK języka JavaScript usługi Application Insights z JS międzyoperacji , aby rejestrować błędy bezpośrednio w usłudze Application Insights z poziomu aplikacji po stronie klienta.
‹Dotyczy aplikacji zaplecza interfejsu API sieci Web ASP.NET Core po stronie serwera dla Blazor aplikacji. Aplikacje po stronie klienta wychwytywania i wysyłania informacji o błędach do internetowego interfejsu API, który rejestruje informacje o błędzie u dostawcy trwałego rejestrowania.
Miejsca, w których mogą wystąpić błędy
Kod platformy i aplikacji może wyzwalać nieobsługiwane wyjątki w dowolnej z następujących lokalizacji, które zostały opisane w poniższych sekcjach tego artykułu:
Tworzenie wystąpienia składnika
Podczas Blazor tworzenia wystąpienia składnika:
- Wywoływany jest konstruktor składnika.
- Konstruktory usług DI dostarczonych do konstruktora składnika za pośrednictwem
@inject
dyrektywy lub atrybutu[Inject]
są wywoływane.
Błąd w wykonanym konstruktorze lub ustawieniu dla dowolnej [Inject]
właściwości powoduje nieobsługiwany wyjątek i uniemożliwia utworzenie wystąpienia składnika przez strukturę. Jeśli aplikacja działa w obwodzie, obwód ulegnie awarii. Jeśli logika konstruktora może zgłaszać wyjątki, aplikacja powinna wychwycić wyjątki przy użyciu instrukcji z obsługą try-catch
błędów i rejestrowaniem.
Metody cyklu życia
W okresie istnienia składnika Blazor wywołuje metody cyklu życia. Jeśli jakakolwiek metoda cyklu życia zgłasza wyjątek, synchronicznie lub asynchronicznie, wyjątek jest krytyczny dla obwodu. Aby składniki obsługiwały błędy w metodach cyklu życia, dodaj logikę obsługi błędów.
W poniższym przykładzie, w którym OnParametersSetAsync wywołuje metodę w celu uzyskania produktu:
- Wyjątek zgłaszany w metodzie
ProductRepository.GetProductByIdAsync
jest obsługiwany przez instrukcjętry-catch
. - Po wykonaniu
catch
bloku:loadFailed
jest ustawiona natrue
wartość , która służy do wyświetlania użytkownikowi komunikatu o błędzie.- Błąd jest rejestrowany.
@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 renderowania
Znacznik deklaratywny w Razor pliku składnika (.razor
) jest kompilowany w metodzie języka C# o nazwie BuildRenderTree. Gdy składnik renderuje, BuildRenderTree wykonuje i tworzy strukturę danych opisującą elementy, tekst i składniki podrzędne renderowanego składnika.
Logika renderowania może zgłosić wyjątek. Przykład tego scenariusza występuje, gdy @someObject.PropertyName
jest obliczany, ale @someObject
to null
. W przypadku Blazor aplikacji działających w obwodzie nieobsługiwany wyjątek zgłaszany przez logikę renderowania jest krytyczny dla obwodu aplikacji.
Aby zapobiec NullReferenceException logice renderowania, sprawdź null
obiekt przed uzyskaniem dostępu do jego elementów członkowskich. W poniższym przykładzie właściwości nie są dostępne, person.Address
jeśli person.Address
to 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>
}
Powyższy kod zakłada, że person
nie null
jest to . Często struktura kodu gwarantuje, że obiekt istnieje w momencie renderowania składnika. W takich przypadkach nie jest konieczne sprawdzenie null
w logice renderowania. W poprzednim przykładzie może istnieć gwarancja istnienia, person
ponieważ person
jest tworzona po utworzeniu wystąpienia składnika, jak pokazano w poniższym przykładzie:
@code {
private Person person = new();
...
}
Procedury obsługi zdarzeń
Kod po stronie klienta wyzwala wywołania kodu języka C#, gdy programy obsługi zdarzeń są tworzone przy użyciu:
@onclick
@onchange
- Inne
@on...
atrybuty @bind
Kod procedury obsługi zdarzeń może zgłosić nieobsługiwany wyjątek w tych scenariuszach.
Jeśli aplikacja wywołuje kod, który może zakończyć się niepowodzeniem ze względów zewnętrznych, wychwytuje wyjątki przy użyciu instrukcji z obsługą try-catch
błędów i rejestrowaniem.
Jeśli program obsługi zdarzeń zgłasza nieobsługiwany wyjątek (na przykład zapytanie bazy danych kończy się niepowodzeniem), które nie jest uwięzione i obsługiwane przez kod dewelopera:
- Struktura rejestruje wyjątek.
- W aplikacji działającej Blazor w obwodzie wyjątek jest krytyczny dla obwodu aplikacji.
Usuwanie składników
Składnik może zostać usunięty z interfejsu użytkownika, na przykład dlatego, że użytkownik przeszedł do innej strony. Gdy składnik implementujący System.IDisposable zostanie usunięty z interfejsu użytkownika, struktura wywołuje metodę składnika Dispose .
Jeśli metoda składnika Dispose
zgłasza nieobsługiwany wyjątek w aplikacji działającej Blazor w obwodzie, wyjątek jest krytyczny dla obwodu aplikacji.
Jeśli logika usuwania może zgłaszać wyjątki, aplikacja powinna wychwycić wyjątki przy użyciu instrukcji z obsługą try-catch
błędów i rejestrowaniem.
Aby uzyskać więcej informacji na temat usuwania składników, zobacz ASP.NET Core component lifecycle (Cykl życia składnika podstawowego ASP.NET).Razor
Międzyoperacyjność w języku JavaScript
IJSRuntime program jest zarejestrowany przez platformę Blazor . IJSRuntime.InvokeAsync Umożliwia programowi .NET wykonywanie wywołań asynchronicznych do środowiska uruchomieniowego JavaScript (JS) w przeglądarce użytkownika.
Następujące warunki dotyczą obsługi błędów za pomocą polecenia InvokeAsync:
- Jeśli wywołanie InvokeAsync nie powiedzie się synchronicznie, wystąpi wyjątek platformy .NET. Wywołanie polecenia może zakończyć się InvokeAsync niepowodzeniem, na przykład dlatego, że podanych argumentów nie można serializować. Kod dewelopera musi przechwycić wyjątek. Jeśli kod aplikacji w procedurze obsługi zdarzeń lub cyklu życia składnika nie obsługuje wyjątku w aplikacji działającej Blazor w obwodzie, wynikowy wyjątek jest krytyczny dla obwodu aplikacji.
- Jeśli wywołanie InvokeAsync nie powiedzie się asynchronicznie, platforma .NET Task zakończy się niepowodzeniem. Wywołanie polecenia może zakończyć się InvokeAsync niepowodzeniem, na przykład dlatego, że JSkod -side zgłasza wyjątek lub zwraca wartość ukończoną
Promise
jakorejected
. Kod dewelopera musi przechwycić wyjątek. Jeśli używaszawait
operatora, rozważ zawijanie wywołania metody w instrukcji z obsługątry-catch
błędów i rejestrowaniem. W przeciwnym razie w aplikacji działającej Blazor w obwodzie kod, który kończy się niepowodzeniem, powoduje nieobsługiwany wyjątek krytyczny dla obwodu aplikacji. - Wywołania muszą InvokeAsync zostać ukończone w określonym przedziale czasu lub w przeciwnym razie limit czasu wywołania. Domyślny okres limitu czasu to jedna minuta. Limit czasu chroni kod przed utratą łączności sieciowej lub JS kodu, który nigdy nie wysyła komunikatu ukończenia. Jeśli wywołanie zostanie upłynął, wynik System.Threading.Tasks zakończy się niepowodzeniem z parametrem OperationCanceledException. Pułapka i przetwarzanie wyjątku z rejestrowaniem.
JS Podobnie kod może inicjować wywołania metod platformy .NET wskazywanych [JSInvokable]
przez atrybut . Jeśli te metody platformy .NET zgłaszają nieobsługiwany wyjątek:
- W aplikacji działającej Blazor w obwodzie wyjątek nie jest traktowany jako krytyczny dla obwodu aplikacji.
- Strona JS-
Promise
jest odrzucana.
Istnieje możliwość użycia kodu obsługi błędów po stronie platformy .NET lub JS po stronie wywołania metody.
Aby uzyskać więcej informacji, zobacz następujące artykuły:
- Wywoływanie funkcji języka JavaScript z metod platformy .NET na platformie ASP.NET Core Blazor
- Wywoływanie metod platformy .NET z funkcji języka JavaScript z na platformie ASP.NET Core Blazor
Prerendering
Razor składniki są domyślnie wstępnie obsługiwane, dzięki czemu ich renderowane znaczniki HTML są zwracane w ramach początkowego żądania HTTP użytkownika.
W aplikacji działającej Blazor na obwodzie wstępne działanie działa przez:
- Utworzenie nowego obwodu dla wszystkich wstępnie wstępnie zainstalowanych składników, które są częścią tej samej strony.
- Generowanie początkowego kodu HTML.
- Traktowanie obwodu jako
disconnected
do momentu nawiązania połączenia z powrotem przez przeglądarkę SignalR użytkownika z tym samym serwerem. Po nawiązaniu połączenia interakcyjność w obwodzie jest wznawiana, a znaczniki HTML składników są aktualizowane.
W przypadku wstępnie utworzonych składników po stronie klienta prerendering działa przez:
- Generowanie początkowego kodu HTML na serwerze dla wszystkich wstępnie wstępnie utworzonych składników, które są częścią tej samej strony.
- Interakcyjny składnik na kliencie po załadowaniu przez przeglądarkę skompilowanego kodu aplikacji i środowiska uruchomieniowego platformy .NET (jeśli jeszcze nie został załadowany) w tle.
Jeśli składnik zgłasza nieobsługiwany wyjątek podczas prerenderingu, na przykład podczas metody cyklu życia lub w logice renderowania:
- W aplikacji działającej Blazor w obwodzie wyjątek jest krytyczny dla obwodu. W przypadku wstępnie utworzonych składników po stronie klienta wyjątek uniemożliwia renderowanie składnika.
- Wyjątek jest zgłaszany w stosie wywołań z obiektu ComponentTagHelper.
W normalnych okolicznościach, gdy wstępne przetwarzanie nie powiedzie się, kontynuowanie kompilowania i renderowania składnika nie ma sensu, ponieważ nie można renderować składnika roboczego.
Aby tolerować błędy, które mogą wystąpić podczas prerenderingu, logika obsługi błędów musi zostać umieszczona wewnątrz składnika, który może zgłaszać wyjątki. Używanie try-catch
instrukcji z obsługą błędów i rejestrowaniem. Zamiast zawijać w ComponentTagHelper instrukcji, umieść logikę try-catch
obsługi błędów w składniku ComponentTagHelperrenderowany przez element .
Zaawansowane scenariusze
Renderowanie cykliczne
Składniki mogą być zagnieżdżone rekursywnie. Jest to przydatne do reprezentowania cyklicznych struktur danych. Na przykład TreeNode
składnik może renderować więcej TreeNode
składników dla każdego elementu podrzędnego węzła.
Podczas renderowania cyklicznego unikaj wzorców kodowania, które powodują nieskończoną rekursję:
- Nie rekursywnie renderuj struktury danych, która zawiera cykl. Na przykład nie renderuj węzła drzewa, którego elementy podrzędne zawierają się.
- Nie twórz łańcucha układów zawierających cykl. Na przykład nie twórz układu, którego układ jest sam.
- Nie zezwalaj użytkownikowi końcowemu na naruszenie rekursji (reguł) za pośrednictwem złośliwych wpisów danych lub wywołań międzyoperacyjnych języka JavaScript.
Pętle nieskończone podczas renderowania:
- Powoduje, że proces renderowania będzie kontynuowany na zawsze.
- Jest odpowiednikiem tworzenia nieokreślonej pętli.
W tych scenariuszach awaria kończy się niepowodzeniem Blazor i zwykle próbuje wykonać następujące czynności:
- Zużywaj tyle czasu procesora CPU, ile jest dozwolone przez system operacyjny, na czas nieokreślony.
- Zużywa nieograniczoną ilość pamięci. Korzystanie z nieograniczonej ilości pamięci jest równoważne scenariuszowi, w którym nieokreślona pętla dodaje wpisy do kolekcji w każdej iteracji.
Aby uniknąć nieskończonych wzorców rekursji, upewnij się, że kod renderowania cyklicznego zawiera odpowiednie warunki zatrzymywania.
Niestandardowa logika drzewa renderowania
Większość Razor składników jest implementowana jako Razor pliki składników (.razor
) i jest kompilowana przez platformę w celu utworzenia logiki, która działa w RenderTreeBuilder celu renderowania danych wyjściowych. Deweloper może jednak ręcznie zaimplementować RenderTreeBuilder logikę przy użyciu kodu proceduralnego języka C#. Aby uzyskać więcej informacji, zobacz ASP.NET Core Blazor advanced scenarios (renderowanie konstrukcji drzewa).
Ostrzeżenie
Korzystanie z ręcznej logiki konstruktora drzewa renderowania jest uznawane za zaawansowany i niebezpieczny scenariusz, który nie jest zalecany w przypadku ogólnego tworzenia składników.
Jeśli RenderTreeBuilder kod jest napisany, deweloper musi zagwarantować poprawność kodu. Na przykład deweloper musi upewnić się, że:
- Wywołania do OpenElement i CloseElement są prawidłowo wyważone.
- Atrybuty są dodawane tylko w odpowiednich miejscach.
Niepoprawna ręczna logika konstruktora drzewa renderowania może spowodować dowolne niezdefiniowane zachowanie, w tym awarie, aplikację lub serwer, aby przestać odpowiadać i luki w zabezpieczeniach.
Rozważ ręczne renderowanie logiki konstruktora drzewa na tym samym poziomie złożoności i z tym samym poziomem zagrożenia co pisanie kodu zestawu lub instrukcji języka Microsoft Intermediate Language (MSIL) ręcznie.
Dodatkowe zasoby
†Applies do zaplecza ASP.NET Core internetowych aplikacji interfejsu API używanych przez aplikacje po stronie Blazor klienta do rejestrowania.