AspNetCore.Docs/aspnetcore/blazor/fundamentals/signalr.md

17 KiB

title author description monikerRange ms.author ms.custom ms.date no-loc uid zone_pivot_groups
ASP.NET Core Blazor SignalR guidance guardrex Learn how to configure and manage Blazor SignalR connections. >= aspnetcore-3.1 riande mvc 03/12/2021
Home
Privacy
Kestrel
appsettings.json
ASP.NET Core Identity
cookie
Cookie
Blazor
Blazor Server
Blazor WebAssembly
Identity
Let's Encrypt
Razor
SignalR
blazor/fundamentals/signalr blazor-hosting-models

ASP.NET Core Blazor SignalR guidance

::: zone pivot="webassembly"

This article explains how to configure and manage SignalR connections in Blazor apps.

For general guidance on ASP.NET Core SignalR configuration, see the topics in the xref:signalr/introduction area of the documentation. To configure SignalR added to a hosted Blazor WebAssembly solution, see xref:signalr/configuration#configure-server-options.

SignalR cross-origin negotiation for authentication

To configure SignalR's underlying client to send credentials, such as cookies or HTTP authentication headers:

For more information, see xref:signalr/configuration#configure-additional-options.

::: moniker range=">= aspnetcore-5.0"

Render mode

If a Blazor WebAssembly app that uses SignalR is configured to prerender on the server, prerendering occurs before the client connection to the server is established. For more information, see the following articles:

::: moniker-end

Additional resources

::: zone-end

::: zone pivot="server"

This article explains how to configure and manage SignalR connections in Blazor apps.

For general guidance on ASP.NET Core SignalR configuration, see the topics in the xref:signalr/introduction area of the documentation. To configure SignalR added to a hosted Blazor WebAssembly solution, see xref:signalr/configuration#configure-server-options.

Circuit handler options

Configure the Blazor Server circuit with the xref:Microsoft.AspNetCore.Components.Server.CircuitOptions shown in the following table.

Option Default Description
xref:Microsoft.AspNetCore.Components.Server.CircuitOptions.DetailedErrors false Send detailed exception messages to JavaScript when an unhandled exception occurs on the circuit or when a .NET method invocation through JS interop results in an exception.
xref:Microsoft.AspNetCore.Components.Server.CircuitOptions.DisconnectedCircuitMaxRetained 100 Maximum number of disconnected circuits that the server holds in memory at a time.
xref:Microsoft.AspNetCore.Components.Server.CircuitOptions.DisconnectedCircuitRetentionPeriod 3 minutes Maximum amount of time a disconnected circuit is held in memory before being torn down.
xref:Microsoft.AspNetCore.Components.Server.CircuitOptions.JSInteropDefaultCallTimeout 1 minute Maximum amount of time the server waits before timing out an asynchronous JavaScript function invocation.
xref:Microsoft.AspNetCore.Components.Server.CircuitOptions.MaxBufferedUnacknowledgedRenderBatches 10 Maximum number of unacknowledged render batches the server keeps in memory per circuit at a given time to support robust reconnection. After reaching the limit, the server stops producing new render batches until one or more batches are acknowledged by the client.

Configure the options in Startup.ConfigureServices with an options delegate to xref:Microsoft.Extensions.DependencyInjection.ComponentServiceCollectionExtensions.AddServerSideBlazor%2A. The following example assigns the default option values shown in the preceding table. Confirm that Startup.cs uses the xref:System namespace (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;
});

To configure the xref:Microsoft.AspNetCore.SignalR.HubConnectionContext, use xref:Microsoft.AspNetCore.SignalR.HubConnectionContextOptions with xref:Microsoft.Extensions.DependencyInjection.ServerSideBlazorBuilderExtensions.AddHubOptions%2A. For option descriptions, see xref:signalr/configuration#configure-server-options. The following example assigns the default option values. Confirm that Startup.cs uses the xref:System namespace (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;
    });

Reflect the connection state in the UI

When the client detects that the connection has been lost, a default UI is displayed to the user while the client attempts to reconnect. If reconnection fails, the user is provided the option to retry.

To customize the UI, define an element with an id of components-reconnect-modal in the <body> of the _Host.cshtml Razor page.

Pages/_Host.cshtml:

<div id="components-reconnect-modal">
    ...
</div>

Add the following CSS styles to the site's stylesheet.

wwwroot/css/site.css:

#components-reconnect-modal {
    display: none;
}

#components-reconnect-modal.components-reconnect-show {
    display: block;
}

The following table describes the CSS classes applied to the components-reconnect-modal element by the Blazor framework.

CSS class Indicates…
components-reconnect-show A lost connection. The client is attempting to reconnect. Show the modal.
components-reconnect-hide An active connection is re-established to the server. Hide the modal.
components-reconnect-failed Reconnection failed, probably due to a network failure. To attempt reconnection, call window.Blazor.reconnect() in JavaScript.
components-reconnect-rejected Reconnection rejected. The server was reached but refused the connection, and the user's state on the server is lost. To reload the app, call location.reload() in JavaScript. This connection state may result when:
  • A crash in the server-side circuit occurs.
  • The client is disconnected long enough for the server to drop the user's state. Instances of the user's components are disposed.
  • The server is restarted, or the app's worker process is recycled.

Render mode

By default, Blazor Server apps prerender the UI on the server before the client connection to the server is established. For more information, see xref:mvc/views/tag-helpers/builtin-th/component-tag-helper.

Initialize the Blazor circuit

Configure the manual start of a Blazor Server app's SignalR circuit in the Pages/_Host.cshtml file:

  • Add an autostart="false" attribute to the <script> tag for the blazor.server.js script.
  • Place a script that calls Blazor.start after the blazor.server.js script's tag and inside the closing </body> tag.

When autostart is disabled, any aspect of the app that doesn't depend on the circuit works normally. For example, client-side routing is operational. However, any aspect that depends on the circuit isn't operational until Blazor.start is called. App behavior is unpredictable without an established circuit. For example, component methods fail to execute while the circuit is disconnected.

Initialize Blazor when the document is ready

Pages/_Host.cshtml:

<body>
    ...

    <script autostart="false" src="_framework/blazor.server.js"></script>
    <script>
      document.addEventListener("DOMContentLoaded", function() {
        Blazor.start();
      });
    </script>
</body>

Chain to the Promise that results from a manual start

To perform additional tasks, such as JS interop initialization, use then to chain to the Promise that results from a manual Blazor app start.

Pages/_Host.cshtml:

<body>
    ...

    <script autostart="false" src="_framework/blazor.server.js"></script>
    <script>
      Blazor.start().then(function () {
        ...
      });
    </script>
</body>

Configure SignalR client logging

On the client builder, pass in the configureSignalR configuration object that calls configureLogging with the log level.

Pages/_Host.cshtml:

<body>
    ...

    <script autostart="false" src="_framework/blazor.server.js"></script>
    <script>
      Blazor.start({
        configureSignalR: function (builder) {
          builder.configureLogging("information");
        }
      });
    </script>
</body>

In the preceding example, information is equivalent to a log level of xref:Microsoft.Extensions.Logging.LogLevel.Information?displayProperty=nameWithType.

Modify the reconnection handler

The reconnection handler's circuit connection events can be modified for custom behaviors, such as:

  • To notify the user if the connection is dropped.
  • To perform logging (from the client) when a circuit is connected.

To modify the connection events, register callbacks for the following connection changes:

  • Dropped connections use onConnectionDown.
  • Established/re-established connections use onConnectionUp.

Both onConnectionDown and onConnectionUp must be specified.

Pages/_Host.cshtml:

<body>
    ...

    <script autostart="false" src="_framework/blazor.server.js"></script>
    <script>
      Blazor.start({
        reconnectionHandler: {
          onConnectionDown: (options, error) => console.error(error),
          onConnectionUp: () => console.log("Up, up, and away!")
        }
      });
    </script>
</body>

Adjust the reconnection retry count and interval

To adjust the reconnection retry count and interval, set the number of retries (maxRetries) and period in milliseconds permitted for each retry attempt (retryIntervalMilliseconds).

Pages/_Host.cshtml:

<body>
    ...

    <script autostart="false" src="_framework/blazor.server.js"></script>
    <script>
      Blazor.start({
        reconnectionOptions: {
          maxRetries: 3,
          retryIntervalMilliseconds: 2000
        }
      });
    </script>
</body>

Hide or replace the reconnection display

To hide the reconnection display, set the reconnection handler's _reconnectionDisplay to an empty object ({} or new Object()).

Pages/_Host.cshtml:

<body>
    ...

    <script autostart="false" src="_framework/blazor.server.js"></script>
    <script>
      window.addEventListener('beforeunload', function () {
        Blazor.defaultReconnectionHandler._reconnectionDisplay = {};
      });

      Blazor.start();
    </script>
</body>

To replace the reconnection display, set _reconnectionDisplay in the preceding example to the element for display:

Blazor.defaultReconnectionHandler._reconnectionDisplay = 
  document.getElementById("{ELEMENT ID}");

The placeholder {ELEMENT ID} is the ID of the HTML element to display.

::: moniker range=">= aspnetcore-5.0"

Customize the delay before the reconnection display appears by setting the transition-delay property in the site's CSS for the modal element. The following example sets the transition delay from 500 ms (default) to 1,000 ms (1 second).

wwwroot/css/site.css:

#components-reconnect-modal {
    transition: visibility 0s linear 1000ms;
}

Disconnect the Blazor circuit from the client

By default, a Blazor circuit is disconnected when the unload page event is triggered. To disconnect the circuit for other scenarios on the client, invoke Blazor.disconnect in the appropriate event handler. In the following example, the circuit is disconnected when the page is hidden (pagehide event):

window.addEventListener('pagehide', () => {
  Blazor.disconnect();
});

::: moniker-end

Blazor Server circuit handler

Blazor Server allows code to define a circuit handler, which allows running code on changes to the state of a user's circuit. A circuit handler is implemented by deriving from xref:Microsoft.AspNetCore.Components.Server.Circuits.CircuitHandler and registering the class in the app's service container. The following example of a circuit handler tracks open SignalR connections.

TrackingCircuitHandler.cs:

::: moniker range=">= aspnetcore-5.0"

[!code-csharp]

::: moniker-end

::: moniker range="< aspnetcore-5.0"

[!code-csharp]

::: moniker-end

Circuit handlers are registered using DI. Scoped instances are created per instance of a circuit. Using the TrackingCircuitHandler in the preceding example, a singleton service is created because the state of all circuits must be tracked.

Startup.cs:

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

If a custom circuit handler's methods throw an unhandled exception, the exception is fatal to the Blazor Server circuit. To tolerate exceptions in a handler's code or called methods, wrap the code in one or more try-catch statements with error handling and logging.

When a circuit ends because a user has disconnected and the framework is cleaning up the circuit state, the framework disposes of the circuit's DI scope. Disposing the scope disposes any circuit-scoped DI services that implement xref:System.IDisposable?displayProperty=fullName. If any DI service throws an unhandled exception during disposal, the framework logs the exception.

Azure SignalR Service

We recommend using the Azure SignalR Service for Blazor Server apps hosted in Microsoft Azure. The service allows for scaling up a Blazor Server app to a large number of concurrent SignalR connections. In addition, the SignalR Service's global reach and high-performance data centers significantly aid in reducing latency due to geography. For prerendering support with the Azure SignalR Service, configure the app to use sticky sessions. For more information, see xref:blazor/host-and-deploy/server.

Additional resources

::: zone-end