ASP.NET Core BlazorSignalR ガイダンス
注意
これは、この記事の最新バージョンではありません。 最新のバージョンに切り替えるには、目次の上部にある ASP.NET Core バージョン セレクターを使用します。
セレクターが狭いブラウザー ウィンドウに表示されない場合は、ウィンドウの幅を広げるか、垂直省略記号 ([⋮]) >[目次] の順に選択します。
この記事では、Blazor アプリで SignalR 接続を構成および管理する方法について説明します。
ASP.NET Core SignalR の構成の一般的なガイダンスについては、ドキュメントの ASP.NET Core SignalR の概要に関するトピックをご覧ください。 ホストされている Blazor WebAssembly アプリに追加された SignalR を構成する場合 (たとえば、「ASP.NET Core SignalR を Blazor と共に使用する」チュートリアルの場合や、SignalR を使用するスタンドアロン Blazor WebAssembly アプリ) は、ASP.NET Core SignalR の構成に関する記事を参照してください。
ホット リロードの応答圧縮を無効にする
ホット リロードを使用する場合は、Development
環境の応答圧縮ミドルウェアを無効にします。 次の例では、Blazor プロジェクト テンプレートから作成されたプロジェクトで既存の環境チェックを使用しています。 プロジェクト テンプレートからの既定のコードを使用するかどうかに関係なく、要求処理パイプラインで常に最初に UseResponseCompression を呼び出します。
Blazor Server アプリの Program.cs
:
if (!app.Environment.IsDevelopment())
{
app.UseResponseCompression();
app.UseExceptionHandler("/Error");
app.UseHsts();
}
ホストされている Blazor WebAssembly ソリューションの Client プロジェクトの Program.cs
:
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
}
else
{
app.UseResponseCompression();
app.UseExceptionHandler("/Error");
app.UseHsts();
}
認証のための SignalR のクロスオリジン ネゴシエーション (Blazor WebAssembly)
このセクションでは、cookie や HTTP 認証ヘッダーなどの資格情報を送信するように SignalR の基となるクライアントを構成する方法について説明します。
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();
HubConnectionBuilder hubConnection;
...
hubConnection = new HubConnectionBuilder()
.WithUrl(new Uri(Navigation.ToAbsoluteUri("/chathub")), options =>
{
options.HttpMessageHandlerFactory = innerHandler =>
new IncludeRequestCredentialsMessageHandler { InnerHandler = innerHandler };
}).Build();
前の例では、ハブ接続 URL を /chathub
の絶対 URI アドレスに構成しています。これは、Index
コンポーネント (Pages/Index.razor
) の Blazor と SignalR のチュートリアルで使用されている URL です。 URI は、文字列 (https://signalr.example.com
など) または構成を使用して設定することもできます。 Navigation
は挿入された NavigationManager です。
詳しくは、「ASP.NET Core SignalR の構成」をご覧ください。
レンダリング モード (Blazor WebAssembly)
プリレンダリングするために SignalR を使用する Blazor WebAssembly アプリがサーバーに構成されている場合、サーバーへのクライアント接続が確立される前に、プリレンダリングが行われます。 詳細については、次の記事を参照してください。
Blazor WebAssembly アプリに関するその他のリソース
- ホストされた Blazor WebAssembly: SignalR ハブをセキュリティで保護する
- ASP.NET Core のホストと展開Blazor WebAssembly
- ASP.NET Core の概要SignalR
- ASP.NET Core SignalR の構成
- Blazor サンプル GitHub リポジトリ (
dotnet/blazor-samples
)
webfarm ホスティングにスティッキー セッションを使う (Blazor Server)
最初のクライアント要求への応答として、Blazor Server アプリによってプリレンダリングされます。これにより、サーバー上で UI の状態が作成されます。 クライアントで SignalR 接続の作成が再試行される際は、クライアントを同じサーバーに再接続する必要があります。 複数のバックエンド サーバーを使用する Blazor Server アプリでは、SignalR 接続に "スティッキー セッション" を実装する必要があります。
Note
Webfarm でスティッキー セッションを有効にしていないアプリからは次のエラーがスローされます。
blazor.server.js:1 Uncaught (in promise) Error: Invocation canceled due to the underlying connection being closed. (blazor.server.js:1 捕捉されない (promise 内の) エラー: 基となる接続が閉じられたため、呼び出しが取り消されました。)
Azure SignalR Service (Blazor Server)
Microsoft Azure でホストされている Blazor Server アプリには、Azure SignalR Service を使用することをお勧めします。 このサービスはアプリの Blazor Hub と連携して、Blazor Server アプリを多数の SignalR 同時接続にスケールアップします。 さらに、SignalR Service のグローバル リーチとハイパフォーマンスのデータ センターは、地理的条件による待機時間の短縮に役立ちます。
Azure SignalR サービスでは、サービスの ServerStickyMode
オプションまたは構成値を Required
に設定することにより、スティッキー セッションが有効になります。 詳細については、「ASP.NET Core Blazor Server のホストと展開」を参照してください。
Blazor Server アプリの回線ハンドラーのオプション
次の表に示す CircuitOptions を使用して Blazor Server の回線を構成してください。
オプション | Default | 説明 |
---|---|---|
DetailedErrors | false |
回線でハンドルされない例外が発生した場合、または JS 相互運用機能を介した .NET メソッドの呼び出しの結果として例外が発生した場合に、詳細な例外メッセージを JavaScript に送信します。 |
DisconnectedCircuitMaxRetained | 100 | サーバーが一度にメモリに保持する切断された回線の最大数。 |
DisconnectedCircuitRetentionPeriod | 3 分 | 切断された回線が破棄されるまでにメモリに保持される最大時間。 |
JSInteropDefaultCallTimeout | 1 分 | 非同期の JavaScript 関数呼び出しがタイムアウトするまでにサーバーが待機する最大時間。 |
MaxBufferedUnacknowledgedRenderBatches | 10 | 堅牢な再接続をサポートするために、サーバーが一定期間、メモリに保持する回線あたりの未確認のレンダリング バッチの最大数。 制限に達すると、クライアントによって 1 つ以上のバッチが確認されるまで、サーバーにより新しいレンダリング バッチの生成が停止されます。 |
オプションの AddServerSideBlazor へのデリゲートを使用して Program.cs
のオプションを構成します。 次の例では、前の表に示した既定のオプション値を割り当てます。 Program.cs
で System 名前空間が使用されていることを確認します (using System;
)。
Program.cs
の場合:
builder.Services.AddServerSideBlazor(options =>
{
options.DetailedErrors = false;
options.DisconnectedCircuitMaxRetained = 100;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(3);
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(1);
options.MaxBufferedUnacknowledgedRenderBatches = 10;
});
オプションの AddServerSideBlazor へのデリゲートを使用して Startup.ConfigureServices
のオプションを構成します。 次の例では、前の表に示した既定のオプション値を割り当てます。 Startup.cs
で System 名前空間が使用されていることを確認します (using System;
)。
Startup.ConfigureServices
の場合:
services.AddServerSideBlazor(options =>
{
options.DetailedErrors = false;
options.DisconnectedCircuitMaxRetained = 100;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(3);
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(1);
options.MaxBufferedUnacknowledgedRenderBatches = 10;
});
HubConnectionContext を構成するには、HubConnectionContextOptions と共に AddHubOptions を使用します。 オプションの説明については、「ASP.NET Core SignalR の構成」をご覧ください。 次の例では、既定のオプション値を割り当てます。 ファイルで System 名前空間 (using System;
) が使われていることを確認します。
Program.cs
:
builder.Services.AddServerSideBlazor()
.AddHubOptions(options =>
{
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
options.EnableDetailedErrors = false;
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.MaximumParallelInvocationsPerClient = 1;
options.MaximumReceiveMessageSize = 32 * 1024;
options.StreamBufferCapacity = 10;
});
Startup.ConfigureServices
の場合:
services.AddServerSideBlazor()
.AddHubOptions(options =>
{
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
options.EnableDetailedErrors = false;
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.MaximumParallelInvocationsPerClient = 1;
options.MaximumReceiveMessageSize = 32 * 1024;
options.StreamBufferCapacity = 10;
});
警告
MaximumReceiveMessageSize の既定値は 32 KB です。 この値を大きくすると、サービス拒否 (DoS) 攻撃のリスクが高まるおそれがあります。
Blazor Server のメモリ モデルに関する詳細については、「ASP.NET Core Blazor Server のホストと展開」を参照してください。
Blazor Hub のエンドポイント ルートの構成 (Blazor Server)
Program.cs
で、Blazor Server アプリによって MapBlazorHub が呼び出され、BlazorHub がアプリの既定のパスにマップされます。 Blazor Server スクリプト (blazor.server.js
) は、MapBlazorHub によって作成されたエンドポイントを自動的に指します。
UI に接続状態を反映する (Blazor Server)
接続が失われたことがクライアントで検出されると、クライアントによって再接続が試行される間、ユーザーに対して既定の UI が表示されます。 再接続に失敗した場合、ユーザーには再試行のオプションが表示されます。
UI をカスタマイズするには、components-reconnect-modal
の id
を持つ単一の要素を定義します。 次の例では、ホスト ページに要素を配置します。
Pages/_Host.cshtml
:
UI をカスタマイズするには、components-reconnect-modal
の id
を持つ単一の要素を定義します。 次の例では、レイアウト ページに要素を配置します。
Pages/_Layout.cshtml
:
UI をカスタマイズするには、components-reconnect-modal
の id
を持つ単一の要素を定義します。 次の例では、ホスト ページに要素を配置します。
Pages/_Host.cshtml
:
<div id="components-reconnect-modal">
There was a problem with the connection!
</div>
注意
components-reconnect-modal
の id
を持つ複数の要素がアプリによってレンダリングされる場合、最初にレンダリングされた要素のみが CSS クラスの変更を受け取り、要素を表示または非表示にします。
次の 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
プロパティを設定して、再接続表示が表示されるまでの遅延時間をカスタマイズします。 次の例では、移行遅延時間を 500 ms (既定値) から 1,000 ms (1 秒) に設定しています。
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)
レンダリング モード (Blazor Server)
既定では、サーバーへのクライアント接続が確立される前に、Blazor Server アプリによってサーバー上の UI がプリレンダリングされます。 詳細については、「ASP.NET Core のコンポーネント タグ ヘルパー」を参照してください。
回線アクティビティを監視する (Blazor Server)
CircuitHandler 上で CreateInboundActivityHandler
メソッドを使って、Blazor Server アプリの受信回線アクティビティを監視します。 受信回線アクティビティとは、UI イベントや JavaScript から .NET への相互運用呼び出しなど、ブラウザーからサーバーに送信されるすべてのアクティビティです。
たとえば、回線アクティビティ ハンドラーを使って、クライアントがアイドル状態かどうかを検出できます。
public sealed class IdleCircuitHandler : CircuitHandler, IDisposable
{
readonly Timer timer;
readonly ILogger logger;
public IdleCircuitHandler(IOptions<IdleCircuitOptions> options,
ILogger<IdleCircuitHandler> logger)
{
timer = new Timer();
timer.Interval = options.Value.IdleTimeout.TotalMilliseconds;
timer.AutoReset = false;
timer.Elapsed += CircuitIdle;
this.logger = logger;
}
private void CircuitIdle(object? sender, System.Timers.ElapsedEventArgs e)
{
logger.LogInformation("{Circuit} is idle", nameof(CircuitIdle));
}
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;
}
}
また、回線アクティビティ ハンドラーには、他の Blazor ではない依存関係の挿入 (DI) スコープからスコープ付き Blazor サービスにアクセスするためのアプローチも用意されています。 詳細および例については、次をご覧ください。
Blazor の起動
Pages/_Host.cshtml
ファイル (Blazor Server、ASP.NET Core 6.0 を除くすべてのバージョン)、Pages/_Layout.cshtml
ファイル (Blazor Server、ASP.NET Core 6.0)、または wwwroot/index.html
(SignalR が実装済みで Blazor WebAssembly がホストされている) で、Blazor アプリの SignalR 回路の手動による開始を構成します。
blazor.{server|webassembly}.js
スクリプトの<script>
タグにautostart="false"
属性を追加します。Blazor.start()
を呼び出すスクリプトを、Blazor スクリプトが読み込まれた後の終了</body>
タグ内に配置します。
autostart
が無効になっている場合、回線に依存しないアプリのすべての側面が正常に動作します。 たとえば、クライアント側のルーティングは動作します。 ただし、回線に依存する側面はすべて、Blazor.start()
が呼び出されるまで動作しません。 回線が確立されていなければ、アプリの動作は予測不可能です。 たとえば、回線が切断されている間、コンポーネント メソッドは実行できません。
ドキュメントの準備が完了したときに Blazor を初期化する方法や JS Promise
に連結する方法を含む詳細については、ASP.NET Core Blazor の起動に関する記事を参照してください。
クライアントで SignalR タイムアウトと Keep-Alive を構成する
クライアントに対して次の値を構成します。
serverTimeoutInMilliseconds
: サーバーのタイムアウト (ミリ秒単位)。 サーバーからメッセージを受信せずにこのタイムアウトが経過すると、接続はエラーで終了します。 タイムアウトの既定値は 30 秒です。 サーバー タイムアウトは、Keep-Alive 間隔 (keepAliveIntervalInMilliseconds
) に割り当てられた値の少なくとも 2 倍にする必要があります。keepAliveIntervalInMilliseconds
: サーバーに ping を実行する既定の間隔。 この設定により、サーバーでハードの切断を検出できます。たとえば、クライアントがコンピューターをネットワークから取り外したときなどです。 ping は、最大でサーバーの ping と同じ頻度で発生します。 サーバーが 5 秒ごとに ping を実行する場合、5000
(5 秒) 未満の値を割り当てると 5 秒ごとに ping が実行されます。 既定値は 15 秒です。 Keep-Alive 間隔は、サーバー タイムアウト (serverTimeoutInMilliseconds
) に割り当てられた値の半分以下にする必要があります。
Pages/_Host.cshtml
ファイル (Blazor Server、ASP.NET Core 6.0 を除くすべてのバージョン)、Pages/_Layout.cshtml
ファイル (Blazor Server、ASP.NET Core 6.0)、または wwwroot/index.html
(Blazor WebAssembly) の次の例では既定値が使用されます。
<script src="_framework/blazor.{HOSTING MODEL}.js" autostart="false"></script>
<script>
Blazor.start({
configureSignalR: function (builder) {
let c = builder.build();
c.serverTimeoutInMilliseconds = 30000;
c.keepAliveIntervalInMilliseconds = 15000;
builder.build = () => {
return c;
};
}
});
</script>
前記のマークアップの {HOSTING MODEL}
プレースホルダーは、server
(Blazor Server アプリの場合) または webassembly
(Blazor WebAssembly アプリの場合) です。
コンポーネントでハブ接続を作成する場合は、ビルドされた HubConnection に ServerTimeout (既定値: 30 秒)、HandshakeTimeout (既定値: 15 秒)、KeepAliveInterval (既定値: 15 秒) を設定します。 次の例は、Blazor を使用した SignalR のチュートリアルの Index
コンポーネントに基づいており、既定値を使用しています。
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 間隔に割り当てられた値の少なくとも 2 倍にする必要があります。
- Keep-Alive 間隔は、サーバー タイムアウトに割り当てられた値の半分以下にする必要があります。
詳細については、以下の記事の「グローバル展開と接続エラー」のセクションを参照してください。
再接続ハンドラーを変更する (Blazor Server)
再接続ハンドラーの回線接続イベントは、次のようなカスタム動作を行うように変更できます。
- 接続が切断された場合にユーザーに通知する。
- 回線が接続されているときに (クライアントから) ログ記録を実行する。
接続イベントを変更するには、次の接続の変更に対してコールバックを登録します。
- 切断された接続では、
onConnectionDown
が使用されます。 - 確立または再確立された接続では、
onConnectionUp
が使用されます。
onConnectionDown
と onConnectionUp
の両方を指定する必要があります。
Pages/_Host.cshtml
:
Pages/_Layout.cshtml
:
Pages/_Host.cshtml
:
<body>
...
<script src="_framework/blazor.server.js" autostart="false"></script>
<script>
Blazor.start({
reconnectionHandler: {
onConnectionDown: (options, error) => console.error(error),
onConnectionUp: () => console.log("Up, up, and away!")
}
});
</script>
</body>
再接続に失敗したときにページを自動的に更新する (Blazor Server)
既定の再接続動作では、再接続が失敗した後にページを更新するための手動アクションをユーザーが行う必要があります。 ただし、カスタム再接続ハンドラーを使用すると、ページを自動的に更新できます。
Pages/_Host.cshtml
:
<body>
...
<div id="reconnect-modal" style="display: none;"></div>
<script src="_framework/blazor.server.js" autostart="false"></script>
<script src="boot.js"></script>
</body>
wwwroot/boot.js
:
(() => {
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 の起動」をご覧ください。
再接続の再試行回数と間隔を調整する (Blazor Server)
再接続の再試行の回数と間隔を調整するには、再試行の回数 (maxRetries
) と、各再試行で許可されるミリ秒単位の期間 (retryIntervalMilliseconds
) を設定します。
Pages/_Host.cshtml
:
Pages/_Layout.cshtml
:
Pages/_Host.cshtml
:
<body>
...
<script src="_framework/blazor.server.js" autostart="false"></script>
<script>
Blazor.start({
reconnectionOptions: {
maxRetries: 3,
retryIntervalMilliseconds: 2000
}
});
</script>
</body>
Blazor の起動について詳しくは、「ASP.NET Core Blazor の起動」をご覧ください。
クライアントから Blazor 回線を切断する (Blazor Server)
既定では、unload
ページ イベントがトリガーされると、Blazor 回線が切断されます。 クライアント上の他のシナリオで回線を切断するには、適切なイベント ハンドラーで Blazor.disconnect
を呼び出します。 次の例では、ページが非表示になると、回線が切断されます (pagehide
イベント)。
window.addEventListener('pagehide', () => {
Blazor.disconnect();
});
Blazor の起動について詳しくは、「ASP.NET Core Blazor の起動」をご覧ください。
Blazor Server 回線ハンドラー
Blazor Server を使用すると、コードで "回線ハンドラー" を定義できます。これにより、ユーザーの回線の状態の変更時にコードを実行できます。 回線ハンドラーは、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.cs
:
builder.Services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();
Startup.cs
の Startup.ConfigureServices
で:
services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();
カスタム回線ハンドラーのメソッドでハンドルされない例外がスローされる場合は、その例外は Blazor Server 回線にとって致命的です。 ハンドラーのコードまたはメソッドで例外が許容されるようにするには、エラー処理とログを含む 1 つ以上の try-catch
ステートメントでコードをラップします。
ユーザーが切断し、フレームワークで回線の状態がクリーンアップされていることが原因で回線が終了すると、フレームワークによって回線の DI スコープが破棄されます。 スコープが破棄されると、System.IDisposable を実装するサーキットスコープの DI サービスはすべて破棄されます。 破棄中にいずれかの DI サービスでハンドルされない例外がスローされると、フレームワークによって例外がログに記録されます。 詳細については、「ASP.NET Core Blazor の依存関係の挿入」を参照してください。
カスタム サービスのユーザーをキャプチャするための Blazor Server 回線ハンドラー
AuthenticationStateProvider からユーザーをキャプチャして、サービスでそのユーザーを設定するには、CircuitHandler を使います。 詳細とコード例については、「ASP.NET Core Blazor Server のその他のセキュリティ シナリオ」をご覧ください。
Razor コンポーネントでは IHttpContextAccessor
/HttpContext
を使わない
Blazor Server アプリの Razor コンポーネントでは、IHttpContextAccessor/HttpContext を直接または間接的に使わないでください。 Blazor アプリは、ASP.NET Core パイプラインのコンテキストの外部で実行されます。 HttpContext は、IHttpContextAccessor 内で使用できるとは限りません。また、HttpContext は、Blazor アプリを開始したコンテキストが保持されることも保証されません。
アプリの初期レンダリング中にルート コンポーネント パラメーターを使って要求の状態を Blazor アプリに渡すことをお勧めします。 または、ルート コンポーネントの初期化ライフサイクル イベントにおいてアプリでスコープ サービスにデータをコピーすることで、アプリ全体で使用することもできます。 詳細については、「ASP.NET Core Blazor Server のその他のセキュリティ シナリオ」を参照してください。
Blazor Server のセキュリティの重要な側面は、特定の回線に接続されているユーザーは Blazor 回線が確立された後のある時点で更新される可能性がありますが、IHttpContextAccessor は "更新されない" ということです。 カスタム サービスでこの状況に対処する方法について詳しくは、「ASP.NET Core Blazor Server のセキュリティに関するその他のシナリオ」をご覧ください。
Blazor Server アプリに関するその他のリソース
- Blazor Server ホストとデプロイのガイダンス: SignalR の構成
- ASP.NET Core の概要SignalR
- ASP.NET Core SignalR の構成
- Blazor Server のセキュリティに関するドキュメント
- Blazor Server 再接続イベントとコンポーネント ライフサイクル イベント
- Azure SignalR Service とは
- Azure SignalR Service のパフォーマンス ガイド
- Azure App Service に ASP.NET Core SignalR アプリを発行する
- Blazor サンプル GitHub リポジトリ (
dotnet/blazor-samples
)