SignalRASP.NET Core Cliente de JavaScript
Por Rachel Appel
La ASP.NET Core cliente de JavaScript permite a los desarrolladores SignalR llamar al código central del lado servidor.
Vea o descargue el código de ejemplo (cómo descargarlo)
Instalación del SignalR paquete de cliente
La SignalR biblioteca cliente de JavaScript se entrega como un paquete npm. En las secciones siguientes se describen las distintas formas de instalar la biblioteca cliente.
Instalación con npm
Para Visual Studio, ejecute los siguientes comandos desde Administrador de paquetes Console 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 *node_modules \ @microsoft\signalr\dist\browser* carpeta. Cree una carpeta denominada signalr en la carpeta wwwroot \ lib. Copie el signalr.js en la carpeta wwwroot\lib\signalr.
Haga referencia SignalR al cliente de JavaScript en el elemento <script> . Por ejemplo:
<script src="~/lib/signalr/signalr.js"></script>
Usar un Content Delivery Network (CDN)
Para usar la biblioteca cliente sin el requisito previo de npm, haga referencia a una CDN hospedada de la biblioteca cliente. Por ejemplo:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7/signalr.min.js"></script>
La biblioteca cliente está disponible en las siguientes CDN:
Instalación con LibMan
LibMan se puede usar para instalar archivos de biblioteca de cliente específicos desde la CDN cliente hospedada de forma local. Por ejemplo, agregue solo el archivo JavaScript minificado al proyecto. Para obtener más información sobre ese enfoque, vea Agregar la SignalR biblioteca cliente.
Conectar a un centro
El código siguiente crea e inicia una conexión. El nombre del centro no tiene en cuenta 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.
Importante
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 entre orígenes, habilitela en la Startup clase :
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");
});
}
}
}
Métodos del centro de llamadas desde el cliente
Los clientes de JavaScript llaman a métodos públicos en centros mediante el método invoke de HubConnection. El invoke método acepta:
- Nombre del método central.
- Cualquier argumento definido en el método hub.
En el ejemplo siguiente, el nombre del método en el centro es SendMessage . Segundo y tercer argumentos pasados para invoke asignarse a los argumentos user y del método message central:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Nota
La llamada a métodos de concentrador desde un cliente solo se admite cuando se usa el servicio de Azure SignalR en modo predeterminado. Para obtener más información, consulte Preguntas más frecuentes (azure-signalr GitHub repositorio).
El invoke método devuelve una promesa de JavaScript. se Promise resuelve con el valor devuelto (si existe) cuando se devuelve el método en el servidor. Si el método del servidor produce un error, Promise se rechaza con el mensaje de error. Use async y o los await Promise métodos y para controlar estos then catch casos.
Los clientes de JavaScript también pueden llamar a métodos públicos en centros a través del método send de HubConnection . A diferencia invoke del método , el método no espera una respuesta del send servidor. El send método devuelve un javascript Promise . se Promise resuelve cuando el mensaje se ha enviado al servidor. Si se produce un error al enviar el mensaje, Promise se rechaza con el mensaje de error. Use async y o los await Promise métodos y para controlar estos then catch casos.
Nota
El send uso de no espera hasta que el servidor ha 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 del concentrador, defina un método mediante el método on de HubConnection .
- Nombre del método de cliente de JavaScript.
- Argumentos que el concentrador 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 de cliente se debe llamar haciendo coincidir el nombre del método y los argumentos definidos en SendAsync y connection.on .
Nota
Como procedimiento recomendado, llame al método start en después HubConnection de on . Al hacerlo, se asegura de que los controladores se registran antes de recibir los mensajes.
Registro y control de errores
Use try y con y o el método de para controlar los errores del lado catch async await Promise catch cliente. Use console.error para generar errores en la consola del explorador:
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 cuando se realiza la conexión. Los mensajes se registran con el nivel de registro especificado y superior. Los niveles de registro disponibles son los siguientes:
signalR.LogLevel.Error: mensajes de error. SoloErrorregistra mensajes.signalR.LogLevel.Warning: mensajes de advertencia sobre posibles errores. RegistraWarningyErrormensajes.signalR.LogLevel.Information: mensajes de estado sin errores. RegistraInformationlos mensajes , yWarningError.signalR.LogLevel.Trace: mensajes de seguimiento. Registra todo, incluidos los datos que se transportan 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 conectar automáticamente
El cliente de JavaScript para se puede configurar para volver a conectarse automáticamente SignalR mediante el método en withAutomaticReconnect 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, configura el cliente para esperar 0, 2, 10 y 30 segundos respectivamente antes de intentar cada intento de reconexión, deteniéndose después de cuatro withAutomaticReconnect() intentos fallidos.
Antes de iniciar los intentos de reconexión, pasará al estado y activará sus devoluciones de llamada en lugar de realizar la transición al estado y desencadenar sus devoluciones de llamada como sin necesidad de volver HubConnection HubConnectionState.Reconnecting a onreconnecting Disconnected onclose HubConnection conectarse automáticamente 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("messagesList").appendChild(li);
});
Si el cliente se vuelve a conectar correctamente dentro de sus primeros cuatro intentos, volverá a pasar al estado y HubConnection Connected se desenlazará onreconnected sus devoluciones de llamada. Esto proporciona una oportunidad para informar a los usuarios de que se ha restablecido la conexión.
Puesto que la conexión parece completamente nueva para el servidor, se proporciona un nuevo a connectionId la devolución onreconnected de llamada.
Advertencia
El onreconnected parámetro de la devolución de llamada será connectionId indefinido si se HubConnection 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("messagesList").appendChild(li);
});
withAutomaticReconnect() no configurará para reintentar los errores iniciales de inicio, por lo que los errores de inicio deben HubConnection 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 vuelve a conectar correctamente dentro de sus primeros cuatro intentos, el pasará al estado y se desenlazará sus HubConnection devoluciones de llamada de Disconnected cierre. Esto proporciona una oportunidad para informar a los usuarios de que la conexión se ha perdido permanentemente y recomendar 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("messagesList").appendChild(li);
});
Para configurar un número personalizado de intentos de reconexión antes de desconectarse o cambiar el tiempo de reconexión, acepta una matriz de números que representa el retraso en milisegundos que se debe esperar antes de iniciar cada intento de withAutomaticReconnect reconexión.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect([0, 0, 10000])
.build();
// .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
En el ejemplo anterior se configura para que empiece a intentar volver a HubConnection conectarse 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, que es de nuevo como la configuración predeterminada.
A continuación, el comportamiento personalizado diverge de nuevo del comportamiento predeterminado al detenerse después del tercer error de intento de reconexión en lugar de intentar un intento de reconexión más en otros 30 segundos como lo haría en la configuración predeterminada.
Si desea tener aún más control sobre el tiempo y el número de intentos de reconexión automática, acepta un objeto que implementa la interfaz , que tiene withAutomaticReconnect un único método denominado IRetryPolicy nextRetryDelayInMilliseconds .
nextRetryDelayInMilliseconds toma un único argumento con el tipo RetryContext . tiene RetryContext tres propiedades: previousRetryCount , y que son , y , y , elapsedMilliseconds retryReason number number Error respectivamente. Antes del primer intento de reconexión, y serán cero, y será el error que hizo que se previousRetryCount elapsedMilliseconds perdiera la retryReason conexión. Después de cada intento de reintento con error, se incrementará en uno, se actualizará para reflejar la cantidad de tiempo empleado en volver a conectarse hasta ahora en milisegundos y será el error que provocó el último error en el intento de previousRetryCount elapsedMilliseconds retryReason reconexión.
nextRetryDelayInMilliseconds debe devolver un número que representa el número de milisegundos que hay que esperar antes del siguiente intento de reconexión o si debe dejar de volver a null HubConnection conectarse.
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 Volver a conectar manualmente.
Volver a conectar manualmente
El código siguiente 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
startla función en el controlador de eventos de laoncloseconexió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 explorador en estado de inmoción
Algunos exploradores tienen una característica de inmovilización o suspensión de pestañas para reducir el uso de recursos de equipo para las pestañas inactivas. Esto puede hacer que SignalR las conexiones se cierren y puede dar lugar a una experiencia de usuario no deseada. Los exploradores usan la heurística para averiguar si se debe poner en suspensión una pestaña, como:
- Reproducción de audio
- Mantener un bloqueo web
- Mantener un
IndexedDBbloqueo - Estar conectado a un dispositivo USB
- Captura de vídeo o audio
- Creación de reflejo
- Captura de una ventana o una pantalla
Nota
Estas heurísticas pueden cambiar con el tiempo o diferir entre exploradores. Compruebe la matriz de compatibilidad y averiguar qué método funciona mejor para sus escenarios.
Para evitar poner una aplicación en suspensión, la aplicación debe desencadenar una de las heurísticas que usa el explorador.
En el ejemplo de código siguiente se muestra cómo usar un bloqueo web para mantener una pestaña activa 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 ( ) se almacena para que el bloqueo se pueda liberar cuando sea aceptable que
lockResolverla pestaña se bloquee. - Al cerrar la conexión, el bloqueo se libera mediante una llamada a
lockResolver(). Cuando se libera el bloqueo, se permite que la pestaña entre en suspensión.
Recursos adicionales
Por Rachel Appel
La ASP.NET Core cliente de JavaScript permite a los desarrolladores SignalR llamar al código central del lado servidor.
Vea o descargue el código de ejemplo (cómo descargarlo)
Instalación del SignalR paquete de cliente
La SignalR biblioteca cliente de JavaScript se entrega como un paquete npm. En las secciones siguientes se describen diferentes maneras de instalar la biblioteca cliente.
Instalación con npm
Si usa Visual Studio, ejecute los siguientes comandos desde Administrador de paquetes Console 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 @aspnet/signalr
npm instala el contenido del paquete en *node_modules \ @aspnet\signalr\dist\browser* carpeta. Cree una carpeta denominada signalr en la carpeta wwwroot \ lib. Copie el signalr.js en la carpeta wwwroot\lib\signalr.
Haga referencia SignalR al cliente de JavaScript en el elemento <script> . Por ejemplo:
<script src="~/lib/signalr/signalr.js"></script>
Usar un Content Delivery Network (CDN)
Para usar la biblioteca cliente sin el requisito previo de npm, haga referencia a una CDN hospedada en la biblioteca cliente. Por ejemplo:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.3/signalr.min.js"></script>
La biblioteca cliente está disponible en las siguientes CDN:
Instalación con LibMan
LibMan se puede usar para instalar archivos de biblioteca cliente específicos desde la CDN cliente hospedada de forma local. Por ejemplo, agregue solo el archivo JavaScript minificado al proyecto. Para obtener más información sobre ese enfoque, vea Agregar la SignalR biblioteca cliente.
Conectar a un centro
El código siguiente crea e inicia una conexión. El nombre del centro no tiene en cuenta las mayúsculas y minúsculas.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.configureLogging(signalR.LogLevel.Information)
.build();
async function start() {
try {
await connection.start();
console.log("connected");
} catch (err) {
console.log(err);
setTimeout(() => start(), 5000);
}
};
connection.onclose(async () => {
await start();
});
// Start the connection.
start();
/* this is here to show an alternative to start, with a then
connection.start().then(() => console.log("connected"));
*/
/* this is here to show another alternative to start, with a catch
connection.start().catch(err => console.error(err));
*/
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.
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, habilitela en la Startup clase .
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SignalRChat.Hubs;
namespace SignalRChat
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc();
services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder.AllowAnyMethod().AllowAnyHeader()
.WithOrigins("http://localhost:55830")
.AllowCredentials();
}));
services.AddSignalR();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseCors("CorsPolicy");
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/chathub");
});
app.UseMvc();
}
}
}
Métodos del centro de llamadas desde el cliente
Los clientes de JavaScript llaman a métodos públicos en centros mediante el método invoke de HubConnection. El invoke método acepta dos argumentos:
Nombre del método de concentrador. En el ejemplo siguiente, el nombre del método en el centro es
SendMessage.Cualquier argumento definido en el método hub. En el ejemplo siguiente, el nombre del argumento es
message. El código de ejemplo usa la sintaxis de función de flecha que se admite en las versiones actuales de todos los exploradores principales excepto Internet Explorer.connection.invoke("SendMessage", user, message).catch(err => console.error(err));
Nota
La llamada a métodos de concentrador desde un cliente solo se admite cuando se usa el servicio de Azure SignalR en modo predeterminado. Para más información, consulte Preguntas más frecuentes (azure-signalr GitHub repositorio).
El invoke método devuelve una promesa de JavaScript. se Promise resuelve con el valor devuelto (si existe) cuando se devuelve el método en el servidor. Si el método del servidor produce un error, Promise se rechaza con el mensaje de error. Use los then catch métodos y en Promise el propio para controlar estos casos (o await sintaxis).
El send método devuelve un javascript Promise . se Promise resuelve cuando el mensaje se ha enviado al servidor. Si se produce un error al enviar el mensaje, Promise se rechaza con el mensaje de error. Use los then catch métodos y en Promise el propio para controlar estos casos (o await sintaxis).
Nota
El send uso de no espera hasta que el servidor ha 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 del concentrador, defina un método mediante el método on de HubConnection .
- Nombre del método de cliente de JavaScript. En el ejemplo siguiente, el nombre del método es
ReceiveMessage. - Argumentos que el concentrador pasa al método . En el ejemplo siguiente, el valor del argumento es
message.
connection.on("ReceiveMessage", (user, message) => {
const encodedMsg = `${user} says ${message}`;
const li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").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 de cliente se debe llamar haciendo coincidir el nombre del método y los argumentos definidos en SendAsync y connection.on .
Nota
Como procedimiento recomendado, llame al método start en después HubConnection de on . Al hacerlo, se asegura de que los controladores se registran antes de recibir los mensajes.
Registro y control de errores
catchEncadenar un método al final del método para controlar los errores del lado start cliente. Use console.error para generar errores en la consola del explorador.
connection.start().catch(err => console.error(err));
Configure el seguimiento de registros del lado cliente pasando un registrador y un tipo de evento para registrar cuando se realiza la conexión. Los mensajes se registran con el nivel de registro especificado y superior. Los niveles de registro disponibles son los siguientes:
signalR.LogLevel.Error: mensajes de error. SoloErrorregistra mensajes.signalR.LogLevel.Warning: mensajes de advertencia sobre posibles errores. RegistraWarningyErrormensajes.signalR.LogLevel.Information: mensajes de estado sin errores. RegistraInformationlos mensajes , yWarningError.signalR.LogLevel.Trace: mensajes de seguimiento. Registra todo, incluidos los datos que se transportan 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 conectar manualmente
Advertencia
Antes de la versión 3.0, el cliente de JavaScript para SignalR no se vuelve a conectar automáticamente. Debe escribir código que vuelva a conectar el cliente manualmente.
El código siguiente muestra un enfoque típico de reconexión manual:
- Se crea una función (en este caso,
startla función) para iniciar la conexión. - Llame a
startla función en el controlador de eventos de laoncloseconexión.
async function start() {
try {
await connection.start();
console.log("connected");
} catch (err) {
console.log(err);
setTimeout(() => start(), 5000);
}
};
connection.onclose(async () => {
await start();
});
Una implementación real usaría un retroceso exponencial o reintentaría un número especificado de veces antes de dar por hecho.