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 |
|
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:
-
Use xref:Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions.SetBrowserRequestCredentials%2A to set xref:Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCredentials.Include on cross-origin
fetch
requests.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); } }
-
Where a hub connection is built, assign the xref:System.Net.Http.HttpMessageHandler to the xref:Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionOptions.HttpMessageHandlerFactory option:
HubConnectionBuilder hubConnecton; ... hubConnecton = new HubConnectionBuilder() .WithUrl(new Uri(NavigationManager.ToAbsoluteUri("/chathub")), options => { options.HttpMessageHandlerFactory = innerHandler => new IncludeRequestCredentialsMessageHandler { InnerHandler = innerHandler }; }).Build();
The preceding example configures the hub connection URL to the absolute URI address at
/chathub
, which is the URL used in the SignalR with Blazor tutorial in theIndex
component (Pages/Index.razor
). The URI can also be set via a string, for examplehttps://signalr.example.com
, or via configuration.
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:
- xref:mvc/views/tag-helpers/builtin-th/component-tag-helper
- xref:blazor/components/prerendering-and-integration
::: 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:
|
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 theblazor.server.js
script. - Place a script that calls
Blazor.start
after theblazor.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"
::: moniker-end
::: moniker range="< aspnetcore-5.0"
::: 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
- xref:signalr/introduction
- xref:signalr/configuration
- xref:blazor/security/server/threat-mitigation
- Blazor Server reconnection events and component lifecycle events
::: zone-end