處理 ASP.NET Core Blazor 應用程式中的錯誤

本文說明如何 Blazor 管理未處理的例外狀況,以及如何開發偵測及處理錯誤的應用程式。

開發期間的詳細錯誤

Blazor當應用程式在開發期間無法正常運作時,從應用程式接收詳細的錯誤資訊有助於進行疑難排解和修正問題。 發生錯誤時, Blazor 應用程式會在畫面底部顯示淺黃色列:

  • 在開發期間,列會將您導向瀏覽器主控台,您可以在其中看到例外狀況。
  • 在生產環境中,列會通知使用者發生錯誤,並建議重新整理瀏覽器。

此錯誤處理體驗的 UI 是專案範本的 Blazor一部分。

Blazor Server在應用程式中,自訂檔案中的 Pages/_Layout.cshtml 體驗:

<div id="blazor-error-ui">
    <environment include="Staging,Production">
        An error has occurred. This application may no longer respond until reloaded.
    </environment>
    <environment include="Development">
        An unhandled exception has occurred. See browser dev tools for details.
    </environment>
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

Blazor WebAssembly在應用程式中,自訂檔案中的 wwwroot/index.html 體驗:

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

元素 blazor-error-ui 通常會隱藏,因為 display: none 網站樣式表單中 CSS 類別的樣式 blazor-error-ui (wwwroot/css/app.csswwwroot/css/site.cssBlazor ServerBlazor WebAssembly 或) 。 發生錯誤時,架構會 display: block 套用至 專案。

詳細的線路錯誤

本節適用于 Blazor Server 應用程式。

用戶端錯誤不包含呼叫堆疊,也不會提供錯誤原因的詳細資料,但伺服器記錄檔確實包含這類資訊。 為了開發目的,可藉由啟用詳細的錯誤,讓用戶端可以使用敏感性線路錯誤資訊。

CircuitOptions.DetailedErrors 設定為 true。 如需詳細資訊和範例,請參閱ASP.NET Core BlazorSignalR 指引

設定 CircuitOptions.DetailedErrors 的替代方法是在 DetailedErrors 應用程式的開發環境設定檔 () appsettings.Development.json 中將組態索引鍵 true 設定為 。 此外,將伺服器端記錄 () 設定 SignalR為 [錯] 或 [追蹤] 以取得詳細 SignalR 記錄。 Microsoft.AspNetCore.SignalR

appsettings.Development.json:

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

設定 DetailedErrors 機碼也可以設定為 true 使用 ASPNETCORE_DETAILEDERRORS 環境變數搭配開發/預備環境伺服器或本機系統上的值 true

警告

請務必避免將錯誤資訊公開給網際網路上的用戶端,這是安全性風險。

在開發人員程式碼中管理未處理的例外狀況

若要讓應用程式在發生錯誤之後繼續,應用程式必須具有錯誤處理邏輯。 本文稍後的章節將說明未處理的例外狀況的潛在來源。

在生產環境中,請勿在 UI 中轉譯架構例外狀況訊息或堆疊追蹤。 轉譯例外狀況訊息或堆疊追蹤可能會:

  • 向使用者揭露敏感性資訊。
  • 協助惡意使用者探索應用程式中可能會危害應用程式、伺服器或網路安全性的弱點。

Blazor Server 未處理的例外狀況

本節適用于 Blazor Server 應用程式。

Blazor Server 是具狀態架構。 當使用者與應用程式互動時,他們會維護與稱為 線路的伺服器連線。 線路會保存作用中的元件實例,再加上狀態的其他許多層面,例如:

  • 元件的最新轉譯輸出。
  • 用戶端事件可能觸發的目前事件處理委派集。

如果使用者在多個瀏覽器索引標籤中開啟應用程式,使用者就會建立多個獨立線路。

Blazor 會將大部分未處理的例外狀況視為發生所線上路的嚴重例外狀況。 如果線路因未處理的例外狀況而終止,則使用者只能重載頁面以建立新的線路,繼續與應用程式互動。 在終止的線路之外,其他使用者或其他瀏覽器索引標籤的線路不會受到影響。 此案例類似于當機的桌面應用程式。 損毀的應用程式必須重新開機,但其他應用程式不會受到影響。

架構會在發生未處理的例外狀況時終止線路,原因如下:

  • 未處理的例外狀況通常會讓線路處於未定義的狀態。
  • 在未處理的例外狀況之後,無法保證應用程式的正常作業。
  • 如果線路繼續處於未定義狀態,應用程式可能會顯示安全性弱點。

錯誤界限

Blazor 是單頁應用程式, (SPA) 用戶端架構。 瀏覽器可作為應用程式的主機,因此會根據流覽和靜態資產的 URI 要求,作為個別 Razor 元件的處理管線。 不同于 ASP.NET Core使用中介軟體處理管線在伺服器上執行的應用程式,沒有任何中介軟體管線會處理可用於全域錯誤處理的元件要求 Razor 。 不過,應用程式可以使用錯誤處理元件作為串聯值,以集中方式處理錯誤。

錯誤界限 提供方便的方法來處理例外狀況。 元件 ErrorBoundary

  • 發生錯誤時,轉譯其子內容。
  • 擲回未處理的例外狀況時,轉譯錯誤 UI。

若要定義錯誤界限,請使用 ErrorBoundary 元件來包裝現有的內容。 例如,可以在應用程式主要版面配置主體內容周圍新增錯誤界限。

Shared/MainLayout.razor:

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

應用程式會繼續正常運作,但錯誤界限會處理未處理的例外狀況。

請考慮下列範例,其中 Counter ,如果計數遞增超過五,元件會擲回例外狀況。

Pages/Counter.razor 中:

private void IncrementCount()
{
    currentCount++;

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

如果擲回未處理的例外狀況超過 currentCount 五個:

  • 例外狀況是由錯誤界限處理。
  • 錯誤 UI 會轉譯 (An error has occurred.) 。

根據預設, ErrorBoundary 元件會針對其錯誤內容轉譯具有 CSS 類別的 blazor-error-boundary 空白 <div> 元素。 預設 UI 的色彩、文字和圖示是使用資料夾中應用程式樣式表單 wwwroot 中的 CSS 來定義,因此您可以自由地自訂錯誤 UI。

您也可以藉由設定 ErrorContent 屬性來變更預設的錯誤內容:

<ErrorBoundary>
    <ChildContent>
        @Body
    </ChildContent>
    <ErrorContent>
        <p class="errorUI">Nothing to see here right now. Sorry!</p>
    </ErrorContent>
</ErrorBoundary>

因為錯誤界限是在上述範例的版面配置中定義,所以不論使用者流覽到哪一個頁面,都會看到錯誤 UI。 我們建議在大部分情況下,將錯誤界限的範圍縮小。 如果您廣泛限定錯誤界限的範圍,您可以藉由呼叫錯誤界限的 Recover 方法,將其重設為後續頁面導覽事件的非錯誤狀態:

...

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

...

@code {
    private ErrorBoundary? errorBoundary;

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

替代的全域例外狀況處理

() ErrorBoundary 使用Error 界限的替代方法是將自訂錯誤元件當做 CascadingValue 傳遞至子元件。 使用元件而非使用 插入的服務 或自訂記錄器實作的優點是,串聯元件可以在發生錯誤時轉譯內容並套用 CSS 樣式。

下列 Error 元件範例只會記錄錯誤,但元件的方法可以透過應用程式所需的任何方式處理錯誤,包括透過使用多個錯誤處理方法。

Shared/Error.razor:

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

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

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

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

注意

如需 的詳細資訊 RenderFragment ,請參閱ASP.NET Core Razor 元件

在 元件中 App ,使用 Router 元件包裝元件 Error 。 這可讓 Error 元件串聯至接收 CascadingParameter 元件之 Error 應用程式的任何元件。

App.razor:

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

若要處理元件中的錯誤:

  • Error 元件指定為 CascadingParameter 區塊中的 @code 。 在以專案範本為基礎的 Blazor 應用程式中範例 Counter 元件中,新增下列 Error 屬性:

    [CascadingParameter]
    public Error? Error { get; set; }
    
  • 在任何具有適當例外狀況類型的區塊中 catch 呼叫錯誤處理方法。 範例 Error 元件只提供單 ProcessError 一方法,但錯誤處理元件可以提供任意數目的錯誤處理方法,以解決整個應用程式的替代錯誤處理需求。 在下列 Counter 元件範例中,當計數大於五時,會擲回並截獲例外狀況:

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

使用上述 Error 元件搭配對元件的先前變更 Counter ,瀏覽器的開發人員工具主控台會指出已截獲、記錄的錯誤:

fail: BlazorSample.Shared.Error[0]
Error:ProcessError - Type: System.InvalidOperationException Message: Current count is over five!

ProcessError如果方法直接參與轉譯,例如顯示自訂錯誤訊息列或變更轉譯專案的 CSS 樣式,請在 方法結尾 ProcessErrors 呼叫 StateHasChanged 以重新呈現 UI。

由於本節中的方法會處理語句的錯誤,因此當發生錯誤 try-catch 且線路保持運作時, Blazor Server 用戶端與伺服器之間的應用程式 SignalR 連線不會中斷。 其他未處理的例外狀況對線路仍然嚴重。 如需詳細資訊,請參閱上一節,以瞭解 應用程式如何 Blazor Server 回應未處理的例外狀況

記錄持續性提供者的錯誤

如果發生未處理的例外狀況,則會將例外狀況記錄到 ILogger 服務容器中設定的實例。 根據預設, Blazor 應用程式會使用主控台記錄提供者登入主控台輸出。 請考慮使用記錄管理大小和記錄輪替的提供者,針對應用程式) 的伺服器 (或後端 Web API Blazor WebAssembly 上的位置。 或者,應用程式可以使用應用程式效能管理 (APM) 服務,例如Azure 應用程式 Insights (Azure 監視器)

注意

支援Google Analytics之應用程式和原生架構支援的原生 BlazorApplication Insights功能 Blazor WebAssembly ,在未來的這些技術版本中可能會變成可用。 如需詳細資訊,請參閱 支援 WASM 用戶端中的 Blazor App Insights (microsoft/ApplicationInsights-dotnet #2143) Web 分析和診斷 (包含社群實作的連結,) (dotnet/aspnetcore #5461) 。 同時,用戶端應用程式 Blazor WebAssembly 可以使用Application Insights JavaScript SDK搭配JS Interop,直接從用戶端應用程式將錯誤記錄至 Application Insights。

在應用程式開發 Blazor Server 期間,應用程式通常會將例外狀況的完整詳細資料傳送至瀏覽器的主控台,以協助偵錯。 在生產環境中,詳細錯誤不會傳送至用戶端,但例外狀況的完整詳細資料會記錄在伺服器上。

您必須決定要記錄的事件,以及記錄事件的嚴重性層級。 惡意使用者可能能夠刻意觸發錯誤。 例如,請勿從顯示產品詳細資料的元件 URL 中提供未知 ProductId 的錯誤記錄事件。 並非所有錯誤都應該視為記錄的事件。

如需詳細資訊,請參閱下列文章:

≦適用于 Blazor Server 的應用程式和其他伺服器端 ASP.NET Core應用程式,這些應用程式是 的 Blazor Web API 後端應用程式。 Blazor WebAssembly 應用程式可以將用戶端上的錯誤資訊陷阱並傳送至 Web API,以將錯誤資訊記錄至持續性記錄提供者。

可能發生錯誤的位置

架構和應用程式程式碼可能會在下列任何位置觸發未處理的例外狀況,本文下列各節會進一步說明:

元件具現化

建立元件的實例時 Blazor :

  • 會叫用元件的建構函式。
  • 會透過 @inject 指示詞或[Inject] 屬性叫用提供給元件建構函式的 DI 服務的建構函式。

任何屬性的執行建構函式或 setter [Inject] 錯誤會導致未處理的例外狀況,並阻止架構具現化元件。 如果應用程式是 Blazor Server 應用程式,線路就會失敗。 如果建構函式邏輯可能會擲回例外狀況,應用程式應該使用 try-catch 具有錯誤處理和記錄的 語句來捕捉例外狀況。

生命週期方法

在元件的存留期間, Blazor 叫用 生命週期方法。 如果有任何生命週期方法以同步或非同步方式擲回例外狀況,則例外狀況對 Blazor Server 線路是嚴重的。 若要讓元件處理生命週期方法中的錯誤,請新增錯誤處理邏輯。

在下列範例中,其中 OnParametersSetAsync 會呼叫 方法來取得產品:

  • 方法中擲回的 ProductRepository.GetProductByIdAsync 例外狀況是由 try-catch 語句處理。
  • catch執行 區塊時:
    • loadFailed 設定為 true ,用來向使用者顯示錯誤訊息。
    • 系統會記錄錯誤。
@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;
            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);
    }
}

轉譯邏輯

元件檔案中的 Razor 宣告式標記 () .razor 編譯成稱為 的 BuildRenderTree C# 方法。 當元件轉譯時, BuildRenderTree 會執行並建置描述轉譯元件之專案、文字和子元件的資料結構。

轉譯邏輯可能會擲回例外狀況。 當 評估 但 @someObjectnull@someObject.PropertyName ,就會發生此案例的範例。 對於 Blazor Server 應用程式,轉譯邏輯擲回的未處理例外狀況對應用程式的線路而言十分嚴重。

若要防止轉 NullReferenceException 譯邏輯中的 ,請先檢查物件, null 再存取其成員。 在下列範例中,如果 person.Addressnullperson.Address 則不會存取屬性:

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

上述程式碼假設 person 不是 null 。 通常,程式碼的結構保證元件轉譯時存在物件。 在這些情況下,不需要在轉譯邏輯中檢查 null 。 在先前的範例中, person 可能會保證存在,因為在 person 具現化元件時會建立,如下列範例所示:

@code {
    private Person person = new();

    ...
}

事件處理常式

使用下列專案建立事件處理常式時,用戶端程式代碼會觸發 C# 程式碼的調用:

  • @onclick
  • @onchange
  • 其他 @on... 屬性
  • @bind

事件處理常式程式碼可能會在這些案例中擲回未處理的例外狀況。

如果應用程式呼叫可能會因為外部原因而失敗的程式碼,請使用 try-catch 具有錯誤處理和記錄的 語句來捕捉例外狀況。

例如,如果事件處理常式擲回未處理的例外狀況 (,資料庫查詢就會失敗,) 開發人員程式碼不會截獲並加以處理:

  • 架構會記錄例外狀況。
  • 在 Blazor Server 應用程式中,例外狀況對應用程式的線路是嚴重的。

元件處置

例如,使用者已流覽至另一個頁面,所以可能會從 UI 中移除元件。 從 UI 移除實作 System.IDisposable 的元件時,架構會呼叫元件的 Dispose 方法。

如果元件的 Dispose 方法在 Blazor Server 應用程式中擲回未處理的例外狀況,則此例外狀況對應用程式的線路是嚴重的。

如果處置邏輯可能會擲回例外狀況,應用程式應該使用 try-catch 具有錯誤處理和記錄的 語句來捕捉例外狀況。

如需元件處置的詳細資訊,請參閱ASP.NET Core Razor 元件生命週期

JavaScript Interop

IJSRuntime 是由 Blazor 架構註冊。 IJSRuntime.InvokeAsync 允許 .NET 程式碼在使用者的瀏覽器中對 JavaScript (JS) 執行時間進行非同步呼叫。

下列條件適用于 使用 InvokeAsync 的錯誤處理:

  • 如果呼叫 InvokeAsync 同步失敗,就會發生 .NET 例外狀況。 例如,呼叫 InvokeAsync 可能會失敗,因為提供的引數無法序列化。 開發人員程式碼必須攔截例外狀況。 如果事件處理常式或元件生命週期方法中的應用程式程式碼未處理應用程式中的例外 Blazor Server 狀況,則產生的例外狀況對應用程式的線路會嚴重。
  • 如果呼叫 InvokeAsync 以非同步方式失敗,.NET Task 就會失敗。 例如,呼叫 InvokeAsync 可能會失敗,因為 JS -side 程式碼會擲回例外狀況,或傳回 Promise 完成為 rejected 的 。 開發人員程式碼必須攔截例外狀況。 如果使用 await 運算子,請考慮將方法呼叫包裝在語句中 try-catch ,並包含錯誤處理和記錄。 否則,在 Blazor Server 應用程式中,失敗的程式碼會導致應用程式線路嚴重未處理的例外狀況。
  • 根據預設,呼叫 InvokeAsync 必須在特定期間內完成,否則呼叫逾時。預設逾時期間為一分鐘。 逾時可保護程式碼免于網路連線中斷或 JS 永遠不會傳回完成訊息的程式碼。 如果呼叫逾時,產生的 System.Threading.Tasks 會失敗並 OperationCanceledException 出現 。 使用記錄來捕捉和處理例外狀況。

同樣地,程式 JS 代碼可能會起始對 屬性所指示之 .NET 方法的[JSInvokable]呼叫。 如果這些 .NET 方法擲回未處理的例外狀況:

  • Blazor Server在應用程式中,例外狀況不會被視為應用程式的線路嚴重。
  • JS拒絕 -side Promise

您可以選擇在 .NET 端或 JS 方法呼叫端使用錯誤處理常式代碼。

如需詳細資訊,請參閱下列文章:

預先呈現

Razor 元件可以使用 元件標籤協助程式 預先呈現,以便將其轉譯的 HTML 標籤傳回為使用者初始 HTTP 要求的一部分。

在 中 Blazor Server ,預先呈現的運作方式如下:

  • 為所有屬於相同頁面一部分的預先呈現元件建立新的線路。
  • 產生初始 HTML。
  • 將線路 disconnected 視為 ,直到使用者的瀏覽器建立 SignalR 與相同伺服器的連線為止。 建立連線時,會繼續線路上的互動性,並更新元件的 HTML 標籤。

在預先呈現中 Blazor WebAssembly ,預先呈現的運作方式如下:

  • 針對屬於相同頁面的所有預先呈現元件,在伺服器上產生初始 HTML。
  • 在瀏覽器載入應用程式的已編譯器代碼和 .NET 執行時間之後,讓用戶端上的元件成為互動式元件,如果尚未在背景中載入) , (。

如果元件在預先呈現期間擲回未處理的例外狀況,例如,在生命週期方法或轉譯邏輯中:

  • 在 Blazor Sever 應用程式中,例外狀況對線路是嚴重的。 在預先呈現 Blazor WebAssembly 的應用程式中,例外狀況會防止轉譯元件。
  • 例外狀況會從 ComponentTagHelper 擲回呼叫堆疊。

在預先呈現失敗的一般情況下,繼續建置和轉譯元件並不合理,因為無法轉譯工作元件。

若要容許在預先呈現期間可能發生的錯誤,錯誤處理邏輯必須放在可能會擲回例外狀況的元件內。 使用 try-catch 語句搭配錯誤處理和記錄。 不要將 包裝 ComponentTagHelper 在 語句中 try-catch ,而是將錯誤處理邏輯放在 所轉譯的 ComponentTagHelper 元件中。

進階案例

遞迴轉譯

元件可以遞迴方式巢狀。 這適用于表示遞迴資料結構。 例如, TreeNode 元件可以轉譯每個節點子系的更多 TreeNode 元件。

以遞迴方式轉譯時,請避免產生無限遞迴的編碼模式:

  • 請勿以遞迴方式呈現包含迴圈的資料結構。 例如,請勿轉譯其子系包含本身的樹狀節點。
  • 請勿建立包含迴圈的版面配置鏈結。 例如,請勿建立其版面配置本身的版面配置。
  • 不允許終端使用者違反遞迴不變數 (規則) 惡意資料輸入或 JavaScript Interop 呼叫。

轉譯期間的無限迴圈:

  • 讓轉譯程式永遠繼續。
  • 相當於建立未終止的迴圈。

在這些案例中 Blazor WebAssembly ,執行緒或 Blazor Server 線路會失敗,而且通常會嘗試:

  • 無限期地耗用作業系統所允許的 CPU 時間。
  • 取用無限數量的記憶體。 使用無限制的記憶體相當於未定迴圈在每個反復專案上將專案新增至集合的案例。

若要避免無限遞迴模式,請確定遞迴轉譯程式碼包含適當的停止條件。

自訂轉譯樹狀結構邏輯

大部分 Razor 的元件會實作為 Razor 元件檔案 (.razor) ,並由架構編譯,以產生在 上 RenderTreeBuilder 運作以轉譯其輸出的邏輯。 不過,開發人員可以使用程式 C# 程式碼手動實 RenderTreeBuilder 作邏輯。 如需詳細資訊,請參閱ASP.NET Core Blazor 進階案例 () 轉譯樹狀結構。

警告

使用手動轉譯樹狀結構產生器邏輯被視為進階且不安全的案例,不建議用於一般元件開發。

如果 RenderTreeBuilder 撰寫程式碼,開發人員必須保證程式碼的正確性。 例如,開發人員必須確定:

不正確的手動轉譯樹狀結構產生器邏輯可能會導致任意未定義的行為,包括當機、應用程式 () Blazor WebAssembly 或伺服器 (Blazor Server) 停止回應,以及安全性弱點。

請考慮以相同層級的複雜度手動轉譯樹狀結構產生器邏輯,並使用與手動撰寫元件程式碼或Microsoft 中繼語言 (MSIL) 指示相同的危險層級。

其他資源

†用戶端應用程式用於記錄的後端 ASP.NET Core Web API 應用程式 Blazor WebAssembly 。

應用程式 Blazor WebAssembly 開發期間的詳細錯誤

Blazor當應用程式在開發期間無法正常運作時,從應用程式接收詳細的錯誤資訊有助於進行疑難排解並修正問題。 發生錯誤時, Blazor 應用程式會在畫面底部顯示淺黃色列:

  • 在開發期間,列會將您導向至瀏覽器主控台,您可以在其中看到例外狀況。
  • 在生產環境中,列會通知使用者發生錯誤,並建議重新整理瀏覽器。

此錯誤處理體驗的 UI 是專案範本的 Blazor一部分。

Blazor WebAssembly在應用程式中,自訂檔案中的 wwwroot/index.html 體驗:

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

元素 blazor-error-ui 通常會隱藏,因為 display: none 應用程式的樣式表單中存在 CSS 類別的樣式 blazor-error-ui , (wwwroot/css/app.css) 。 發生錯誤時,架構會 display: block 套用至 專案。

在開發人員程式碼中管理未處理的例外狀況

若要讓應用程式在發生錯誤之後繼續,應用程式必須有錯誤處理邏輯。 本文稍後的各節說明未處理例外狀況的潛在來源。

在生產環境中,請勿在 UI 中轉譯架構例外狀況訊息或堆疊追蹤。 轉譯例外狀況訊息或堆疊追蹤可能會:

  • 向使用者揭露敏感性資訊。
  • 協助惡意使用者探索應用程式中可能會危害應用程式、伺服器或網路安全性的弱點。

全域例外狀況處理

Blazor 是單頁應用程式, (SPA) 用戶端架構。 瀏覽器可作為應用程式的主機,因此會根據流覽和靜態資產的 URI 要求,作為個別 Razor 元件的處理管線。 不同于使用中介軟體處理管線在伺服器上執行的 ASP.NET Core應用程式,沒有任何中介軟體管線會處理可用於全域錯誤處理的元件要求 Razor 。 不過,應用程式可以使用錯誤處理元件作為串聯值,以集中式方式處理錯誤。

下列 Error 元件會將本身當做 CascadingValue 傳遞至子元件。 下列範例只會記錄錯誤,但元件的方法可以透過應用程式所需的任何方式處理錯誤,包括透過使用多個錯誤處理方法。 使用元件而非使用 插入的服務 或自訂記錄器實作的優點是,串聯元件可以在發生錯誤時轉譯內容並套用 CSS 樣式。

Shared/Error.razor:

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

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

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

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

注意

如需 的詳細資訊 RenderFragment ,請參閱ASP.NET Core Razor 元件

在 元件中 App ,使用 RouterError 元件包裝元件。 這可讓 Error 元件串聯至接收 CascadingParameter 元件之應用程式 Error 的任何元件。

App.razor:

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

若要處理元件中的錯誤:

  • 在 區塊中 @codeError 元件指定為 CascadingParameter

    [CascadingParameter]
    public Error Error { get; set; }
    
  • 在任何具有適當例外狀況類型的區塊中 catch 呼叫錯誤處理方法。 此範例 Error 元件只提供單 ProcessError 一方法,但錯誤處理元件可以提供任意數目的錯誤處理方法,以解決整個應用程式的替代錯誤處理需求。

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

使用上述範例 Error 元件和 ProcessError 方法,瀏覽器的開發人員工具主控台會指出已截獲、記錄的錯誤:

fail: Blazor Sample.Shared.Error[0] Error:ProcessError - 類型:System.NullReferenceException Message:物件參考未設定為物件的實例。

ProcessError如果方法直接參與轉譯,例如顯示自訂錯誤訊息列或變更轉譯專案的 CSS 樣式,請在方法結尾 ProcessErrors 呼叫 StateHasChanged 以重新呈現 UI。

因為本節中的方法會處理語句的錯誤 try-catch ,所以當發生錯誤且線路保持運作時, Blazor Server 用戶端與伺服器之間的應用程式 SignalR 連線不會中斷。 任何未處理的例外狀況對線路都十分嚴重。 如需詳細資訊,請參閱上一節,以瞭解 應用程式如何 Blazor Server 回應未處理的例外狀況

記錄持續性提供者的錯誤 (Blazor WebAssembly)

如果發生未處理的例外狀況,則會將例外狀況記錄到 ILogger 服務容器中設定的實例。 根據預設, Blazor 應用程式會使用主控台記錄提供者登入主控台輸出。 請考慮將錯誤資訊傳送至後端 Web API,以使用記錄檔大小管理和記錄輪替的記錄提供者,以記錄至伺服器上的更永久位置。 或者,後端 Web API 應用程式可以使用應用程式效能管理 (APM) 服務,例如Azure 應用程式 Insights (Azure 監視器) †,來記錄它從用戶端接收的錯誤資訊。

您必須決定要記錄的事件,以及記錄事件的嚴重性層級。 惡意使用者可能能夠刻意觸發錯誤。 例如,請勿從顯示產品詳細資料的元件 URL 中提供未知 ProductId 的錯誤記錄事件。 並非所有錯誤都應該視為記錄的事件。

如需詳細資訊,請參閱下列文章:

†Native Application Insights功能,以支援 Blazor WebAssemblyGoogle Analytics的應用程式和原生 Blazor 架構支援,可能會在未來的這些技術版本中提供。 如需詳細資訊,請參閱 在 Blazor WASM 用戶端支援 App Insights (microsoft/ApplicationInsights-dotnet #2143) Web 分析和診斷 (包含社群實作) (dotnet/aspnetcore #5461) 的連結 。 同時,用戶端應用程式 Blazor WebAssembly 可以使用Application Insights JavaScript SDK搭配JS Interop,直接從用戶端應用程式將錯誤記錄至 Application Insights。

適用于應用程式 Web API 後端應用程式的伺服器端 ASP.NET Core應用程式 Blazor 。 用戶端應用程式會捕捉錯誤資訊,並將錯誤資訊傳送至 Web API,以將錯誤資訊記錄至持續性記錄提供者。

應用程式可能發生 Blazor WebAssembly 錯誤的位置

架構和應用程式程式碼可能會在下列任何位置觸發未處理的例外狀況,本文的下列各節會進一步說明:

元件具現化 (Blazor WebAssembly)

建立元件的實例時 Blazor :

  • 叫用元件的建構函式。
  • 會透過 @inject 指示詞或[Inject] 屬性叫用提供給元件建構函式之 DI 服務的建構函式。

任何屬性的執行建構函式或 setter [Inject] 錯誤會導致未處理的例外狀況,並停止架構具現化元件。 如果建構函式邏輯可能會擲回例外狀況,應用程式應該使用語句來攔截例外狀況 try-catch ,並搭配錯誤處理和記錄。

生命週期方法 (Blazor WebAssembly)

在元件的存留期間, Blazor 叫用 生命週期方法。 若要讓元件處理生命週期方法中的錯誤,請新增錯誤處理邏輯。

在下列範例中,呼叫 OnParametersSetAsync 方法來取得產品:

  • 方法中擲回的 ProductRepository.GetProductByIdAsync 例外狀況是由 try-catch 語句處理。
  • catch執行 區塊時:
    • loadFailed 設定為 true ,用來向使用者顯示錯誤訊息。
    • 記錄錯誤。
@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;
            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);
    }
}

轉譯邏輯 (Blazor WebAssembly)

元件檔案中的 Razor 宣告式標記 () .razor 會編譯成稱為 BuildRenderTree 的 C# 方法。 當元件轉譯時, BuildRenderTree 會執行並建置描述所轉譯元件之元素、文字和子元件的資料結構。

轉譯邏輯可能會擲回例外狀況。 評估 但 @someObjectnull@someObject.PropertyName ,就會發生此案例的範例。

若要防止 NullReferenceException 在轉譯邏輯中,請先檢查 null 物件,再存取其成員。 在下列範例中,如果 是 person.Addressnullperson.Address 則不會存取屬性:

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

上述程式碼假設 person 不是 null 。 程式碼的結構通常保證元件呈現時存在物件。 在這些情況下,不需要檢查 null 轉譯邏輯中的 。 在先前的範例中, person 可能會保證存在,因為 person 會在具現化元件時建立,如下列範例所示:

@code {
    private Person person = new();

    ...
}

事件處理常式 (Blazor WebAssembly)

使用下列專案建立事件處理常式時,用戶端程式代碼會觸發 C# 程式碼的調用:

  • @onclick
  • @onchange
  • 其他 @on... 屬性
  • @bind

在這些情況下,事件處理常式程式碼可能會擲回未處理的例外狀況。

如果應用程式呼叫因外部原因而可能會失敗的程式碼,請使用語句搭配錯誤處理和記錄來 try-catch 捕捉例外狀況。

如果使用者程式碼不會設陷並處理例外狀況,架構會記錄例外狀況。

元件處置 (Blazor WebAssembly)

例如,使用者已流覽至另一個頁面,因此可能會從 UI 中移除元件。 從 UI 移除實作 System.IDisposable 的元件時,架構會呼叫元件的 Dispose 方法。

如果處置邏輯可能會擲回例外狀況,則應用程式應該使用語句來攔截例外狀況 try-catch ,其中包含錯誤處理和記錄。

如需元件處置的詳細資訊,請參閱ASP.NET Core Razor 元件生命週期

JavaScript interop (Blazor WebAssembly)

IJSRuntime 是由 Blazor 架構註冊。 IJSRuntime.InvokeAsync 可讓 .NET 程式碼在使用者的瀏覽器中對 JavaScript 執行時間進行非同步呼叫。

下列條件適用于 使用 InvokeAsync 的錯誤處理:

  • 如果呼叫 InvokeAsync 同步失敗,就會發生 .NET 例外狀況。 例如,呼叫 InvokeAsync 可能會失敗,因為提供的引數無法序列化。 開發人員程式碼必須攔截例外狀況。
  • 如果以非同步方式呼叫 InvokeAsync 失敗,.NET Task 會失敗。 例如,呼叫 InvokeAsync 可能會失敗,因為 JavaScript 端程式碼會擲回例外狀況,或傳回以 的形式 rejected 完成的 Promise 。 開發人員程式碼必須攔截例外狀況。 await如果使用 運算子,請考慮將方法呼叫包裝在語句中 try-catch ,並包含錯誤處理和記錄。
  • 根據預設,呼叫 InvokeAsync 必須在特定期間內完成,否則呼叫逾時。預設逾時期間為一分鐘。 逾時可保護程式碼免于網路連線中斷或永遠不會傳回完成訊息的 JavaScript 程式碼。 如果呼叫逾時,產生的 System.Threading.Tasks 會失敗並 OperationCanceledException 出現 。 利用記錄來設陷和處理例外狀況。

同樣地,JavaScript 程式碼可能會起始對 屬性所指示之 .NET 方法的[JSInvokable]呼叫。 如果這些 .NET 方法擲回未處理的例外狀況,則會拒絕 JavaScript 端 Promise

您可以選擇在 .NET 端或方法呼叫的 JavaScript 端使用錯誤處理常式代碼。

如需詳細資訊,請參閱下列文章:

應用程式開發 Blazor Server 期間的詳細錯誤

Blazor當應用程式在開發期間無法正常運作時,從應用程式接收詳細的錯誤資訊有助於進行疑難排解和修正問題。 發生錯誤時, Blazor 應用程式會在畫面底部顯示淺黃色列:

  • 在開發期間,列會將您導向瀏覽器主控台,您可以在其中看到例外狀況。
  • 在生產環境中,列會通知使用者發生錯誤,並建議重新整理瀏覽器。

此錯誤處理體驗的 UI 是專案範本的 Blazor一部分。

Blazor Server在應用程式中,自訂檔案中的 Pages/_Host.cshtml 體驗:

<div id="blazor-error-ui">
    <environment include="Staging,Production">
        An error has occurred. This application may no longer respond until reloaded.
    </environment>
    <environment include="Development">
        An unhandled exception has occurred. See browser dev tools for details.
    </environment>
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

由於網站樣式表單中的 CSS 類別樣式 () wwwroot/css/site.css ,所以 blazor-error-ui 元素通常會隱藏 display: noneblazor-error-ui 發生錯誤時,架構會 display: block 套用至 專案。

Blazor Server 詳細的線路錯誤

用戶端錯誤不包含呼叫堆疊,也不會提供錯誤原因的詳細資料,但伺服器記錄檔確實包含這類資訊。 為了開發目的,可藉由啟用詳細的錯誤,讓用戶端可以使用敏感性線路錯誤資訊。

CircuitOptions.DetailedErrors 設定為 true。 如需詳細資訊和範例,請參閱ASP.NET Core BlazorSignalR 指引

設定 CircuitOptions.DetailedErrors 的替代方法是在 DetailedErrors 應用程式的開發環境設定檔 () appsettings.Development.json 中將組態索引鍵 true 設定為 。 此外,將伺服器端記錄 () 設定 SignalR為 [錯] 或 [追蹤] 以取得詳細 SignalR 記錄。 Microsoft.AspNetCore.SignalR

appsettings.Development.json:

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

設定 DetailedErrors 機碼也可以設定為 true 使用 ASPNETCORE_DETAILEDERRORS 環境變數搭配開發/預備環境伺服器或本機系統上的值 true

警告

請務必避免將錯誤資訊公開給網際網路上的用戶端,這是安全性風險。

Blazor Server應用程式如何回應未處理的例外狀況

Blazor Server 是具狀態架構。 當使用者與應用程式互動時,他們會維護與稱為 線路的伺服器連線。 線路會保存作用中的元件實例,再加上狀態的其他許多層面,例如:

  • 元件的最新轉譯輸出。
  • 用戶端事件可能觸發的目前事件處理委派集。

如果使用者在多個瀏覽器索引標籤中開啟應用程式,使用者就會建立多個獨立線路。

Blazor 會將大部分未處理的例外狀況視為發生所線上路的嚴重例外狀況。 如果線路因未處理的例外狀況而終止,則使用者只能重載頁面以建立新的線路,繼續與應用程式互動。 在終止的線路之外,其他使用者或其他瀏覽器索引標籤的線路不會受到影響。 此案例類似于當機的桌面應用程式。 損毀的應用程式必須重新開機,但其他應用程式不會受到影響。

架構會在發生未處理的例外狀況時終止線路,原因如下:

  • 未處理的例外狀況通常會讓線路處於未定義的狀態。
  • 在未處理的例外狀況之後,無法保證應用程式的正常作業。
  • 如果線路繼續處於未定義狀態,應用程式可能會顯示安全性弱點。

記錄持續性提供者 () Blazor Server 的錯誤

如果發生未處理的例外狀況,則會將例外狀況記錄到 ILogger 服務容器中設定的實例。 根據預設, Blazor 應用程式會使用主控台記錄提供者登入主控台輸出。 請考慮使用記錄管理大小和記錄輪替的提供者,記錄到伺服器上的更永久位置。 或者,應用程式可以使用應用程式效能管理 (APM) 服務,例如Azure 應用程式 Insights (Azure 監視器)

在開發期間, Blazor Server 應用程式通常會將例外狀況的完整詳細資料傳送至瀏覽器的主控台,以協助偵錯。 在生產環境中,詳細錯誤不會傳送至用戶端,但例外狀況的完整詳細資料會記錄在伺服器上。

您必須決定要記錄的事件,以及記錄事件的嚴重性層級。 惡意使用者可能能夠刻意觸發錯誤。 例如,請勿從顯示產品詳細資料的元件 URL 中提供未知 ProductId 的錯誤記錄事件。 並非所有錯誤都應該視為記錄的事件。

如需詳細資訊,請參閱下列文章:

†將應用程式套用至伺服器端 ASP.NET Core應用程式,這些應用程式是適用于 Blazor 應用程式的 Web API 後端應用程式。

應用程式可能發生 Blazor Server 錯誤的位置

架構和應用程式程式碼可能會在下列任何位置觸發未處理的例外狀況,本文的下列各節會進一步說明:

元件具現化 (Blazor Server)

建立元件的實例時 Blazor :

  • 叫用元件的建構函式。
  • 會叫用透過 @inject 指示詞或[Inject] 屬性提供給元件建構函式之任何非單一 DI 服務的建構函式。

Blazor Server當任何 [Inject] 屬性的任何執行建構函式或 setter 擲回未處理的例外狀況時,線路就會失敗。 例外狀況嚴重,因為架構無法具現化元件。 如果建構函式邏輯可能會擲回例外狀況,應用程式應該使用語句來攔截例外狀況 try-catch ,並搭配錯誤處理和記錄。

生命週期方法 (Blazor Server)

在元件的存留期間, Blazor 叫用 生命週期方法。 如果有任何生命週期方法以同步或非同步方式擲回例外狀況,則例外狀況對 Blazor Server 線路會嚴重。 若要讓元件處理生命週期方法中的錯誤,請新增錯誤處理邏輯。

在下列範例中,呼叫 OnParametersSetAsync 方法來取得產品:

  • 方法中擲回的 ProductRepository.GetProductByIdAsync 例外狀況是由 try-catch 語句處理。
  • catch執行 區塊時:
    • loadFailed 設定為 true ,用來向使用者顯示錯誤訊息。
    • 記錄錯誤。
@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;
            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);
    }
}

轉譯邏輯 (Blazor Server)

元件檔案中的 Razor 宣告式標記 () .razor 會編譯成稱為 BuildRenderTree 的 C# 方法。 當元件轉譯時, BuildRenderTree 會執行並建置描述所轉譯元件之元素、文字和子元件的資料結構。

轉譯邏輯可能會擲回例外狀況。 評估 但 @someObjectnull@someObject.PropertyName ,就會發生此案例的範例。 轉譯邏輯擲回的未處理例外狀況對線路造成嚴重錯誤 Blazor Server 。

若要防止 NullReferenceException 在轉譯邏輯中,請先檢查 null 物件,再存取其成員。 在下列範例中,如果 是 person.Addressnullperson.Address 則不會存取屬性:

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

上述程式碼假設 person 不是 null 。 程式碼的結構通常保證元件呈現時存在物件。 在這些情況下,不需要檢查 null 轉譯邏輯中的 。 在先前的範例中, person 可能會保證存在,因為 person 會在具現化元件時建立,如下列範例所示:

@code {
    private Person person = new();

    ...
}

事件處理常式 (Blazor Server)

使用下列專案建立事件處理常式時,用戶端程式代碼會觸發 C# 程式碼的調用:

  • @onclick
  • @onchange
  • 其他 @on... 屬性
  • @bind

在這些情況下,事件處理常式程式碼可能會擲回未處理的例外狀況。

例如,如果事件處理常式擲回未處理的例外狀況 (,資料庫查詢會失敗) ,則此例外狀況對 Blazor Server 線路而言是嚴重性的。 如果應用程式呼叫因外部原因而可能會失敗的程式碼,請使用語句搭配錯誤處理和記錄來 try-catch 捕捉例外狀況。

如果使用者程式碼不會攔截並處理例外狀況,架構會記錄例外狀況並終止線路。

元件處置 (Blazor Server)

例如,使用者已流覽至另一個頁面,因此可能會從 UI 中移除元件。 從 UI 移除實作 System.IDisposable 的元件時,架構會呼叫元件的 Dispose 方法。

如果元件的 Dispose 方法擲回未處理的例外狀況,則例外狀況對線路造成嚴重錯誤 Blazor Server 。 如果處置邏輯可能會擲回例外狀況,則應用程式應該使用語句來攔截例外狀況 try-catch ,其中包含錯誤處理和記錄。

如需元件處置的詳細資訊,請參閱ASP.NET Core Razor 元件生命週期

JavaScript interop (Blazor Server)

IJSRuntime 是由 Blazor 架構註冊。 IJSRuntime.InvokeAsync 可讓 .NET 程式碼在使用者的瀏覽器中對 JavaScript 執行時間進行非同步呼叫。

下列條件適用于 使用 InvokeAsync 的錯誤處理:

  • 如果呼叫 InvokeAsync 同步失敗,就會發生 .NET 例外狀況。 例如,呼叫 InvokeAsync 可能會失敗,因為提供的引數無法序列化。 開發人員程式碼必須攔截例外狀況。 如果事件處理常式或元件生命週期方法中的應用程式程式碼無法處理例外狀況,則產生的例外狀況對 Blazor Server 線路會嚴重。
  • 如果以非同步方式呼叫 InvokeAsync 失敗,.NET Task 會失敗。 例如,呼叫 InvokeAsync 可能會失敗,因為 JavaScript 端程式碼會擲回例外狀況,或傳回以 的形式 rejected 完成的 Promise 。 開發人員程式碼必須攔截例外狀況。 await如果使用 運算子,請考慮將方法呼叫包裝在語句中 try-catch ,並包含錯誤處理和記錄。 否則,失敗的程式碼會導致線路嚴重 Blazor Server 未處理的例外狀況。
  • 根據預設,呼叫 InvokeAsync 必須在特定期間內完成,否則呼叫逾時。預設逾時期間為一分鐘。 逾時可保護程式碼免于網路連線中斷或永遠不會傳回完成訊息的 JavaScript 程式碼。 如果呼叫逾時,產生的 System.Threading.Tasks 會失敗並 OperationCanceledException 出現 。 利用記錄來設陷和處理例外狀況。

同樣地,JavaScript 程式碼可能會起始對 屬性所指示之 .NET 方法的[JSInvokable]呼叫。 如果這些 .NET 方法擲回未處理的例外狀況:

  • 例外狀況不會視為線路嚴重 Blazor Server 。
  • JavaScript 端 Promise 遭到拒絕。

您可以選擇在 .NET 端或方法呼叫的 JavaScript 端使用錯誤處理常式代碼。

如需詳細資訊,請參閱下列文章:

預先呈現 (Blazor Server)

Razor 元件可以使用 元件標籤協助程式 預先呈現,以便將其轉譯的 HTML 標籤當做使用者初始 HTTP 要求的一部分傳回。 運作方式如下:

  • 為屬於相同頁面一部分的所有預先呈現元件建立新的線路。
  • 產生初始 HTML。
  • 將線路 disconnected 視為 ,直到使用者的瀏覽器建立 SignalR 與相同伺服器的連線為止。 建立連線時,會繼續線路上的互動功能,並更新元件的 HTML 標籤。

如果任何元件在預先呈現期間擲回未處理的例外狀況,例如,在生命週期方法或轉譯邏輯中:

  • 線路的例外狀況嚴重。
  • 例外狀況會從 ComponentTagHelper 標籤協助程式擲回呼叫堆疊。 因此,除非開發人員程式碼明確攔截到例外狀況,否則整個 HTTP 要求會失敗。

在預先呈現失敗的正常情況下,繼續建置和轉譯元件並不合理,因為無法轉譯工作元件。

若要容許在預先呈現期間可能發生的錯誤,錯誤處理邏輯必須放在可能會擲回例外狀況的元件內。 使用 try-catch 語句搭配錯誤處理和記錄。 不要將標籤協助套裝程式裝 ComponentTagHelper 在 語句中 try-catch ,而是將錯誤處理邏輯放在標籤協助程式所轉譯的元件中 ComponentTagHelper

進階案例

遞迴轉譯

元件可以遞迴方式巢狀化。 這適用于表示遞迴資料結構。 例如, TreeNode 元件可以轉譯每個節點子系的更多 TreeNode 元件。

以遞迴方式轉譯時,請避免產生無限遞迴的編碼模式:

  • 不要以遞迴方式呈現包含迴圈的資料結構。 例如,不要轉譯其子系包含本身的樹狀節點。
  • 請勿建立包含迴圈的版面配置鏈結。 例如,不要建立其版面配置本身的版面配置。
  • 不允許終端使用者違反遞迴不變數 (規則,) 惡意資料輸入或 JavaScript Interop 呼叫。

轉譯期間的無限迴圈:

  • 讓轉譯程式永遠繼續。
  • 相當於建立未結束的迴圈。

在這些情況下, Blazor WebAssembly 執行緒或 Blazor Server 線路會失敗,而且通常會嘗試:

  • 無限期地耗用作業系統所允許的 CPU 時間。
  • 耗用無限數量的記憶體。 使用無限制記憶體相當於不定迴圈在每個反復專案上將專案新增至集合的案例。

若要避免無限遞迴模式,請確定遞迴轉譯程式碼包含適當的停止條件。

自訂轉譯樹狀結構邏輯

大部分 Razor 元件會實作為 Razor 元件 .razor 檔 () ,並由架構編譯,以產生在 上 RenderTreeBuilder 運作以轉譯其輸出的邏輯。 不過,開發人員可以使用程式 C# 程式碼手動實 RenderTreeBuilder 作邏輯。 如需詳細資訊,請參閱ASP.NET Core (Blazor) 轉譯樹狀結構建構的進階案例

警告

使用手動轉譯樹狀結構產生器邏輯會被視為進階和不安全的案例,不建議用於一般元件開發。

如果 RenderTreeBuilder 撰寫程式碼,開發人員必須保證程式碼的正確性。 例如,開發人員必須確定:

不正確的手動轉譯樹狀結構產生器邏輯可能會導致任意未定義的行為,包括當機、應用程式 (Blazor WebAssembly) 或伺服器 (Blazor Server) 停止回應,以及安全性弱點。

請考慮在相同層級的複雜度上手動轉譯樹狀結構產生器邏輯,並使用與手動撰寫元件程式碼或Microsoft Intermediate Language (MSIL) 指令相同的危險層級。

其他資源

Blazor WebAssembly

†用戶端應用程式用於記錄的後端 ASP.NET Core Web API 應用程式 Blazor WebAssembly 。

Blazor Server

†將應用程式套用至伺服器端 ASP.NET Core應用程式,這些應用程式是適用于 Blazor 應用程式的 Web API 後端應用程式。

應用程式開發 Blazor WebAssembly 期間的詳細錯誤

Blazor當應用程式在開發期間無法正常運作時,從應用程式接收詳細的錯誤資訊有助於進行疑難排解和修正問題。 發生錯誤時, Blazor 應用程式會在畫面底部顯示淺黃色列:

  • 在開發期間,列會將您導向瀏覽器主控台,您可以在其中看到例外狀況。
  • 在生產環境中,列會通知使用者發生錯誤,並建議重新整理瀏覽器。

此錯誤處理體驗的 UI 是專案範本的 Blazor一部分。

Blazor WebAssembly在應用程式中,自訂檔案中的 wwwroot/index.html 體驗:

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

元素 blazor-error-ui 通常會隱藏, display: none 因為應用程式樣式表單中的 CSS 類別樣式 blazor-error-ui () wwwroot/css/app.css 。 發生錯誤時,架構會 display: block 套用至 專案。

在開發人員程式碼中管理未處理的例外狀況

若要讓應用程式在發生錯誤之後繼續,應用程式必須具有錯誤處理邏輯。 本文稍後的章節將說明未處理的例外狀況的潛在來源。

在生產環境中,請勿在 UI 中轉譯架構例外狀況訊息或堆疊追蹤。 轉譯例外狀況訊息或堆疊追蹤可能會:

  • 向使用者揭露敏感性資訊。
  • 協助惡意使用者探索應用程式中可能會危害應用程式、伺服器或網路安全性的弱點。

全域例外狀況處理

Blazor 是單頁應用程式, (SPA) 用戶端架構。 瀏覽器可作為應用程式的主機,因此會根據流覽和靜態資產的 URI 要求,作為個別 Razor 元件的處理管線。 不同于 ASP.NET Core使用中介軟體處理管線在伺服器上執行的應用程式,沒有任何中介軟體管線會處理可用於全域錯誤處理的元件要求 Razor 。 不過,應用程式可以使用錯誤處理元件作為串聯值,以集中方式處理錯誤。

下列 Error 元件會將本身當作 CascadingValue 傳遞至子元件。 下列範例只會記錄錯誤,但元件的方法可以透過應用程式所需的任何方式處理錯誤,包括透過使用多個錯誤處理方法。 使用元件而非使用 插入的服務 或自訂記錄器實作的優點是,串聯元件可以在發生錯誤時轉譯內容並套用 CSS 樣式。

Shared/Error.razor:

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

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

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

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

注意

如需 的詳細資訊 RenderFragment ,請參閱ASP.NET Core Razor 元件

在 元件中 App ,使用 Router 元件包裝元件 Error 。 這可讓 Error 元件串聯至接收 CascadingParameter 元件之 Error 應用程式的任何元件。

App.razor:

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

若要處理元件中的錯誤:

  • Error 元件指定為 CascadingParameter 區塊中的 @code

    [CascadingParameter]
    public Error Error { get; set; }
    
  • 在任何具有適當例外狀況類型的區塊中 catch 呼叫錯誤處理方法。 範例 Error 元件只提供單 ProcessError 一方法,但錯誤處理元件可以提供任意數目的錯誤處理方法,以解決整個應用程式的替代錯誤處理需求。

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

使用上述範例 Error 元件和 ProcessError 方法,瀏覽器的開發人員工具主控台會指出已截獲、記錄的錯誤:

fail: Blazor Sample.Shared.Error[0] Error:ProcessError - Type: System.NullReferenceException Message: Object reference not set to an instance of an object.

ProcessError如果方法直接參與轉譯,例如顯示自訂錯誤訊息列或變更轉譯專案的 CSS 樣式,請在 方法結尾 ProcessErrors 呼叫 StateHasChanged 以重新呈現 UI。

由於本節中的方法會處理語句的錯誤,因此當發生錯誤 try-catch 且線路保持運作時, Blazor Server 用戶端與伺服器之間的應用程式 SignalR 連線不會中斷。 任何未處理的例外狀況對線路都是嚴重性的。 如需詳細資訊,請參閱上一節,以瞭解 應用程式如何 Blazor Server 回應未處理的例外狀況

記錄持續性提供者 () Blazor WebAssembly 的錯誤

如果發生未處理的例外狀況,則會將例外狀況記錄到 ILogger 服務容器中設定的實例。 根據預設, Blazor 應用程式會使用主控台記錄提供者登入主控台輸出。 請考慮將錯誤資訊傳送至後端 Web API,以使用記錄檔大小管理和記錄輪替的記錄提供者,以記錄到伺服器上的更永久位置。 或者,後端 Web API 應用程式可以使用「應用程式效能管理」 (APM) 服務,例如Azure 應用程式 Insights (Azure 監視器) †,來記錄從用戶端收到的錯誤資訊。

您必須決定要記錄的事件,以及記錄事件的嚴重性層級。 惡意使用者可能能夠刻意觸發錯誤。 例如,請勿從顯示產品詳細資料的元件 URL 中提供未知 ProductId 的錯誤記錄事件。 並非所有錯誤都應該視為記錄的事件。

如需詳細資訊,請參閱下列文章:

†Native Application Insights功能,以支援 Blazor WebAssemblyGoogle Analytics的應用程式和原生 Blazor 架構支援,在未來的這些技術版本中可能會變成可用。 如需詳細資訊,請參閱 支援 WASM 用戶端中的 Blazor App Insights (microsoft/ApplicationInsights-dotnet #2143) Web 分析和診斷 (包含社群實作的連結,) (dotnet/aspnetcore #5461) 。 同時,用戶端應用程式 Blazor WebAssembly 可以使用Application Insights JavaScript SDK搭配JS Interop,直接從用戶端應用程式將錯誤記錄到 Application Insights。

適用于應用程式 Web API 後端應用程式的伺服器端 ASP.NET Core應用程式 Blazor 。 用戶端應用程式會捕捉錯誤資訊,並將錯誤資訊傳送至 Web API,以將錯誤資訊記錄至持續性記錄提供者。

在應用程式中可能發生 Blazor WebAssembly 錯誤的位置

架構和應用程式程式碼可能會在下列任何位置觸發未處理的例外狀況,本文下列各節會進一步說明:

元件具現化 () Blazor WebAssembly

建立元件的實例時 Blazor :

  • 會叫用元件的建構函式。
  • 會透過 @inject 指示詞或[Inject] 屬性叫用提供給元件建構函式的任何非單一 DI 服務的建構函式。

任何屬性的執行建構函式或 setter [Inject] 錯誤會導致未處理的例外狀況,並阻止架構具現化元件。 如果建構函式邏輯可能會擲回例外狀況,應用程式應該使用 try-catch 具有錯誤處理和記錄的 語句來捕捉例外狀況。

生命週期方法 (Blazor WebAssembly)

在元件的存留期間, Blazor 叫用 生命週期方法。 若要讓元件處理生命週期方法中的錯誤,請新增錯誤處理邏輯。

在下列範例中,其中 OnParametersSetAsync 會呼叫 方法來取得產品:

  • 方法中擲回的 ProductRepository.GetProductByIdAsync 例外狀況是由 try-catch 語句處理。
  • catch執行 區塊時:
    • loadFailed 設定為 true ,用來向使用者顯示錯誤訊息。
    • 系統會記錄錯誤。
@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;
            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);
    }
}

轉譯邏輯 (Blazor WebAssembly)

元件檔案中的 Razor 宣告式標記 () .razor 編譯成稱為 的 BuildRenderTree C# 方法。 當元件轉譯時, BuildRenderTree 會執行並建置描述轉譯元件之專案、文字和子元件的資料結構。

轉譯邏輯可能會擲回例外狀況。 當 評估 但 @someObjectnull@someObject.PropertyName ,就會發生此案例的範例。

若要防止轉 NullReferenceException 譯邏輯中的 ,請先檢查物件, null 再存取其成員。 在下列範例中,如果 person.Addressnullperson.Address 則不會存取屬性:

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

上述程式碼假設 person 不是 null 。 通常,程式碼的結構保證元件轉譯時存在物件。 在這些情況下,不需要在轉譯邏輯中檢查 null 。 在先前的範例中, person 可能會保證存在,因為在 person 具現化元件時會建立,如下列範例所示:

@code {
    private Person person = new Person();

    ...
}

事件處理常式 (Blazor WebAssembly)

使用下列專案建立事件處理常式時,用戶端程式代碼會觸發 C# 程式碼的調用:

  • @onclick
  • @onchange
  • 其他 @on... 屬性
  • @bind

事件處理常式程式碼可能會在這些案例中擲回未處理的例外狀況。

如果應用程式呼叫可能會因為外部原因而失敗的程式碼,請使用 try-catch 具有錯誤處理和記錄的 語句來捕捉例外狀況。

如果使用者程式碼未捕捉並處理例外狀況,架構會記錄例外狀況。

元件處置 (Blazor WebAssembly)

例如,使用者已流覽至另一個頁面,所以可能會從 UI 中移除元件。 從 UI 移除實作 System.IDisposable 的元件時,架構會呼叫元件的 Dispose 方法。

如果處置邏輯可能會擲回例外狀況,應用程式應該使用 try-catch 具有錯誤處理和記錄的 語句來捕捉例外狀況。

如需元件處置的詳細資訊,請參閱ASP.NET Core Razor 元件生命週期

JavaScript interop (Blazor WebAssembly)

IJSRuntime 是由 Blazor 架構註冊。 IJSRuntime.InvokeAsync 允許 .NET 程式碼在使用者的瀏覽器中對 JavaScript 執行時間進行非同步呼叫。

下列條件適用于 使用 InvokeAsync 的錯誤處理:

  • 如果呼叫 InvokeAsync 同步失敗,就會發生 .NET 例外狀況。 例如,呼叫 InvokeAsync 可能會失敗,因為提供的引數無法序列化。 開發人員程式碼必須攔截例外狀況。
  • 如果呼叫 InvokeAsync 以非同步方式失敗,.NET Task 就會失敗。 例如,呼叫 InvokeAsync 可能會失敗,因為 JavaScript 端程式碼擲回例外狀況,或傳回 Promise 完成為 rejected 的 。 開發人員程式碼必須攔截例外狀況。 如果使用 await 運算子,請考慮將方法呼叫包裝在語句中 try-catch ,並包含錯誤處理和記錄。
  • 根據預設,呼叫 InvokeAsync 必須在特定期間內完成,否則呼叫逾時。預設逾時期間為一分鐘。 逾時可保護程式碼免于網路連線或永遠不會傳回完成訊息的 JavaScript 程式碼遺失。 如果呼叫逾時,產生的 System.Threading.Tasks 會失敗並 OperationCanceledException 出現 。 使用記錄來捕捉和處理例外狀況。

同樣地,JavaScript 程式碼可能會起始對 屬性所指示之 .NET 方法的[JSInvokable]呼叫。 如果這些 .NET 方法擲回未處理的例外狀況,則會拒絕 JavaScript 端 Promise

您可以選擇在 .NET 端或方法呼叫的 JavaScript 端使用錯誤處理常式代碼。

如需詳細資訊,請參閱下列文章:

應用程式 Blazor Server 開發期間的詳細錯誤

Blazor當應用程式在開發期間無法正常運作時,從應用程式接收詳細的錯誤資訊有助於進行疑難排解並修正問題。 發生錯誤時, Blazor 應用程式會在畫面底部顯示淺黃色列:

  • 在開發期間,列會將您導向至瀏覽器主控台,您可以在其中看到例外狀況。
  • 在生產環境中,列會通知使用者發生錯誤,並建議重新整理瀏覽器。

此錯誤處理體驗的 UI 是專案範本的 Blazor一部分。

Blazor Server在應用程式中,自訂檔案中的 Pages/_Host.cshtml 體驗:

<div id="blazor-error-ui">
    <environment include="Staging,Production">
        An error has occurred. This application may no longer respond until reloaded.
    </environment>
    <environment include="Development">
        An unhandled exception has occurred. See browser dev tools for details.
    </environment>
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

專案 blazor-error-ui 通常會隱藏,因為網站樣式 blazor-error-ui 表單 wwwroot/css/site.css 中的 CSS 類別樣式 (display: none) 。 發生錯誤時,架構會 display: block 套用至 專案。

Blazor Server 詳細的線路錯誤

用戶端錯誤不包含呼叫堆疊,也不會提供錯誤原因的詳細資料,但伺服器記錄檔確實包含這類資訊。 為了開發目的,可藉由啟用詳細的錯誤,讓用戶端能夠使用敏感性線路錯誤資訊。

CircuitOptions.DetailedErrors 設定為 true。 如需詳細資訊和範例,請參閱ASP.NET Core BlazorSignalR 指引

設定的 CircuitOptions.DetailedErrors 替代方式是在應用程式的開發環境設定檔 () appsettings.Development.json 中將組態機碼 true 設定 DetailedErrors 為 。 此外,將伺服器端記錄 () 設定 SignalR[偵錯] 或 [追蹤] 以取得詳細 SignalR 記錄。 Microsoft.AspNetCore.SignalR

appsettings.Development.json:

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

設定 DetailedErrors 機碼也可以設定為 true 使用 ASPNETCORE_DETAILEDERRORS 環境變數搭配開發/預備環境伺服器或本機系統上的值 true

警告

請一律避免將錯誤資訊公開給網際網路上的用戶端,這是安全性風險。

Blazor Server應用程式如何回應未處理的例外狀況

Blazor Server 是具狀態架構。 當使用者與應用程式互動時,他們會維護與稱為 線路的伺服器連線。 線路會保存作用中的元件實例,以及狀態的其他許多層面,例如:

  • 元件的最新轉譯輸出。
  • 用戶端事件可能會觸發的目前事件處理委派集。

如果使用者在多個瀏覽器索引標籤中開啟應用程式,使用者就會建立多個獨立線路。

Blazor 將大部分未處理的例外狀況視為發生其所線上路的嚴重例外狀況。 如果線路因為未處理的例外狀況而終止,使用者只能重載頁面以建立新的線路,繼續與應用程式互動。 在終止的線路之外,其他使用者或其他瀏覽器索引標籤的線路不會受到影響。 此案例類似于當機的桌面應用程式。 損毀的應用程式必須重新開機,但其他應用程式不會受到影響。

架構會在發生未處理的例外狀況時終止線路,原因如下:

  • 未處理的例外狀況通常會讓線路處於未定義的狀態。
  • 在未處理的例外狀況之後,無法保證應用程式的正常作業。
  • 如果線路繼續處於未定義狀態,安全性弱點可能會出現在應用程式中。

記錄持續性提供者的錯誤 (Blazor Server)

如果發生未處理的例外狀況,則會將例外狀況記錄到 ILogger 服務容器中設定的實例。 根據預設, Blazor 應用程式會使用主控台記錄提供者登入主控台輸出。 請考慮使用記錄管理大小和記錄輪替的提供者,登入伺服器上的永久位置。 或者,應用程式可以使用應用程式效能管理 (APM) 服務,例如Azure 應用程式 Insights (Azure 監視器)

在開發期間, Blazor Server 應用程式通常會將例外狀況的完整詳細資料傳送至瀏覽器的主控台,以協助偵錯。 在生產環境中,詳細錯誤不會傳送至用戶端,但例外狀況的完整詳細資料會記錄在伺服器上。

您必須決定要記錄的事件,以及記錄事件的嚴重性層級。 惡意使用者可能能夠刻意觸發錯誤。 例如,請勿從顯示產品詳細資料的元件 URL 中提供未知 ProductId 的錯誤記錄事件。 並非所有錯誤都應該視為記錄的事件。

如需詳細資訊,請參閱下列文章:

†將應用程式套用至伺服器端 ASP.NET Core應用程式,這些應用程式是適用于 Blazor 應用程式的 Web API 後端應用程式。

在應用程式中可能發生 Blazor Server 錯誤的位置

架構和應用程式程式碼可能會在下列任何位置觸發未處理的例外狀況,本文下列各節會進一步說明:

元件具現化 () Blazor Server

建立元件的實例時 Blazor :

  • 會叫用元件的建構函式。
  • 會透過 @inject 指示詞或[Inject] 屬性叫用提供給元件建構函式的任何非單一 DI 服務的建構函式。

Blazor Server當任何 [Inject] 屬性執行之建構函式或 setter 擲回未處理的例外狀況時,線路就會失敗。 例外狀況嚴重,因為架構無法具現化元件。 如果建構函式邏輯可能會擲回例外狀況,應用程式應該使用 try-catch 具有錯誤處理和記錄的 語句來捕捉例外狀況。

生命週期方法 (Blazor Server)

在元件的存留期間, Blazor 叫用 生命週期方法。 如果有任何生命週期方法以同步或非同步方式擲回例外狀況,則例外狀況對 Blazor Server 線路是嚴重的。 若要讓元件處理生命週期方法中的錯誤,請新增錯誤處理邏輯。

在下列範例中,其中 OnParametersSetAsync 會呼叫 方法來取得產品:

  • 方法中擲回的 ProductRepository.GetProductByIdAsync 例外狀況是由 try-catch 語句處理。
  • catch執行 區塊時:
    • loadFailed 設定為 true ,用來向使用者顯示錯誤訊息。
    • 系統會記錄錯誤。
@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;
            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);
    }
}

轉譯邏輯 (Blazor Server)

元件檔案中的 Razor 宣告式標記 () .razor 編譯成稱為 的 BuildRenderTree C# 方法。 當元件轉譯時, BuildRenderTree 會執行並建置描述轉譯元件之專案、文字和子元件的資料結構。

轉譯邏輯可能會擲回例外狀況。 當 評估 但 @someObjectnull@someObject.PropertyName ,就會發生此案例的範例。 轉譯邏輯擲回的未處理例外狀況對線路十 Blazor Server 分嚴重。

若要防止轉 NullReferenceException 譯邏輯中的 ,請先檢查物件, null 再存取其成員。 在下列範例中,如果 person.Addressnullperson.Address 則不會存取屬性:

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

上述程式碼假設 person 不是 null 。 通常,程式碼的結構保證元件轉譯時存在物件。 在這些情況下,不需要在轉譯邏輯中檢查 null 。 在先前的範例中, person 可能會保證存在,因為在 person 具現化元件時會建立,如下列範例所示:

@code {
    private Person person = new Person();

    ...
}

事件處理常式 (Blazor Server)

使用下列專案建立事件處理常式時,用戶端程式代碼會觸發 C# 程式碼的調用:

  • @onclick
  • @onchange
  • 其他 @on... 屬性
  • @bind

事件處理常式程式碼可能會在這些案例中擲回未處理的例外狀況。

例如,如果事件處理常式擲回未處理的例外狀況 (,資料庫查詢就會失敗) ,此例外狀況對 Blazor Server 線路而言是嚴重的。 如果應用程式呼叫可能會因為外部原因而失敗的程式碼,請使用 try-catch 具有錯誤處理和記錄的 語句來捕捉例外狀況。

如果使用者程式碼未捕捉並處理例外狀況,架構會記錄例外狀況並終止線路。

元件處置 (Blazor Server)

例如,使用者已流覽至另一個頁面,所以可能會從 UI 中移除元件。 從 UI 移除實作 System.IDisposable 的元件時,架構會呼叫元件的 Dispose 方法。

如果元件的 Dispose 方法擲回未處理的例外狀況,則線路的 Blazor Server 例外狀況會嚴重。 如果處置邏輯可能會擲回例外狀況,應用程式應該使用 try-catch 具有錯誤處理和記錄的 語句來捕捉例外狀況。

如需元件處置的詳細資訊,請參閱ASP.NET Core Razor 元件生命週期

JavaScript interop (Blazor Server)

IJSRuntime 是由 Blazor 架構註冊。 IJSRuntime.InvokeAsync 允許 .NET 程式碼在使用者的瀏覽器中對 JavaScript 執行時間進行非同步呼叫。

下列條件適用于 使用 InvokeAsync 的錯誤處理:

  • 如果呼叫 InvokeAsync 同步失敗,就會發生 .NET 例外狀況。 例如,呼叫 InvokeAsync 可能會失敗,因為提供的引數無法序列化。 開發人員程式碼必須攔截例外狀況。 如果事件處理常式或元件生命週期方法中的應用程式程式碼未處理例外狀況,則產生的例外狀況對 Blazor Server 線路是嚴重的。
  • 如果呼叫 InvokeAsync 以非同步方式失敗,.NET Task 就會失敗。 例如,呼叫 InvokeAsync 可能會失敗,因為 JavaScript 端程式碼擲回例外狀況,或傳回 Promise 完成為 rejected 的 。 開發人員程式碼必須攔截例外狀況。 如果使用 await 運算子,請考慮將方法呼叫包裝在語句中 try-catch ,並包含錯誤處理和記錄。 否則,失敗的程式碼會導致線路嚴重 Blazor Server 未處理的例外狀況。
  • 根據預設,呼叫 InvokeAsync 必須在特定期間內完成,否則呼叫逾時。預設逾時期間為一分鐘。 逾時可保護程式碼免于網路連線或永遠不會傳回完成訊息的 JavaScript 程式碼遺失。 如果呼叫逾時,產生的 System.Threading.Tasks 會失敗並 OperationCanceledException 出現 。 使用記錄來捕捉和處理例外狀況。

同樣地,JavaScript 程式碼可能會起始對 屬性所指示之 .NET 方法的[JSInvokable]呼叫。 如果這些 .NET 方法擲回未處理的例外狀況:

  • 例外狀況不會被視為線路嚴重 Blazor Server 。
  • JavaScript 端 Promise 遭到拒絕。

您可以選擇在 .NET 端或方法呼叫的 JavaScript 端使用錯誤處理常式代碼。

如需詳細資訊,請參閱下列文章:

預先呈現 (Blazor Server)

Razor 元件可以使用 元件標籤協助程式 預先呈現,以便將其轉譯的 HTML 標籤傳回為使用者初始 HTTP 要求的一部分。 運作方式如下:

  • 為所有屬於相同頁面一部分的預先呈現元件建立新的線路。
  • 產生初始 HTML。
  • 將線路 disconnected 視為 ,直到使用者的瀏覽器建立 SignalR 與相同伺服器的連線為止。 建立連線時,會繼續線路上的互動性,並更新元件的 HTML 標籤。

如果任何元件在預先呈現期間擲回未處理的例外狀況,例如,在生命週期方法或轉譯邏輯中:

  • 此例外狀況對線路十分嚴重。
  • 例外狀況會從 ComponentTagHelper 標籤協助程式擲回呼叫堆疊。 因此,除非開發人員程式碼明確攔截例外狀況,否則整個 HTTP 要求會失敗。

在預先呈現失敗的一般情況下,繼續建置和轉譯元件並不合理,因為無法轉譯工作元件。

若要容許在預先呈現期間可能發生的錯誤,錯誤處理邏輯必須放在可能會擲回例外狀況的元件內。 使用 try-catch 語句搭配錯誤處理和記錄。 不要將 ComponentTagHelper 標籤協助套裝程式裝在 語句中 try-catch ,而是將錯誤處理邏輯放在標籤協助程式所轉譯的 ComponentTagHelper 元件中。

進階案例

遞迴轉譯

元件可以遞迴方式巢狀。 這適用于表示遞迴資料結構。 例如, TreeNode 元件可以轉譯每個節點子系的更多 TreeNode 元件。

以遞迴方式轉譯時,請避免產生無限遞迴的編碼模式:

  • 請勿以遞迴方式呈現包含迴圈的資料結構。 例如,請勿轉譯其子系包含本身的樹狀節點。
  • 請勿建立包含迴圈的版面配置鏈結。 例如,請勿建立其版面配置本身的版面配置。
  • 不允許終端使用者違反遞迴不變數 (規則) 惡意資料輸入或 JavaScript Interop 呼叫。

轉譯期間的無限迴圈:

  • 讓轉譯程式永遠繼續。
  • 相當於建立未終止的迴圈。

在這些案例中 Blazor WebAssembly ,執行緒或 Blazor Server 線路會失敗,而且通常會嘗試:

  • 無限期地耗用作業系統所允許的 CPU 時間。
  • 取用無限數量的記憶體。 使用無限制的記憶體相當於未定迴圈在每個反復專案上將專案新增至集合的案例。

若要避免無限遞迴模式,請確定遞迴轉譯程式碼包含適當的停止條件。

自訂轉譯樹狀結構邏輯

大部分 Razor 的元件會實作為 Razor 元件檔案 (.razor) ,並由架構編譯,以產生在 上 RenderTreeBuilder 運作以轉譯其輸出的邏輯。 不過,開發人員可以使用程式 C# 程式碼手動實 RenderTreeBuilder 作邏輯。 如需詳細資訊,請參閱ASP.NET Core Blazor 進階案例 () 轉譯樹狀結構。

警告

使用手動轉譯樹狀結構產生器邏輯被視為進階且不安全的案例,不建議用於一般元件開發。

如果 RenderTreeBuilder 撰寫程式碼,開發人員必須保證程式碼的正確性。 例如,開發人員必須確定:

不正確的手動轉譯樹狀結構產生器邏輯可能會導致任意未定義的行為,包括當機、應用程式 () Blazor WebAssembly 或伺服器 (Blazor Server) 停止回應,以及安全性弱點。

請考慮以相同層級的複雜度手動轉譯樹狀結構產生器邏輯,並使用與手動撰寫元件程式碼或Microsoft 中繼語言 (MSIL) 指示相同的危險層級。

其他資源

Blazor WebAssembly

†用戶端應用程式用於記錄的後端 ASP.NET Core Web API 應用程式 Blazor WebAssembly 。

Blazor Server

†將應用程式套用至伺服器端 ASP.NET Core應用程式,這些應用程式是適用于 Blazor 應用程式的 Web API 後端應用程式。

開發期間的詳細錯誤

Blazor當應用程式在開發期間無法正常運作時,從應用程式接收詳細的錯誤資訊有助於進行疑難排解並修正問題。 發生錯誤時, Blazor 應用程式會在畫面底部顯示淺黃色列:

  • 在開發期間,列會將您導向至瀏覽器主控台,您可以在其中看到例外狀況。
  • 在生產環境中,列會通知使用者發生錯誤,並建議重新整理瀏覽器。

此錯誤處理體驗的 UI 是專案範本的 Blazor一部分。

Blazor Server在應用程式中,自訂檔案中的 Pages/_Host.cshtml 體驗:

<div id="blazor-error-ui">
    <environment include="Staging,Production">
        An error has occurred. This application may no longer respond until reloaded.
    </environment>
    <environment include="Development">
        An unhandled exception has occurred. See browser dev tools for details.
    </environment>
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

Blazor WebAssembly在應用程式中,自訂檔案中的 wwwroot/index.html 體驗:

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

專案 blazor-error-ui 通常會隱藏,因為 display: none 月臺樣式表單中有 CSS 類別的樣式 blazor-error-ui , (wwwroot/css/site.cssBlazor Server 或 wwwroot/css/app.css) Blazor WebAssembly 。 發生錯誤時,架構會 display: block 套用至 專案。

詳細的線路錯誤

本節適用于 Blazor Server 應用程式。

用戶端錯誤不包含呼叫堆疊,也不會提供錯誤原因的詳細資料,但伺服器記錄檔確實包含這類資訊。 為了開發目的,可藉由啟用詳細的錯誤,讓用戶端能夠使用敏感性線路錯誤資訊。

CircuitOptions.DetailedErrors 設定為 true。 如需詳細資訊和範例,請參閱ASP.NET Core BlazorSignalR 指引

設定的 CircuitOptions.DetailedErrors 替代方式是在應用程式的開發環境設定檔 () appsettings.Development.json 中將組態機碼 true 設定 DetailedErrors 為 。 此外,將伺服器端記錄 () 設定 SignalR[偵錯] 或 [追蹤] 以取得詳細 SignalR 記錄。 Microsoft.AspNetCore.SignalR

appsettings.Development.json:

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

設定 DetailedErrors 機碼也可以設定為 true 使用 ASPNETCORE_DETAILEDERRORS 環境變數搭配開發/預備環境伺服器或本機系統上的值 true

警告

請一律避免將錯誤資訊公開給網際網路上的用戶端,這是安全性風險。

在開發人員程式碼中管理未處理的例外狀況

若要讓應用程式在發生錯誤之後繼續,應用程式必須有錯誤處理邏輯。 本文稍後的各節說明未處理例外狀況的潛在來源。

在生產環境中,請勿在 UI 中轉譯架構例外狀況訊息或堆疊追蹤。 轉譯例外狀況訊息或堆疊追蹤可能會:

  • 向使用者揭露敏感性資訊。
  • 協助惡意使用者探索應用程式中可能會危害應用程式、伺服器或網路安全性的弱點。

Blazor Server 未處理的例外狀況

本節適用于 Blazor Server 應用程式。

Blazor Server 是具狀態架構。 當使用者與應用程式互動時,他們會維護與稱為 線路的伺服器連線。 線路會保存作用中的元件實例,以及狀態的其他許多層面,例如:

  • 元件的最新轉譯輸出。
  • 用戶端事件可能會觸發的目前事件處理委派集。

如果使用者在多個瀏覽器索引標籤中開啟應用程式,使用者就會建立多個獨立線路。

Blazor 將大部分未處理的例外狀況視為發生其所線上路的嚴重例外狀況。 如果線路因為未處理的例外狀況而終止,使用者只能重載頁面以建立新的線路,繼續與應用程式互動。 在終止的線路之外,其他使用者或其他瀏覽器索引標籤的線路不會受到影響。 此案例類似于當機的桌面應用程式。 損毀的應用程式必須重新開機,但其他應用程式不會受到影響。

架構會在發生未處理的例外狀況時終止線路,原因如下:

  • 未處理的例外狀況通常會讓線路處於未定義的狀態。
  • 在未處理的例外狀況之後,無法保證應用程式的正常作業。
  • 如果線路繼續處於未定義狀態,安全性弱點可能會出現在應用程式中。

錯誤界限

Blazor 是單頁應用程式, (SPA) 用戶端架構。 瀏覽器可作為應用程式的主機,因此會根據流覽和靜態資產的 URI 要求,作為個別 Razor 元件的處理管線。 不同于使用中介軟體處理管線在伺服器上執行的 ASP.NET Core應用程式,沒有任何中介軟體管線會處理可用於全域錯誤處理的元件要求 Razor 。 不過,應用程式可以使用錯誤處理元件作為串聯值,以集中式方式處理錯誤。

錯誤界限 提供處理例外狀況的便利方法。 元件 ErrorBoundary

  • 發生錯誤時,轉譯其子內容。
  • 擲回未處理的例外狀況時,轉譯錯誤 UI。

若要定義錯誤界限,請使用 ErrorBoundary 元件來包裝現有的內容。 例如,可以在應用程式主要版面配置主體內容周圍新增錯誤界限。

Shared/MainLayout.razor:

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

應用程式會繼續正常運作,但錯誤界限會處理未處理的例外狀況。

請考慮下列範例,其中 Counter ,如果計數遞增超過五,元件會擲回例外狀況。

Pages/Counter.razor 中:

private void IncrementCount()
{
    currentCount++;

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

如果擲回未處理的例外狀況超過 currentCount 五個:

  • 例外狀況是由錯誤界限處理。
  • 錯誤 UI 會轉譯 (An error has occurred.) 。

根據預設, ErrorBoundary 元件會針對其錯誤內容轉譯具有 CSS 類別的 blazor-error-boundary 空白 <div> 元素。 預設 UI 的色彩、文字和圖示是使用資料夾中應用程式樣式表單 wwwroot 中的 CSS 來定義,因此您可以自由地自訂錯誤 UI。

您也可以藉由設定 ErrorContent 屬性來變更預設的錯誤內容:

<ErrorBoundary>
    <ChildContent>
        @Body
    </ChildContent>
    <ErrorContent>
        <p class="errorUI">Nothing to see here right now. Sorry!</p>
    </ErrorContent>
</ErrorBoundary>

因為錯誤界限是在上述範例的版面配置中定義,所以不論使用者流覽到哪一個頁面,都會看到錯誤 UI。 我們建議在大部分情況下,將錯誤界限的範圍縮小。 如果您廣泛限定錯誤界限的範圍,您可以藉由呼叫錯誤界限的 Recover 方法,將其重設為後續頁面導覽事件的非錯誤狀態:

...

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

...

@code {
    private ErrorBoundary? errorBoundary;

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

替代的全域例外狀況處理

() ErrorBoundary 使用Error 界限的替代方法是將自訂錯誤元件當做 CascadingValue 傳遞至子元件。 使用元件而非使用 插入的服務 或自訂記錄器實作的優點是,串聯元件可以在發生錯誤時轉譯內容並套用 CSS 樣式。

下列 Error 元件範例只會記錄錯誤,但元件的方法可以透過應用程式所需的任何方式處理錯誤,包括透過使用多個錯誤處理方法。

Shared/Error.razor:

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

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

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

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

注意

如需 的詳細資訊 RenderFragment ,請參閱ASP.NET Core Razor 元件

在 元件中 App ,使用 Router 元件包裝元件 Error 。 這可讓 Error 元件串聯至接收 CascadingParameter 元件之 Error 應用程式的任何元件。

App.razor:

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

若要處理元件中的錯誤:

  • Error 元件指定為 CascadingParameter 區塊中的 @code 。 在以專案範本為基礎的 Blazor 應用程式中範例 Counter 元件中,新增下列 Error 屬性:

    [CascadingParameter]
    public Error? Error { get; set; }
    
  • 在任何具有適當例外狀況類型的區塊中 catch 呼叫錯誤處理方法。 範例 Error 元件只提供單 ProcessError 一方法,但錯誤處理元件可以提供任意數目的錯誤處理方法,以解決整個應用程式的替代錯誤處理需求。 在下列 Counter 元件範例中,當計數大於五時,會擲回並截獲例外狀況:

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

使用上述 Error 元件搭配對元件的先前變更 Counter ,瀏覽器的開發人員工具主控台會指出已截獲、記錄的錯誤:

fail: BlazorSample.Shared.Error[0]
Error:ProcessError - Type: System.InvalidOperationException Message: Current count is over five!

ProcessError如果方法直接參與轉譯,例如顯示自訂錯誤訊息列或變更轉譯專案的 CSS 樣式,請在 方法結尾 ProcessErrors 呼叫 StateHasChanged 以重新呈現 UI。

由於本節中的方法會處理語句的錯誤,因此當發生錯誤 try-catch 且線路保持運作時, Blazor Server 用戶端與伺服器之間的應用程式 SignalR 連線不會中斷。 其他未處理的例外狀況對線路仍然嚴重。 如需詳細資訊,請參閱上一節,以瞭解 應用程式如何 Blazor Server 回應未處理的例外狀況

記錄持續性提供者的錯誤

如果發生未處理的例外狀況,則會將例外狀況記錄到 ILogger 服務容器中設定的實例。 根據預設, Blazor 應用程式會使用主控台記錄提供者登入主控台輸出。 請考慮使用記錄管理大小和記錄輪替的提供者,針對應用程式) 的伺服器 (或後端 Web API Blazor WebAssembly 上的位置。 或者,應用程式可以使用應用程式效能管理 (APM) 服務,例如Azure 應用程式 Insights (Azure 監視器)

注意

支援Google Analytics之應用程式和原生架構支援的原生 BlazorApplication Insights功能 Blazor WebAssembly ,在未來的這些技術版本中可能會變成可用。 如需詳細資訊,請參閱 支援 WASM 用戶端中的 Blazor App Insights (microsoft/ApplicationInsights-dotnet #2143) Web 分析和診斷 (包含社群實作的連結,) (dotnet/aspnetcore #5461) 。 同時,用戶端應用程式 Blazor WebAssembly 可以使用Application Insights JavaScript SDK搭配JS Interop,直接從用戶端應用程式將錯誤記錄到 Application Insights。

在應用程式的開發 Blazor Server 期間,應用程式通常會將例外狀況的完整詳細資料傳送至瀏覽器的主控台,以協助偵錯。 在生產環境中,詳細錯誤不會傳送至用戶端,但例外狀況的完整詳細資料會記錄在伺服器上。

您必須決定要記錄的事件,以及記錄事件的嚴重性層級。 惡意使用者可能能夠刻意觸發錯誤。 例如,請勿從顯示產品詳細資料的元件 URL 中提供未知 ProductId 的錯誤記錄事件。 並非所有錯誤都應該視為記錄的事件。

如需詳細資訊,請參閱下列文章:

≦適用于 Blazor Server 的應用程式和其他伺服器端 ASP.NET Core應用程式,這些應用程式是 的 Blazor Web API 後端應用程式。 Blazor WebAssembly 應用程式可以將用戶端上的錯誤資訊設陷並傳送至 Web API,以將錯誤資訊記錄到持續性記錄提供者。

可能發生錯誤的位置

架構和應用程式程式碼可能會在下列任何位置觸發未處理的例外狀況,本文的下列各節會進一步說明:

元件具現化

建立元件的實例時 Blazor :

  • 叫用元件的建構函式。
  • 會透過 @inject 指示詞或[Inject] 屬性叫用提供給元件建構函式之 DI 服務的建構函式。

任何屬性的執行建構函式或 setter [Inject] 錯誤會導致未處理的例外狀況,並停止架構具現化元件。 如果應用程式是 Blazor Server 應用程式,線路就會失敗。 如果建構函式邏輯可能會擲回例外狀況,應用程式應該使用語句來攔截例外狀況 try-catch ,並搭配錯誤處理和記錄。

生命週期方法

在元件的存留期間, Blazor 叫用 生命週期方法。 如果有任何生命週期方法以同步或非同步方式擲回例外狀況,則例外狀況對 Blazor Server 線路會嚴重。 若要讓元件處理生命週期方法中的錯誤,請新增錯誤處理邏輯。

在下列範例中,呼叫 OnParametersSetAsync 方法來取得產品:

  • 方法中擲回的 ProductRepository.GetProductByIdAsync 例外狀況是由 try-catch 語句處理。
  • catch執行 區塊時:
    • loadFailed 設定為 true ,用來向使用者顯示錯誤訊息。
    • 記錄錯誤。
@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;
            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);
    }
}

轉譯邏輯

元件檔案中的 Razor 宣告式標記 () .razor 會編譯成稱為 BuildRenderTree 的 C# 方法。 當元件轉譯時, BuildRenderTree 會執行並建置描述所轉譯元件之元素、文字和子元件的資料結構。

轉譯邏輯可能會擲回例外狀況。 評估 但 @someObjectnull@someObject.PropertyName ,就會發生此案例的範例。 對於 Blazor Server 應用程式,轉譯邏輯擲回的未處理例外狀況對應用程式的線路而言是嚴重錯誤。

若要防止 NullReferenceException 在轉譯邏輯中,請先檢查 null 物件,再存取其成員。 在下列範例中,如果 是 person.Addressnullperson.Address 則不會存取屬性:

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

上述程式碼假設 person 不是 null 。 程式碼的結構通常保證元件呈現時存在物件。 在這些情況下,不需要檢查 null 轉譯邏輯中的 。 在先前的範例中, person 可能會保證存在,因為 person 會在具現化元件時建立,如下列範例所示:

@code {
    private Person person = new();

    ...
}

事件處理常式

使用下列專案建立事件處理常式時,用戶端程式代碼會觸發 C# 程式碼的調用:

  • @onclick
  • @onchange
  • 其他 @on... 屬性
  • @bind

在這些情況下,事件處理常式程式碼可能會擲回未處理的例外狀況。

如果應用程式呼叫因外部原因而可能會失敗的程式碼,請使用語句搭配錯誤處理和記錄來 try-catch 捕捉例外狀況。

例如,如果事件處理常式擲回未處理的例外狀況 (,資料庫查詢會失敗,) 開發人員程式碼未截獲和處理:

  • 架構會記錄例外狀況。
  • Blazor Server在應用程式中,應用程式線路的例外狀況嚴重。

元件處置

例如,使用者已流覽至另一個頁面,因此可能會從 UI 中移除元件。 從 UI 移除實作 System.IDisposable 的元件時,架構會呼叫元件的 Dispose 方法。

如果元件的 Dispose 方法在應用程式中擲回未處理的例外 Blazor Server 狀況,則應用程式線路的例外狀況會嚴重。

如果處置邏輯可能會擲回例外狀況,則應用程式應該使用語句來攔截例外狀況 try-catch ,其中包含錯誤處理和記錄。

如需元件處置的詳細資訊,請參閱ASP.NET Core Razor 元件生命週期

JavaScript Interop

IJSRuntime 是由 Blazor 架構註冊。 IJSRuntime.InvokeAsync 可讓 .NET 程式碼在使用者的瀏覽器中對 JavaScript (JS) 執行時間進行非同步呼叫。

下列條件適用于 使用 InvokeAsync 的錯誤處理:

  • 如果呼叫 InvokeAsync 同步失敗,就會發生 .NET 例外狀況。 例如,呼叫 InvokeAsync 可能會失敗,因為提供的引數無法序列化。 開發人員程式碼必須攔截例外狀況。 如果事件處理常式或元件生命週期方法中的 Blazor Server 應用程式程式碼未處理應用程式中的例外狀況,則產生的例外狀況對應用程式的線路會嚴重。
  • 如果以非同步方式呼叫 InvokeAsync 失敗,.NET Task 會失敗。 InvokeAsync例如,呼叫 可能會失敗,因為 JS -side 程式碼會擲回例外狀況,或傳回以 的形式 rejected 完成的 Promise 。 開發人員程式碼必須攔截例外狀況。 await如果使用 運算子,請考慮將方法呼叫包裝在語句中 try-catch ,並包含錯誤處理和記錄。 否則,在 Blazor Server 應用程式中,失敗的程式碼會導致應用程式線路嚴重失敗的例外狀況。
  • 根據預設,呼叫 InvokeAsync 必須在特定期間內完成,否則呼叫逾時。預設逾時期間為一分鐘。 逾時可保護程式碼免于網路連線中斷,或 JS 永遠不會傳回完成訊息的程式碼。 如果呼叫逾時,產生的 System.Threading.Tasks 會失敗並 OperationCanceledException 出現 。 利用記錄來設陷和處理例外狀況。

同樣地, JS 程式碼可能會起始對 屬性所指示之 .NET 方法的[JSInvokable]呼叫。 如果這些 .NET 方法擲回未處理的例外狀況:

  • Blazor Server在應用程式中,例外狀況不會被視為應用程式的線路嚴重。
  • 拒絕 JS -side Promise

您可以選擇在 .NET 端或 JS 方法呼叫端使用錯誤處理常式代碼。

如需詳細資訊,請參閱下列文章:

預先呈現

Razor 元件可以使用 元件標籤協助程式 預先呈現,以便將其轉譯的 HTML 標籤當做使用者初始 HTTP 要求的一部分傳回。

在 中 Blazor Server ,預先呈現的運作方式如下:

  • 為屬於相同頁面一部分的所有預先呈現元件建立新的線路。
  • 產生初始 HTML。
  • 將線路 disconnected 視為 ,直到使用者的瀏覽器建立 SignalR 與相同伺服器的連線為止。 建立連線時,會繼續線路上的互動功能,並更新元件的 HTML 標籤。

在預先呈現 Blazor WebAssembly 中,預先呈現的運作方式如下:

  • 針對屬於相同頁面的所有預先呈現元件,在伺服器上產生初始 HTML。
  • 在瀏覽器載入應用程式的編譯器代碼和 .NET 執行時間之後,在用戶端上讓元件成為互動式元件,如果尚未在背景中載入) ,則為 .NET 執行時間 (。

如果元件在預先呈現期間擲回未處理的例外狀況,例如,在生命週期方法或轉譯邏輯中:

  • 在 Blazor Sever 應用程式中,例外狀況對線路是嚴重性的。 在預先呈現 Blazor WebAssembly 的應用程式中,例外狀況會防止轉譯元件。
  • 例外狀況會從 ComponentTagHelper 擲回呼叫堆疊。

在預先呈現失敗的正常情況下,繼續建置和轉譯元件並不合理,因為無法轉譯工作元件。

若要容許在預先呈現期間可能發生的錯誤,錯誤處理邏輯必須放在可能會擲回例外狀況的元件內。 使用 try-catch 語句搭配錯誤處理和記錄。 不要將 包裝 ComponentTagHelper 在 語句中 try-catch ,而是將錯誤處理邏輯放在 所轉譯的 ComponentTagHelper 元件中。

進階案例

遞迴轉譯

元件可以遞迴方式巢狀化。 這適用于表示遞迴資料結構。 例如, TreeNode 元件可以轉譯每個節點子系的更多 TreeNode 元件。

以遞迴方式轉譯時,請避免產生無限遞迴的編碼模式:

  • 不要以遞迴方式呈現包含迴圈的資料結構。 例如,不要轉譯其子系包含本身的樹狀節點。
  • 請勿建立包含迴圈的版面配置鏈結。 例如,不要建立其版面配置本身的版面配置。
  • 不允許終端使用者違反遞迴不變數 (規則,) 惡意資料輸入或 JavaScript Interop 呼叫。

轉譯期間的無限迴圈:

  • 讓轉譯程式永遠繼續。
  • 相當於建立未結束的迴圈。

在這些情況下, Blazor WebAssembly 執行緒或 Blazor Server 線路會失敗,而且通常會嘗試:

  • 無限期地耗用作業系統所允許的 CPU 時間。
  • 耗用無限數量的記憶體。 使用無限制記憶體相當於不定迴圈在每個反復專案上將專案新增至集合的案例。

若要避免無限遞迴模式,請確定遞迴轉譯程式碼包含適當的停止條件。

自訂轉譯樹狀結構邏輯

大部分 Razor 元件會實作為 Razor 元件 .razor 檔 () ,並由架構編譯,以產生在 上 RenderTreeBuilder 運作以轉譯其輸出的邏輯。 不過,開發人員可以使用程式 C# 程式碼手動實 RenderTreeBuilder 作邏輯。 如需詳細資訊,請參閱ASP.NET Core (Blazor) 轉譯樹狀結構建構的進階案例

警告

使用手動轉譯樹狀結構產生器邏輯會被視為進階和不安全的案例,不建議用於一般元件開發。

如果 RenderTreeBuilder 撰寫程式碼,開發人員必須保證程式碼的正確性。 例如,開發人員必須確定:

不正確的手動轉譯樹狀結構產生器邏輯可能會導致任意未定義的行為,包括當機、應用程式 (Blazor WebAssembly) 或伺服器 (Blazor Server) 停止回應,以及安全性弱點。

請考慮在相同層級的複雜度上手動轉譯樹狀結構產生器邏輯,並使用與手動撰寫元件程式碼或Microsoft Intermediate Language (MSIL) 指令相同的危險層級。

其他資源

†用戶端應用程式用於記錄的後端 ASP.NET Core Web API 應用程式 Blazor WebAssembly 。