Guía de BlazorSignalR para ASP.NET Core

Nota

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión .NET 8 de este artículo.

Importante

Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Para la versión actual, consulte la versión .NET 8 de este artículo.

En este artículo se explica cómo configurar y administrar conexiones de SignalR en aplicaciones Blazor.

Para obtener instrucciones generales sobre la configuración de SignalR para ASP.NET Core, vea los temas del área Información general de ASP.NET Core SignalR de la documentación, especialmente la Configuración SignalR de ASP.NET Core.

Las aplicaciones del lado del servidor usan ASP.NET Core SignalR para comunicarse con el explorador. SignalRLas condiciones de hospedaje y escalado de se aplican a las aplicaciones del lado del servidor.

Blazor funciona mejor cuando se usa WebSockets como transporte de SignalR debido a su menor latencia, confiabilidad y seguridad. SignalR usa el sondeo largo cuando WebSockets no está disponible o cuando la aplicación está configurada explícitamente para usarlo.

Compresión de WebSocket para componentes de Servidor interactivo

De forma predeterminada, los componentes de Interactive Server:

  • Habilite la compresión para las conexiones de WebSocket. ConfigureWebsocketOptions controla la compresión de WebSocket.

  • Adopte una directiva de frame-ancestorsdirectiva de seguridad de contenido (CSP ) establecida en 'self', que solo permite insertar la aplicación en un <iframe> del origen desde el que se sirve la aplicación cuando se habilita la compresión o cuando se proporciona una configuración para el contexto de WebSocket. ContentSecurityFrameAncestorPolicy controla el CSP de frame-ancestors.

El CSP de frame-ancestors se puede quitar manualmente estableciendo el valor de ConfigureWebSocketOptions en null, ya que puede configurar el CSP de forma centralizada. Cuando el CSP de frame-ancestors se administra de forma centralizada, se debe tener cuidado para aplicar una directiva cada vez que se represente el primer documento. No se recomienda quitar completamente la directiva, ya que podría hacer que la aplicación sea vulnerable a ataques.

Ejemplos de uso:

Deshabilite la compresión estableciendo ConfigureWebSocketOptions en null, lo que reduce la vulnerabilidad de la aplicación para atacar, pero puede provocar un rendimiento reducido:

builder.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode(o => o.ConfigureWebSocketOptions = null)

Cuando la compresión está habilitada, configure un CSP de frame-ancestors más estricto con un valor de 'none' (comillas simples requeridas), lo que permite la compresión de WebSocket, pero impide que los exploradores inserte la aplicación en cualquier <iframe>:

builder.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")

Cuando la compresión está habilitada, quite el frame-ancestorsCSP de estableciendo ContentSecurityFrameAncestorsPolicy en null. Este escenario solo se recomienda para las aplicaciones que establecer el CSP de forma centralizada:

builder.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = null)

Importante

Los exploradores aplican directivas CSP desde varios encabezados CSP mediante el valor de directiva de directiva más estricto. Por lo tanto, un desarrollador no puede agregar una directiva más débil frame-ancestors que 'self' a propósito o por error.

Las comillas simples son necesarias en el valor de cadena pasado a ContentSecurityFrameAncestorsPolicy:

Valores no admitidos:none, self

valores admitidos:'none', 'self'

Entre las opciones adicionales se incluyen especificar uno o varios orígenes de host y orígenes de esquema.

Para ver las implicaciones de seguridad, vea Guía de mitigación de amenazas para ASP.NET Core Blazor representación interactiva del lado servidor. Para obtener más información sobre la directiva frame-ancestors, vea CSP: frame-ancestors (documentación de MDN).

Deshabilitación de la compresión de respuesta para Recarga activa

Al usar Recarga activa, deshabilite el middleware de compresión de respuesta en el entorno Development. Tanto si se utiliza el código predeterminado de una plantilla de proyecto como si no, siempre se llama primero a UseResponseCompression en la canalización de procesamiento de solicitudes.

En el archivo Program:

if (!app.Environment.IsDevelopment())
{
    app.UseResponseCompression();
}

Negociación entre orígenes de SignalR del lado cliente para la autenticación

En esta sección se explica cómo configurar el cliente subyacente de SignalR para que envíe credenciales, como cookies o encabezados de autenticación HTTP.

Use SetBrowserRequestCredentials para establecer Include en solicitudes de fetch entre orígenes.

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

Si existe una conexión con un centro, asigne HttpMessageHandler a la opción HttpMessageHandlerFactory:

private HubConnectionBuilder? hubConnection;

...

hubConnection = new HubConnectionBuilder()
    .WithUrl(new Uri(Navigation.ToAbsoluteUri("/chathub")), options =>
    {
        options.HttpMessageHandlerFactory = innerHandler => 
            new IncludeRequestCredentialsMessageHandler { InnerHandler = innerHandler };
    }).Build();

En el ejemplo anterior se configura la dirección URL de conexión del centro a la dirección URI absoluta en /chathub. El URI también se puede establecer por medio de una cadena (por ejemplo, https://signalr.example.com) o mediante configuración. Navigation es un objeto NavigationManager insertado.

Para más información, vea Configuración de SignalR en ASP.NET Core.

Representación del lado cliente

Si se configura la representación previa, esta se produce antes de establecer la conexión de cliente con el servidor. Para más información, consulte Representación previa de componentes Razor de ASP.NET Core .

Si se configura la representación previa, esta se produce antes de establecer la conexión de cliente con el servidor. Para obtener más información, vea los artículos siguientes:

Tamaño de estado antes de la representación y límite de tamaño del mensaje SignalR

Un tamaño grande de estado anterior a la representación puede superar el límite de tamaño del mensaje de circuito SignalR, lo que da como resultado lo siguiente:

  • El circuito SignalR no se puede inicializar con un error en el cliente: Circuit host not initialized.
  • El cuadro de diálogo de reconexión del cliente aparece cuando se produce un error en el circuito. No es posible la recuperación.

Para resolver el problema, use uno de los enfoques siguientes:

  • Reduzca la cantidad de datos que se están colocando en el estado anterior a la representación.
  • Aumente el límite de tamaño del mensajeSignalR. ADVERTENCIA: Aumentar el límite puede aumentar el riesgo de ataques por denegación de servicio (DoS).

Recursos adicionales del lado cliente

Uso de sesiones permanentes para el hospedaje de granja de servidores del lado servidor

Una aplicación de Blazor se representa previamente en respuesta a la primera solicitud de cliente, que crea el estado de la interfaz de usuario en el servidor. Cuando el cliente intenta crear una conexión SignalR, debe volver a conectarse al mismo servidor. Cuando hay más de un servidor backend en uso, la aplicación debe implementar sesiones permanentes para las conexiones SignalR.

Nota

El siguiente error lo produce una aplicación que no ha habilitado sesiones permanentes en una granja de servidores web:

blazor.server.js:1 Uncaught (in promise) Error: invocación cancelada debido a que se cerró la conexión subyacente.

Servicio de Azure SignalR del lado servidor

Se recomienda usar el servicio de Azure SignalR para el desarrollo del lado servidor hospedado en Microsoft Azure. El servicio funciona junto con el centro Blazor de la aplicación para escalar verticalmente una aplicación del lado servidor a un gran número de conexiones simultáneas SignalR. Además, los centros de datos de alto rendimiento y alcance global de SignalR Service son de gran ayuda a la hora de reducir la latencia ocasionada por la geografía.

Las sesiones permanentes están habilitadas para el Servicio SignalR de Azure estableciendo la opción ServerStickyMode del servicio o el valor de configuración en Required. Para obtener más información, vea Hospedaje e implementación de Blazoraplicaciones del lado servidor de ASP.NET Core.

Opciones del controlador de circuitos del lado servidor

Configura el circuito con CircuitOptions. Visualiza los valores predeterminados en el origen de referencia.

Nota:

Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta de una versión específica, use la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, vea Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Lee o establece las opciones del archivo Program con un delegado de opciones en AddInteractiveServerComponents. El marcador de posición {OPTION} representa la opción y el marcador de posición {VALUE} es el valor.

En el archivo Program:

builder.Services.AddRazorComponents().AddInteractiveServerComponents(options =>
{
    options.{OPTION} = {VALUE};
});

Lee o establece las opciones del archivo Program con un delegado de opciones en AddServerSideBlazor. El marcador de posición {OPTION} representa la opción y el marcador de posición {VALUE} es el valor.

En el archivo Program:

builder.Services.AddServerSideBlazor(options =>
{
    options.{OPTION} = {VALUE};
});

Lee o establece las opciones de Startup.ConfigureServices con un delegado de opciones en AddServerSideBlazor. El marcador de posición {OPTION} representa la opción y el marcador de posición {VALUE} es el valor.

En Startup.ConfigureServices de Startup.cs:

services.AddServerSideBlazor(options =>
{
    options.{OPTION} = {VALUE};
});

Para configurar HubConnectionContext, use HubConnectionContextOptions con AddHubOptions. Visualiza los valores predeterminados de las opciones de contexto de la conexión del centro en el origen de referencia. Para obtener las descripciones de las opciones de la documentación SignalR, consulta ASP.NET Core SignalR Configuración. El marcador de posición {OPTION} representa la opción y el marcador de posición {VALUE} es el valor.

Nota:

Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta de una versión específica, use la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, vea Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).

En el archivo Program:

builder.Services.AddRazorComponents().AddInteractiveServerComponents().AddHubOptions(options =>
{
    options.{OPTION} = {VALUE};
});

En el archivo Program:

builder.Services.AddServerSideBlazor().AddHubOptions(options =>
{
    options.{OPTION} = {VALUE};
});

En Startup.ConfigureServices de Startup.cs:

services.AddServerSideBlazor().AddHubOptions(options =>
{
    options.{OPTION} = {VALUE};
});

Advertencia

El valor predeterminado de MaximumReceiveMessageSize es 32 KB. Aumentar el valor puede aumentar el riesgo de ataques por denegación de servicio (DoS).

Para obtener más información sobre la administración de memoria, vea Hospedaje e implementación de Blazoraplicaciones del lado servidor de ASP.NET Core.

opciones del centro deBlazor

Configura las opciones de MapBlazorHub para controlar HttpConnectionDispatcherOptions del centro de Blazor. Visualiza los valores predeterminados de las opciones de distribuidor de la conexión del centro en el origen de referencia. El marcador de posición {OPTION} representa la opción y el marcador de posición {VALUE} es el valor.

Nota:

Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta de una versión específica, use la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, vea Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Coloque la llamada a app.MapBlazorHub después de la llamada a app.MapRazorComponents en el archivo Program de la aplicación:

app.MapBlazorHub(options =>
{
    options.{OPTION} = {VALUE};
});

Proporcione las opciones para app.MapBlazorHub en el archivo Program de la aplicación:

app.MapBlazorHub(options =>
{
    options.{OPTION} = {VALUE};
});

Proporcione las opciones para app.MapBlazorHub en la configuración de enrutamiento de puntos de conexión:

app.UseEndpoints(endpoints =>
{
    endpoints.MapBlazorHub(options =>
    {
        options.{OPTION} = {VALUE};
    });
    ...
});

Tamaño máximo del mensaje de recepción

Esta sección solo se aplica a los proyectos que implementan SignalR.

El tamaño máximo de mensaje SignalR entrante permitido para los métodos de concentrador está limitado por HubOptions.MaximumReceiveMessageSize (valor predeterminado: 32 KB). Los mensajes SignalR mayores que MaximumReceiveMessageSize producen un error. El marco no impone ningún límite de tamaño para un mensaje SignalR desde el concentrador a un cliente.

Cuando el registro de SignalR no está establecido en SignalR o Seguimiento, solo aparece un error relativo al tamaño del mensaje en la consola de herramientas de desarrollo del explorador:

Error: Conexión desconectada con el error "Error: El servidor ha devuelto un error al cerrarse: Conexión cerrada con un error.".

Cuando el registro del lado servidor de SignalR se establece en Depurar o Seguimiento, el registro del lado servidor inicia una excepción InvalidDataException relativa a un error del tamaño del mensaje.

appsettings.Development.json:

{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      ...
      "Microsoft.AspNetCore.SignalR": "Debug"
    }
  }
}

Error:

System.IO.InvalidDataException: Se ha superado el tamaño máximo del mensaje de 32768B. El tamaño del mensaje se puede configurar en AddHubOptions.

Un enfoque implica aumentar el límite estableciendo MaximumReceiveMessageSize en el archivo Program. En el ejemplo siguiente se establece el tamaño máximo del mensaje de recepción en 64 KB:

builder.Services.AddRazorComponents().AddInteractiveServerComponents()
    .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);

Aumentar el límite de tamaño del mensaje entrante SignalR implica requerir más recursos del servidor y aumenta el riesgo de ataques por denegación de servicio (DoS). Además, la lectura de una gran cantidad de contenido en la memoria, como cadenas o matrices de bytes, también puede dar lugar a que las asignaciones funcionen de forma deficiente con el recolector de elementos no utilizados, lo que puede reducir significativamente el rendimiento.

Una mejor opción para leer cargas grandes consiste en enviar el contenido en fragmentos más pequeños y procesar la carga como Stream. Esto se puede usar al leer cargas grandes de interoperabilidad de JSON de JavaScript (JS) o si los datos de interoperabilidad de JS están disponibles como bytes sin formato. Para obtener un ejemplo en el que se muestra el envío de cargas binarias de gran tamaño en aplicaciones del lado servidor que usan técnicas similares a las del componente InputFile, vea la Aplicación de ejemplo de envíos binarios y el Ejemplo de componente BlazorInputLargeTextArea.

Nota

Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta de una versión específica, use la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, vea Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Los formularios que procesan cargas de gran tamaño de SignalR también pueden usar la interoperabilidad de streaming de JS directamente. Para obtener más información, vea Llamada a métodos de .NET desde funciones de JavaScript en ASP.NET Core Blazor. Para obtener un ejemplo de formularios que transmite datos <textarea> al servidor, consulte Solucionar problemas de formularios ASP.NET Core Blazor.

Un enfoque implica aumentar el límite estableciendo MaximumReceiveMessageSize en el archivo Program. En el ejemplo siguiente se establece el tamaño máximo del mensaje de recepción en 64 KB:

builder.Services.AddServerSideBlazor()
    .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);

Aumentar el límite de tamaño del mensaje entrante SignalR implica requerir más recursos del servidor y aumenta el riesgo de ataques por denegación de servicio (DoS). Además, la lectura de una gran cantidad de contenido en la memoria, como cadenas o matrices de bytes, también puede dar lugar a que las asignaciones funcionen de forma deficiente con el recolector de elementos no utilizados, lo que puede reducir significativamente el rendimiento.

Una mejor opción para leer cargas grandes consiste en enviar el contenido en fragmentos más pequeños y procesar la carga como Stream. Esto se puede usar al leer cargas grandes de interoperabilidad de JSON de JavaScript (JS) o si los datos de interoperabilidad de JS están disponibles como bytes sin formato. Para obtener un ejemplo en el que se muestra el envío de cargas binarias de gran tamaño en Blazor Server que usa técnicas similares a las del componente InputFile, vea la aplicación de ejemplo de envíos binarios y el ejemplo de componente InputLargeTextArea de Blazor.

Nota

Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta de una versión específica, use la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, vea Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Los formularios que procesan cargas de gran tamaño de SignalR también pueden usar la interoperabilidad de streaming de JS directamente. Para obtener más información, vea Llamada a métodos de .NET desde funciones de JavaScript en ASP.NET Core Blazor. Para ver un ejemplo de formularios que transmite datos <textarea> en una aplicación Blazor Server, consulte Solucionar problemas de formularios ASP.NET CoreBlazor.

Para aumentar el límite, establezca MaximumReceiveMessageSize en Startup.ConfigureServices:

services.AddServerSideBlazor()
    .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);

Aumentar el límite de tamaño del mensaje entrante SignalR implica requerir más recursos del servidor y aumenta el riesgo de ataques por denegación de servicio (DoS). Además, la lectura de una gran cantidad de contenido en la memoria, como cadenas o matrices de bytes, también puede dar lugar a que las asignaciones funcionen de forma deficiente con el recolector de elementos no utilizados, lo que puede reducir significativamente el rendimiento.

Tenga en cuenta las instrucciones siguientes al desarrollar código que transfiera un gran volumen de datos:

  • Aproveche la compatibilidad de interoperabilidad de transmisión nativa de JS para transferir datos mayores que el límite de tamaño del mensaje entrante SignalR:
  • Sugerencias generales:
    • No asigne objetos grandes en código JS y C#.
    • Libere la memoria consumida al completar o cancelar el proceso.
    • Aplique los requisitos adicionales siguientes por motivos de seguridad:
      • Declare el tamaño máximo del archivo o los datos que se pueden pasar.
      • Declare la tasa mínima de carga desde el cliente al servidor.
    • Después de que el servidor reciba los datos, los datos se pueden:
      • Almacenar temporalmente en un búfer de memoria hasta que se recopilen todos los segmentos.
      • Consumir inmediatamente. Por ejemplo, los datos se pueden almacenar inmediatamente en una base de datos o escribir en el disco a medida que se reciba cada segmento.
  • Segmente los datos en partes más pequeñas y envíe los segmentos de datos secuencialmente hasta que el servidor reciba todos los datos.
  • No asigne objetos grandes en código JS y C#.
  • No bloquee el subproceso de interfaz de usuario principal durante períodos largos al enviar o recibir datos.
  • Libere la memoria consumida al completar o cancelar el proceso.
  • Aplique los requisitos adicionales siguientes por motivos de seguridad:
    • Declare el tamaño máximo del archivo o los datos que se pueden pasar.
    • Declare la tasa mínima de carga desde el cliente al servidor.
  • Después de que el servidor reciba los datos, los datos se pueden:
    • Almacenar temporalmente en un búfer de memoria hasta que se recopilen todos los segmentos.
    • Consumir inmediatamente. Por ejemplo, los datos se pueden almacenar inmediatamente en una base de datos o escribir en el disco a medida que se reciba cada segmento.

Configuración de la ruta del punto de conexión del centro Blazor del lado servidor

En el archivo Program, llame a MapBlazorHub para asignar el BlazorHub a la ruta de acceso predeterminada de la aplicación. El script de Blazor (blazor.*.js) apunta automáticamente al punto de conexión creado por MapBlazorHub.

Reflejar el estado de conexión del lado servidor en la interfaz de usuario

Cuando el cliente detecta que se ha perdido la conexión, se muestra al usuario una interfaz de usuario predeterminada mientras el cliente intenta volver a conectarse. Si se produce un error en la reconexión, se proporciona al usuario la opción de volver a intentarlo.

Para personalizar la interfaz de usuario, defina un único elemento con un id de components-reconnect-modal. En el ejemplo siguiente se coloca el elemento en el componente App.

App.razor:

Para personalizar la interfaz de usuario, defina un único elemento con un id de components-reconnect-modal. En el ejemplo siguiente se coloca el elemento en la página de host.

Pages/_Host.cshtml:

Para personalizar la interfaz de usuario, defina un único elemento con un id de components-reconnect-modal. En el ejemplo siguiente se coloca el elemento en la página de diseño.

Pages/_Layout.cshtml:

Para personalizar la interfaz de usuario, defina un único elemento con un id de components-reconnect-modal. En el ejemplo siguiente se coloca el elemento en la página de host.

Pages/_Host.cshtml:

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

Nota:

Si la aplicación representa más de un elemento con un id de components-reconnect-modal, solo el primer elemento representado recibirá los cambios de clase CSS para mostrar u ocultar el elemento.

Agregue los siguientes estilos CSS a la hoja de estilos del sitio.

wwwroot/app.css:

wwwroot/css/site.css:

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

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

En la siguiente tabla se describen las clases de CSS que el marco Blazor aplica al elemento components-reconnect-modal.

Clase de CSS Indica…
components-reconnect-show Una conexión perdida. El cliente intenta volver a conectarse. Se muestra el modal.
components-reconnect-hide Se restablece una conexión activa con el servidor. Se oculta el modal.
components-reconnect-failed Error de reconexión, probablemente debido a un error de la red. Para intentar la reconexión, llame a window.Blazor.reconnect() en JavaScript.
components-reconnect-rejected Reconexión rechazada. Se ha alcanzado el servidor pero se ha rechazado la conexión y se ha perdido el estado del usuario en el servidor. Para volver a cargar la aplicación, llame a location.reload() en JavaScript. Este estado de conexión se puede producir cuando:
  • Se produce un bloqueo en el circuito del lado servidor.
  • El cliente se desconecta el tiempo suficiente para que el servidor quite el estado del usuario. Las instancias de los componentes del usuario se eliminan.
  • El servidor se reinicia o se recicla el proceso de trabajo de la aplicación.

Personalice el retraso antes de que aparezca la pantalla de reconexión. Para ello, establezca la propiedad transition-delay en el CSS del sitio como elemento modal. En el siguiente ejemplo, el retraso de la transición se cambia de 500 ms (valor predeterminado) a 1000 ms (1 segundo).

wwwroot/app.css:

wwwroot/css/site.css:

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

Para mostrar el intento de reconexión actual, defina un elemento con un id de components-reconnect-current-attempt. Para mostrar el número máximo de reintentos de reconexión, defina un elemento con un id de components-reconnect-max-retries. En el ejemplo siguiente se colocan estos elementos dentro de un elemento modal de intento de reconexión en base al ejemplo anterior.

<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>

Cuando aparece el elemento modal de reconexión personalizado, representa contenido similar al siguiente en función del código anterior:

There was a problem with the connection! (Current reconnect attempt: 3 / 8)

Representación del lado servidor

De forma predeterminada, los componentes realizan una representación previa en el servidor antes de establecer la conexión del cliente con él. Para más información, consulte Representación previa de componentes Razor de ASP.NET Core .

De forma predeterminada, los componentes realizan una representación previa en el servidor antes de establecer la conexión del cliente con él. Para más información, consulte Asistente de etiquetas de componente en ASP.NET Core.

Supervisión de la actividad del circuito del lado servidor

Supervise la actividad del circuito entrante mediante el método CreateInboundActivityHandler en CircuitHandler. La actividad de circuito entrante es cualquier actividad enviada desde el explorador al servidor, como eventos de interfaz de usuario o llamadas de interoperabilidad de JavaScript a .NET.

Por ejemplo, puede usar un controlador de actividad de circuito para detectar si el cliente está inactivo y registrar su identificador de circuito (Circuit.Id):

using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.Options;
using Timer = System.Timers.Timer;

public sealed class IdleCircuitHandler : CircuitHandler, IDisposable
{
    private Circuit? currentCircuit;
    private readonly ILogger logger;
    private readonly Timer timer;

    public IdleCircuitHandler(ILogger<IdleCircuitHandler> logger, 
        IOptions<IdleCircuitOptions> options)
    {
        timer = new Timer
        {
            Interval = options.Value.IdleTimeout.TotalMilliseconds,
            AutoReset = false
        };

        timer.Elapsed += CircuitIdle;
        this.logger = logger;
    }

    private void CircuitIdle(object? sender, System.Timers.ElapsedEventArgs e)
    {
        logger.LogInformation("{CircuitId} is idle", currentCircuit?.Id);
    }

    public override Task OnCircuitOpenedAsync(Circuit circuit, 
        CancellationToken cancellationToken)
    {
        currentCircuit = circuit;

        return Task.CompletedTask;
    }

    public override Func<CircuitInboundActivityContext, Task> CreateInboundActivityHandler(
        Func<CircuitInboundActivityContext, Task> next)
    {
        return context =>
        {
            timer.Stop();
            timer.Start();

            return next(context);
        };
    }

    public void Dispose() => timer.Dispose();
}

public class IdleCircuitOptions
{
    public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(5);
}

public static class IdleCircuitHandlerServiceCollectionExtensions
{
    public static IServiceCollection AddIdleCircuitHandler(
        this IServiceCollection services, 
        Action<IdleCircuitOptions> configureOptions)
    {
        services.Configure(configureOptions);
        services.AddIdleCircuitHandler();

        return services;
    }

    public static IServiceCollection AddIdleCircuitHandler(
        this IServiceCollection services)
    {
        services.AddScoped<CircuitHandler, IdleCircuitHandler>();

        return services;
    }
}

Registrar el servicio en el archivo Program. En el ejemplo siguiente se configura el tiempo de espera de inactividad predeterminado de cinco minutos a cinco segundos para probar la implementación anterior IdleCircuitHandler:

builder.Services.AddIdleCircuitHandler(options => 
    options.IdleTimeout = TimeSpan.FromSeconds(5));

Los controladores de actividad de circuito también proporcionan un enfoque para acceder a los servicios de Blazor con ámbito desde otros ámbitos de inserción de dependencias (DI) que no son de Blazor. Para más información y ejemplos, consulte:

Inicio de Blazor

Configure el inicio manual de un circuito Blazor de la aplicación SignalR en el archivo App.razor de una aplicación web Blazor:

Configure el inicio manual de un circuito Blazor de la aplicación SignalR en el archivo Pages/_Host.cshtml (Blazor Server):

Configure el inicio manual de un circuito Blazor de la aplicación SignalR en el archivo Pages/_Layout.cshtml (Blazor Server):

Configure el inicio manual de un circuito Blazor de la aplicación SignalR en el archivo Pages/_Host.cshtml (Blazor Server):

  • Agregue un atributo autostart="false" a la etiqueta <script> para el script blazor.*.js.
  • Coloque un script que llame a Blazor.start() después de que se cargue el script Blazor y dentro de la etiqueta de cierre </body>.

Cuando autostart está deshabilitado, cualquier aspecto de la aplicación que no depende del circuito funciona normalmente. Por ejemplo, el enrutamiento del lado cliente está operativo. Sin embargo, cualquier aspecto que dependa del circuito no funcionará hasta que se llame a Blazor.start(). El comportamiento de la aplicación es imprevisible sin ningún circuito establecido. Por ejemplo, los métodos de componente no se ejecutan mientras el circuito está desconectado.

Para obtener más información, incluida la forma de inicializar Blazor cuando el documento está listo y de encadenar a JS Promise, vea Inicio de Blazor en ASP.NET Core.

Configure los tiempos de espera de SignalR y Keep-Alive en el cliente

Configure los siguientes valores para el cliente:

  • withServerTimeout: configura el tiempo de espera del servidor en milisegundos. Si este tiempo de espera transcurre sin recibir ningún mensaje del servidor, la conexión finaliza con un error. El valor predeterminado del tiempo de espera es de 30 segundos. El tiempo de espera del servidor debe ser al menos el doble del valor asignado al intervalo de Keep-Alive (withKeepAliveInterval).
  • withKeepAliveInterval: configura el intervalo de Mantener conexión en milisegundos (intervalo predeterminado en el que se hace ping al servidor). Esta configuración permite al servidor detectar desconexiones físicas, como cuando un cliente desconecta su equipo de la red. El ping se produce como máximo las veces que el servidor hace ping. Si el servidor hace ping cada cinco segundos, asignar un valor inferior a 5000 (5 segundos) hace ping cada cinco segundos. El valor predeterminado es 15 segundos. El intervalo de Keep-Alive debe ser menor o igual que la mitad del valor asignado al tiempo de espera del servidor (withServerTimeout).

En el ejemplo siguiente del archivo App.razor (aplicación web Blazor) se muestra la asignación de los valores predeterminados.

Aplicación web Blazor:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    circuit: {
      configureSignalR: function (builder) {
        builder.withServerTimeout(30000).withKeepAliveInterval(15000);
      }
    }
  });
</script>

En el ejemplo siguiente del archivo Pages/_Host.cshtml (Blazor Server, todas las versiones excepto ASP.NET Core en .NET 6) o el archivo Pages/_Layout.cshtml (Blazor Server, ASP.NET Core en .NET 6).

Blazor Server:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    configureSignalR: function (builder) {
        builder.withServerTimeout(30000).withKeepAliveInterval(15000);
  });
</script>

En el ejemplo anterior, el marcador de posición {BLAZOR SCRIPT} es la ruta de acceso del script y el nombre de archivo de Blazor. Para obtener la ubicación del script y la ruta de acceso que se va a usar, consulte ASP.NET Core Blazor estructura de proyecto.

Al crear una conexión de concentrador en un componente, establezca ServerTimeout (valor predeterminado: 30 segundos) y KeepAliveInterval (valor predeterminado: 15 segundos) en HubConnectionBuilder. Establezca el valor HandshakeTimeout (valor predeterminado: 15 segundos) en la complicación HubConnection. En el ejemplo siguiente se muestra la asignación de valores predeterminados:

protected override async Task OnInitializedAsync()
{
    hubConnection = new HubConnectionBuilder()
        .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
        .WithServerTimeout(TimeSpan.FromSeconds(30))
        .WithKeepAliveInterval(TimeSpan.FromSeconds(15))
        .Build();

    hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(15);

    hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...

    await hubConnection.StartAsync();
}

Configure los siguientes valores para el cliente:

  • serverTimeoutInMilliseconds: tiempo de espera del servidor en milisegundos. Si este tiempo de espera transcurre sin recibir ningún mensaje del servidor, la conexión finaliza con un error. El valor predeterminado del tiempo de espera es de 30 segundos. El tiempo de espera del servidor debe ser al menos el doble del valor asignado al intervalo de Keep-Alive (keepAliveIntervalInMilliseconds).
  • keepAliveIntervalInMilliseconds: intervalo predeterminado en el que se va a hacer ping al servidor. Esta configuración permite al servidor detectar desconexiones físicas, como cuando un cliente desconecta su equipo de la red. El ping se produce como máximo las veces que el servidor hace ping. Si el servidor hace ping cada cinco segundos, asignar un valor inferior a 5000 (5 segundos) hace ping cada cinco segundos. El valor predeterminado es 15 segundos. El intervalo de Keep-Alive debe ser menor o igual que la mitad del valor asignado al tiempo de espera del servidor (serverTimeoutInMilliseconds).

En el ejemplo siguiente del archivo Pages/_Host.cshtml (Blazor Server, todas las versiones excepto ASP.NET Core en .NET 6) o el archivo Pages/_Layout.cshtml (Blazor Server, ASP.NET Core en .NET 6):

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    configureSignalR: function (builder) {
      let c = builder.build();
      c.serverTimeoutInMilliseconds = 30000;
      c.keepAliveIntervalInMilliseconds = 15000;
      builder.build = () => {
        return c;
      };
    }
  });
</script>

En el ejemplo anterior, el marcador de posición {BLAZOR SCRIPT} es la ruta de acceso del script y el nombre de archivo de Blazor. Para obtener la ubicación del script y la ruta de acceso que se va a usar, consulte ASP.NET Core Blazor estructura de proyecto.

Al crear una conexión de concentrador en un componente, establezca ServerTimeout (valor predeterminado: 30 segundos), HandshakeTimeout (valor predeterminado: 15 segundos) y KeepAliveInterval (valor predeterminado: 15 segundos) en el HubConnection compilado. En el ejemplo siguiente se muestra la asignación de valores predeterminados:

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

Al cambiar los valores del tiempo de espera del servidor (ServerTimeout) o el intervalo para mantener la conexión (KeepAliveInterval):

  • El tiempo de espera del servidor debe ser al menos el doble del valor asignado al intervalo de Keep-Alive.
  • El intervalo de Keep-Alive debe ser menor o igual que la mitad del valor asignado al tiempo de espera del servidor.

Para obtener más información, consulte las secciones Errores de implementación global y conexión de los siguientes artículos:

Modificación del controlador de reconexión del lado servidor

Los eventos de conexión del circuito del controlador de reconexión pueden modificarse para obtener comportamientos personalizados, por ejemplo:

  • Para notificar al usuario si la conexión se ha quitado.
  • Para realizar el registro (desde el cliente) cuando un circuito está conectado.

Para modificar los eventos de conexión, registre las devoluciones de llamada para los siguientes cambios de conexión:

  • Las conexiones que se quitan usan onConnectionDown.
  • Las conexiones establecidas o restablecidas usan onConnectionUp.

Es necesario especificar tanto onConnectionDown como onConnectionUp.

Aplicación web Blazor:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    circuit: {
      reconnectionHandler: {
        onConnectionDown: (options, error) => console.error(error),
        onConnectionUp: () => console.log("Up, up, and away!")
      }
    }
  });
</script>

Blazor Server:

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

En el ejemplo anterior, el marcador de posición {BLAZOR SCRIPT} es la ruta de acceso del script y el nombre de archivo de Blazor. Para obtener la ubicación del script y la ruta de acceso que se va a usar, consulte ASP.NET Core Blazor estructura de proyecto.

Actualizar automáticamente la página cuando se produzca un error de reconexión del lado servidor

El comportamiento de reconexión predeterminado requiere que el usuario realice una acción manual para actualizar la página después de que se produzca un error en la reconexión. Sin embargo, se puede usar un controlador de reconexión personalizado para actualizar automáticamente la página:

App.razor:

Pages/_Host.cshtml:

<div id="reconnect-modal" style="display: none;"></div>
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script src="boot.js"></script>

En el ejemplo anterior, el marcador de posición {BLAZOR SCRIPT} es la ruta de acceso del script y el nombre de archivo de Blazor. Para obtener la ubicación del script y la ruta de acceso que se va a usar, consulte ASP.NET Core Blazor estructura de proyecto.

Crear el archivo wwwroot/boot.js siguiente.

Aplicación web Blazor:

(() => {
  const maximumRetryCount = 3;
  const retryIntervalMilliseconds = 5000;
  const reconnectModal = document.getElementById('reconnect-modal');

  const startReconnectionProcess = () => {
    reconnectModal.style.display = 'block';

    let isCanceled = false;

    (async () => {
      for (let i = 0; i < maximumRetryCount; i++) {
        reconnectModal.innerText = `Attempting to reconnect: ${i + 1} of ${maximumRetryCount}`;

        await new Promise(resolve => setTimeout(resolve, retryIntervalMilliseconds));

        if (isCanceled) {
          return;
        }

        try {
          const result = await Blazor.reconnect();
          if (!result) {
            // The server was reached, but the connection was rejected; reload the page.
            location.reload();
            return;
          }

          // Successfully reconnected to the server.
          return;
        } catch {
          // Didn't reach the server; try again.
        }
      }

      // Retried too many times; reload the page.
      location.reload();
    })();

    return {
      cancel: () => {
        isCanceled = true;
        reconnectModal.style.display = 'none';
      },
    };
  };

  let currentReconnectionProcess = null;

  Blazor.start({
    circuit: {
      reconnectionHandler: {
        onConnectionDown: () => currentReconnectionProcess ??= startReconnectionProcess(),
        onConnectionUp: () => {
          currentReconnectionProcess?.cancel();
          currentReconnectionProcess = null;
        }
      }
    }
  });
})();

Blazor Server:

(() => {
  const maximumRetryCount = 3;
  const retryIntervalMilliseconds = 5000;
  const reconnectModal = document.getElementById('reconnect-modal');

  const startReconnectionProcess = () => {
    reconnectModal.style.display = 'block';

    let isCanceled = false;

    (async () => {
      for (let i = 0; i < maximumRetryCount; i++) {
        reconnectModal.innerText = `Attempting to reconnect: ${i + 1} of ${maximumRetryCount}`;

        await new Promise(resolve => setTimeout(resolve, retryIntervalMilliseconds));

        if (isCanceled) {
          return;
        }

        try {
          const result = await Blazor.reconnect();
          if (!result) {
            // The server was reached, but the connection was rejected; reload the page.
            location.reload();
            return;
          }

          // Successfully reconnected to the server.
          return;
        } catch {
          // Didn't reach the server; try again.
        }
      }

      // Retried too many times; reload the page.
      location.reload();
    })();

    return {
      cancel: () => {
        isCanceled = true;
        reconnectModal.style.display = 'none';
      },
    };
  };

  let currentReconnectionProcess = null;

  Blazor.start({
    reconnectionHandler: {
      onConnectionDown: () => currentReconnectionProcess ??= startReconnectionProcess(),
      onConnectionUp: () => {
        currentReconnectionProcess?.cancel();
        currentReconnectionProcess = null;
      }
    }
  });
})();

Para más información sobre el inicio de Blazor, vea Inicio de Blazor en ASP.NET Core.

Ajustar el número y el intervalo de reintentos de reconexión del lado servidor

Para ajustar el intervalo y el número de reintentos de reconexión, establezca el número de reintentos (maxRetries) y el período en milisegundos permitido en cada reintento (retryIntervalMilliseconds).

Aplicación web Blazor:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    circuit: {
      reconnectionOptions: {
        maxRetries: 3,
        retryIntervalMilliseconds: 2000
      }
    }
  });
</script>

Blazor Server:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    reconnectionOptions: {
      maxRetries: 3,
      retryIntervalMilliseconds: 2000
    }
  });
</script>

En el ejemplo anterior, el marcador de posición {BLAZOR SCRIPT} es la ruta de acceso del script y el nombre de archivo de Blazor. Para obtener la ubicación del script y la ruta de acceso que se va a usar, consulte ASP.NET Core Blazor estructura de proyecto.

Para más información sobre el inicio de Blazor, vea Inicio de Blazor en ASP.NET Core.

Controlar cuándo aparece la interfaz de usuario de reconexión

Controlar cuándo aparece la interfaz de usuario de reconexión puede ser útil en las siguientes situaciones:

  • Una aplicación implementada muestra con frecuencia la interfaz de usuario de reconexión debido a los tiempos de espera de ping causados por la red interna o la latencia de Internet, y le gustaría aumentar el retraso.
  • Una aplicación debe informar a los usuarios de que la conexión se ha eliminado antes y le gustaría acortar el retraso.

El tiempo de aparición de la interfaz de usuario de reconexión se ve influido por el ajuste del intervalo de mantenimiento de conexión y los tiempos de espera en el cliente. Sin embargo, cambiar una configuración puede requerir cambios en otras configuraciones de mantenimiento de conexión, tiempo de espera y protocolo de enlace.

Como recomendaciones generales para las instrucciones siguientes:

  • El intervalo de mantenimiento de conexión debe coincidir con las configuraciones del cliente y del servidor.
  • Los tiempos de espera deben ser al menos el doble del valor asignado al intervalo de mantenimiento de conexión.

Configuración del servidor

Configure las opciones siguientes:

  • ClientTimeoutInterval (valor predeterminado: 30 segundos): los clientes de la ventana de tiempo tienen que enviar un mensaje antes de que el servidor cierre la conexión.
  • HandshakeTimeout (valor predeterminado: 15 segundos): el intervalo que usa el servidor para esperar las solicitudes de protocolo de enlace entrantes de los clientes.
  • KeepAliveInterval (valor predeterminado: 15 segundos): intervalo que usa el servidor para enviar pings de mantenimiento de conexión a los clientes conectados. Tenga en cuenta que también hay una configuración de intervalo de mantenimiento de conexión en el cliente, que debe coincidir con el valor del servidor.

El ClientTimeoutInterval y HandshakeTimeout pueden aumentar, y el KeepAliveInterval puede permanecer igual. La consideración importante es que, si cambia los valores, se asegure de que los tiempos de espera son al menos el doble del valor del intervalo de mantenimiento de conexión y que el intervalo de mantenimiento de conexión del servidor y del cliente coinciden. Para obtener más información, consulta la sección Configuración de tiempos de espera de SignalR y mantenimiento del cliente.

En el ejemplo siguiente:

  • El ClientTimeoutInterval se incrementa a 60 segundos (valor predeterminado: 30 segundos).
  • El HandshakeTimeout se incrementa a 30 segundos (valor predeterminado: 15 segundos).
  • El KeepAliveInterval no se establece en el código de desarrollador y usa su valor predeterminado de 15 segundos. Al reducir el valor del intervalo de mantenimiento de conexión, aumenta la frecuencia de pings de comunicación, lo que aumenta la carga en la aplicación, el servidor y la red. Se debe tener cuidado para evitar introducir un rendimiento deficiente al reducir el intervalo de mantenimiento de conexión.

Blazor Aplicación web (.NET 8 o posterior) en el archivo Program del proyecto de servidor:

builder.Services.AddRazorComponents().AddInteractiveServerComponents()
    .AddHubOptions(options =>
{
    options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
    options.HandshakeTimeout = TimeSpan.FromSeconds(30);
});

Blazor Server en el archivo Program:

builder.Services.AddServerSideBlazor()
    .AddHubOptions(options =>
    {
        options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
        options.HandshakeTimeout = TimeSpan.FromSeconds(30);
    });

Para obtener más información, consulta la sección Opciones del controlador de circuitos del lado servidor.

Configuración de cliente

Configure las opciones siguientes:

  • withServerTimeout (valor predeterminado: 30 segundos): configura el tiempo de espera del servidor, especificado en milisegundos, para la conexión del concentrador del circuito.
  • withKeepAliveInterval (valor predeterminado: 15 segundos): intervalo, especificado en milisegundos, en el que la conexión envía mensajes de mantenimiento de conexión.

Se puede aumentar el tiempo de espera del servidor y el intervalo de mantenimiento de conexión puede permanecer igual. La consideración importante es que, si cambia los valores, se asegure de que el tiempo de espera del servidor sea al menos el doble del valor del intervalo de mantenimiento de conexión y de que los valores del intervalo de mantenimiento de conexión del servidor y del cliente coinciden. Para obtener más información, consulta la sección Configuración de tiempos de espera de SignalR y mantenimiento del cliente.

En el siguiente ejemplo de configuración de inicio (ubicación del script Blazor), se usa un valor personalizado de 60 segundos para el tiempo de espera del servidor. El intervalo de mantenimiento de conexión (withKeepAliveInterval) no se establece y usa su valor predeterminado de 15 segundos.

Aplicación web Blazor:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    circuit: {
      configureSignalR: function (builder) {
        builder.withServerTimeout(60000);
      }
    }
  });
</script>

Blazor Server:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    configureSignalR: function (builder) {
      builder.withServerTimeout(60000);
    }
  });
</script>

Al crear una conexión de concentrador en un componente, establezca el tiempo de espera del servidor (WithServerTimeout, valor predeterminado: 30 segundos) en HubConnectionBuilder. Establezca el valor HandshakeTimeout (valor predeterminado: 15 segundos) en la complicación HubConnection. Confirme que los tiempos de espera son al menos el doble del intervalo de mantenimiento de conexión (WithKeepAliveInterval/KeepAliveInterval) y que el valor de mantenimiento de conexión del servidor y del cliente coinciden.

El ejemplo siguiente se basa en el componente Index del tutorial SignalR con Blazor. El tiempo de espera del servidor aumenta a 60 segundos y el tiempo de espera del protocolo de enlace aumenta a 30 segundos. El intervalo de mantenimiento de conexión no se establece y usa su valor predeterminado de 15 segundos.

protected override async Task OnInitializedAsync()
{
    hubConnection = new HubConnectionBuilder()
        .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
        .WithServerTimeout(TimeSpan.FromSeconds(60))
        .Build();

    hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(30);

    hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...

    await hubConnection.StartAsync();
}

Configure las opciones siguientes:

  • serverTimeoutInMilliseconds (valor predeterminado: 30 segundos): configura el tiempo de espera del servidor, especificado en milisegundos, para la conexión del concentrador del circuito.
  • keepAliveIntervalInMilliseconds (valor predeterminado: 15 segundos): intervalo, especificado en milisegundos, en el que la conexión envía mensajes de mantenimiento de conexión.

Se puede aumentar el tiempo de espera del servidor y el intervalo de mantenimiento de conexión puede permanecer igual. La consideración importante es que, si cambia los valores, se asegure de que el tiempo de espera del servidor sea al menos el doble del valor del intervalo de mantenimiento de conexión y de que los valores del intervalo de mantenimiento de conexión del servidor y del cliente coinciden. Para obtener más información, consulta la sección Configuración de tiempos de espera de SignalR y mantenimiento del cliente.

En el siguiente ejemplo de configuración de inicio (ubicación del script Blazor), se usa un valor personalizado de 60 segundos para el tiempo de espera del servidor. El intervalo de mantenimiento de conexión (keepAliveIntervalInMilliseconds) no se establece y usa su valor predeterminado de 15 segundos.

En Pages/_Host.cshtml:

<script src="_framework/blazor.server.js" autostart="false"></script>
<script>
  Blazor.start({
    configureSignalR: function (builder) {
      let c = builder.build();
      c.serverTimeoutInMilliseconds = 60000;
      builder.build = () => {
        return c;
      };
    }
  });
</script>

Al crear una conexión de concentrador en un componente, establezca ServerTimeout (valor predeterminado: 30 segundos) y HandshakeTimeout (valor predeterminado: 15 segundos) en el HubConnection compilado. Confirme que los tiempos de espera son al menos el doble del intervalo de mantenimiento de conexión. Confirme que el intervalo de mantenimiento de conexión del servidor y del cliente coinciden.

El ejemplo siguiente se basa en el componente Index del tutorial SignalR con Blazor. El ServerTimeout se incrementa a 60 segundos y HandshakeTimeout se incrementa a 30 segundos. El intervalo de mantenimiento de conexión (KeepAliveInterval) no se establece y usa su valor predeterminado de 15 segundos.

protected override async Task OnInitializedAsync()
{
    hubConnection = new HubConnectionBuilder()
        .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
        .Build();

    hubConnection.ServerTimeout = TimeSpan.FromSeconds(60);
    hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(30);

    hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...

    await hubConnection.StartAsync();
}

Desconexión del circuito Blazor del cliente

De forma predeterminada, un circuito Blazor se desconecta cuando se desencadena el evento de página unload. A fin de desconectar el circuito para otros escenarios en el cliente, invoque Blazor.disconnect en el controlador de eventos adecuado. En el ejemplo siguiente, el circuito se desconecta cuando la página está oculta (evento pagehide):

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

Para más información sobre el inicio de Blazor, vea Inicio de Blazor en ASP.NET Core.

Controlador de circuitos del lado servidor

Puede definir un controlador de circuito, que permite ejecutar el código en los cambios realizados en el estado del circuito de un usuario. Un controlador de circuito se implementa derivando de CircuitHandler y registrando la clase en el contenedor de servicios de la aplicación. El ejemplo siguiente de un controlador de circuito realiza un seguimiento de las conexiones abiertas de 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;
}

Los controladores de circuito se registran mediante DI. Las instancias con ámbito se crean por instancia de un circuito. Mediante el uso de TrackingCircuitHandler en el ejemplo anterior, se crea un servicio singleton porque se debe realizar un seguimiento del estado de todos los circuitos.

En el archivo Program:

builder.Services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();

En Startup.ConfigureServices de Startup.cs:

services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();

Si los métodos de un controlador de circuito personalizado producen una excepción no controlada, la excepción es grave para el circuito de . Para tolerar excepciones en el código de un controlador o en los métodos llamados, encapsule el código en una o varias instrucciones try-catch con control de errores y registro.

Cuando un circuito finaliza porque un usuario se ha desconectado y el marco está limpiando el estado del circuito, el marco desecha el ámbito de DI del circuito. Al desechar el ámbito, se eliminan también todos los servicios de DI con ámbito del circuito que implementan System.IDisposable. Si un servicio de DI produce una excepción no controlada durante la eliminación, el marco registra la excepción. Para obtener más información, vea Inserción de dependencias de Blazor de ASP.NET Core.

Controlador de circuito del lado servidor para capturar usuarios para servicios personalizados

Use un CircuitHandler para capturar un usuario del AuthenticationStateProvider y establecer ese usuario en un servicio. Para más información y código de ejemplo, consulte ASP.NET Core Blazor en el lado del servidor: escenarios de seguridad adicionales.

Cierre de circuitos cuando no haya componentes del servidor interactivo restantes

Los componentes del Servidor interactivo controlan los eventos de la interfaz de usuario web mediante una conexión en tiempo real con el explorador denominado circuito. Un circuito y su estado asociado se crean cuando se representa un componente de Servidor interactivo raíz. El circuito se cierra cuando no quedan componentes de Servidor interactivo en la página, lo que libera los recursos del servidor.

IHttpContextAccessor/HttpContext en Razor componentes

IHttpContextAccessor debe evitarse con la representación interactiva porque no hay un HttpContext válido disponible.

IHttpContextAccessor se puede usar para los componentes que se representan estáticamente en el servidor. Sin embargo, se recomienda evitarlo si es posible.

HttpContextse puede usar como parámetro en cascada solo en componentes raíz representados estáticamente para tareas generales, como inspeccionar y modificar encabezados u otras propiedades del componente App (Components/App.razor). El valor siempre es null para la representación interactiva.

[CascadingParameter]
public HttpContext? HttpContext { get; set; }

En escenarios en los que se requiere el HttpContext en componentes interactivos, se recomienda hacer fluir los datos a través del estado del componente persistente desde el servidor. Para más información, veaOtros escenarios de seguridad de Blazor en ASP.NET Core del lado servidor.

No use IHttpContextAccessor/HttpContext directa o indirectamente en los componentes Razor de las aplicaciones Blazor del lado servidor. Las aplicaciones de Blazor se ejecutan fuera del contexto de la canalización de ASP.NET Core. No se garantiza que HttpContext esté disponible en IHttpContextAccessor, ni tampoco que HttpContext contenga el contexto que ha iniciado la aplicación de Blazor.

El enfoque recomendado para pasar el estado de solicitud a la aplicación de Blazor es a través de parámetros de componente raíz durante la representación inicial de la aplicación. Como alternativa, la aplicación puede copiar los datos en un servicio con ámbito en el evento de ciclo de vida de inicialización del componente raíz para usarlos en toda la aplicación. Para más información, veaOtros escenarios de seguridad de Blazor en ASP.NET Core del lado servidor.

Un aspecto crítico de la seguridad de Blazor del lado servidor es que el usuario asociado a un circuito determinado podría actualizarse en algún momento después de establecer el circuito de Blazor, pero IHttpContextAccessorno se actualiza. Para obtener más información sobre cómo abordar esta situación con servicios personalizados, consulte: ASP.NET Core Blazor en el lado del servidor: escenarios de seguridad adicionales.

Recursos adicionales del lado servidor