ASP.NET Core BlazorSignalR 指引
注意
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本。
本文說明如何設定和管理 Blazor 應用程式中的 SignalR 連線。
如需 ASP.NET Core SignalR 組態的一般指引,請參閱文件的 ASP.NET Core SignalR 概觀區域中的主題,特別是 ASP.NET Core SignalR 組態。
伺服器端應用程式會使用 ASP.NET Core SignalR 來與瀏覽器通訊。 SignalR 的託管和擴縮條件適用於伺服器端應用程式。
Blazor 在使用 WebSocket 作為 SignalR 傳輸協定時效果最佳,因為其延遲性較低且具備可靠性和安全性。 當 WebSocket 無法使用或當應用程式明確設定為使用長輪詢時,SignalR 就會使用長輪詢。
具狀態重新連線的 Azure SignalR 服務
具狀態重新連線 (WithStatefulReconnect) 已隨 .NET 8 一起發佈,但目前不支援 Azure SignalR 服務。 如需詳細資訊,請參閱具狀態重新連線支援?(Azure/azure-signalr
#1878)。
互動式伺服器元件的 WebSocket 壓縮
依預設,互動式伺服器元件:
為 WebSocket 連線啟用壓縮。 DisableWebSocketCompression (預設值:
false
)控制 WebSocket 壓縮。採用設定為
'self'
的frame-ancestors
內容安全原則 (CSP) 指示詞,它只會允許將應用程式內嵌在來源的<iframe>
中,透過它在啟用壓縮或在提供 WebSocket 內容的設定時提供應用程式。ContentSecurityFrameAncestorPolicy
會控制frame-ancestors
CSP。
您可以將 ContentSecurityFrameAncestorsPolicy 的值設定為 null
,以手動移除 frame-ancestors
CSP,因為您可能想要以集中的方式設定 CSP。 以集中的方式管理 frame-ancestors
CSP 時,每當轉譯第一份文件時,套用原則時都必須小心。 我們不建議您完全移除原則,因為它可能會讓應用程式容易受到攻擊。
使用 ConfigureWebSocketAcceptContext 設定 WebSocketAcceptContext 伺服器元件所使用之 Websocket 連線的 。 根據預設,會套用啟用壓縮並設定中 ContentSecurityFrameAncestorsPolicy 定義之框架上階 CSP 的原則。
使用範例:
將 DisableWebSocketCompression 設定為 true
可停用壓縮,這可降低應用程式遭受攻擊的弱點,但可能會導致效能降低:
builder.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(o => o.DisableWebSocketCompression = true)
啟用壓縮時,請使用 'none'
值設定較嚴格的 frame-ancestors
CSP,其會允許 WebSocket 壓縮,但會防止瀏覽器將應用程式內嵌至任何 <iframe>
:
builder.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")
啟用壓縮時,將 ContentSecurityFrameAncestorsPolicy 設定為 null
來移除 frame-ancestors
CSP。 此案例僅建議用於以集中方式設定 CSP 的應用程式:
builder.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = null)
重要
瀏覽器會使用最嚴格的原則指示詞值,從多個 CSP 標頭套用 CSP 指示詞。 因此,開發人員不能故意或錯誤地新增較 'self'
更弱的 frame-ancestors
原則。
傳遞至 ContentSecurityFrameAncestorsPolicy 的字串值需要單引號:
不支援的值: none
、self
支援的值: 'none'
、'self'
其他選項包括指定一或多個主機來源和配置來源。
如需安全性影響,請參閱 ASP.NET Core Blazor 互動式伺服器端轉譯的威脅風險降低指導。 如需 frame-ancestors
指示詞的詳細資訊,請參閱 CSP:frame-ancestors
(MDN 文件)。
停用熱重新載入的回應壓縮
使用熱重新載入時,請在 Development
環境中停用回應壓縮中介軟體。 無論是否使用專案範本中的預設程式碼,請一律先在要求處理管線中呼叫 UseResponseCompression。
在 Program
檔案中:
if (!app.Environment.IsDevelopment())
{
app.UseResponseCompression();
}
用於驗證的用戶端側 SignalR 跨原始來源交涉
本節說明如何設定 SignalR 的底層用戶端傳送憑證,例如 Cookie 或 HTTP 認證標頭。
使用 SetBrowserRequestCredentials 在跨原始來源 fetch
要求上設定 Include。
IncludeRequestCredentialsMessageHandler.cs
:
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Http;
public class IncludeRequestCredentialsMessageHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
return base.SendAsync(request, cancellationToken);
}
}
在建置中樞連線的位置,將 HttpMessageHandler 指派給 HttpMessageHandlerFactory 選項:
private HubConnectionBuilder? hubConnection;
...
hubConnection = new HubConnectionBuilder()
.WithUrl(new Uri(Navigation.ToAbsoluteUri("/chathub")), options =>
{
options.HttpMessageHandlerFactory = innerHandler =>
new IncludeRequestCredentialsMessageHandler { InnerHandler = innerHandler };
}).Build();
上述範例會將中樞連線 URL 設定為位於 /chathub
的絕對 URI 位址。 該 URI 也可以透過字串來設定,例如 https://signalr.example.com
,或透過組態進行設定。 Navigation
是插入的 NavigationManager。
如需詳細資訊,請參閱 ASP.NET Core SignalR 組態。
用戶端轉譯
如果已設定預先轉譯,則會在用戶端與伺服器建立連線之前先進行預先轉譯。 如需詳細資訊,請參閱預先轉譯 ASP.NET Core Razor 元件。
如果已設定預先轉譯,則會在用戶端與伺服器建立連線之前先進行預先轉譯。 如需詳細資訊,請參閱下列文章:
預先轉譯狀態大小和 SignalR 訊息大小限制
大型預先轉譯狀態大小可能超過 SignalR 線路訊息大小限制,這會導致下列結果:
- SignalR 線路無法初始化,且用戶端發生錯誤:Circuit host not initialized.
- 當線路發生中斷時,用戶端會出現重新連線的 UI。 不可能復原。
若要解決該問題,請使用下列任一種方法:
- 減少放入預先轉譯狀態的資料量。
- 新增 SignalR 訊息大小限制。 警告:增加限制可能會增加拒絕服務 (DoS) 攻擊的風險。
其他用戶端側資源
- 保護 SignalR 中樞
- ASP.NET Core SignalR 概觀
- ASP.NET Core SignalR 設定 (部分機器翻譯)
- Blazor 範例 GitHub 存放庫 (
dotnet/blazor-samples
) (如何下載)
使用工作階段親和性(黏性工作階段)進行伺服器端 Webfarm 裝載
使用多個後端伺服器時,應用程式必須實作工作階段親和性,也稱為 黏性工作階段。 工作階段親和性可確保如果中斷連線,用戶端的線路會重新連線到相同的伺服器,這很重要,因為客戶端狀態只會保留在第一次建立用戶端線路的伺服器記憶體中。
未在 webfarm 中啟用工作階段親和性的應用程式會擲回下列錯誤:
Uncaught (in promise) Error: Invocation canceled due to the underlying connection being closed.
有關使用 Azure App Service 主機的工作階段親和性的詳細資訊,請參閱主機和部署 ASP.NET Core 伺服器端 Blazor 應用程式。
Azure SignalR Service
可選的 Azure SignalR Service 與應用程式的 SignalR 集線器配合使用,可將伺服器端應用程式擴充至大量的同時連線。 此外,服務的全球觸達和高效能資料中心可大幅協助降低因地理位置造成的延遲。
在 Azure App Service 或 Azure Container Apps 中託管的 Blazor 應用程式不需要這項服務,但在其他託管環境中可能會有所幫助:
- 為了方便連線向外延展。
- 處理全球配送。
如需詳細資訊,請參閱裝載和部署 ASP.NET Core 伺服器端 Blazor 應用程式。
伺服器端線路處理常式選項
使用 CircuitOptions 設定線路。 檢視參考來源中的預設值。
注意
.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤。
使用委派給 AddInteractiveServerComponents 的選項來讀取或設定 Program
檔案中的選項。 {OPTION}
預留位置代表選項,而 {VALUE}
預留位置是值。
在 Program
檔案中:
builder.Services.AddRazorComponents().AddInteractiveServerComponents(options =>
{
options.{OPTION} = {VALUE};
});
使用委派給 AddServerSideBlazor 的選項來讀取或設定 Program
檔案中的選項。 {OPTION}
預留位置代表選項,而 {VALUE}
預留位置是值。
在 Program
檔案中:
builder.Services.AddServerSideBlazor(options =>
{
options.{OPTION} = {VALUE};
});
使用委派給 AddServerSideBlazor 的選項來讀取或設定 Startup.ConfigureServices
中的選項。 {OPTION}
預留位置代表選項,而 {VALUE}
預留位置是值。
在 Startup.cs
的 Startup.ConfigureServices
中:
services.AddServerSideBlazor(options =>
{
options.{OPTION} = {VALUE};
});
若要設定 HubConnectionContext,請搭配 AddHubOptions 使用 HubConnectionContextOptions。 檢視參考來源中中樞連線內容選項的預設值。 如需 SignalR 文件中的選項描述,請參閱 ASP.NET Core SignalR 設定。 {OPTION}
預留位置代表選項,而 {VALUE}
預留位置是值。
注意
.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤。
在 Program
檔案中:
builder.Services.AddRazorComponents().AddInteractiveServerComponents().AddHubOptions(options =>
{
options.{OPTION} = {VALUE};
});
在 Program
檔案中:
builder.Services.AddServerSideBlazor().AddHubOptions(options =>
{
options.{OPTION} = {VALUE};
});
在 Startup.cs
的 Startup.ConfigureServices
中:
services.AddServerSideBlazor().AddHubOptions(options =>
{
options.{OPTION} = {VALUE};
});
警告
MaximumReceiveMessageSize 的預設值為 32 KB。 提高此值可能會增加拒絕服務 (DoS) 攻擊風險。
Blazor 依賴 MaximumParallelInvocationsPerClient 設定為 1,這是預設值。 如需詳細資訊,請參閱 MaximumParallelInvocationsPerClient > 1 在 Blazor Server 模式 (dotnet/aspnetcore
#53951) 時會中斷檔案上傳。
如需記憶體管理的相關資訊,請參閱裝載和部署 ASP.NET Core 伺服器端 Blazor 應用程式。
Blazor 中樞選項
設定 MapBlazorHub 選項來控制 Blazor 中樞的 HttpConnectionDispatcherOptions。 檢視參考來源中中樞連線 Dispatcher 選項的預設值。 {OPTION}
預留位置代表選項,而 {VALUE}
預留位置是值。
注意
.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤。
將 app.MapBlazorHub
的呼叫放在應用程式 Program
檔案中的 app.MapRazorComponents
呼叫之後:
app.MapBlazorHub(options =>
{
options.{OPTION} = {VALUE};
});
以 MapBlazorHub 設定 AddInteractiveServerRenderMode 所使用的中樞失敗,發生 AmbiguousMatchException
:
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints.
若要解決以 .NET 8 為目標的應用程式發生的問題,請使用 WithOrder 方法為自訂設定的 Blazor 的中樞提供較高的優先順序:
app.MapBlazorHub(options =>
{
options.CloseOnAuthenticationExpiration = true;
}).WithOrder(-1);
如需詳細資訊,請參閱以下資源:
在應用程式的 Program
檔案中提供 app.MapBlazorHub
的選項:
app.MapBlazorHub(options =>
{
options.{OPTION} = {VALUE};
});
在端點路由組態中提供 app.MapBlazorHub
的選項:
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub(options =>
{
options.{OPTION} = {VALUE};
});
...
});
接收訊息大小上限
本節僅適用於實作 SignalR 的專案。
中樞方法允許的傳入 SignalR 訊息大小上限受限於 HubOptions.MaximumReceiveMessageSize (預設值:32 KB)。 SignalR 訊息大於 MaximumReceiveMessageSize 時會擲回錯誤。 該架構不會對從中樞到用戶端的 SignalR 訊息大小施加限制。
如果為將 SignalR 記錄設定為 [偵錯] 或 [追蹤],則訊息大小錯誤僅顯示在瀏覽器的開發人員工具主控台中:
錯誤:連線中斷時出現錯誤「錯誤:伺服器關閉時傳回錯誤:連線已關閉並顯示錯誤」。
當 SignalR 伺服器端記錄設定為 [偵錯] 或 [追蹤] 時,伺服器端記錄會針對訊息大小錯誤顯示 InvalidDataException。
appsettings.Development.json
:
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
...
"Microsoft.AspNetCore.SignalR": "Debug"
}
}
}
錯誤:
System.IO.InvalidDataException:已超過訊息大小上限 32768B。 您可以在 AddHubOptions 中設定訊息大小。
其中一種方法是透過在 Program
檔案中設定 MaximumReceiveMessageSize 來增加限制。 下列範例會將接收訊息大小上限設定為 64 KB:
builder.Services.AddRazorComponents().AddInteractiveServerComponents()
.AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);
提高 SignalR 傳入訊息大小限制,代價是要求更多的伺服器資源,並增加拒絕服務 (DoS) 攻擊的風險。 此外,如果將大量內容以字串或位元組陣列的形式讀入記憶體中,也會導致記憶體回收行程的配置運作狀況不佳,從而產生額外的效能降低。
讀取大型承載的較佳選項是以較小的區塊傳送內容,並將承載作為 Stream 處理。 讀取大型 JavaScript (JS) 互操作 JSON 有效負載時,或 JS 互操作資料為原始位元組時,可以使用此功能。 如需示範如何使用類似 InputFile
元件的技術在伺服器端應用程式中傳送大型二進位承載的範例,請參閱二進位提交範例應用程式和 BlazorInputLargeTextArea
元件範例。
注意
.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤。
透過 SignalR 處理大型承載的表單也可以直接使用串流 JS 交互操作。 如需詳細資訊,請參閱在 ASP.NET Core Blazor 中從 JavaScript 函式呼叫 .NET 方法。 如需將 <textarea>
資料串流至伺服器的表單範例,請參閱疑難排解 ASP.NET Core Blazor 表單。
其中一種方法是透過在 Program
檔案中設定 MaximumReceiveMessageSize 來增加限制。 下列範例會將接收訊息大小上限設定為 64 KB:
builder.Services.AddServerSideBlazor()
.AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);
提高 SignalR 傳入訊息大小限制,代價是要求更多的伺服器資源,並增加拒絕服務 (DoS) 攻擊的風險。 此外,如果將大量內容以字串或位元組陣列的形式讀入記憶體中,也會導致記憶體回收行程的配置運作狀況不佳,從而產生額外的效能降低。
讀取大型承載的較佳選項是以較小的區塊傳送內容,並將承載作為 Stream 處理。 讀取大型 JavaScript (JS) 互操作 JSON 有效負載時,或 JS 互操作資料為原始位元組時,可以使用此功能。 如需示範如何Blazor Server使用類似 InputFile
元件的技術傳送大型二進位承載的範例,請參閱二進位提交範例應用程式和BlazorInputLargeTextArea
元件範例。
注意
.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤。
透過 SignalR 處理大型承載的表單也可以直接使用串流 JS 交互操作。 如需詳細資訊,請參閱在 ASP.NET Core Blazor 中從 JavaScript 函式呼叫 .NET 方法。 如需將 <textarea>
資料串流至 Blazor Server 應用程式的表單範例,請參閱疑難排解 ASP.NET Core Blazor 表單。
藉由在 Startup.ConfigureServices
中設定 MaximumReceiveMessageSize 來提高限制:
services.AddServerSideBlazor()
.AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);
提高 SignalR 傳入訊息大小限制,代價是要求更多的伺服器資源,並增加拒絕服務 (DoS) 攻擊的風險。 此外,如果將大量內容以字串或位元組陣列的形式讀入記憶體中,也會導致記憶體回收行程的配置運作狀況不佳,從而產生額外的效能降低。
開發用於傳輸大量資料的程式碼時,請考慮下列指引:
- 利用原生串流 JS 交互操作支援來傳輸大於 SignalR 傳入訊息大小限制的資料:
- 一般提示:
- 請勿在 JS 和 C# 程式碼中配置大型物件。
- 在流程完成或取消時,釋放已消耗的記憶體。
- 為了安全起見,請強制執行下列額外要求:
- 宣告可傳遞的檔案或資料大小上限。
- 宣告從用戶端到伺服器的最低上傳速率。
- 在伺服器接收資料之後,資料可以:
- 暫時儲存在記憶體緩衝區中,直到收集完所有區段為止。
- 立即取用。 例如,在收到每個區段時,資料可以立即儲存在資料庫中或寫入磁碟。
- 將資料分割成較小的片段,並循序傳送資料區段直到伺服器接收所有資料為止。
- 請勿在 JS 和 C# 程式碼中配置大型物件。
- 傳送或接收資料時,請勿長時間封鎖主要 UI 執行緒。
- 在流程完成或取消時,釋放已消耗的記憶體。
- 為了安全起見,請強制執行下列額外要求:
- 宣告可傳遞的檔案或資料大小上限。
- 宣告從用戶端到伺服器的最低上傳速率。
- 在伺服器接收資料之後,資料可以:
- 暫時儲存在記憶體緩衝區中,直到收集完所有區段為止。
- 立即取用。 例如,在收到每個區段時,資料可以立即儲存在資料庫中或寫入磁碟。
Blazor 伺服器端中樞端點路由設定
在 Program
檔案中,呼叫 MapBlazorHub 以將 BlazorHub 對應至應用程式的預設路徑。 Blazor 指令碼 (blazor.*.js
) 自動指向 MapBlazorHub 所建立的端點。
在 UI 中反映伺服器端連線狀態
當用戶端偵測到連線已遺失時,用戶端嘗試重新連線時會向使用者顯示預設 UI。 如果重新連線失敗,則會向使用者提供重試選項。
若要自訂 UI,請定義一個 id
為 components-reconnect-modal
的元素。 下列範例將該元素放在 App
元件中。
App.razor
:
若要自訂 UI,請定義一個 id
為 components-reconnect-modal
的元素。 下列範例將元素放在主機頁面中。
Pages/_Host.cshtml
:
若要自訂 UI,請定義一個 id
為 components-reconnect-modal
的元素。 下列範例將元素放在版面配置頁面中。
Pages/_Layout.cshtml
:
若要自訂 UI,請定義一個 id
為 components-reconnect-modal
的元素。 下列範例將元素放在主機頁面中。
Pages/_Host.cshtml
:
<div id="components-reconnect-modal">
There was a problem with the connection!
</div>
注意
如果應用程式轉譯了多個具有 id
為 components-reconnect-modal
的元素,則只有第一個轉譯的元素會接收 CSS 類別變更,以顯示或隱藏該元素。
將下列 CSS 樣式新增至網站的樣式表。
wwwroot/app.css
:
wwwroot/css/site.css
:
#components-reconnect-modal {
display: none;
}
#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
display: block;
}
下表描述了 Blazor 架構套用至 components-reconnect-modal
元素的 CSS 類別。
CSS 類別 | 表示… |
---|---|
components-reconnect-show |
遺失的連線。 用戶端嘗試重新連線。 顯示模式。 |
components-reconnect-hide |
重新建立到伺服器的作用中連線。 隱藏模式。 |
components-reconnect-failed |
重新連線失敗,可能是因為網路故障。 若要嘗試重新連線,請在 JavaScript 中呼叫 window.Blazor.reconnect() 。 |
components-reconnect-rejected |
已拒絕重新連線。 已連線到伺服器,但伺服器拒絕了連線,且伺服器上的使用者狀態遺失。 若要重新載入應用程式,請在 JavaScript 中呼叫 location.reload() 。 出現以下情況時,可能會導致此連線狀態:
|
透過在網站的 CSS 中為模式元素設定 transition-delay
屬性,以自訂重新連線 UI 出現之前的延遲時間。 下列範例將轉換延遲從 500 毫秒 (預設值) 設定為 1,000 毫秒 (1 秒)。
wwwroot/app.css
:
wwwroot/css/site.css
:
#components-reconnect-modal {
transition: visibility 0s linear 1000ms;
}
若要顯示目前的重新連線嘗試,請定義一個 components-reconnect-current-attempt
為 id
的元素。 若要顯示重新連線重試次數上限,請定義一個 components-reconnect-max-retries
為 id
的元素。 下列範例會遵循上一個範例,將這些元素放置在重新連線嘗試模式元素內。
<div id="components-reconnect-modal">
There was a problem with the connection!
(Current reconnect attempt:
<span id="components-reconnect-current-attempt"></span> /
<span id="components-reconnect-max-retries"></span>)
</div>
當自訂重新連線模式出現時,它會根據上述程式碼轉譯類似下列的內容:
There was a problem with the connection! (Current reconnect attempt: 3 / 8)
伺服器端轉譯
根據預設,元件會在用戶端與伺服器建立連線之前在伺服器上預先轉譯。 如需詳細資訊,請參閱預先轉譯 ASP.NET Core Razor 元件。
根據預設,元件會在用戶端與伺服器建立連線之前在伺服器上預先轉譯。 如需詳細資訊,請參閱 ASP.NET Core 中的元件標籤協助程式。
監視伺服器端線路活動
使用 CircuitHandler 上的 CreateInboundActivityHandler 方法來監視輸入線路活動。 輸入線路活動是從瀏覽器傳送至伺服器的任何活動,例如 UI 事件或 JavaScript-to-.NET 交互操作呼叫。
例如,您可以使用線路活動處理常式來偵測用戶端是否閒置並記錄其線路識別碼 (Circuit.Id):
using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.Options;
using Timer = System.Timers.Timer;
public sealed class IdleCircuitHandler : CircuitHandler, IDisposable
{
private Circuit? currentCircuit;
private readonly ILogger logger;
private readonly Timer timer;
public IdleCircuitHandler(ILogger<IdleCircuitHandler> logger,
IOptions<IdleCircuitOptions> options)
{
timer = new Timer
{
Interval = options.Value.IdleTimeout.TotalMilliseconds,
AutoReset = false
};
timer.Elapsed += CircuitIdle;
this.logger = logger;
}
private void CircuitIdle(object? sender, System.Timers.ElapsedEventArgs e)
{
logger.LogInformation("{CircuitId} is idle", currentCircuit?.Id);
}
public override Task OnCircuitOpenedAsync(Circuit circuit,
CancellationToken cancellationToken)
{
currentCircuit = circuit;
return Task.CompletedTask;
}
public override Func<CircuitInboundActivityContext, Task> CreateInboundActivityHandler(
Func<CircuitInboundActivityContext, Task> next)
{
return context =>
{
timer.Stop();
timer.Start();
return next(context);
};
}
public void Dispose() => timer.Dispose();
}
public class IdleCircuitOptions
{
public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(5);
}
public static class IdleCircuitHandlerServiceCollectionExtensions
{
public static IServiceCollection AddIdleCircuitHandler(
this IServiceCollection services,
Action<IdleCircuitOptions> configureOptions)
{
services.Configure(configureOptions);
services.AddIdleCircuitHandler();
return services;
}
public static IServiceCollection AddIdleCircuitHandler(
this IServiceCollection services)
{
services.AddScoped<CircuitHandler, IdleCircuitHandler>();
return services;
}
}
在 Program
檔案中註冊服務。 下列範例會設定五分鐘到五秒的預設閒置逾時,以測試上述 IdleCircuitHandler
實作:
builder.Services.AddIdleCircuitHandler(options =>
options.IdleTimeout = TimeSpan.FromSeconds(5));
線路活動處理常式還提供了從其他非 Blazor 相依性插入 (DI) 範圍存取範圍 Blazor 服務的方法。 如需詳細資訊與範例,請參閱:
Blazor 啟動
設定手動啟動 Blazor 應用程式中的 SignalR 線路,位於 Blazor Web App 的 App.razor
檔案中:
在 Pages/_Host.cshtml
檔案中 (Blazor Server) 設定 Blazor 應用程式的 SignalR 線路的手動啟動:
在 Pages/_Layout.cshtml
檔案中 (Blazor Server) 設定 Blazor 應用程式的 SignalR 線路的手動啟動:
在 Pages/_Host.cshtml
檔案中 (Blazor Server) 設定 Blazor 應用程式的 SignalR 線路的手動啟動:
- 將
autostart="false"
屬性新增至<script>
指令碼的blazor.*.js
標記中。 - 放置一個在載入 Blazor 指令碼之後呼叫
Blazor.start()
的指令碼,並將其置於結尾</body>
標記中。
停用 autostart
時,應用程式中不依賴該線路的任何方面都會正常運作。 例如,用戶端側路由正常運作。 不過,在呼叫 Blazor.start()
之前,依賴該線路的任何方面無法正常運作。 如果沒有已建立的線路,應用程式行為是無法預測的。 例如,當線路中斷連線時,元件方法無法執行。
如需詳細資訊,包括如何在文件就緒時初始化 Blazor,以及如何鏈結至 JS Promise
,請參閱 ASP.NET Core Blazor 啟動。
在用戶端上設定 SignalR 逾時和 Keep-Alive
為用戶端設定下列值:
withServerTimeout
:設定伺服器逾時 (以毫秒為單位)。 如果此逾時已過而未從伺服器接收任何訊息,連線就會終止並出現錯誤。 預設的逾時值是 30 秒。 伺服器逾時應至少是指派給 Keep-Alive 間隔 (withKeepAliveInterval
) 的值的兩倍。withKeepAliveInterval
:設定 Keep-Alive 間隔 (以毫秒為單位)(Ping 伺服器採用的預設間隔)。 此設定可讓伺服器偵測強制中斷連線的情況,例如用戶端拔除其電腦與網路的連線。 此 Ping 的發生頻率最多與伺服器 Ping 的頻率一樣。 如果伺服器每隔五秒 ping 一次,則指派的值低於5000
(5 秒),將會每五秒 ping 一次。 預設值為 15 秒。 Keep-Alive 間隔應小於或等於指派給伺服器逾時 (withServerTimeout
) 值的一半。
下列的 App.razor
檔案 (Blazor Web App ) 範例顯示了預設值的指派。
Blazor Web App:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
circuit: {
configureSignalR: function (builder) {
builder.withServerTimeout(30000).withKeepAliveInterval(15000);
}
}
});
</script>
下列範例適用於 Pages/_Host.cshtml
檔案 (Blazor Server,.NET 6 中的 ASP.NET Core 除外的所有版本) 或 Pages/_Layout.cshtml
檔案 (Blazor Server,.NET 6 中的 ASP.NET Core)。
Blazor Server:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
configureSignalR: function (builder) {
builder.withServerTimeout(30000).withKeepAliveInterval(15000);
}
});
</script>
在上述範例中,{BLAZOR SCRIPT}
預留位置是 Blazor 指令碼路徑和檔案名稱。 如需指令碼的位置和要使用的路徑,請參閱 ASP.NET Core Blazor 專案結構。
在元件中建立中樞連線時,請在 HubConnectionBuilder 上設定 ServerTimeout (預設值:30 秒) 和 KeepAliveInterval (預設值:15 秒)。 在建置 HubConnection 上設定 HandshakeTimeout (預設值:15 秒)。 下列範例顯示了預設值的指派:
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"))
.WithServerTimeout(TimeSpan.FromSeconds(30))
.WithKeepAliveInterval(TimeSpan.FromSeconds(15))
.Build();
hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(15);
hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...
await hubConnection.StartAsync();
}
為用戶端設定下列值:
serverTimeoutInMilliseconds
:伺服器逾時 (以毫秒為單位)。 如果此逾時已過而未從伺服器接收任何訊息,連線就會終止並出現錯誤。 預設的逾時值是 30 秒。 伺服器逾時應至少是指派給 Keep-Alive 間隔 (keepAliveIntervalInMilliseconds
) 的值的兩倍。keepAliveIntervalInMilliseconds
:Ping 伺服器採用的預設間隔。 此設定可讓伺服器偵測強制中斷連線的情況,例如用戶端拔除其電腦與網路的連線。 此 Ping 的發生頻率最多與伺服器 Ping 的頻率一樣。 如果伺服器每隔五秒 ping 一次,則指派的值低於5000
(5 秒),將會每五秒 ping 一次。 預設值為 15 秒。 Keep-Alive 間隔應小於或等於指派給伺服器逾時 (serverTimeoutInMilliseconds
) 值的一半。
下列範例適用於 Pages/_Host.cshtml
檔案 (Blazor Server,.NET 6 中的 ASP.NET Core 除外的所有版本) 或 Pages/_Layout.cshtml
檔案 (Blazor Server,.NET 6 中的 ASP.NET Core):
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
configureSignalR: function (builder) {
let c = builder.build();
c.serverTimeoutInMilliseconds = 30000;
c.keepAliveIntervalInMilliseconds = 15000;
builder.build = () => {
return c;
};
}
});
</script>
在上述範例中,{BLAZOR SCRIPT}
預留位置是 Blazor 指令碼路徑和檔案名稱。 如需指令碼的位置和要使用的路徑,請參閱 ASP.NET Core Blazor 專案結構。
在元件中建立中樞連線時,請在建置的 HubConnection 中設定 ServerTimeout (預設值:30 秒)、HandshakeTimeout (預設值:15 秒) 和 KeepAliveInterval (預設值:15 秒)。 下列範例顯示了預設值的指派:
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"))
.Build();
hubConnection.ServerTimeout = TimeSpan.FromSeconds(30);
hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(15);
hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(15);
hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...
await hubConnection.StartAsync();
}
變更伺服器逾時值 (ServerTimeout) 或 Keep-Alive 間隔值 (KeepAliveInterval):
- 伺服器逾時應至少是指派給 Keep-Alive 間隔的值的兩倍。
- Keep-Alive 間隔應小於或等於指派給伺服器逾時值的一半。
如需詳細資訊,請參閱下列文章的全域部署和連線失敗章節:
修改伺服器端重新連線處理常式
可以針對自訂行為修改重新連線處理常式的線路連線活動,例如:
- 在連線中斷時通知使用者。
- 在線路連線時 (來自用戶端) 執行記錄。
若要修改連線活動,請為下列連線變更註冊回呼:
- 使用
onConnectionDown
卸除的連線。 - 使用
onConnectionUp
已建立/重新建立的連線。
必須同時指定 onConnectionDown
和 onConnectionUp
。
Blazor Web App:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
circuit: {
reconnectionHandler: {
onConnectionDown: (options, error) => console.error(error),
onConnectionUp: () => console.log("Up, up, and away!")
}
}
});
</script>
Blazor Server:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
reconnectionHandler: {
onConnectionDown: (options, error) => console.error(error),
onConnectionUp: () => console.log("Up, up, and away!")
}
});
</script>
在上述範例中,{BLAZOR SCRIPT}
預留位置是 Blazor 指令碼路徑和檔案名稱。 如需指令碼的位置和要使用的路徑,請參閱 ASP.NET Core Blazor 專案結構。
伺服器端重新連線失敗後自動重新整理頁面
預設重新連線行為需要使用者在重新連線失敗之後,手動重新整理頁面。 不過,您可使用自訂重新連線處理常式來自動重新整理頁面:
App.razor
:
Pages/_Host.cshtml
:
<div id="reconnect-modal" style="display: none;"></div>
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script src="boot.js"></script>
在上述範例中,{BLAZOR SCRIPT}
預留位置是 Blazor 指令碼路徑和檔案名稱。 如需指令碼的位置和要使用的路徑,請參閱 ASP.NET Core Blazor 專案結構。
建立下列 wwwroot/boot.js
檔案。
Blazor Web App:
(() => {
const maximumRetryCount = 3;
const retryIntervalMilliseconds = 5000;
const reconnectModal = document.getElementById('reconnect-modal');
const startReconnectionProcess = () => {
reconnectModal.style.display = 'block';
let isCanceled = false;
(async () => {
for (let i = 0; i < maximumRetryCount; i++) {
reconnectModal.innerText = `Attempting to reconnect: ${i + 1} of ${maximumRetryCount}`;
await new Promise(resolve => setTimeout(resolve, retryIntervalMilliseconds));
if (isCanceled) {
return;
}
try {
const result = await Blazor.reconnect();
if (!result) {
// The server was reached, but the connection was rejected; reload the page.
location.reload();
return;
}
// Successfully reconnected to the server.
return;
} catch {
// Didn't reach the server; try again.
}
}
// Retried too many times; reload the page.
location.reload();
})();
return {
cancel: () => {
isCanceled = true;
reconnectModal.style.display = 'none';
},
};
};
let currentReconnectionProcess = null;
Blazor.start({
circuit: {
reconnectionHandler: {
onConnectionDown: () => currentReconnectionProcess ??= startReconnectionProcess(),
onConnectionUp: () => {
currentReconnectionProcess?.cancel();
currentReconnectionProcess = null;
}
}
}
});
})();
Blazor Server:
(() => {
const maximumRetryCount = 3;
const retryIntervalMilliseconds = 5000;
const reconnectModal = document.getElementById('reconnect-modal');
const startReconnectionProcess = () => {
reconnectModal.style.display = 'block';
let isCanceled = false;
(async () => {
for (let i = 0; i < maximumRetryCount; i++) {
reconnectModal.innerText = `Attempting to reconnect: ${i + 1} of ${maximumRetryCount}`;
await new Promise(resolve => setTimeout(resolve, retryIntervalMilliseconds));
if (isCanceled) {
return;
}
try {
const result = await Blazor.reconnect();
if (!result) {
// The server was reached, but the connection was rejected; reload the page.
location.reload();
return;
}
// Successfully reconnected to the server.
return;
} catch {
// Didn't reach the server; try again.
}
}
// Retried too many times; reload the page.
location.reload();
})();
return {
cancel: () => {
isCanceled = true;
reconnectModal.style.display = 'none';
},
};
};
let currentReconnectionProcess = null;
Blazor.start({
reconnectionHandler: {
onConnectionDown: () => currentReconnectionProcess ??= startReconnectionProcess(),
onConnectionUp: () => {
currentReconnectionProcess?.cancel();
currentReconnectionProcess = null;
}
}
});
})();
如需關於 Blazor 啟動的詳細資訊,請參閱 ASP.NET Core Blazor 啟動。
調整伺服器端重新連線重試次數和間隔
若要調整重新連線重試次數和間隔,請設定重試次數 (maxRetries
) 和允許每次重試的間隔毫秒數 (retryIntervalMilliseconds
)。
Blazor Web App:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
circuit: {
reconnectionOptions: {
maxRetries: 3,
retryIntervalMilliseconds: 2000
}
}
});
</script>
Blazor Server:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
reconnectionOptions: {
maxRetries: 3,
retryIntervalMilliseconds: 2000
}
});
</script>
在上述範例中,{BLAZOR SCRIPT}
預留位置是 Blazor 指令碼路徑和檔案名稱。 如需指令碼的位置和要使用的路徑,請參閱 ASP.NET Core Blazor 專案結構。
當使用者巡覽回到線路已中斷的應用程式時時,會立即嘗試重新連線,而不是等待下一個重新連線間隔的持續時間。 此行為旨在盡快為使用者恢復連線。
預設的重新連線計時會使用計算的輪詢策略。 在嘗試之間引入計算的延遲時間之前,前幾次重新連線嘗試會快速連續進行。 計算重試間隔的預設邏輯是一個實作細節,可能會在沒有通知的情況下更改,但您可以在 computeDefaultRetryInterval
函式 (參考來源) 中找到 Blazor 架構使用的預設邏輯。
注意
.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤。
透過指定計算重試間隔的函式來自訂重試間隔的行為。 在以下指數輪詢範例中,將先前重新連線嘗試的次數乘以 1,000 毫秒來計算重試間隔。 當先前嘗試重新連線的次數 (previousAttempts
) 大於最大重試限制 (maxRetries
) 時,則會向重試間隔 (retryIntervalMilliseconds
) 指派 null
以停止進一步的重新連線嘗試:
Blazor.start({
circuit: {
reconnectionOptions: {
retryIntervalMilliseconds: (previousAttempts, maxRetries) =>
previousAttempts >= maxRetries ? null : previousAttempts * 1000
},
},
});
另一種方式是指定重試間隔的確切順序。 在上次指定的重試間隔之後,重試會停止,因為 retryIntervalMilliseconds
函式會傳回 undefined
:
Blazor.start({
circuit: {
reconnectionOptions: {
retryIntervalMilliseconds:
Array.prototype.at.bind([0, 1000, 2000, 5000, 10000, 15000, 30000]),
},
},
});
如需關於 Blazor 啟動的詳細資訊,請參閱 ASP.NET Core Blazor 啟動。
控制重新連線 UI 的出現時機
在下列情況中,可能會需要控制重新連線 UI 的出現時機:
- 已部署的應用程式因為網際網路延遲造成 Ping 逾時而經常顯示重新連線 UI,而您想要增加延遲閾值。
- 某個應用程式需要在更短時間內向使用者回報連線已中斷,因此您想要縮短延遲閾值。
調整用戶端上的保持運作 (Keep-Alive) 間隔和逾時,將會影響重新連線 UI 出現的時機。 當用戶端上達到伺服器逾時 (withServerTimeout
,用戶端組態區段),就會顯示重新連線 UI。 不過,變更 withServerTimeout
的值需要變更下列指引中所述的其他 Keep-Alive、逾時和交握設定。
作為指導的一般建議如下:
- 用戶端和伺服器設定的保持運作間隔應相符。
- 逾時應該至少是保持運作間隔指派值的兩倍。
伺服器組態
設定下列內容:
- ClientTimeoutInterval (預設值:30 秒):用戶端必須傳送訊息以避免伺服器關閉連線的時間範圍。
- HandshakeTimeout (預設值:15 秒):伺服器針對用戶端傳入交握要求逾時所採用的間隔。
- KeepAliveInterval (預設值:15 秒):伺服器將保持運作 Ping 傳送給已連線用戶端所採用的間隔。 請注意,用戶端上也有保持運作間隔設定,該值應符合伺服器的值。
ClientTimeoutInterval 和 HandshakeTimeout 可以增加,而 KeepAliveInterval 可以維持不變。 重要的考量是,如果您變更該值,請確定逾時至少是保持運作間隔值的兩倍,且伺服器和用戶端的保持運作間隔相符。 如需詳細資訊,請參閱在用戶端上設定 SignalR 逾時和 Keep-Alive 一節。
在以下範例中:
- ClientTimeoutInterval 會增加到 60 秒 (預設值:30 秒)。
- HandshakeTimeout 會增加到 30 秒 (預設值:15 秒)。
- KeepAliveInterval 未在開發人員程式代碼中設定 ,並使用其預設值 15 秒。 減少保持運作間隔的值會增加通訊 Ping 的頻率,從而增加應用程式、伺服器和網路上的負載。 請務必謹慎避免降低保持運作間隔所導致的效能不佳。
Blazor Web App(.NET 8 或更新版本)在伺服器專案的 Program
檔案中:
builder.Services.AddRazorComponents().AddInteractiveServerComponents()
.AddHubOptions(options =>
{
options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
});
Program
檔案中的 Blazor Server:
builder.Services.AddServerSideBlazor()
.AddHubOptions(options =>
{
options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
});
如需詳細資訊,請參閱伺服器端線路處理常式選項一節。
用戶端組態
設定下列內容:
withServerTimeout
(預設值:30 秒):為線路中樞連線設定以毫秒為單位的伺服器逾時。withKeepAliveInterval
(預設值:15 秒):連線傳送保持運作訊息的間隔,以毫秒為單位。
伺服器逾時可以增加,而保持運作間隔可以維持不變。 重要的考量是,如果您變更該值,請確定伺服器逾時至少是保持運作間隔值的兩倍,且伺服器和用戶端的保持運作間隔值相符。 如需詳細資訊,請參閱在用戶端上設定 SignalR 逾時和 Keep-Alive 一節。
在下列啟動設定範例 (Blazor 指令碼的位置) 中,伺服器逾時會使用 60 秒的自訂值。 保持運作間隔 (withKeepAliveInterval
) 未設定,並使用其預設值 15 秒。
Blazor Web App:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
circuit: {
configureSignalR: function (builder) {
builder.withServerTimeout(60000);
}
}
});
</script>
Blazor Server:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
configureSignalR: function (builder) {
builder.withServerTimeout(60000);
}
});
</script>
在元件中建立中樞連線時,請在 HubConnectionBuilder 上設定伺服器逾時 (WithServerTimeout,預設值:30 秒)。 在建置 HubConnection 上設定 HandshakeTimeout (預設值:15 秒)。 確認逾時至少是保持運作間隔 (WithKeepAliveInterval/KeepAliveInterval) 的兩倍,且保持運作值在伺服器和用戶端之間相符。
下列範例是以 SignalR 與 Blazor 教學課程中的 Index
元件為基礎。 伺服器逾時值會增加到 60 秒,交握逾時值則會增加到 30 秒。 保持運作間隔未設定,並使用其預設值 15 秒。
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"))
.WithServerTimeout(TimeSpan.FromSeconds(60))
.Build();
hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(30);
hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...
await hubConnection.StartAsync();
}
設定下列內容:
serverTimeoutInMilliseconds
(預設值:30 秒):為線路中樞連線設定以毫秒為單位的伺服器逾時。keepAliveIntervalInMilliseconds
(預設值:15 秒):連線傳送保持運作訊息的間隔,以毫秒為單位。
伺服器逾時可以增加,而保持運作間隔可以維持不變。 重要的考量是,如果您變更該值,請確定伺服器逾時至少是保持運作間隔值的兩倍,且伺服器和用戶端的保持運作間隔值相符。 如需詳細資訊,請參閱在用戶端上設定 SignalR 逾時和 Keep-Alive 一節。
在下列啟動設定範例 (Blazor 指令碼的位置) 中,伺服器逾時會使用 60 秒的自訂值。 保持運作間隔 (keepAliveIntervalInMilliseconds
) 未設定,並使用其預設值 15 秒。
在 Pages/_Host.cshtml
中:
<script src="_framework/blazor.server.js" autostart="false"></script>
<script>
Blazor.start({
configureSignalR: function (builder) {
let c = builder.build();
c.serverTimeoutInMilliseconds = 60000;
builder.build = () => {
return c;
};
}
});
</script>
在元件中建立中樞連線時,請在組建 HubConnection 上設定 ServerTimeout (預設值:30 秒) 和 HandshakeTimeout (預設值:15 秒)。 確認逾時至少是保持運作間隔的兩倍。 確認伺服器與用戶端之間的保持運作間隔相符。
下列範例是以 SignalR 與 Blazor 教學課程中的 Index
元件為基礎。 ServerTimeout 會增加到 60 秒,而 HandshakeTimeout 會增加到 30 秒。 保持運作間隔 (KeepAliveInterval) 未設定,並使用其預設值 15 秒。
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"))
.Build();
hubConnection.ServerTimeout = TimeSpan.FromSeconds(60);
hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(30);
hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...
await hubConnection.StartAsync();
}
中斷用戶端與 Blazor 線路的連線
觸發 unload
頁面事件時,Blazor 電路會中斷。 若要中斷用戶端上其他案例的線路連線,請在適當的事件處理常式中叫用 Blazor.disconnect
。 在下列範例中,當頁面隱藏 (pagehide
事件) 時,線路會中斷連線:
window.addEventListener('pagehide', () => {
Blazor.disconnect();
});
如需關於 Blazor 啟動的詳細資訊,請參閱 ASP.NET Core Blazor 啟動。
伺服器端線路處理常式
您可以定義線路處理常式,以允許在使用者線路狀態發生變更時執行程式碼。 線路處理常式是透過衍生自 CircuitHandler 並將該類別註冊至應用程式服務容器中來實作。 下列線路處理常式範例會追蹤開啟的 SignalR 連線。
TrackingCircuitHandler.cs
:
using Microsoft.AspNetCore.Components.Server.Circuits;
public class TrackingCircuitHandler : CircuitHandler
{
private HashSet<Circuit> circuits = new();
public override Task OnConnectionUpAsync(Circuit circuit,
CancellationToken cancellationToken)
{
circuits.Add(circuit);
return Task.CompletedTask;
}
public override Task OnConnectionDownAsync(Circuit circuit,
CancellationToken cancellationToken)
{
circuits.Remove(circuit);
return Task.CompletedTask;
}
public int ConnectedCircuits => circuits.Count;
}
線路處理常式是使用 DI 註冊。 每個線路執行個體都會建立範圍的執行個體。 使用上述範例中的 TrackingCircuitHandler
建立單一服務,因為必須追蹤所有線路的狀態。
在 Program
檔案中:
builder.Services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();
在 Startup.cs
的 Startup.ConfigureServices
中:
services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();
如果自訂線路處理常式的方法擲回未處理的例外狀況,則此例外狀況會導致線路產生嚴重錯誤。 若要容許處理常式程式碼或呼叫方法中的例外狀況,請使用錯誤處理和記錄將程式碼包裝到一或多個 try-catch
陳述式中。
當線路因使用者已中斷連線而結束,且架構正在清除線路狀態時,架構會處置線路的 DI 範圍。 處置範圍會處置任何實作 System.IDisposable 的線路範圍 DI 服務。 如果任何 DI 服務在處置期間擲回未處理的例外狀況,架構會記錄例外狀況。 如需詳細資訊,請參閱 ASP.NET Core Blazor 相依性插入。
用於為自訂服務擷取使用者的伺服器端線路處理常式
使用 CircuitHandler 從 AuthenticationStateProvider 擷取使用者,並在服務中設定該使用者。 如需詳細資訊和範例程式碼,請參閱伺服器端 ASP.NET Core Blazor 其他安全性案例。
沒有剩餘的互動式伺服器元件時關閉線路
互動式伺服器元件會使用與瀏覽器的即時連線來處理 Web UI 事件,稱為線路。 當轉譯根互動式伺服器元件時,會建立線路及其相關聯的狀態。 當頁面上沒有剩餘的互動式伺服器元件時,線路會關閉並釋放伺服器資源。
Razor 元件中的 IHttpContextAccessor
/HttpContext
IHttpContextAccessor 必須避免使用互動式轉譯,因為沒有有效的 HttpContext
可用。
IHttpContextAccessor 可用於伺服器上靜態轉譯的元件。 不過,建議您盡可能避免。
只能在一般工作的靜態轉譯根元件中將 HttpContext 用作 級聯參數,例如檢查和修改 App
元件 (Components/App.razor
) 中的標頭或其他屬性。 用於互動式轉譯的值一律是 null
。
[CascadingParameter]
public HttpContext? HttpContext { get; set; }
針對互動式元件中所需 HttpContext 的案例,建議您透過伺服器的持續元件狀態來流動資料。 如需詳細資訊,請參閱 伺服器端 ASP.NET Core Blazor的其他安全性案例。
請勿在伺服器端 Blazor 應用程式的 Razor 元件中直接或間接使用 IHttpContextAccessor/HttpContext。 Blazor 應用程式會在 ASP.NET Core 管線內容之外執行。 既不保證 HttpContext 在 IHttpContextAccessor 中可用,也不保證 HttpContext 會保留啟動了 Blazor 應用程式的內容。
建議在 Blazor 應用程式的初始轉譯期間,透過根元件參數將要求狀態傳遞給此應用程式。 或者,應用程式可以將資料複製到根元件初始化生命週期事件中的範圍服務,以便在整個應用程式中使用。 如需詳細資訊,請參閱伺服器端 ASP.NET Core Blazor 其他安全性案例。
伺服器端 Blazor 安全性的一個重要層面是,附加至指定線路的使用者可能會在建立 Blazor 線路後的某個時間點進行更新,但 IHttpContextAccessor不會更新。 如需使用自訂服務解決這種情況的詳細資訊,請參閱伺服器端 ASP.NET Core Blazor 其他安全性案例。