Delen via


ASP.NET Core SignalR JavaScript-client

Door Rachel Appel

Met de ASP.NET Core SignalR JavaScript-clientbibliotheek kunnen ontwikkelaars SignalR hubcode aan de serverzijde aanroepen.

Het SignalR-clientpakket installeren

De SignalR JavaScript-clientbibliotheek wordt geleverd als een npm--pakket. In de volgende secties worden verschillende manieren beschreven om de clientbibliotheek te installeren.

Installeren met npm

Voer de volgende opdrachten uit vanuit Package Manager Console:

npm init -y
npm install @microsoft/signalr

npm installeert de inhoud van het pakket in de map node_modules\@microsoft\signalr\dist\browser. Maak de map wwwroot/lib/signalr. Kopieer het signalr.js bestand naar de map wwwroot/lib/signalr.

Verwijs naar de SignalR JavaScript-client in het <script>-element. Bijvoorbeeld:

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

Een CDN (Content Delivery Network) gebruiken

Als u de clientbibliotheek wilt gebruiken zonder de npm-vereiste, verwijst u naar een door CDN gehoste kopie van de clientbibliotheek. Bijvoorbeeld:

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

In de voorgaande markeringen wordt versie 6.0.1 opgegeven. Kies uit een van de volgende CDN's om de nieuwste clientbibliotheekversie op te halen:

Installeren met LibMan

LibMan- kan worden gebruikt voor het installeren van specifieke clientbibliotheekbestanden uit de door CDN gehoste clientbibliotheek. Voeg bijvoorbeeld alleen het minified JavaScript-bestand toe aan het project. Voor details over die benadering, zie Voeg de SignalR-clientbibliotheek toe.

Verbinding maken met een hub

Met de volgende code wordt een verbinding gemaakt en gestart. De naam van de hub is niet hoofdlettergevoelig:

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

Cross-origin-verbindingen (CORS)

Normaal gesproken laden browsers verbindingen vanuit hetzelfde domein als de aangevraagde pagina. Er zijn echter situaties waarin een verbinding met een ander domein is vereist.

Bij het maken van aanvragen voor meerdere domeinen, moet de clientcode een absolute URL gebruiken in plaats van een relatieve URL. Voor aanvragen tussen domeinen wijzigt u .withUrl("/chathub") in .withUrl("https://{App domain name}/chathub").

Als u wilt voorkomen dat een kwaadwillende site gevoelige gegevens van een andere site leest, worden cross-origin-verbindingen standaard uitgeschakeld. Als u een cross-origin-aanvraag wilt toestaan, schakelt u CORSin:

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 moet worden aangeroepen voordat u MapHubaanroept.

Hubmethoden aanroepen vanaf de client

JavaScript-clients roepen openbare methoden van hubs aan via de -methode van de HubConnection. De methode invoke accepteert:

  • De naam van de hubmethode.
  • Alle argumenten die zijn gedefinieerd in de hubmethode.

In de volgende gemarkeerde code is de naam van de methode op de hub SendMessage. De tweede en derde argumenten die zijn doorgegeven aan invoke komen overeen met de user- en message-argumenten van de hubmethode.

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

Hubmethoden aanroepen vanaf een client wordt alleen ondersteund wanneer u de Azure SignalR Service gebruikt in de modus Standaard. Zie Veelgestelde vragen (azure-signalr GitHub-opslagplaats)voor meer informatie.

De methode invoke retourneert een JavaScript-Promise. De Promise wordt opgelost met de retourwaarde (indien aanwezig) wanneer de methode vanaf de server retourneert. Als de methode op de server een fout genereert, wordt de Promise afgewezen met de foutmelding. Gebruik async en await of de Promisethen en catch methoden om deze gevallen af te handelen.

JavaScript-clients kunnen ook openbare methoden op hubs aanroepen via de methode van de HubConnection. In tegenstelling tot de methode invoke, wacht de send methode niet op een reactie van de server. De methode send retourneert een JavaScript-Promise. De Promise wordt opgelost wanneer het bericht naar de server is verzonden. Als er een fout optreedt bij het verzenden van het bericht, wordt de Promise geweigerd met het foutbericht. Gebruik async en await of de Promisethen en catch methoden om deze gevallen af te handelen.

Als u sendgebruikt, wordt er niet gewacht totdat de server het bericht heeft ontvangen. Daarom is het niet mogelijk om gegevens of fouten van de server te retourneren.

Clientmethoden aanroepen vanuit de hub

Als u berichten van de hub wilt ontvangen, definieert u een methode met behulp van de op methode van de HubConnection.

  • De naam van de JavaScript-clientmethode.
  • Argumenten die de hub doorgeeft aan de methode.

In het volgende voorbeeld is de methodenaam ReceiveMessage. De argumentnamen zijn user en message:

connection.on("ReceiveMessage", (user, message) => {
    const li = document.createElement("li");
    li.textContent = `${user}: ${message}`;
    document.getElementById("messageList").appendChild(li);
});

De voorgaande code in connection.on wordt uitgevoerd wanneer code aan de serverzijde deze aanroept met behulp van de methode 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);
    }
}

SignalR bepaalt welke clientmethode moet worden aangeroepen door de methodenaam en argumenten te vergelijken die zijn gedefinieerd in SendAsync en connection.on.

Een best practice is het aanroepen van de start methode op de HubConnection na on. Dit zorgt ervoor dat de handlers worden geregistreerd voordat berichten worden ontvangen.

Foutafhandeling en logboekregistratie

Gebruik console.error om fouten uit te voeren naar de console van de browser wanneer de client geen verbinding kan maken of een bericht kan verzenden:

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

Stel logboektracering aan de clientzijde in door een logboekregistratie en het type gebeurtenis door te geven dat moet worden geregistreerd wanneer de verbinding wordt gemaakt. Berichten worden geregistreerd met het opgegeven logboekniveau en hoger. De beschikbare logboekniveaus zijn als volgt:

  • signalR.LogLevel.Error: foutberichten. Registreert alleen Error berichten.
  • signalR.LogLevel.Warning: waarschuwingsberichten over mogelijke fouten. Registreert berichten Warningen Error.
  • signalR.LogLevel.Information: statusberichten zonder fouten. Registreert Information, Warningen Error berichten.
  • signalR.LogLevel.Trace: Berichten traceren. Registreert alles, inclusief gegevens die tussen hub en client worden vervoerd.

Gebruik de methode configureLogging op HubConnectionBuilder om het logboekniveau te configureren. Berichten worden vastgelegd in de browserconsole:

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .configureLogging(signalR.LogLevel.Information)
    .build();

Clients opnieuw verbinden

Automatisch opnieuw verbinding maken

De JavaScript-client voor SignalR kan worden geconfigureerd om automatisch opnieuw verbinding te maken met behulp van de methode WithAutomaticReconnect op HubConnectionBuilder. Standaard wordt er niet automatisch opnieuw verbinding gemaakt.

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

Zonder parameters configureert WithAutomaticReconnect de client respectievelijk 0, 2, 10 en 30 seconden te wachten voordat elke poging opnieuw wordt geprobeerd. Na vier mislukte pogingen stopt het met proberen opnieuw verbinding te maken.

Voordat u opnieuw verbinding probeert te maken, moet u ervoor zorgen dat de configuratie van het HubConnectioncorrect is ingesteld.

  • Gaat over naar de HubConnectionState.Reconnecting status en activeert de onreconnecting callbacks.
  • De overgang naar de Disconnected-status gebeurt niet en het activeert niet de onclose-callbacks, net als een HubConnection zonder automatische herverbinding geconfigureerd.

De benadering voor opnieuw verbinden biedt de volgende mogelijkheden:

  • Waarschuw gebruikers dat de verbinding is verbroken.
  • Schakel UI-elementen uit.
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);
});

Als de client opnieuw verbinding maakt binnen de eerste vier pogingen, wordt de HubConnection teruggezet naar de status van de Connected en worden de onreconnected callbacks geactiveerd. Dit biedt de mogelijkheid om gebruikers te informeren dat de verbinding opnieuw tot stand is gebracht.

Omdat de verbinding volledig nieuw lijkt voor de server, wordt een nieuwe connectionId verstrekt aan de onreconnected callback.

De connectionId parameter van de onreconnected callback is niet-gedefinieerde als de HubConnection is geconfigureerd om onderhandeling over te slaan.

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 configureert de HubConnection niet om initiële startfouten opnieuw uit te voeren. Startfouten moeten dus handmatig worden afgehandeld:

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

Als de client niet succesvol opnieuw verbinding maakt binnen de eerste vier pogingen, gaat de HubConnection over naar de Disconnected staat en worden de -callbacks geactiveerd. Dit biedt de mogelijkheid om gebruikers te informeren:

  • De verbinding is permanent verloren gegaan.
  • Probeer de pagina te vernieuwen:
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);
});

Als u een aangepast aantal nieuwe verbindingspogingen wilt configureren voordat u de tijdsinstellingen voor opnieuw verbinden verbreekt of wijzigt, accepteert withAutomaticReconnect een matrix met getallen die de vertraging in milliseconden vertegenwoordigen om te wachten voordat elke poging voor opnieuw verbinden wordt gestart.

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withAutomaticReconnect([0, 0, 10000])
    .build();

    // .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior

In het voorgaande voorbeeld wordt de HubConnection geconfigureerd om opnieuw verbinding te maken direct nadat de verbinding is verbroken. De standaardconfiguratie wacht ook op nul seconden om opnieuw verbinding te maken.

Als de eerste poging om opnieuw verbinding te maken mislukt, wordt de tweede poging om opnieuw verbinding te maken ook onmiddellijk gestart in plaats van 2 seconden te wachten met behulp van de standaardconfiguratie.

Als de tweede poging om opnieuw verbinding te maken mislukt, begint de derde poging om opnieuw verbinding te maken in 10 seconden, wat hetzelfde is als de standaardconfiguratie.

De geconfigureerde timing voor opnieuw verbinden wijkt af van het standaardgedrag door te stoppen na de fout met de derde poging om opnieuw verbinding te maken in plaats van een andere poging om opnieuw verbinding te maken in een andere 30 seconden.

Voor meer controle over de timing en het aantal automatische nieuwe verbindingspogingen accepteert withAutomaticReconnect een object dat de IRetryPolicy-interface implementeert, met één methode met de naam nextRetryDelayInMilliseconds.

nextRetryDelayInMilliseconds gebruikt één argument met het type RetryContext. De RetryContext heeft drie eigenschappen: previousRetryCount, elapsedMilliseconds en retryReason, wat respectievelijk een number, een number en een Error zijn. Voordat de eerste poging om opnieuw verbinding te maken, zijn zowel previousRetryCount als elapsedMilliseconds nul en is de retryReason de fout waardoor de verbinding is verbroken. Na elke mislukte herstelpoging wordt previousRetryCount met één verhoogd, elapsedMilliseconds wordt bijgewerkt om de hoeveelheid tijd in milliseconden die tot nu toe aan opnieuw verbinden is besteed weer te geven, en retryReason is de Fout die de laatste poging om opnieuw verbinding te maken heeft laten mislukken.

nextRetryDelayInMilliseconds moet een getal retourneren dat het aantal milliseconden aangeeft dat moet worden gewacht voordat de volgende poging tot opnieuw verbinding wordt gemaakt of null als de HubConnection moet stoppen met opnieuw verbinding maken.

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

Code kan ook worden geschreven waarmee de client handmatig opnieuw wordt verbonden, zoals wordt weergegeven in de volgende sectie.

Handmatig opnieuw verbinding maken

De volgende code demonstreert een typische benadering voor handmatig opnieuw verbinden:

  1. Er wordt een functie (in dit geval de start functie) gemaakt om de verbinding te starten.
  2. Roep de start functie aan in de onclose gebeurtenis-handler van de verbinding.
async function start() {
    try {
        await connection.start();
        console.log("SignalR Connected.");
    } catch (err) {
        console.log(err);
        setTimeout(start, 5000);
    }
};

connection.onclose(async () => {
    await start();
});

Productie-implementaties gebruiken doorgaans een exponentieel uitstel of proberen een opgegeven aantal keren opnieuw.

Tabblad Slaapstand van browser

Sommige browsers hebben een functie voor het blokkeren of slapen van tabbladen om het gebruik van computerbronnen voor inactieve tabbladen te verminderen. Hierdoor kunnen SignalR verbindingen worden gesloten en kan dit leiden tot een ongewenste gebruikerservaring. Browsers gebruiken heuristieken om erachter te komen of een tabblad in de slaapstand moet worden geplaatst, zoals:

  • Audio afspelen
  • Een webvergrendeling vasthouden
  • Een IndexedDB slot vasthouden
  • Verbinding maken met een USB-apparaat
  • Video of audio vastleggen
  • Wordt gespiegeld
  • Een venster of weergave vastleggen

Browser-heuristiek kan na verloop van tijd veranderen en kan verschillen tussen browsers. Bekijk de ondersteuningsmatrix en zoek uit welke methode het beste werkt voor uw scenario's.

Om te voorkomen dat een app in de slaapstand wordt gezet, moet de app een van de heuristieken activeren die door de browser worden gebruikt.

In het volgende codevoorbeeld ziet u hoe u een webvergrendeling gebruikt om een tabblad wakker te houden en een onverwachte sluiting van de verbinding te voorkomen.

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

Voor het voorgaande codevoorbeeld:

  • Webvergrendelingen zijn experimenteel. De voorwaardelijke controle bevestigt dat de browser webvergrendelingen ondersteunt.
  • De promise-afhandelaar, lockResolver, wordt opgeslagen zodat de vergrendeling kan worden vrijgegeven wanneer het tabblad in de slaapstand kan gaan.
  • Wanneer u de verbinding sluit, wordt de vergrendeling vrijgegeven door lockResolver()aan te roepen. Wanneer de vergrendeling wordt losgelaten, kan het tabblad in de slaapstand gaan.

Aanvullende informatiebronnen

Door Rachel Appel

Met de ASP.NET Core SignalR JavaScript-clientbibliotheek kunnen ontwikkelaars hubcode aanroepen aan de serverzijde.

Bekijk of download voorbeeldcode (hoe te downloaden)

Het SignalR-clientpakket installeren

De SignalR JavaScript-clientbibliotheek wordt geleverd als een npm--pakket. In de volgende secties worden verschillende manieren beschreven om de clientbibliotheek te installeren.

Installeren met npm

Voer voor Visual Studio de volgende commando's uit vanuit Package Manager Console terwijl je in de hoofdmap bent. Voer voor Visual Studio Code de volgende opdrachten uit vanuit de Integrated Terminal.

npm init -y
npm install @microsoft/signalr

npm installeert de inhoud van het pakket in de map node_modules\@microsoft\signalr\dist\browser. Maak een nieuwe map met de naam signalr onder de map wwwroot\lib. Kopieer het signalr.js bestand naar de map wwwroot\lib\signalr.

Verwijs naar de SignalR JavaScript-client in het <script>-element. Bijvoorbeeld:

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

Een CDN (Content Delivery Network) gebruiken

Als u de clientbibliotheek wilt gebruiken zonder de npm-vereiste, verwijst u naar een door CDN gehoste kopie van de clientbibliotheek. Bijvoorbeeld:

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

De clientbibliotheek is beschikbaar op de volgende CDN's:

Installeren met LibMan

LibMan- kan worden gebruikt voor het installeren van specifieke clientbibliotheekbestanden uit de door CDN gehoste clientbibliotheek. Voeg bijvoorbeeld alleen het minified JavaScript-bestand toe aan het project. Zie De SignalR-clientbibliotheek toevoegenvoor meer informatie over deze benadering.

Verbinding maken met een hub

Met de volgende code wordt een verbinding gemaakt en gestart. De naam van de hub is niet hoofdlettergevoelig:

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

Cross-origin-verbindingen

Normaal gesproken laden browsers verbindingen vanuit hetzelfde domein als de aangevraagde pagina. Er zijn echter situaties waarin een verbinding met een ander domein is vereist.

Belangrijk

De clientcode moet een absolute URL gebruiken in plaats van een relatieve URL. Wijzig .withUrl("/chathub") in .withUrl("https://myappurl/chathub").

Als u wilt voorkomen dat een kwaadwillende site gevoelige gegevens van een andere site leest, worden cross-origin-verbindingen standaard uitgeschakeld. Als u een cross-origin-aanvraag wilt toestaan, schakelt u deze in de Startup klasse in:

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

Hubmethoden aanroepen vanaf de client

JavaScript-clients roepen openbare methoden op de hubs aan via de -methode van de HubConnection. De methode invoke accepteert:

  • De naam van de hubmethode.
  • Alle argumenten die zijn gedefinieerd in de hubmethode.

In het volgende voorbeeld is de naam van de methode op de hub SendMessage. De tweede en derde argumenten die zijn doorgegeven aan invoke worden toegewezen aan de user- en message-argumenten van de hubmethode:

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

Notitie

Hubmethoden aanroepen vanaf een client wordt alleen ondersteund wanneer u de Azure SignalR Service gebruikt in standaardmodus. Zie Veelgestelde vragen (azure-signalr GitHub-opslagplaats)voor meer informatie.

De methode invoke retourneert een JavaScript-Promise. De Promise wordt afgehandeld met de retourwaarde (indien aanwezig) wanneer de methode op de server retourneert. Als de methode op de server een fout genereert, wordt de Promise geweigerd met de foutmelding. Gebruik async en await of de Promisethen en catch methoden om deze gevallen af te handelen.

JavaScript-clients kunnen ook via de verzend-methode openbare methoden op hubs aanroepen van de HubConnection. In tegenstelling tot de methode invoke, wacht de send methode niet op een reactie van de server. De methode send retourneert een JavaScript-Promise. De Promise wordt opgelost wanneer het bericht naar de server is verzonden. Als er een fout optreedt bij het verzenden van het bericht, wordt de Promise geweigerd met het foutbericht. Gebruik async en await of de Promisethen en catch methoden om deze gevallen af te handelen.

Notitie

Het gebruik van send wacht niet totdat de server het bericht heeft ontvangen. Daarom is het niet mogelijk om gegevens of fouten van de server te retourneren.

Clientmethoden aanroepen vanuit de hub

Als u berichten van de hub wilt ontvangen, definieert u een methode met behulp van de op methode van de HubConnection.

  • De naam van de JavaScript-clientmethode.
  • Argumenten die de hub doorgeeft aan de methode.

In het volgende voorbeeld is de methode ReceiveMessagegenoemd. De argumentnamen zijn user en message:

connection.on("ReceiveMessage", (user, message) => {
    const li = document.createElement("li");
    li.textContent = `${user}: ${message}`;
    document.getElementById("messageList").appendChild(li);
});

De voorgaande code in connection.on wordt uitgevoerd wanneer code aan de serverzijde deze aanroept met behulp van de methode SendAsync:

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

SignalR bepaalt welke clientmethode moet worden aangeroepen door de methodenaam en argumenten te vergelijken die zijn gedefinieerd in SendAsync en connection.on.

Notitie

Als best practice roept u de methode op de HubConnection na onaan. Dit zorgt ervoor dat uw handlers worden geregistreerd voordat berichten worden ontvangen.

Foutafhandeling en logboekregistratie

Gebruik try en catch met async en await of de methode van de Promise's catch om fouten aan de cliëntzijde af te handelen. Gebruik console.error om fouten weer te geven in console van de browser.

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

Stel logboektracering aan de clientzijde in door een logger en het type gebeurtenis door te geven bij het tot stand brengen van de verbinding. Berichten worden geregistreerd met het opgegeven logboekniveau en hoger. De beschikbare logboekniveaus zijn als volgt:

  • signalR.LogLevel.Error: foutberichten. Registreert alleen berichten van Error.
  • signalR.LogLevel.Warning: waarschuwingsberichten over mogelijke fouten. Registreert de berichten Warningen Error.
  • signalR.LogLevel.Information: statusberichten zonder fouten. Registreert Information, Warningen Error berichten.
  • signalR.LogLevel.Trace: Berichten traceren. Registreert alles, inclusief gegevens die tussen hub en client worden vervoerd.

Gebruik de methode configureLogging op HubConnectionBuilder om het logboekniveau te configureren. Berichten worden vastgelegd in de browserconsole:

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .configureLogging(signalR.LogLevel.Information)
    .build();

Clients opnieuw verbinden

Automatisch opnieuw verbinding maken

De JavaScript-client voor SignalR kan worden geconfigureerd om automatisch opnieuw verbinding te maken met behulp van de methode withAutomaticReconnect op HubConnectionBuilder. Standaard wordt er niet automatisch opnieuw verbinding gemaakt.

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

Zonder parameters configureert withAutomaticReconnect() de client om respectievelijk 0, 2, 10 en 30 seconden te wachten voordat elke poging om opnieuw verbinding te maken, stopt na vier mislukte pogingen.

Voordat u een poging tot opnieuw verbinden onderneemt, zal de HubConnection naar de toestand HubConnectionState.Reconnecting overgaan en zullen de onreconnecting callbacks geactiveerd worden in plaats van naar de toestand Disconnected over te gaan en de onclose callbacks te activeren, zoals een HubConnection zonder automatische opnieuw verbinding configuratie. Dit biedt de mogelijkheid om gebruikers te waarschuwen dat de verbinding is verbroken en om UI-elementen uit te schakelen.

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

Als de client opnieuw verbinding maakt binnen de eerste vier pogingen, wordt de HubConnection teruggezet naar de status van de Connected en worden de onreconnected callbacks geactiveerd. Dit biedt de mogelijkheid om gebruikers te informeren dat de verbinding opnieuw tot stand is gebracht.

Omdat de verbinding er volledig nieuw uitziet voor de server, wordt er een nieuwe connectionId aan de onreconnected callback verstrekt.

Waarschuwing

De connectionId parameter van de onreconnected callback zal niet gedefinieerd zijn als de HubConnection is geconfigureerd om onderhandeling over te slaan.

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() configureert de HubConnection niet om initiële startfouten opnieuw uit te voeren. Startfouten moeten dus handmatig worden afgehandeld:

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

Als de client binnen de eerste vier pogingen niet succesvol herverbinding maakt, zal de HubConnection overgaan naar de staat Disconnected en zijn onclose callbacks uitvoeren. Dit biedt de mogelijkheid om gebruikers te informeren dat de verbinding definitief verloren is gegaan en het wordt aangeraden de pagina te vernieuwen:

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

Als u een aangepast aantal nieuwe verbindingspogingen wilt configureren voordat u de tijdsinstellingen voor opnieuw verbinden verbreekt of wijzigt, accepteert withAutomaticReconnect een matrix met getallen die de vertraging in milliseconden vertegenwoordigen om te wachten voordat elke poging voor opnieuw verbinden wordt gestart.

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withAutomaticReconnect([0, 0, 10000])
    .build();

    // .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior

In het voorgaande voorbeeld wordt de HubConnection geconfigureerd om opnieuw verbinding te maken direct nadat de verbinding is verbroken. Dit geldt ook voor de standaardconfiguratie.

Als de eerste poging om opnieuw verbinding te maken mislukt, wordt de tweede poging om opnieuw verbinding te maken ook onmiddellijk gestart in plaats van 2 seconden te wachten, net zoals in de standaardconfiguratie.

Als de tweede poging om opnieuw verbinding te maken mislukt, wordt de derde poging om opnieuw verbinding te maken binnen 10 seconden gestart, wat weer lijkt op de standaardconfiguratie.

Het aangepaste gedrag wijkt vervolgens opnieuw af van het standaardgedrag door te stoppen na de fout met de derde poging om opnieuw verbinding te maken in plaats van in een andere 30 seconden opnieuw verbinding te maken, net zoals in de standaardconfiguratie.

Als u nog meer controle wilt over de timing en het aantal pogingen om automatisch opnieuw verbinding te maken, accepteert withAutomaticReconnect een object dat de IRetryPolicy-interface implementeert, met één methode met de naam nextRetryDelayInMilliseconds.

nextRetryDelayInMilliseconds gebruikt één argument met het type RetryContext. De RetryContext heeft drie eigenschappen: previousRetryCount, elapsedMilliseconds en retryReason, respectievelijk een number, een number en een Error. Voordat de eerste poging om opnieuw verbinding te maken, zijn zowel previousRetryCount als elapsedMilliseconds nul en is de retryReason de fout waardoor de verbinding is verbroken. Na elke mislukte herhaalpoging wordt previousRetryCount met één verhoogd, wordt elapsedMilliseconds bijgewerkt om de tijd die tot nu toe in milliseconden aan opnieuw verbinden is besteed, en is retryReason de fout die de laatste poging om opnieuw te verbinden deed mislukken.

nextRetryDelayInMilliseconds moet een getal retourneren dat het aantal milliseconden aangeeft dat moet worden gewacht voordat de volgende poging tot opnieuw verbinding wordt gemaakt of null als de HubConnection moet stoppen met opnieuw verbinding maken.

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

U kunt ook code schrijven waarmee uw client handmatig opnieuw wordt verbonden, zoals wordt weergegeven in Handmatig opnieuw verbinding maken.

Handmatig opnieuw verbinding maken

De volgende code demonstreert een typische benadering voor handmatig opnieuw verbinden:

  1. Er wordt een functie (in dit geval de start functie) gemaakt om de verbinding te starten.
  2. Roep de start functie aan in de onclose gebeurtenis-handler van de verbinding.
async function start() {
    try {
        await connection.start();
        console.log("SignalR Connected.");
    } catch (err) {
        console.log(err);
        setTimeout(start, 5000);
    }
};

connection.onclose(async () => {
    await start();
});

Productie-implementaties gebruiken doorgaans een exponentieel uitstel of proberen een opgegeven aantal keren opnieuw.

Tabblad Slaapstand van browser

Sommige browsers hebben een functie voor het blokkeren of slapen van tabbladen om het gebruik van computerbronnen voor inactieve tabbladen te verminderen. Hierdoor kunnen SignalR verbindingen worden gesloten en kan dit leiden tot een ongewenste gebruikerservaring. Browsers gebruiken heuristieken om erachter te komen of een tabblad in de slaapstand moet worden geplaatst, zoals:

  • Audio afspelen
  • Een webvergrendeling vasthouden
  • Het vasthouden van een IndexedDB-slot
  • Verbinding maken met een USB-apparaat
  • Het vastleggen van video of audio
  • Gespiegeld
  • Een venster of weergave vastleggen

Notitie

Deze heuristiek kan na verloop van tijd veranderen of verschillen tussen browsers. Controleer uw ondersteuningsmatrix en zoek uit welke methode het beste werkt voor uw scenario's.

Om te voorkomen dat een app in de slaapstand wordt gezet, moet de app een van de heuristieken activeren die door de browser worden gebruikt.

In het volgende codevoorbeeld ziet u hoe u een webvergrendeling gebruikt om een tabblad wakker te houden en een onverwachte sluiting van de verbinding te voorkomen.

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

Voor het voorgaande codevoorbeeld:

  • Webvergrendelingen zijn experimenteel. De voorwaardelijke controle bevestigt dat de browser webvergrendelingen ondersteunt.
  • De belofte-resolver (lockResolver) wordt opgeslagen, zodat de vergrendeling kan worden vrijgegeven wanneer het acceptabel is voor de slaapstand van het tabblad.
  • Wanneer u de verbinding sluit, wordt de vergrendeling vrijgegeven door lockResolver()aan te roepen. Wanneer de vergrendeling wordt losgelaten, kan het tabblad in de slaapstand gaan.

Aanvullende informatiebronnen