ASP.NET Core BlazorSignalR ガイダンス

この記事では、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);
        }
    }
    
  • ハブ接続が構築されている場合は、HttpMessageHandlerHttpMessageHandlerFactory オプションに割り当てます。

    private 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 アプリに関するその他のリソース

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.csSystem 名前空間が使用されていることを確認します (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;
});

HubConnectionContext を構成するには、HubConnectionContextOptions と共に AddHubOptions を使用します。 オプションの説明については、「ASP.NET Core SignalR の構成」をご覧ください。 次の例では、既定のオプション値を割り当てます。 Program.csSystem 名前空間が使用されていることを確認します (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;
    });

警告

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-modalid を持つ単一の要素を定義します。 次の例では、ホスト ページに要素を配置します。

Pages/_Host.cshtml:

<div id="components-reconnect-modal">
    There was a problem with the connection!
</div>

注意

components-reconnect-modalid を持つ複数の要素がアプリによってレンダリングされる場合、最初にレンダリングされた要素のみが 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-attemptid を使って要素を定義します。 再接続の再試行の最大数を表示するには、components-reconnect-max-retriesid を使って要素を定義します。 次の例では、前の例に従って、これらの要素をホスト ページの再接続試行モーダル要素内に配置します。

Pages/_Host.cshtml:

<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 の起動

Pages/_Host.cshtml ファイル (Blazor Server) または 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) または 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 アプリの場合) です。

コンポーネントでハブ接続を作成する場合は、ビルドされた HubConnectionServerTimeout (既定値: 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 が使用されます。

onConnectionDownonConnectionUp の両方を指定する必要があります。

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:

<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>();

カスタム回線ハンドラーのメソッドでハンドルされない例外がスローされる場合は、その例外は Blazor Server 回線にとって致命的です。 ハンドラーのコードまたはメソッドで例外が許容されるようにするには、エラー処理とログを含む 1 つ以上の try-catch ステートメントでコードをラップします。

ユーザーが切断し、フレームワークで回線の状態がクリーンアップされていることが原因で回線が終了すると、フレームワークによって回線の DI スコープが破棄されます。 スコープが破棄されると、System.IDisposable を実装するサーキットスコープの DI サービスはすべて破棄されます。 破棄中にいずれかの DI サービスでハンドルされない例外がスローされると、フレームワークによって例外がログに記録されます。 詳細については、「ASP.NET Core Blazor の依存関係の挿入」を参照してください。

Razor コンポーネントでは IHttpContextAccessor を回避する

Blazor Server アプリの Razor コンポーネントでは IHttpContextAccessor を使用しないでください。 Blazor アプリは、ASP.NET Core パイプラインのコンテキストの外部で実行されます。 HttpContext は、IHttpContextAccessor 内で使用できるとは限りません。また、HttpContext は、Blazor アプリを開始したコンテキストが保持されることも保証されません。 詳細については、「Blazor Server で IHttpContextAccessor を使用することによるセキュリティへの影響 (dotnet/aspnetcore #45699)」を参照してください。 Blazor Server アプリでユーザーの状態を維持する方法の詳細については、「ASP.NET Core Blazor 状態管理」を参照してください。

Blazor Server アプリに関するその他のリソース

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);
        }
    }
    
  • ハブ接続が構築されている場合は、HttpMessageHandlerHttpMessageHandlerFactory オプションに割り当てます。

    private 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 です。

注意

SignalR ハブのユーザーを認証するには、セキュリティで保護された ASP.NET CoreBlazor WebAssembly に関するページを参照してください。

詳しくは、「ASP.NET Core SignalR の構成」をご覧ください。

レンダリング モード (Blazor WebAssembly)

プリレンダリングするために SignalR を使用する Blazor WebAssembly アプリがサーバーに構成されている場合、サーバーへのクライアント接続が確立される前に、プリレンダリングが行われます。 詳細については、次の記事を参照してください。

Blazor WebAssembly アプリに関するその他のリソース

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.csSystem 名前空間が使用されていることを確認します (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;
});

HubConnectionContext を構成するには、HubConnectionContextOptions と共に AddHubOptions を使用します。 オプションの説明については、「ASP.NET Core SignalR の構成」をご覧ください。 次の例では、既定のオプション値を割り当てます。 Program.csSystem 名前空間が使用されていることを確認します (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;
    });

警告

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-modalid を持つ単一の要素を定義します。 次の例では、レイアウト ページに要素を配置します。

Pages/_Layout.cshtml:

<div id="components-reconnect-modal">
    There was a problem with the connection!
</div>

注意

components-reconnect-modalid を持つ複数の要素がアプリによってレンダリングされる場合、最初にレンダリングされた要素のみが 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-attemptid を使って要素を定義します。 再接続の再試行の最大数を表示するには、components-reconnect-max-retriesid を使って要素を定義します。 次の例では、前の例に従って、これらの要素をレイアウト ページの再接続試行モーダル要素内に配置します。

Pages/_Layout.cshtml:

<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 の起動

Pages/_Layout.cshtml ファイル (Blazor Server) または 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 を構成する

Blazor Server ハブ

このセクションは Blazor Server アプリにのみ適用されます。

クライアント上の Blazor Server ハブ接続に対して次の値を構成します。

  • serverTimeoutInMilliseconds: サーバーのタイムアウト (ミリ秒単位)。 サーバーからメッセージを受信せずにこのタイムアウトが経過すると、接続はエラーで終了します。 タイムアウトの既定値は 30 秒です。 サーバー タイムアウトは、Keep-Alive 間隔 (keepAliveIntervalInMilliseconds) に割り当てられた値の少なくとも 2 倍にする必要があります。
  • keepAliveIntervalInMilliseconds: サーバーに ping を実行する既定の間隔。 この設定により、サーバーでハードの切断を検出できます。たとえば、クライアントがコンピューターをネットワークから取り外したときなどです。 ping は、最大でサーバーの ping と同じ頻度で発生します。 サーバーが 5 秒ごとに ping を実行する場合、5000 (5 秒) 未満の値を割り当てると 5 秒ごとに ping が実行されます。 既定値は 15 秒です。 Keep-Alive 間隔は、サーバー タイムアウト (serverTimeoutInMilliseconds) に割り当てられた値の半分以下にする必要があります。

Pages/_Layout.cshtml (Blazor Server) または wwwroot/index.html (Blazor WebAssembly) に対する次の例では、既定値が使用されます。

<script src="_framework/blazor.server.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>

Razor コンポーネントで作成されたハブ接続

このセクションは、Blazor Server コンポーネントと、ホストされている Blazor WebAssembly ソリューションの Client プロジェクト内のコンポーネントにのみ適用されます。

コンポーネントでハブ接続を作成する場合は、ビルドされた HubConnectionServerTimeout (既定値: 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 が使用されます。

onConnectionDownonConnectionUp の両方を指定する必要があります。

Pages/_Layout.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 の起動について詳しくは、「ASP.NET Core Blazor の起動」をご覧ください。

再接続の再試行回数と間隔を調整する (Blazor Server)

再接続の再試行の回数と間隔を調整するには、再試行の回数 (maxRetries) と、各再試行で許可されるミリ秒単位の期間 (retryIntervalMilliseconds) を設定します。

Pages/_Layout.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>();

カスタム回線ハンドラーのメソッドでハンドルされない例外がスローされる場合は、その例外は Blazor Server 回線にとって致命的です。 ハンドラーのコードまたはメソッドで例外が許容されるようにするには、エラー処理とログを含む 1 つ以上の try-catch ステートメントでコードをラップします。

ユーザーが切断し、フレームワークで回線の状態がクリーンアップされていることが原因で回線が終了すると、フレームワークによって回線の DI スコープが破棄されます。 スコープが破棄されると、System.IDisposable を実装するサーキットスコープの DI サービスはすべて破棄されます。 破棄中にいずれかの DI サービスでハンドルされない例外がスローされると、フレームワークによって例外がログに記録されます。 詳細については、「ASP.NET Core Blazor の依存関係の挿入」を参照してください。

Razor コンポーネントでは IHttpContextAccessor を回避する

Blazor Server アプリの Razor コンポーネントでは IHttpContextAccessor を使用しないでください。 Blazor アプリは、ASP.NET Core パイプラインのコンテキストの外部で実行されます。 HttpContext は、IHttpContextAccessor 内で使用できるとは限りません。また、HttpContext は、Blazor アプリを開始したコンテキストが保持されることも保証されません。 詳細については、「Blazor Server で IHttpContextAccessor を使用することによるセキュリティへの影響 (dotnet/aspnetcore #45699)」を参照してください。 Blazor Server アプリでユーザーの状態を維持する方法の詳細については、「ASP.NET Core Blazor 状態管理」を参照してください。

Blazor Server アプリに関するその他のリソース

ASP.NET Core SignalR の構成の一般的なガイダンスについては、ドキュメントの ASP.NET Core SignalR の概要に関するトピックをご覧ください。 ホストされている Blazor WebAssembly アプリに追加された SignalR を構成する場合 (たとえば、「ASP.NET Core SignalR を Blazor と共に使用する」チュートリアルの場合や、SignalR を使用するスタンドアロン Blazor WebAssembly アプリ) は、ASP.NET Core SignalR の構成に関する記事を参照してください。

認証のための 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);
        }
    }
    
  • ハブ接続が構築されている場合は、HttpMessageHandlerHttpMessageHandlerFactory オプションに割り当てます。

    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 です。

注意

SignalR ハブのユーザーを認証するには、セキュリティで保護された ASP.NET CoreBlazor WebAssembly に関するページを参照してください。

詳しくは、「ASP.NET Core SignalR の構成」をご覧ください。

レンダリング モード (Blazor WebAssembly)

プリレンダリングするために SignalR を使用する Blazor WebAssembly アプリがサーバーに構成されている場合、サーバーへのクライアント接続が確立される前に、プリレンダリングが行われます。 詳細については、次の記事を参照してください。

Blazor WebAssembly アプリに関するその他のリソース

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 Service でのプリレンダリング サポートについては、"スティッキー セッション" を使用するようにアプリを構成します。 詳細については、「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 へのデリゲートを使用して Startup.ConfigureServices のオプションを構成します。 次の例では、前の表に示した既定のオプション値を割り当てます。 Startup.csSystem 名前空間が使用されていることを確認します (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 の構成」をご覧ください。 次の例では、既定のオプション値を割り当てます。 Startup.csSystem 名前空間が使用されていることを確認します (using System;)。

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)

Startup.Configure で、Blazor Server アプリによって UseEndpointsIEndpointRouteBuilderMapBlazorHub が呼び出されて、BlazorHub がアプリの既定のパスにマップされます。 Blazor Server スクリプト (blazor.server.js) は、MapBlazorHub によって作成されたエンドポイントを自動的に指します。

UI に接続状態を反映する (Blazor Server)

接続が失われたことがクライアントで検出されると、クライアントによって再接続が試行される間、ユーザーに対して既定の UI が表示されます。 再接続に失敗した場合、ユーザーには再試行のオプションが表示されます。

UI をカスタマイズするには、components-reconnect-modalid を持つ単一の要素を定義します。 次の例では、ホスト ページに要素を配置します。

Pages/_Host.cshtml:

<div id="components-reconnect-modal">
    There was a problem with the connection!
</div>

注意

components-reconnect-modalid を持つ複数の要素がアプリによってレンダリングされる場合、最初にレンダリングされた要素のみが 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-attemptid を使って要素を定義します。 再接続の再試行の最大数を表示するには、components-reconnect-max-retriesid を使って要素を定義します。 次の例では、前の例に従って、これらの要素をホスト ページの再接続試行モーダル要素内に配置します。

Pages/_Host.cshtml:

<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 の起動

Pages/_Host.cshtml ファイル (Blazor Server) または 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 を構成する

Blazor Server ハブ

このセクションは Blazor Server アプリにのみ適用されます。

クライアント上の Blazor Server ハブ接続に対して次の値を構成します。

  • 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) または wwwroot/index.html (Blazor WebAssembly) に対する次の例では、既定値が使用されます。

<script src="_framework/blazor.server.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>

Razor コンポーネントで作成されたハブ接続

このセクションは、Blazor Server コンポーネントと、ホストされている Blazor WebAssembly ソリューションの Client プロジェクト内のコンポーネントにのみ適用されます。

コンポーネントでハブ接続を作成する場合は、ビルドされた HubConnectionServerTimeout (既定値: 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 が使用されます。

onConnectionDownonConnectionUp の両方を指定する必要があります。

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 の起動について詳しくは、「ASP.NET Core Blazor の起動」をご覧ください。

再接続の再試行回数と間隔を調整する (Blazor Server)

再接続の再試行の回数と間隔を調整するには、再試行の回数 (maxRetries) と、各再試行で許可されるミリ秒単位の期間 (retryIntervalMilliseconds) を設定します。

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 System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
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 を使用すると、すべての回線の状態を追跡する必要があるため、シングルトン サービスが作成されます。

Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();
}

カスタム回線ハンドラーのメソッドでハンドルされない例外がスローされる場合は、その例外は Blazor Server 回線にとって致命的です。 ハンドラーのコードまたはメソッドで例外が許容されるようにするには、エラー処理とログを含む 1 つ以上の try-catch ステートメントでコードをラップします。

ユーザーが切断し、フレームワークで回線の状態がクリーンアップされていることが原因で回線が終了すると、フレームワークによって回線の DI スコープが破棄されます。 スコープが破棄されると、System.IDisposable を実装するサーキットスコープの DI サービスはすべて破棄されます。 破棄中にいずれかの DI サービスでハンドルされない例外がスローされると、フレームワークによって例外がログに記録されます。 詳細については、「ASP.NET Core Blazor の依存関係の挿入」を参照してください。

Razor コンポーネントでは IHttpContextAccessor を回避する

Blazor Server アプリの Razor コンポーネントでは IHttpContextAccessor を使用しないでください。 Blazor アプリは、ASP.NET Core パイプラインのコンテキストの外部で実行されます。 HttpContext は、IHttpContextAccessor 内で使用できるとは限りません。また、HttpContext は、Blazor アプリを開始したコンテキストが保持されることも保証されません。 詳細については、「Blazor Server で IHttpContextAccessor を使用することによるセキュリティへの影響 (dotnet/aspnetcore #45699)」を参照してください。 Blazor Server アプリでユーザーの状態を維持する方法の詳細については、「ASP.NET Core Blazor 状態管理」を参照してください。

Blazor Server アプリに関するその他のリソース

ASP.NET Core SignalR の構成の一般的なガイダンスについては、ドキュメントの ASP.NET Core SignalR の概要に関するトピックをご覧ください。 ホストされている Blazor WebAssembly アプリに追加された SignalR を構成する場合 (たとえば、「ASP.NET Core SignalR を Blazor と共に使用する」チュートリアルの場合や、SignalR を使用するスタンドアロン Blazor WebAssembly アプリ) は、ASP.NET Core SignalR の構成に関する記事を参照してください。

認証のための 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);
        }
    }
    
  • ハブ接続が構築されている場合は、HttpMessageHandlerHttpMessageHandlerFactory オプションに割り当てます。

    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 です。

注意

SignalR ハブのユーザーを認証するには、セキュリティで保護された ASP.NET CoreBlazor WebAssembly に関するページを参照してください。

詳しくは、「ASP.NET Core SignalR の構成」をご覧ください。

Blazor WebAssembly アプリに関するその他のリソース

webfarm ホスティングにスティッキー セッションを使う (Blazor Server)

最初のクライアント要求への応答として、Blazor Server アプリによってプリレンダリングされます。これにより、サーバー上で UI の状態が作成されます。 クライアントで SignalR 接続の作成が再試行される際は、クライアントを同じサーバーに再接続する必要があります。 複数のバックエンド サーバーを使用する Blazor Server アプリでは、SignalR 接続に "スティッキー セッション" を実装する必要があります。 詳細については、「ASP.NET Core Blazor Server のホストと展開」を参照してください。

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 Service でのプリレンダリング サポートについては、"スティッキー セッション" を使用するようにアプリを構成します。 詳細については、「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 へのデリゲートを使用して Startup.ConfigureServices のオプションを構成します。 次の例では、前の表に示した既定のオプション値を割り当てます。 Startup.csSystem 名前空間が使用されていることを確認します (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 の構成」をご覧ください。 次の例では、既定のオプション値を割り当てます。 Startup.csSystem 名前空間が使用されていることを確認します (using System;)。

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)

Startup.Configure で、Blazor Server アプリによって UseEndpointsIEndpointRouteBuilderMapBlazorHub が呼び出されて、BlazorHub がアプリの既定のパスにマップされます。 Blazor Server スクリプト (blazor.server.js) は、MapBlazorHub によって作成されたエンドポイントを自動的に指します。

UI に接続状態を反映する (Blazor Server)

接続が失われたことがクライアントで検出されると、クライアントによって再接続が試行される間、ユーザーに対して既定の UI が表示されます。 再接続に失敗した場合、ユーザーには再試行のオプションが表示されます。

UI をカスタマイズするには、components-reconnect-modalid を持つ単一の要素を定義します。 次の例では、ホスト ページに要素を配置します。

Pages/_Host.cshtml:

<div id="components-reconnect-modal">
    There was a problem with the connection!
</div>

注意

components-reconnect-modalid を持つ複数の要素がアプリによってレンダリングされる場合、最初にレンダリングされた要素のみが 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() を呼び出します。 この接続状態は、次の場合に発生する可能性があります。
  • サーバー側回線でクラッシュが発生した場合。
  • クライアントが長時間切断されているため、サーバーでユーザーの状態が削除された場合。 ユーザーのコンポーネントのインスタンスは破棄されます。
  • サーバーが再起動されたか、アプリのワーカー プロセスがリサイクルされた場合。

レンダリング モード (Blazor Server)

既定では、サーバーへのクライアント接続が確立される前に、Blazor Server アプリによってサーバー上の UI がプリレンダリングされます。 詳細については、「ASP.NET Core のコンポーネント タグ ヘルパー」を参照してください。

Blazor の起動

Pages/_Host.cshtml ファイル (Blazor Server) または 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 を構成する

Blazor Server ハブ

このセクションは Blazor Server アプリにのみ適用されます。

クライアント上の Blazor Server ハブ接続に対して次の値を構成します。

  • 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) または wwwroot/index.html (Blazor WebAssembly) に対する次の例では、既定値が使用されます。

<script src="_framework/blazor.server.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>

Razor コンポーネントで作成されたハブ接続

このセクションは、Blazor Server コンポーネントと、ホストされている Blazor WebAssembly ソリューションの Client プロジェクト内のコンポーネントにのみ適用されます。

コンポーネントでハブ接続を作成する場合は、ビルドされた HubConnectionServerTimeout (既定値: 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 が使用されます。

onConnectionDownonConnectionUp の両方を指定する必要があります。

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 の起動について詳しくは、「ASP.NET Core Blazor の起動」をご覧ください。

再接続の再試行回数と間隔を調整する (Blazor Server)

再接続の再試行の回数と間隔を調整するには、再試行の回数 (maxRetries) と、各再試行で許可されるミリ秒単位の期間 (retryIntervalMilliseconds) を設定します。

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 Server 回線ハンドラー

Blazor Server を使用すると、コードで "回線ハンドラー" を定義できます。これにより、ユーザーの回線の状態の変更時にコードを実行できます。 回線ハンドラーは、CircuitHandler から派生させ、そのクラスをアプリのサービス コンテナーに登録することで実装します。 次の回線ハンドラーの例では、開いている SignalR 接続を追跡します。

TrackingCircuitHandler.cs:

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Server.Circuits;

public class TrackingCircuitHandler : CircuitHandler
{
    private HashSet<Circuit> circuits = new HashSet<Circuit>();

    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 を使用すると、すべての回線の状態を追跡する必要があるため、シングルトン サービスが作成されます。

Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();
}

カスタム回線ハンドラーのメソッドでハンドルされない例外がスローされる場合は、その例外は Blazor Server 回線にとって致命的です。 ハンドラーのコードまたはメソッドで例外が許容されるようにするには、エラー処理とログを含む 1 つ以上の try-catch ステートメントでコードをラップします。

ユーザーが切断し、フレームワークで回線の状態がクリーンアップされていることが原因で回線が終了すると、フレームワークによって回線の DI スコープが破棄されます。 スコープが破棄されると、System.IDisposable を実装するサーキットスコープの DI サービスはすべて破棄されます。 破棄中にいずれかの DI サービスでハンドルされない例外がスローされると、フレームワークによって例外がログに記録されます。 詳細については、「ASP.NET Core Blazor の依存関係の挿入」を参照してください。

Razor コンポーネントでは IHttpContextAccessor を回避する

Blazor Server アプリの Razor コンポーネントでは IHttpContextAccessor を使用しないでください。 Blazor アプリは、ASP.NET Core パイプラインのコンテキストの外部で実行されます。 HttpContext は、IHttpContextAccessor 内で使用できるとは限りません。また、HttpContext は、Blazor アプリを開始したコンテキストが保持されることも保証されません。 詳細については、「Blazor Server で IHttpContextAccessor を使用することによるセキュリティへの影響 (dotnet/aspnetcore #45699)」を参照してください。 Blazor Server アプリでユーザーの状態を維持する方法の詳細については、「ASP.NET Core Blazor 状態管理」を参照してください。

Blazor Server アプリに関するその他のリソース