Partilhar via


ASP.NET Core cliente JavaScript SignalR

A biblioteca de cliente JavaScript principal SignalR do ASP.NET permite que os desenvolvedores chamem o código de hub do lado SignalR do servidor.

Instalar o pacote do SignalR cliente

A SignalR biblioteca de cliente JavaScript é fornecida como um pacote npm . As seções a seguir descrevem diferentes maneiras de instalar a biblioteca do cliente.

Instalar com npm

Execute os seguintes comandos a partir da Consola do Gestor de Pacotes:

npm init -y
npm install @microsoft/signalr

O npm instala o conteúdo do pacote na pasta node_modules\@microsoft\signalr\dist\browser . Crie a pasta wwwroot/lib/signalr. Copie o signalr.js arquivo para a pasta wwwroot/lib/signalr .

Faça referência ao SignalR cliente JavaScript no <script> elemento . Por exemplo:

<script src="~/lib/signalr/signalr.js"></script>

Usar uma Rede de Distribuição de Conteúdo (CDN)

Para usar a biblioteca de cliente sem o pré-requisito npm, faça referência a uma cópia hospedada pela CDN da biblioteca do cliente. Por exemplo:

<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>

Na marcação anterior, a versão 6.0.1 é especificada. Escolha uma das seguintes CDNs para obter a versão mais recente da biblioteca do cliente:

Instalar com LibMan

O LibMan pode ser usado para instalar arquivos específicos da biblioteca de cliente hospedada pela CDN. Por exemplo, adicione apenas o arquivo JavaScript minificado ao projeto. Para obter 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();

Ligações entre origens (CORS)

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.

Ao fazer solicitações entre domínios, o código do cliente deve usar uma URL absoluta em vez de uma URL relativa. Para solicitações entre domínios, altere .withUrl("/chathub") para .withUrl("https://{App domain name}/chathub").

Para impedir 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 de origem cruzada, 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();

UseCors deve ser chamado antes de chamar MapHub.

Chamar métodos no hub a partir do cliente

Clientes JavaScript chamam métodos públicos em hubs por meio do método invoke do HubConnection. O invoke método aceita:

  • O nome do método hub.
  • Quaisquer argumentos definidos no método hub.

No código realçado a seguir, o nome do método no hub é SendMessage. Os segundo e 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);
}

A chamada de métodos de hub de um cliente só é suportada ao usar o Serviço do Azure SignalR no modo Padrão . Para obter mais informações, consulte Perguntas freqüentes (repositório GitHub azure-signalr).

O invoke método retorna um JavaScript Promise. O Promise é resolvido com o valor de retorno (se houver) quando o método no servidor retorna. Se o método no servidor gera um erro, o Promise é rejeitado com a mensagem de erro. Use async e/ou await ou os métodos de Promisethen e 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 invoke método, o send método não espera por uma resposta do servidor. O send método retorna um JavaScript Promise. O Promise é resolvido quando a mensagem foi enviada para o servidor. Se houver um erro ao enviar a mensagem, Promise é rejeitado com a mensagem de erro. Use async e/ou await ou os métodos de Promisethen e catch para lidar com esses casos.

Usar sendnão espera até que o servidor tenha recebido a mensagem. Consequentemente, não é possível retornar dados ou erros do servidor.

Chamar métodos de 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 transmite 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 é connection.on executado quando o código do lado do servidor o chama usando o 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 qual método de cliente chamar combinando o nome do método e os argumentos definidos em SendAsync e connection.on.

Uma melhor prática é chamar o método start no HubConnection após on. Isso garante que os manipuladores sejam registrados antes que qualquer mensagem seja recebida.

Tratamento e registo de erros

Use console.error para enviar erros para o 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 log do lado do cliente passando um registrador e um tipo de evento para registrar quando a conexão for feita. 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. Regista apenas mensagens Error.
  • signalR.LogLevel.Warning: Mensagens de aviso sobre possíveis erros. Logs Warning, e Error mensagens.
  • signalR.LogLevel.Information: Mensagens de status sem erros. Regista Information, Warning e Error.
  • signalR.LogLevel.Trace: Mensagens de rastreamento. Registra tudo, incluindo dados transportados entre hub e 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

Reconecte-se 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 quaisquer 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 falhadas, deixa de tentar voltar a ligar-se.

Antes de iniciar qualquer tentativa de reconexão, o HubConnection:

  • Transições para o estado HubConnectionState.Reconnecting e aciona as suas chamadas de retorno onreconnecting.
  • Não faz a transição para o estado Disconnected nem aciona os seus retornos de chamada onclose, como ocorreria se HubConnection não estivesse configurado para reconexão automática.

A abordagem de reconexão oferece uma oportunidade para:

  • Avise os usuários que a conexão foi perdida.
  • Desative os elementos da interface.
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 suas primeiras quatro tentativas, a HubConnection transição voltará para o estado Connected e acionará os seus onreconnected callbacks. Isso oferece uma oportunidade de informar aos usuários que a conexão foi restabelecida.

Como a conexão parece totalmente nova para o servidor, uma nova connectionId é fornecida à chamada de retorno onreconnected.

O parâmetro onreconnected do connectionId callback é 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 voltar a tentar em caso de falhas iniciais de arranque, portanto, as falhas de arranque 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 nas suas primeiras quatro tentativas, faz a transição para o estado HubConnection e dispara os seus retornos de chamada Disconnected. Isto proporciona uma oportunidade para informar os utilizadores:

  • A conexão foi perdida permanentemente.
  • Tente 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 iniciar tentativas de reconexão imediatamente após a perda de conexão. A configuração padrão também aguarda zero 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 esperar 2 segundos usando a 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 é o mesmo que a configuração padrão.

O tempo de reconexão configurado difere do comportamento padrão parando após a terceira tentativa de reconexão em vez de tentar mais uma tentativa de reconexão em outros 30 segundos.

Para obter mais controle sobre o tempo e o número de tentativas de reconexão automática, withAutomaticReconnect aceita um objeto que implementa a IRetryPolicy interface, 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 a number, a number e um Error respectivamente. Antes da primeira tentativa de reconexão, tanto previousRetryCount quanto elapsedMilliseconds estarão a zero, e retryReason será o erro que causou a perda da conexão. Após cada nova tentativa falhada, previousRetryCount será incrementado por um, elapsedMilliseconds será atualizado para refletir a quantidade de tempo gasto a reconectar 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 aguardar antes da próxima tentativa de reconexão ou null caso HubConnection deva parar de 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, o código pode ser escrito para reconectar o cliente manualmente, conforme demonstrado na seção a seguir.

Reconectar manualmente

O código a seguir demonstra uma abordagem típica de reconexão manual:

  1. Uma função (neste caso, a start função) é criada para iniciar a conexão.
  2. Chame a função start no manipulador de eventos onclose da 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();
});

As implementações em produção normalmente recorrem a um mecanismo de retrocesso exponencial ou reexecutam um número especificado de vezes.

Guia de suspensão do navegador

Alguns navegadores têm uma funcionalidade para congelar ou adormecer abas a fim de reduzir o uso de recursos do computador para abas inativas. Isso pode fazer com que SignalR as conexões sejam fechadas e pode resultar em uma experiência de usuário indesejada. Os navegadores usam heurísticas para descobrir se uma guia deve ser colocada em repouso, como:

  • Reprodução de áudio
  • Mantendo um bloqueio na Web
  • Segurar um IndexedDB bloqueio
  • Estar ligado a um dispositivo USB
  • Captura de vídeo ou áudio
  • Ser espelhado
  • Capturar uma janela ou ecrã

A heurística do navegador pode mudar ao longo do tempo e pode diferir entre navegadores. Verifique a matriz de suporte e descubra qual método funciona melhor para seus cenários.

Para evitar colocar uma aplicação em repouso, a aplicação deve acionar uma das heurísticas que o navegador utiliza.

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 suporta bloqueios da Web.
  • O resolvedor de promessas, lockResolver, é armazenado para que o bloqueio possa ser liberado quando for aceitável que a guia entre em suspensão.
  • Ao fechar a conexão, o bloqueio é liberado chamando lockResolver(). Quando o bloqueio é desbloqueado, a guia é colocada em modo de suspensão.

Recursos adicionais

Por Rachel Appel

A biblioteca de cliente JavaScript principal SignalR do ASP.NET permite que os desenvolvedores chamem o código do hub do lado do servidor.

Visualizar ou descarregar amostra de código (como descarregar)

Instalar o pacote do SignalR cliente

A SignalR biblioteca de cliente JavaScript é fornecida como um pacote npm . As seções a seguir descrevem diferentes maneiras de instalar a biblioteca do cliente.

Instalar com npm

Para Visual Studio, execute os seguintes comandos do Console do Gerenciador de Pacotes enquanto estiver na pasta raiz. Para Visual Studio Code, execute os seguintes comandos a partir do 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 signalr.js arquivo para a pasta wwwroot\lib\signalr .

Faça referência ao SignalR cliente JavaScript no <script> elemento . Por exemplo:

<script src="~/lib/signalr/signalr.js"></script>

Usar uma Rede de Distribuição de Conteúdo (CDN)

Para usar a biblioteca de cliente sem o pré-requisito npm, faça referência a uma cópia hospedada pela CDN da biblioteca do cliente. Por exemplo:

<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7/signalr.js"></script>

A biblioteca de cliente está disponível nas seguintes CDNs:

Instalar com LibMan

O LibMan pode ser usado para instalar arquivos específicos da biblioteca de cliente hospedada pela CDN. Por exemplo, adicione apenas o arquivo JavaScript minificado ao projeto. Para obter 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();

Ligaçõ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. Altere .withUrl("/chathub") para .withUrl("https://myappurl/chathub").

Para impedir 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 de origem cruzada, 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 no hub a partir do cliente

Clientes JavaScript chamam métodos públicos em hubs por meio do método invoke do HubConnection. O invoke método aceita:

  • O nome do método hub.
  • Quaisquer argumentos definidos no método hub.

No exemplo a seguir, o nome do método no hub é SendMessage. Os segundo e 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

A chamada de métodos de hub a partir de um cliente só é suportada ao utilizar o Serviço SignalR do Azure no modo Padrão. Para obter mais informações, consulte Perguntas freqüentes (repositório GitHub azure-signalr).

O invoke método retorna um JavaScript Promise. O Promise é resolvido com o valor de retorno (se houver) quando o método no servidor retorna. Se o método no servidor gera um erro, o Promise é rejeitado com a mensagem de erro. Use async e/ou await ou os métodos de Promisethen e 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 invoke método, o send método não espera por uma resposta do servidor. O send método retorna um JavaScript Promise. O Promise é resolvido quando a mensagem foi enviada para o servidor. Se houver um erro ao enviar a mensagem, Promise é rejeitado com a mensagem de erro. Use async e/ou await ou os métodos de Promisethen e catch para lidar com esses casos.

Note

Ao utilizar o send, não se espera até que o servidor receba a mensagem. Consequentemente, não é possível retornar dados ou erros do servidor.

Chamar métodos de 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 transmite 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 é connection.on executado quando o código do lado do servidor o chama usando o SendAsync método:

public async Task SendMessage(string user, string message)
{
    await Clients.All.SendAsync("ReceiveMessage", user, message);
}

SignalR Determina qual método de cliente chamar combinando o nome do método e os argumentos definidos em SendAsync e connection.on.

Note

Como prática recomendada, chame o método start no HubConnection depois de on. Isso garante que seus manipuladores sejam registrados antes que qualquer mensagem seja recebida.

Tratamento e registo de erros

Use try e catch com async e await ou o método do Promisecatch para lidar com erros do lado cliente. Use console.error para enviar erros para o console do navegador:

try {
    await connection.invoke("SendMessage", user, message);
} catch (err) {
    console.error(err);
}

Configure o rastreamento de log do lado do cliente passando um registrador e um tipo de evento para registrar quando a conexão for feita. 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. Regista apenas mensagens Error.
  • signalR.LogLevel.Warning: Mensagens de aviso sobre possíveis erros. Logs Warning, e Error mensagens.
  • signalR.LogLevel.Information: Mensagens de status sem erros. Regista Information, Warning e Error.
  • signalR.LogLevel.Trace: Mensagens de rastreamento. Registra tudo, incluindo dados transportados entre hub e 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

Reconecte-se automaticamente

O cliente JavaScript para SignalR pode ser configurado para se reconectar automaticamente usando o withAutomaticReconnect método no HubConnectionBuilder. Ele não se reconectará automaticamente por padrão.

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withAutomaticReconnect()
    .build();

Sem quaisquer 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 falhadas.

Antes de iniciar qualquer tentativa de reconexão, o HubConnection fará a transição para o estado HubConnectionState.Reconnecting e disparará os respetivos retornos de chamada, em vez de fazer a transição para o estado onreconnecting e acionar os respetivos retornos de chamada, como um Disconnected sem reconexão automática configurada. Isso oferece uma oportunidade para avisar os usuários que a conexão foi perdida e desabilitar os 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 suas primeiras quatro tentativas, o HubConnection fará a transição de volta para o estado Connected e disparará os seus onreconnected callbacks. Isso oferece uma oportunidade de informar aos usuários que a conexão foi restabelecida.

Como a conexão parece totalmente nova para o servidor, uma nova connectionId será fornecida à chamada de retorno onreconnected.

Warning

O parâmetro onreconnected do connectionId estará indefinido se HubConnection tiver sido 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 voltar a tentar em caso de falhas iniciais de arranque, portanto, as falhas de arranque 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 dentro das suas primeiras quatro tentativas, o HubConnection fará a transição para o estado Disconnected e chamará os seus callbacks de onclose. Isso oferece uma oportunidade de informar aos usuários que a conexão foi perdida permanentemente e recomendar a atualização da 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 iniciar tentativas de reconexão imediatamente após a perda de conexão. 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 esperar 2 segundos, como faria 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 terceira tentativa de reconexão em vez de tentar mais uma tentativa de reconexão em outros 30 segundos, como faria na configuração padrão.

Se você quiser ainda mais controle sobre o tempo e o número de tentativas de reconexão automática, withAutomaticReconnect aceita um objeto que implementa a IRetryPolicy interface, 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 a number, a number e um Error respectivamente. Antes da primeira tentativa de reconexão, tanto previousRetryCount quanto elapsedMilliseconds estarão a zero, e retryReason será o erro que causou a perda da conexão. Após cada nova tentativa falhada, previousRetryCount será incrementado por um, elapsedMilliseconds será atualizado para refletir a quantidade de tempo gasto a reconectar 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 aguardar antes da próxima tentativa de reconexão ou null caso HubConnection deva parar de 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á seu cliente manualmente, conforme demonstrado em Reconectar manualmente.

Reconectar manualmente

O código a seguir demonstra uma abordagem típica de reconexão manual:

  1. Uma função (neste caso, a start função) é criada para iniciar a conexão.
  2. Chame a função start no manipulador de eventos onclose da 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();
});

As implementações em produção normalmente recorrem a um mecanismo de retrocesso exponencial ou reexecutam um número especificado de vezes.

Guia de suspensão do navegador

Alguns navegadores têm uma funcionalidade para congelar ou adormecer abas a fim de reduzir o uso de recursos do computador para abas inativas. Isso pode fazer com que SignalR as conexões sejam fechadas e pode resultar em uma experiência de usuário indesejada. Os navegadores usam heurísticas para descobrir se uma guia deve ser colocada em repouso, como:

  • Reprodução de áudio
  • Mantendo um bloqueio na Web
  • Segurar um IndexedDB bloqueio
  • Estar ligado a um dispositivo USB
  • Captura de vídeo ou áudio
  • Ser espelhado
  • Capturar uma janela ou ecrã

Note

Estas heurísticas podem mudar ao longo do tempo ou diferir entre navegadores. Verifique sua matriz de suporte e descubra qual método funciona melhor para seus cenários.

Para evitar colocar uma aplicação em repouso, a aplicação deve acionar uma das heurísticas que o navegador utiliza.

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 suporta bloqueios da Web.
  • O resolvedor de promessas (lockResolver) é armazenado para que o bloqueio possa ser liberado quando for aceitável que a guia entre em suspensão.
  • Ao fechar a conexão, o bloqueio é liberado chamando lockResolver(). Quando o bloqueio é desbloqueado, a guia é colocada em modo de suspensão.

Recursos adicionais