Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
La biblioteca de cliente JavaScript de ASP.NET Core SignalR permite a los desarrolladores llamar al código del hub del servidor SignalR. En este artículo se describe cómo usar las API para conectarse a un SignalR hub y llamar a los métodos del hub y del cliente de JavaScript.
Vea o descargue el código de ejemplo (cómo descargarlo)
Instalación del paquete cliente SignalR
La biblioteca de cliente de JavaScript SignalR se entrega como un paquete npm. Hay varias maneras de instalar la biblioteca cliente:
- Ejecute comandos
npmen Visual Studio Administrador de paquetes Console. - Ejecute comandos
npmen el terminal integrado de Visual Studio Code. - Haga referencia a una copia de la biblioteca cliente hospedada en una red de entrega de contenido (CDN).
- Use LibMan e instale archivos de biblioteca cliente específicos desde una biblioteca cliente hospedada en CDN.
Instalación con npm
Ejecute los siguientes comandos npm en la ventana Administrador de paquetes Console:
npm init -y
npm install @microsoft/signalr
npm instala el contenido del paquete en la carpeta node_modules\@microsoft\signalr\dist\browser .
Completa la configuración:
Cree la carpeta wwwroot/lib/signalr .
Copie el archivo signalr.js en la carpeta wwwroot/lib/signalr .
Ahora puede hacer referencia al cliente de JavaScript instalado en el elemento SignalR. Por ejemplo:
<script src="~/lib/signalr/signalr.js"></script>
Uso de una red CDN
Para usar la biblioteca cliente sin el requisito previo de npm, haga referencia a una copia hospedada en CDN de la biblioteca cliente. Por ejemplo:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>
El ejemplo de código especifica la versión 6.0.1. Para obtener la versión más reciente de la biblioteca cliente, elija una de las siguientes redes CDN:
Instalación con LibMan
Otro enfoque consiste en usar LibMan e instalar solo archivos de biblioteca cliente específicos desde la biblioteca cliente hospedada en CDN. Por ejemplo, solo puede agregar el archivo JavaScript mínimo al proyecto.
Para obtener más información sobre este enfoque, consulte Incorporación de la SignalR biblioteca cliente.
Conexión a un centro
El código siguiente crea e inicia una conexión. El nombre del centro no distingue mayúsculas de minúsculas:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
// Start the connection.
start();
Conexiones entre distintos orígenes (CORS)
Normalmente, los exploradores cargan conexiones desde el mismo dominio que la página solicitada. Sin embargo, hay ocasiones en las que se requiere una conexión a otro dominio.
Para las solicitudes entre dominios, el código de cliente debe usar una dirección URL absoluta en lugar de una dirección URL relativa. Cuando use solicitudes entre dominios, cambie .withUrl("/chathub") a .withUrl("https://{App domain name}/chathub").
Para evitar que un sitio malintencionado lea datos confidenciales de otro sitio, las conexiones entre orígenes están deshabilitadas de forma predeterminada. Para permitir una solicitud entre orígenes, habilite CORS:
using SignalRChat.Hubs;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
builder.WithOrigins("https://example.com")
.AllowAnyHeader()
.WithMethods("GET", "POST")
.AllowCredentials();
});
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
// UseCors must be called before MapHub.
app.UseCors();
app.MapRazorPages();
app.MapHub<ChatHub>("/chatHub");
app.Run();
Se debe llamar al método UseCors antes de llamar al método MapHub.
Llamada a los métodos del centro desde el cliente
Los clientes de JavaScript llaman a métodos públicos en centros a través del método invoke de HubConnection. El método invoke acepta:
- Nombre del método de concentrador.
- Cualquiera de los argumentos definidos en el método del hub.
En el código resaltado siguiente, el nombre del método en el centro es SendMessage. El segundo y el tercer argumento que se pasan a invoke se corresponden con los parámetros user y message del método del hub:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
La llamada a métodos del hub desde un cliente solo se admite cuando se usa el servicio Azure SignalR en modo predeterminado.
El invoke método devuelve un objeto Promise de JavaScript. El objeto Promise se resuelve con el valor devuelto (si lo hay) cuando el método del servidor devuelve un resultado. Si el método del servidor produce un error, el Promise objeto se rechaza con el mensaje de error. Para gestionar estos casos, use async y await o los métodos Promise y then del objeto catch.
Los clientes de JavaScript también pueden llamar a métodos públicos de los hubs a través del método send del HubConnection. A diferencia del método invoke, el método send no espera una respuesta del servidor. El send método devuelve un objeto JavaScript Promise . El Promise objeto se resuelve cuando el mensaje se envía al servidor. Si se produce un error al enviar el mensaje, el Promise objeto se rechaza con el mensaje de error. Para gestionar estos casos, use async y await o los métodos Promise y then del objeto catch.
El uso de sendno espera a que el servidor reciba el mensaje, por lo que no es posible devolver datos ni errores desde el servidor.
Llamada a métodos de cliente desde el centro
Para recibir mensajes del centro, defina un método mediante el método on de HubConnection. El método on acepta:
- Nombre del método cliente de JavaScript.
- Argumentos que el centro pasa al método .
En el ejemplo siguiente, el nombre del método es ReceiveMessage. Los nombres de argumento son user y message:
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messageList").appendChild(li);
});
El código anterior de connection.on se ejecuta cuando el código del lado servidor lo llama mediante el SendAsync método :
using Microsoft.AspNetCore.SignalR;
namespace SignalRChat.Hubs;
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
SignalR determina a qué método cliente se debe llamar haciendo coincidir el nombre del método y los argumentos definidos en SendAsync y connection.on.
Un procedimiento recomendado es llamar al método start en HubConnection después de on. Este enfoque garantiza que los controladores se registren antes de recibir los mensajes.
Registro y control de errores
Use console.error para mostrar errores en la consola del navegador cuando el cliente no pueda conectarse o enviar un mensaje:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Configure el seguimiento de registros del lado cliente pasando un registrador y un tipo de evento para registrar cuándo se realiza la conexión. Los mensajes se registran con el nivel de registro especificado y superiores. Los niveles de registro disponibles son los siguientes:
| Nivel de registro | Datos registrados | Descripción |
|---|---|---|
signalR.LogLevel.Error |
Mensajes de error | Solo registra mensajes de Error. |
signalR.LogLevel.Warning |
Mensajes de advertencia sobre posibles errores | Registra mensajes Warning y Error. |
signalR.LogLevel.Information |
Mensajes de estado sin errores | Registra mensajes de Information, Warning y Error. |
signalR.LogLevel.Trace |
Mensajes de seguimiento | Registra todo, incluidos los datos transportados entre el centro y el cliente. |
Use el método configureLogging en HubConnectionBuilder para configurar el nivel de registro. Los mensajes se registran en la consola del explorador:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
Volver a conectar clientes
Para volver a conectarse con clientes, puede configurar la reconexión automática o configurar la conexión manualmente.
Volver a conectarse automáticamente
El cliente de JavaScript para SignalR se puede configurar para volver a conectarse automáticamente mediante el método WithAutomaticReconnect en HubConnectionBuilder. De forma predeterminada, no se vuelve a conectar automáticamente.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect()
.build();
Sin ningún parámetro, WithAutomaticReconnect configura el cliente para que espere 0, 2, 10 y 30 segundos respectivamente antes de intentar cada intento de reconexión. Después de cuatro intentos fallidos, deja de intentar volver a conectarse.
Antes de iniciar cualquier reconexión, el HubConnection:
- Pasa al estado HubConnectionState.Reconnecting y activa sus
onreconnectingdevoluciones de llamada. - No pasa al estado
Disconnectedni activa susonclosedevoluciones de llamada como unHubConnectionsin reconexión automática configurada.
El enfoque de reconexión proporciona una oportunidad para advertir a los usuarios de que la conexión se pierde y para deshabilitar los elementos de la interfaz de usuario.
connection.onreconnecting(error => {
console.assert(connection.state === signalR.HubConnectionState.Reconnecting);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection lost due to error "${error}". Reconnecting.`;
document.getElementById("messageList").appendChild(li);
});
Si el cliente se reconecta correctamente en sus cuatro primeros intentos, HubConnection vuelve al estado Connected y activa sus callbacks onreconnected. Este enfoque proporciona una oportunidad para informar a los usuarios de que la conexión se ha restablecido.
Dado que la conexión parece completamente nueva para el servidor, se proporciona un nuevo connectionId a la devolución de llamada onreconnected.
El parámetro connectionId de la devolución de llamada onreconnected es indefinido si el HubConnection está configurado para omitir la negociación.
connection.onreconnected(connectionId => {
console.assert(connection.state === signalR.HubConnectionState.Connected);
document.getElementById("messageInput").disabled = false;
const li = document.createElement("li");
li.textContent = `Connection reestablished. Connected with connectionId "${connectionId}".`;
document.getElementById("messageList").appendChild(li);
});
withAutomaticReconnect no configura el/la HubConnection para reintentar los fallos iniciales de arranque, por lo que los fallos de arranque deben gestionarse manualmente:
async function start() {
try {
await connection.start();
console.assert(connection.state === signalR.HubConnectionState.Connected);
console.log("SignalR Connected.");
} catch (err) {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
console.log(err);
setTimeout(() => start(), 5000);
}
};
Si el cliente no se reconecta con éxito en sus cuatro primeros intentos, el HubConnection pasa al estado Disconnected y desencadena sus devoluciones de llamada onclose. Este enfoque proporciona una oportunidad para informar a los usuarios de que la conexión se pierde permanentemente y para intentar actualizar la página:
connection.onclose(error => {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection closed due to error "${error}". Try refreshing this page to restart the connection.`;
document.getElementById("messageList").appendChild(li);
});
Para configurar un número personalizado de intentos de reconexión antes de desconectar o cambiar el tiempo de reconexión, withAutomaticReconnect acepta una matriz de números que representa el retraso en milisegundos para esperar antes de iniciar cada intento de reconexión.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect([0, 0, 10000])
.build();
// .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
El ejemplo anterior configura el HubConnection para que empiece a intentar reconectarse inmediatamente después de que se pierda la conexión. La configuración predeterminada también espera cero (0) segundos para intentar volver a conectarse.
Si se produce un error en el primer intento de reconexión, el segundo intento de reconexión también se inicia inmediatamente en lugar de esperar 2 segundos como se define en la configuración predeterminada.
Si se produce un error en el segundo intento de reconexión, el tercer intento de reconexión se inicia en 10 segundos, que es el mismo comportamiento definido en la configuración predeterminada.
El tiempo de reconexión configurado difiere del comportamiento predeterminado deteniendo después del tercer error de intento de reconexión. En la configuración predeterminada, se realiza un intento de reconexión más después de otros 30 segundos.
Para un mayor control sobre el tiempo y el número de intentos de reconexión automática, withAutomaticReconnect acepta un objeto que implemente la interfaz IRetryPolicy, que tiene un único método llamado nextRetryDelayInMilliseconds.
nextRetryDelayInMilliseconds toma un único argumento con el tipo RetryContext.
RetryContext Tiene tres propiedades: previousRetryCount (tipo number), elapsedMilliseconds (tipo number) y retryReason (tipo Error).
Antes del primer intento de reconexión,
previousRetryCountyelapsedMillisecondsson cero (0) yretryReasones el error que provocó la conexión perdida.Después de cada intento de reintento con error,
previousRetryCountse incrementa en uno,elapsedMillisecondsse actualiza para reflejar la cantidad de tiempo invertido en volver a conectarse hasta ahora en milisegundos, yretryReasones el error que provocó el último intento de reconexión.
nextRetryDelayInMilliseconds debe devolver un número que represente el número de milisegundos que hay que esperar antes del siguiente intento de reconexión o null si HubConnection debe dejar de reconectarse.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: retryContext => {
if (retryContext.elapsedMilliseconds < 60000) {
// If we've been reconnecting for less than 60 seconds so far,
// wait between 0 and 10 seconds before the next reconnect attempt.
return Math.random() * 10000;
} else {
// If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
return null;
}
}
})
.build();
Como alternativa, puede escribir código para volver a conectar el cliente manualmente, como se muestra en la sección siguiente.
Volver a conectarse manualmente
En el código siguiente se muestra un enfoque típico de reconexión manual:
Para iniciar la conexión, cree una función. En este caso, la función
start.Llame a la función
starten el controlador de eventosonclosede la conexión.
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
Las implementaciones de producción suelen usar un retroceso exponencial o reintentar un número especificado de veces.
Pestaña de suspensión del explorador
Algunos navegadores tienen una función de congelación de pestañas o suspensión de pestañas que reduce el uso de recursos del ordenador en las pestañas inactivas. Sin embargo, esta funcionalidad puede hacer SignalR que las conexiones se cierren y provoquen una experiencia de usuario no deseada.
Los exploradores implementan heurística para determinar si una pestaña se debe poner en suspensión, como:
- Reproducción de audio
- Mantener un bloqueo de la web
- Mantener un bloqueo
IndexedDB - Estar conectado a un dispositivo USB
- Capturar vídeo o audio
- Crear un reflejo
- Capturar una ventana o pantalla
La heurística puede cambiar con el tiempo y también diferir entre exploradores. Compruebe la matriz de compatibilidad y descubra qué método funciona mejor para sus escenarios.
Para evitar poner una aplicación en suspensión, esta debe activar una de las heurísticas que utiliza el navegador.
En el ejemplo de código siguiente se muestra cómo usar un bloqueo web para mantener activa una pestaña y evitar un cierre de conexión inesperado:
var lockResolver;
if (navigator && navigator.locks && navigator.locks.request) {
const promise = new Promise((res) => {
lockResolver = res;
});
navigator.locks.request('unique_lock_name', { mode: "shared" }, () => {
return promise;
});
}
En el código anterior:
- Los bloqueos web son experimentales. La comprobación condicional confirma que el explorador admite bloqueos web.
- El resolvedor de promesas de JavaScript (
lockResolver) se almacena para poder liberar el bloqueo cuando sea aceptable que la pestaña se suspenda. - Cuando se cierra la conexión, el bloqueo se libera llamando a
lockResolver(). Cuando se libera el bloqueo, la pestaña puede suspenderse.
Actualizar la autenticación sin volver a conectarse
En .NET 11 y versiones posteriores, un cliente puede reemplazar las credenciales de una conexión abierta en lugar de permitir que la conexión se cierre cuando expire su token de acceso. El servidor debe habilitar la actualización de autenticación para el centro. Llame withAuthenticationRefresh a para programar una actualización automática antes de la expiración que notifica el servidor:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub", { accessTokenFactory: () => getAccessToken() })
.withAuthenticationRefresh()
.build();
Cada actualización llama accessTokenFactory de nuevo para obtener un nuevo token. Para actualizar inmediatamente, por ejemplo, después de que la aplicación obtenga notificaciones actualizadas, llame a connection.refreshAuthentication().
Para las opciones de actualización, los controladores de éxito y error, y la configuración del servidor que habilita esta funcionalidad, consulte SignalR autenticación y autorización.
Contenido relacionado
Por Rachel Appel
La biblioteca cliente de JavaScript de ASP.NET Core SignalR permite a los desarrolladores llamar al código del hub en el lado servidor.
Vea o descargue el código de ejemplo (cómo descargarlo)
Instalación del paquete cliente SignalR
La biblioteca de cliente de JavaScript SignalR se entrega como un paquete npm. En las secciones siguientes se describen diferentes formas de instalar la biblioteca cliente.
Instalación con npm
Para Visual Studio, ejecute los siguientes comandos desde la consola del administrador de paquetes mientras se encuentra en la carpeta raíz. Para Visual Studio Code, ejecute los siguientes comandos desde el Terminal integrado.
npm init -y
npm install @microsoft/signalr
npm instala el contenido del paquete en la carpeta node_modules\@microsoft\signalr\dist\browser . Cree una carpeta denominada signalr en la carpeta wwwroot\lib . Copie el signalr.js archivo en la carpeta wwwroot\lib\signalr .
Haga referencia al cliente de JavaScript SignalR en el elemento <script>. Por ejemplo:
<script src="~/lib/signalr/signalr.js"></script>
Uso de una red de entrega de contenido (CDN)
Para usar la biblioteca cliente sin el requisito previo de npm, haga referencia a una copia hospedada en CDN de la biblioteca cliente. Por ejemplo:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7/signalr.js"></script>
La biblioteca cliente está disponible en las siguientes redes CDN:
Instalación con LibMan
LibMan se puede usar para instalar archivos de biblioteca cliente específicos desde la biblioteca cliente hospedada en CDN. Por ejemplo, agregue solo el archivo JavaScript minimizado al proyecto. Para más detalles sobre ese método, consulte Agregue la biblioteca cliente SignalR.
Conexión a un centro
El código siguiente crea e inicia una conexión. El nombre del centro no distingue mayúsculas de minúsculas:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
// Start the connection.
start();
Conexiones entre orígenes
Normalmente, los exploradores cargan conexiones desde el mismo dominio que la página solicitada. Sin embargo, hay ocasiones en las que se requiere una conexión a otro dominio.
Important
El código de cliente debe usar una dirección URL absoluta en lugar de una dirección URL relativa. Cambio de .withUrl("/chathub") a .withUrl("https://myappurl/chathub").
Para evitar que un sitio malintencionado lea datos confidenciales de otro sitio, las conexiones entre orígenes están deshabilitadas de forma predeterminada. Para permitir una solicitud de origen cruzado, habilítela en la clase Startup:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SignalRChat.Hubs;
namespace SignalRChat
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSignalR();
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.WithOrigins("https://example.com")
.AllowCredentials();
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chathub");
});
}
}
}
Llamada a los métodos del centro desde el cliente
Los clientes de JavaScript llaman a métodos públicos en centros a través del método invoke de HubConnection. El método invoke acepta:
- Nombre del método de concentrador.
- Cualquiera de los argumentos definidos en el método del hub.
En el siguiente ejemplo, el nombre del método en el centro es SendMessage. El segundo y el tercer argumento que se pasan a invoke se corresponden con los parámetros user y message del método del hub:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Note
La llamada a métodos del concentrador desde un cliente solo se admite al usar el servicio Azure SignalR en modo predeterminado.
El método invoke devuelve un objeto Promise de JavaScript.
Promise se resuelve con el valor devuelto (si existe) cuando el método del servidor devuelve. Si el método del servidor produce un error, Promise se rechaza con el mensaje de error. Use async y await o los métodos Promise y then de catch para controlar estos casos.
Los clientes de JavaScript también pueden llamar a métodos públicos de los hubs a través del método send del HubConnection. A diferencia del método invoke, el método send no espera una respuesta del servidor. El método send devuelve un objeto Promise de JavaScript. El Promise se resuelve cuando el mensaje ha sido enviado al servidor. Si se produce un error al enviar el mensaje, Promise se rechaza con el mensaje de error. Use async y await o los métodos Promise y then de catch para controlar estos casos.
Note
El uso de send no espera a que el servidor haya recibido el mensaje. Por lo tanto, no es posible devolver datos o errores del servidor.
Llamada a métodos de cliente desde el centro
Para recibir mensajes desde el centro, defina un método mediante el método on del HubConnection.
- Nombre del método cliente de JavaScript.
- Argumentos que el centro pasa al método .
En el ejemplo siguiente, el nombre del método es ReceiveMessage. Los nombres de argumento son user y message:
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messageList").appendChild(li);
});
El código anterior de connection.on se ejecuta cuando el código del lado servidor lo llama mediante el método SendAsync:
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
SignalR determina a qué método cliente se debe llamar haciendo coincidir el nombre del método y los argumentos definidos en SendAsync y connection.on.
Note
Un procedimiento recomendado es llamar al método start en HubConnection después de on. Al hacerlo, se asegura de que los controladores se registren antes de que se reciban mensajes.
Registro y control de errores
Utilice try y catch con async y await o el método Promise de catch para controlar los errores del lado del cliente. Use console.error para mostrar errores en la consola del navegador:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Configure el seguimiento de registros del lado cliente pasando un registrador y un tipo de evento para registrar cuándo se realiza la conexión. Los mensajes se registran con el nivel de registro especificado y superiores. Los niveles de registro disponibles son los siguientes:
-
signalR.LogLevel.Error: mensajes de error. Solo registra mensajes deError. -
signalR.LogLevel.Warning: mensajes de advertencia sobre posibles errores. Registra mensajes deWarningyError. -
signalR.LogLevel.Information: mensajes de estado sin errores. Registra mensajes deInformation,WarningyError. -
signalR.LogLevel.Trace: mensajes de seguimiento. Registra todo, incluidos los datos transportados entre el centro y el cliente.
Use el método configureLogging en HubConnectionBuilder para configurar el nivel de registro. Los mensajes se registran en la consola del explorador:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
Volver a conectar clientes
Volver a conectarse automáticamente
El cliente de JavaScript para SignalR puede configurarse para reconectarse automáticamente usando el método withAutomaticReconnect en HubConnectionBuilder. No se volverá a conectar automáticamente de forma predeterminada.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect()
.build();
Sin ningún parámetro, withAutomaticReconnect() configura el cliente para que espere 0, 2, 10 y 30 segundos respectivamente antes de intentar cada intento de reconexión, deteniéndose tras cuatro intentos fallidos.
Antes de iniciar cualquier intento de reconexión, el HubConnection pasará al estado HubConnectionState.Reconnecting y ejecutará sus callbacks de onreconnecting en lugar de pasar al estado Disconnected y ejecutar sus callbacks de onclose, como lo haría un HubConnection sin reconexión automática configurada. Esto proporciona una oportunidad para advertir a los usuarios de que se ha perdido la conexión y deshabilitar los elementos de la interfaz de usuario.
connection.onreconnecting(error => {
console.assert(connection.state === signalR.HubConnectionState.Reconnecting);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection lost due to error "${error}". Reconnecting.`;
document.getElementById("messageList").appendChild(li);
});
Si el cliente se reconecta con éxito en sus cuatro primeros intentos, el HubConnection vuelve al estado Connected y desencadena sus devoluciones de llamada de onreconnected. Esto proporciona la oportunidad de informar a los usuarios de la conexión que se ha restablecido.
Dado que la conexión parece totalmente nueva para el servidor, se proporciona un nuevo connectionId a la devolución de llamada de onreconnected.
Warning
El parámetro connectionId de la devolución de llamada onreconnected será indefinido si HubConnection se configuró para omitir la negociación.
connection.onreconnected(connectionId => {
console.assert(connection.state === signalR.HubConnectionState.Connected);
document.getElementById("messageInput").disabled = false;
const li = document.createElement("li");
li.textContent = `Connection reestablished. Connected with connectionId "${connectionId}".`;
document.getElementById("messageList").appendChild(li);
});
withAutomaticReconnect() no configurará el HubConnection para reintentar los fallos de arranque iniciales, por lo que los fallos de arranque deben controlarse manualmente:
async function start() {
try {
await connection.start();
console.assert(connection.state === signalR.HubConnectionState.Connected);
console.log("SignalR Connected.");
} catch (err) {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
console.log(err);
setTimeout(() => start(), 5000);
}
};
Si el cliente no se reconecta con éxito en sus cuatro primeros intentos, HubConnection pasa al estado Disconnected y desencadena sus devoluciones de llamada onclose. Esto proporciona la oportunidad de informar a los usuarios de que la conexión se ha perdido permanentemente y recomendar la actualización de la página:
connection.onclose(error => {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection closed due to error "${error}". Try refreshing this page to restart the connection.`;
document.getElementById("messageList").appendChild(li);
});
Para configurar un número personalizado de intentos de reconexión antes de desconectar o cambiar el tiempo de reconexión, withAutomaticReconnect acepta una matriz de números que representa el retraso en milisegundos para esperar antes de iniciar cada intento de reconexión.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect([0, 0, 10000])
.build();
// .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
El ejemplo anterior configura el HubConnection para que empiece a intentar reconectarse inmediatamente después de que se pierda la conexión. Esto también es cierto para la configuración predeterminada.
Si se produce un error en el primer intento de reconexión, el segundo intento de reconexión también se iniciará inmediatamente en lugar de esperar 2 segundos como lo haría en la configuración predeterminada.
Si se produce un error en el segundo intento de reconexión, el tercer intento de reconexión se iniciará en 10 segundos, como la configuración predeterminada.
Después, el comportamiento personalizado vuelve a desviarse del comportamiento predeterminado al detenerse tras el fracaso del tercer intento de reconexión en lugar de intentar una reconexión más en otros 30 segundos como haría en la configuración predeterminada.
Si quiere aún más control sobre el tiempo y el número de intentos de reconexión automática, withAutomaticReconnect acepta un objeto que implemente la interfaz IRetryPolicy, que tiene un único método llamado nextRetryDelayInMilliseconds.
nextRetryDelayInMilliseconds toma un único argumento con el tipo RetryContext. El RetryContext tiene tres propiedades: previousRetryCount, elapsedMilliseconds y retryReason que son number, number y Error respectivamente. Antes del primer intento de reconexión, tanto previousRetryCount como elapsedMilliseconds serán cero, y retryReason será el Error que provocó la pérdida de conexión. Después de cada intento de reconexión fallido, previousRetryCount se incrementará en uno, elapsedMilliseconds se actualizará para reflejar la cantidad de tiempo empleado en la reconexión hasta el momento en milisegundos, y retryReason será el Error que causó el último intento de reconexión fallido.
nextRetryDelayInMilliseconds debe devolver un número que represente el número de milisegundos que hay que esperar antes del siguiente intento de reconexión o null si HubConnection debe dejar de reconectarse.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: retryContext => {
if (retryContext.elapsedMilliseconds < 60000) {
// If we've been reconnecting for less than 60 seconds so far,
// wait between 0 and 10 seconds before the next reconnect attempt.
return Math.random() * 10000;
} else {
// If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
return null;
}
}
})
.build();
Como alternativa, puede escribir código que vuelva a conectar el cliente manualmente, como se muestra en Reconexión manual.
Volver a conectarse manualmente
En el código siguiente se muestra un enfoque típico de reconexión manual:
- Se crea una función (en este caso, la función
start) para iniciar la conexión. - Llame a la función
starten el controlador de eventosonclosede la conexión.
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
Las implementaciones de producción suelen usar un retroceso exponencial o reintentar un número especificado de veces.
Pestaña de suspensión del explorador
Algunos exploradores tienen una característica de bloqueo de pestañas o de suspensión para reducir el uso de recursos del equipo para pestañas inactivas. Esto puede provocar el cierre de conexiones de SignalR y puede dar lugar a una experiencia de usuario no deseada. Los exploradores usan heurística para averiguar si se debe poner en suspensión una pestaña, como:
- Reproducción de audio
- Mantener un bloqueo de la web
- Mantener un bloqueo
IndexedDB - Estar conectado a un dispositivo USB
- Capturar vídeo o audio
- Crear un reflejo
- Capturar una ventana o pantalla
Note
Esta heurística puede cambiar con el tiempo o diferir entre navegadores. Compruebe la matriz de compatibilidad y descubra qué método funciona mejor para sus escenarios.
Para evitar poner una aplicación en suspensión, esta debe activar una de las heurísticas que utiliza el navegador.
En el ejemplo de código siguiente se muestra cómo usar un bloqueo web para mantener activa una pestaña y evitar un cierre de conexión inesperado.
var lockResolver;
if (navigator && navigator.locks && navigator.locks.request) {
const promise = new Promise((res) => {
lockResolver = res;
});
navigator.locks.request('unique_lock_name', { mode: "shared" }, () => {
return promise;
});
}
Para el ejemplo de código anterior:
- Los bloqueos web son experimentales. La comprobación condicional confirma que el explorador admite bloqueos web.
- El solucionador de promesas (
lockResolver) se almacena para que el bloqueo se pueda liberar cuando sea aceptable que la pestaña se suspenda. - Al cerrar la conexión, el bloqueo se libera mediante una llamada a
lockResolver(). Cuando se libera el bloqueo, la pestaña puede suspenderse.