Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
A biblioteca de cliente JavaScript SignalR do ASP.NET Core permite que os desenvolvedores chamem o código de hub SignalR no servidor. Este artigo descreve como usar as APIs para se conectar a um SignalR hub e chamar o hub JavaScript e os métodos de cliente.
Exibir ou baixar código de exemplo (como baixar)
Instalar o pacote do cliente SignalR
A biblioteca do cliente JavaScript do SignalR é fornecida como um pacote npm. Há várias maneiras de instalar a biblioteca de clientes:
- Execute comandos
npmno Console do Gerenciador de Pacotes do Visual Studio. - Execute
npmcomandos no terminal integrado do Visual Studio Code. - Faça referência a uma cópia da biblioteca de clientes hospedada em uma CDN (Rede de Distribuição de Conteúdo).
- Use o LibMan e instale arquivos específicos da biblioteca do cliente de uma biblioteca do cliente hospedada em CDN.
Instalar com npm
Execute os seguintes comandos npm na janela Gerenciador de Pacotes Console:
npm init -y
npm install @microsoft/signalr
O npm instala o conteúdo do pacote na pasta node_modules\@microsoft\signalr\dist\browser.
Conclua a configuração:
Crie a pasta wwwroot/lib/signalr.
Copie o arquivo signalr.js para a pasta wwwroot/lib/signalr .
Agora você pode referenciar o cliente JavaScript instalado SignalR no <script> elemento. Por exemplo:
<script src="~/lib/signalr/signalr.js"></script>
Usar uma CDN
Para usar a biblioteca de clientes sem o pré-requisito npm, faça referência a uma cópia hospedada por CDN da biblioteca de clientes. Por exemplo:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>
O exemplo de código especifica a versão 6.0.1. Para obter a versão mais recente da biblioteca de clientes, escolha uma das seguintes CDNs:
Instalar com o LibMan
Outra abordagem é usar o LibMan e instalar apenas arquivos de biblioteca de clientes específicos da biblioteca de clientes hospedada pela CDN. Por exemplo, você pode adicionar apenas o arquivo JavaScript minificado ao projeto.
Para mais detalhes sobre essa abordagem, consulte Adicionar a biblioteca de SignalR cliente.
Conectar-se a um hub
O código a seguir cria e inicia uma conexão. O nome do hub não diferencia maiú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();
CORS (conexões entre origens)
Normalmente, os navegadores carregam conexões do mesmo domínio que a página solicitada. No entanto, há ocasiões em que uma conexão com outro domínio é necessária.
Para solicitações entre domínios, o código do cliente deve usar uma URL absoluta em vez de uma URL relativa. Quando você usa solicitações entre domínios, altere .withUrl("/chathub") para .withUrl("https://{App domain name}/chathub").
Para evitar que um site mal-intencionado leia dados confidenciais de outro site, as conexões entre origens são desabilitadas por padrão. Para permitir uma solicitação entre origens, habilite o 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();
O UseCors método deve ser chamado antes de chamar o MapHub método.
Chamar métodos do hub a partir do cliente
Os clientes JavaScript chamam métodos públicos em hubs por meio do método invoke do HubConnection. O método invoke aceita:
- O nome do método de hub.
- Todos os argumentos definidos no método de hub.
No código realçado a seguir, o nome do método no hub é SendMessage. O segundo e o terceiro argumentos passados para invoke correspondem aos argumentos user e message do método do hub:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Os métodos de hub de chamada de um cliente só têm suporte ao usar o serviço Azure SignalR no modo Padrão. Para obter mais informações, consulte perguntas frequentes (repositório GitHub do azure-signalr).
O invoke método retorna um objeto JavaScript Promise . O Promise objeto é resolvido com o valor retornado (se houver) quando o método no servidor retorna. Se o método no servidor gerar um erro, o Promise objeto será rejeitado com a mensagem de erro. Para lidar com esses casos, use async e await ou os métodos then e catch do objeto Promise.
Os clientes JavaScript também podem chamar métodos públicos em hubs por meio do método send do HubConnection. Ao contrário do método invoke, o método send não aguarda uma resposta do servidor. O send método retorna um objeto JavaScript Promise . O Promise objeto é resolvido quando a mensagem é enviada ao servidor. Se ocorrer um erro ao enviar a mensagem, o Promise objeto será rejeitado com a mensagem de erro. Para lidar com esses casos, use async e await ou os métodos then e catch do objeto Promise.
O uso sendnão aguarda até que o servidor receba a mensagem, portanto, não é possível retornar dados ou erros do servidor.
Chamar métodos do cliente a partir do hub
Para receber mensagens do hub, defina um método usando o método on do HubConnection. O método on aceita:
- O nome do método de cliente JavaScript.
- Argumentos que o hub passa para o método.
No exemplo a seguir, o nome do método é ReceiveMessage. Os nomes dos argumentos são user e message:
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messageList").appendChild(li);
});
O código anterior no connection.on é executado quando o código do lado do servidor o chama usando o método SendAsync:
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);
}
}
O SignalR determina qual método de cliente chamar correspondendo ao nome do método e aos argumentos definidos em SendAsync e connection.on.
Uma melhor prática é chamar o método start no HubConnection após on. Essa abordagem garante que os manipuladores sejam registrados antes que qualquer mensagem seja recebida.
Registro e tratamento de erros
Use console.error para gerar erros no console do navegador quando o cliente não puder se conectar ou enviar uma mensagem:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Configure o rastreamento de logs do lado do cliente passando um logger e o tipo de evento a ser registrado quando a conexão for estabelecida. As mensagens são registradas com o nível de log especificado e superior. Os níveis de log disponíveis são os seguintes:
| Nível de log | Dados registrados | Descrição |
|---|---|---|
signalR.LogLevel.Error |
Mensagens de erro | Registra somente mensagens de Error. |
signalR.LogLevel.Warning |
Mensagens de aviso sobre possíveis erros | Registra as mensagens Warning e Error. |
signalR.LogLevel.Information |
Mensagens de status sem erros | Registra mensagens de Information, Warning e Error. |
signalR.LogLevel.Trace |
Rastrear mensagens | Registra tudo, incluindo dados transportados entre o hub e o cliente. |
Use o método configureLogging no HubConnectionBuilder para configurar o nível de log. As mensagens são registradas no console do navegador:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
Reconectar clientes
Para se reconectar com clientes, você pode configurar a reconexão automática ou configurar a conexão manualmente.
Reconectar automaticamente
O cliente JavaScript de SignalR pode ser configurado para se reconectar automaticamente usando o método WithAutomaticReconnect em HubConnectionBuilder. Ele não se reconecta automaticamente por padrão.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect()
.build();
Sem parâmetros, WithAutomaticReconnect configura o cliente para aguardar 0, 2, 10 e 30 segundos, respectivamente, antes de tentar cada tentativa de reconexão. Após quatro tentativas frustradas, para de tentar se reconectar.
Antes de iniciar qualquer reconexão, o HubConnection:
- Faz a transição para o estado HubConnectionState.Reconnecting e dispara seus
onreconnectingretornos de chamada. - Não faz a transição para o estado
Disconnectednem dispara seus callbacks deonclose, como umHubConnectionsem reconexão automática configurada.
A abordagem de reconexão oferece uma oportunidade para avisar os usuários de que a conexão foi perdida e desabilitar elementos da interface do usuário.
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);
});
Se o cliente se reconectar com êxito nas quatro primeiras tentativas, o HubConnection volta ao estado Connected e dispara seus callbacks onreconnected. Essa abordagem oferece uma oportunidade para informar aos usuários que a conexão foi restabelecida.
Como a conexão parece ser totalmente nova para o servidor, um novo connectionId é fornecido ao callback onreconnected.
O parâmetro onreconnected do retorno de chamada connectionId será indefinido se o HubConnection estiver configurado para ignorar a negociação.
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 não configura o HubConnection para tentar novamente após falhas iniciais de inicialização, portanto, as falhas de inicialização precisam ser tratadas 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);
}
};
Se o cliente não se reconectar com êxito em suas quatro primeiras tentativas, o HubConnection fará a transição para o estado Disconnected e disparará seus retornos de chamada onclose. Essa abordagem oferece uma oportunidade para informar aos usuários que a conexão está permanentemente perdida e tentar atualizar a 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 um número personalizado de tentativas de reconexão antes de desconectar ou alterar o tempo de reconexão, withAutomaticReconnect aceita uma matriz de números que representam o atraso em milissegundos para aguardar antes de iniciar cada tentativa de reconexão.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect([0, 0, 10000])
.build();
// .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
O exemplo anterior configura o HubConnection para começar a tentar se conectar novamente imediatamente após a conexão ser perdida. A configuração padrão também aguarda zero (0) segundos para tentar se reconectar.
Se a primeira tentativa de reconexão falhar, a segunda tentativa de reconexão também será iniciada imediatamente em vez de aguardar 2 segundos, conforme definido na configuração padrão.
Se a segunda tentativa de reconexão falhar, a terceira tentativa de reconexão será iniciada em 10 segundos, que é o mesmo comportamento definido na configuração padrão.
O tempo de reconexão configurado difere do comportamento padrão, parando após a terceira falha de tentativa de reconexão. Na configuração padrão, mais uma tentativa de reconexão é feita após mais 30 segundos.
Para ter mais controle sobre o tempo e o número de tentativas de reconexão automática, withAutomaticReconnect aceita um objeto que implementa a interface de IRetryPolicy, que tem um único método chamado nextRetryDelayInMilliseconds.
nextRetryDelayInMilliseconds usa um único argumento com o tipo RetryContext. O RetryContext tem três propriedades: previousRetryCount (tipo number), elapsedMilliseconds (tipo number) e retryReason (tipo Error).
Antes da primeira tentativa de reconexão,
previousRetryCounteelapsedMillisecondssão ambos zero (0), eretryReasoné o erro que causou a perda de conexão.Após cada tentativa de repetição malsucedida,
previousRetryCounté incrementado em uma unidade,elapsedMillisecondsé atualizado para refletir o tempo gasto na reconexão até o momento, em milissegundos, eretryReasoné o erro que fez com que a última tentativa de reconexão falhasse.
nextRetryDelayInMilliseconds deve retornar um número que represente o número de milissegundos a serem aguardados antes da próxima tentativa de reconexão ou null se o HubConnection deve parar de se reconectar.
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, você pode escrever código para reconectar o cliente manualmente, conforme demonstrado na próxima seção.
Reconectar manualmente
O código a seguir demonstra uma abordagem típica de reconexão manual:
Para iniciar a conexão, crie uma função. Nesse caso, a função
start.Chame a função
startno manipulador de eventosoncloseda conexão.
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
Implementações de produção normalmente usam um recuo exponencial ou tentam novamente um número específico de vezes.
Aba do navegador em suspensão
Alguns navegadores têm um recurso de congelamento de guias ou de suspensão de guias que reduz o uso de recursos do computador para guias inativas. No entanto, essa funcionalidade pode fazer com que as conexões SignalR sejam fechadas e resultar em uma experiência do usuário indesejável.
Os navegadores implementam heurísticas para determinar se uma guia deve ser suspensa, tais como:
- Reproduzindo áudio
- Manter um bloqueio da Web
- Manter um bloqueio
IndexedDB - Estar conectado a um dispositivo USB
- Capturar vídeo ou áudio
- Sendo espelhado
- Capturar uma janela ou exibição
A heurística pode mudar ao longo do tempo e também difere entre navegadores. Verifique a matriz de suporte e descubra qual método funciona melhor para seus cenários.
Para evitar que um aplicativo entre em suspensão, ele deve acionar uma das heurísticas usadas pelo navegador.
O exemplo de código a seguir mostra como usar um Web Lock para manter uma aba ativa e evitar um fechamento inesperado da conexão:
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;
});
}
No código anterior apresentado:
- Os Bloqueios da Web são experimentais. A verificação condicional confirma se o navegador dá suporte a Bloqueios da Web.
- O resolvedor de promessas javaScript (
lockResolver) é armazenado para que o bloqueio possa ser liberado quando for aceitável que a guia durma. - Quando a conexão está fechando, o bloqueio é liberado chamando
lockResolver(). Quando o bloqueio é liberado, a guia pode entrar em suspensão.
Conteúdo relacionado
Por Rachel Appel
A biblioteca de cliente JavaScript do ASP.NET Core SignalR permite aos desenvolvedores chamar o código do hub no servidor.
Exibir ou baixar código de exemplo (como baixar)
Instalar o pacote do cliente SignalR
A biblioteca do cliente JavaScript do SignalR é fornecida como um pacote npm. As seções a seguir descrevem diferentes maneiras de instalar a biblioteca de clientes.
Instalar com npm
Para o Visual Studio, execute os comandos a seguir no Console do Gerenciador de Pacotes enquanto estiver na pasta raiz. Para o Visual Studio Code, execute os comandos a seguir no Terminal Integrado.
npm init -y
npm install @microsoft/signalr
O npm instala o conteúdo do pacote na pasta node_modules\@microsoft\signalr\dist\browser. Crie uma nova pasta chamada signalr na pasta wwwroot\lib. Copie o arquivo signalr.js para a pasta wwwroot\lib\signalr.
Faça referência ao SignalR cliente JavaScript no elemento <script>. Por exemplo:
<script src="~/lib/signalr/signalr.js"></script>
Usar uma CDN (Rede de Distribuição de Conteúdo)
Para usar a biblioteca de clientes sem o pré-requisito npm, faça referência a uma cópia hospedada por CDN da biblioteca de clientes. Por exemplo:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7/signalr.js"></script>
A biblioteca de clientes está disponível nas seguintes CDNs:
Instalar com o LibMan
O LibMan pode ser usado para instalar arquivos de biblioteca de clientes específicos da biblioteca de clientes hospedada pela CDN. Por exemplo, adicione apenas o arquivo JavaScript minificado ao projeto. Para obter detalhes sobre essa abordagem, consulte Adicionar a SignalR biblioteca de cliente.
Conectar-se a um hub
O código a seguir cria e inicia uma conexão. O nome do hub não faz distinção entre maiúsculas e 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();
Conexões entre origens
Normalmente, os navegadores carregam conexões do mesmo domínio que a página solicitada. No entanto, há ocasiões em que uma conexão com outro domínio é necessária.
Important
O código do cliente deve usar uma URL absoluta em vez de uma URL relativa. Alterar .withUrl("/chathub") para .withUrl("https://myappurl/chathub").
Para evitar que um site mal-intencionado leia dados confidenciais de outro site, as conexões entre origens são desabilitadas por padrão. Para permitir uma solicitação entre origens, habilite-a na classe 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");
});
}
}
}
Chamar métodos do hub a partir do cliente
Os clientes JavaScript chamam métodos públicos em hubs por meio do método invoke do HubConnection. O método invoke aceita:
- O nome do método de hub.
- Todos os argumentos definidos no método de hub.
No exemplo a seguir, o nome do método mo hub é SendMessage. O segundo e o terceiro argumentos passados para invoke correspondem aos argumentos user e message do método do hub:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Note
Os métodos de hub de chamada de um cliente só têm suporte ao usar o Serviço SignalR do Azure no modo Padrão. Para obter mais informações, consulte perguntas frequentes (repositório GitHub do azure-signalr).
O método invoke retorna um JavaScript Promise. O Promise é resolvido com o valor retornado (se houver) quando o método no servidor retorna. Se o método no servidor gerar um erro, o Promise será rejeitado com a mensagem de erro. Use async e await ou os métodos Promise e then de catch para lidar com esses casos.
Os clientes JavaScript também podem chamar métodos públicos em hubs por meio do método send do HubConnection. Ao contrário do método invoke, o método send não aguarda uma resposta do servidor. O método send retorna um JavaScript Promise. O Promise é resolvido quando a mensagem é enviada ao servidor. Se houver um erro ao enviar a mensagem, o Promise será rejeitado com a mensagem de erro. Use async e await ou os métodos Promise e then de catch para lidar com esses casos.
Note
O uso de send não aguarda até que o servidor tenha recebido a mensagem. Consequentemente, não é possível retornar dados ou erros do servidor.
Chamar métodos do cliente a partir do hub
Para receber mensagens do hub, defina um método usando o método on do HubConnection.
- O nome do método de cliente JavaScript.
- Argumentos que o hub passa para o método.
No exemplo a seguir, o nome do método é ReceiveMessage. Os nomes dos argumentos são user e message:
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messageList").appendChild(li);
});
O código anterior em connection.on é executado quando o código do lado do servidor o chama usando o método SendAsync:
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
O SignalR determina qual método de cliente chamar correspondendo ao nome do método e aos argumentos definidos em SendAsync e connection.on.
Note
Como uma melhor prática, chame o método start no HubConnection após on. Isso garante que os manipuladores sejam registrados antes que as mensagens sejam recebidas.
Registro e tratamento de erros
Use try e catch com async e await ou o método catch de Promise para lidar com erros no lado do cliente. Use console.error para exibir erros no console do navegador:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Configure o rastreamento de logs do lado do cliente passando um logger e o tipo de evento a ser registrado quando a conexão for estabelecida. As mensagens são registradas com o nível de log especificado e superior. Os níveis de log disponíveis são os seguintes:
-
signalR.LogLevel.Error: mensagens de erro. Registra somente mensagens deError. -
signalR.LogLevel.Warning: mensagens de aviso sobre possíveis erros. Registra mensagens deWarningeError. -
signalR.LogLevel.Information: mensagens de status sem erros. Registra mensagens deInformation,WarningeError. -
signalR.LogLevel.Trace: Mensagens de rastreamento. Registra tudo, incluindo dados transportados entre o hub e o cliente.
Use o método configureLogging no HubConnectionBuilder para configurar o nível de log. As mensagens são registradas no console do navegador:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
Reconectar clientes
Reconectar automaticamente
O cliente JavaScript para SignalR pode ser configurado para se reconectar automaticamente usando o método withAutomaticReconnect no HubConnectionBuilder. Ele não se reconectará automaticamente por padrão.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect()
.build();
Sem parâmetros, withAutomaticReconnect() configura o cliente para aguardar 0, 2, 10 e 30 segundos, respectivamente, antes de tentar cada tentativa de reconexão, parando após quatro tentativas com falha.
Antes de iniciar qualquer tentativa de reconexão, o HubConnection fará a transição para o estado HubConnectionState.Reconnecting e disparará seus retornos de chamada onreconnecting em vez de fazer a transição para o estado Disconnected e disparará seus retornos de chamada onclose como um HubConnection sem reconexão automática configurada. Isso oferece uma oportunidade de avisar os usuários de que a conexão foi perdida e desabilitar elementos da interface do usuário.
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);
});
Se o cliente se reconectar com êxito em suas quatro primeiras tentativas, o HubConnection fará a transição de volta para o estado Connected e disparará seus retornos de chamada onreconnected. Isso oferece uma oportunidade de informar aos usuários que a conexão foi restabelecida.
Como a conexão parece inteiramente nova para o servidor, um novo connectionId será fornecido ao callback onreconnected.
Warning
O parâmetro onreconnected do retorno de chamada connectionId será indefinido se o HubConnection estiver configurado para ignorar a negociação.
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() não configurará o HubConnection para repetir falhas iniciais de início, portanto, as falhas de inicialização precisam ser tratadas 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);
}
};
Se o cliente não se reconectar com êxito em suas quatro primeiras tentativas, o HubConnection fará a transição para o estado Disconnected e disparará seus retornos de chamada onclose. Isso oferece uma oportunidade para informar aos usuários que a conexão foi perdida permanentemente e recomenda atualizar a 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 um número personalizado de tentativas de reconexão antes de desconectar ou alterar o tempo de reconexão, withAutomaticReconnect aceita uma matriz de números que representam o atraso em milissegundos para aguardar antes de iniciar cada tentativa de reconexão.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect([0, 0, 10000])
.build();
// .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
O exemplo anterior configura o HubConnection para começar a tentar se conectar novamente imediatamente após a conexão ser perdida. Isso também é verdadeiro para a configuração padrão.
Se a primeira tentativa de reconexão falhar, a segunda tentativa de reconexão também será iniciada imediatamente em vez de aguardar 2 segundos como ocorreria na configuração padrão.
Se a segunda tentativa de reconexão falhar, a terceira tentativa de reconexão será iniciada em 10 segundos, o que é, novamente, como a configuração padrão.
Em seguida, o comportamento personalizado diverge novamente do comportamento padrão, parando após a falha da terceira tentativa de reconexão, em vez de tentar mais uma tentativa de reconexão em mais 30 segundos, como faria na configuração padrão.
Se você quiser ter ainda mais controle sobre o tempo e o número de tentativas de reconexão automática, withAutomaticReconnect aceitará um objeto que implementa a interface IRetryPolicy, que tem um único método chamado nextRetryDelayInMilliseconds.
nextRetryDelayInMilliseconds usa um único argumento com o tipo RetryContext. O RetryContext tem três propriedades: previousRetryCount, elapsedMilliseconds e retryReason, que são um number, um number e um Error, respectivamente. Antes da primeira tentativa de reconexão, previousRetryCount e elapsedMilliseconds serão zero e o retryReason será o erro que causou a perda da conexão. Após cada tentativa de repetição com falha, previousRetryCount será incrementado em um, elapsedMilliseconds será atualizado para refletir o tempo gasto na reconexão até agora em milissegundos e retryReason será o erro que causou a falha da última tentativa de reconexão.
nextRetryDelayInMilliseconds deve retornar um número que represente o número de milissegundos a serem aguardados antes da próxima tentativa de reconexão ou null se o HubConnection deve parar de se reconectar.
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, você pode escrever código que reconectará o cliente manualmente, conforme demonstrado em Reconectar manualmente.
Reconectar manualmente
O código a seguir demonstra uma abordagem típica de reconexão manual:
- Uma função (nesse caso, a função
start) é criada para iniciar a conexão. - Chame a função
startno manipulador de eventosoncloseda conexão.
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
Implementações de produção normalmente usam um recuo exponencial ou tentam novamente um número específico de vezes.
Aba do navegador em suspensão
Alguns navegadores têm um recurso de congelar ou suspender abas para reduzir o uso de recursos do computador em abas inativas. Isso pode fazer com que conexões do SignalR sejam fechadas e possam resultar em uma experiência de usuário indesejada. Os navegadores usam heurísticas para determinar se uma guia deve entrar em suspensão, como:
- Reproduzindo áudio
- Manter um bloqueio da Web
- Manter um bloqueio
IndexedDB - Estar conectado a um dispositivo USB
- Capturar vídeo ou áudio
- Sendo espelhado
- Capturar uma janela ou exibição
Note
Essas heurísticas podem mudar ao longo do tempo ou diferir entre navegadores. Verifique a matriz de suporte e descubra qual método funciona melhor para seus cenários.
Para evitar que um aplicativo entre em suspensão, ele deve acionar uma das heurísticas usadas pelo navegador.
O exemplo de código a seguir mostra como usar um Bloqueio da Web para manter uma guia ativa e evitar um fechamento de conexão 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 o exemplo de código anterior:
- Os Bloqueios da Web são experimentais. A verificação condicional confirma que o navegador dá suporte a Bloqueios da Web.
- O resolvedor da promessa (
lockResolver) é armazenado para que o bloqueio possa ser liberado quando for permitido que a guia entre em suspensão. - Ao fechar a conexão, o bloqueio é liberado chamando
lockResolver(). Quando o bloqueio é liberado, a guia pode entrar em suspensão.