ASP.NET Core SignalR klient JavaScript

Biblioteka kliencka JavaScript platformy ASP.NET Core SignalR umożliwia deweloperom wywoływanie kodu huba po stronie serwera SignalR. W tym artykule opisano, jak używać interfejsów API do łączenia z koncentratorem SignalR oraz wywoływania metod koncentratora i klienta w języku JavaScript.

Wyświetl lub pobierz przykładowy kod (jak pobrać)

SignalR Instalowanie pakietu klienta

SignalR Biblioteka klienta języka JavaScript jest dostarczana jako pakiet npm. Istnieje kilka sposobów instalowania biblioteki klienta:

  • Uruchom polecenia npm w Visual Studio Menedżer pakietów Console.
  • Uruchom polecenia npm w zintegrowanym terminalu w Visual Studio Code.
  • Odwołaj się do kopii biblioteki klienta hostowanej w sieci dostarczania treści (CDN).
  • Użyj biblioteki LibMan i zainstaluj określone pliki biblioteki klienta z biblioteki klienta hostowanej przez sieć CDN.

Instalowanie za pomocą narzędzia npm

Uruchom następujące polecenia npm w oknie Menedżer pakietów Console:

npm init -y
npm install @microsoft/signalr

Narzędzie npm instaluje zawartość pakietu w folderze node_modules\@microsoft\signalr\dist\ browser.

Dokończ konfigurację:

  1. Utwórz folder wwwroot/lib/signalr.

  2. Skopiuj plik signalr.js do folderu wwwroot/lib/signalr .

Teraz możesz odwołać się do zainstalowanego SignalR klienta JavaScript w elemencie <script> . Przykład:

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

Korzystanie z sieci CDN

Aby użyć biblioteki klienta bez wymagań wstępnych npm, należy odwołać się do kopii biblioteki klienta hostowanej przez sieć CDN. Przykład:

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

Przykład kodu określa wersję 6.0.1. Aby uzyskać najnowszą wersję biblioteki klienta, wybierz jedną z następujących sieci CDN:

Instalowanie za pomocą biblioteki LibMan

Innym podejściem jest użycie biblioteki LibMan i zainstalowanie tylko określonych plików biblioteki klienta z biblioteki klienta hostowanej przez sieć CDN. Na przykład do projektu można dodać tylko minyfikowany plik JavaScript.

Aby uzyskać szczegółowe informacje na temat tego podejścia, zobacz Dodawanie SignalR biblioteki klienta.

Połącz z centralą

Poniższy kod tworzy i uruchamia połączenie. Nazwa centrum jest niewrażliwa na wielkość liter:

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

Połączenia między źródłami (CORS)

Zazwyczaj przeglądarki ładują połączenia z tej samej domeny co żądana strona. Istnieją jednak sytuacje, w których wymagane jest połączenie z inną domeną.

W przypadku żądań między domenami kod klienta musi używać bezwzględnego adresu URL, a nie względnego adresu URL. W przypadku używania żądań między domenami zmień wartość .withUrl("/chathub") na .withUrl("https://{App domain name}/chathub").

Aby zapobiec odczytywaniu poufnych danych z innej witryny przez złośliwą witrynę, połączenia między źródłami są domyślnie wyłączone. Aby zezwolić na żądanie między źródłami, włącz mechanizm 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();

Metodę UseCors należy wywołać przed wywołaniem metody MapHub.

Metody centrum wywołań z klienta

Klienci języka JavaScript wywołują metody publiczne w centrach za pośrednictwem metody invoke w usłudze HubConnection. Metoda invoke akceptuje:

  • Nazwa metody centrum.
  • Dowolne argumenty zdefiniowane w metodzie centrum.

W poniższym wyróżnionym kodzie nazwa metody w centrum to SendMessage. Drugi i trzeci argument przekazane do invoke odpowiadają argumentom user i message metody koncentratora:

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

Wywoływanie metod hubu od klienta jest obsługiwane tylko w przypadku korzystania z usługi Azure SignalR w trybie domyślnym.

Metoda invoke zwraca obiekt Promise języka JavaScript. Obiekt Promise jest rozpoznawany przy użyciu wartości zwracanej (jeśli istnieje), gdy metoda na serwerze zwraca. Jeśli metoda na serwerze zgłasza błąd, Promise obiekt zostanie odrzucony z komunikatem o błędzie. Aby obsłużyć te przypadki, użyj async i await albo metod Promise i then obiektu catch.

Klienci języka JavaScript mogą również wywoływać metody publiczne w centrach za pomocą metody send klasy HubConnection. invoke W przeciwieństwie do metody send metoda ta nie czeka na odpowiedź z serwera. Metoda send zwraca obiekt JavaScript Promise . Obiekt Promise jest rozwiązywany w momencie wysyłania wiadomości na serwer. Jeśli podczas wysyłania komunikatu wystąpi błąd, Promise obiekt zostanie odrzucony z komunikatem o błędzie. Aby obsłużyć te przypadki, użyj async i await albo metod Promise i then obiektu catch.

Użycie senddoesn't nie czeka, aż serwer odbierze komunikat, więc nie można zwrócić danych ani błędów z serwera.

Wywoływanie metod klienta z centrum

Aby odbierać komunikaty z koncentratora, zdefiniuj metodę, używając metody on obiektu HubConnection. Metoda on akceptuje:

  • Nazwa metody klienta JavaScript.
  • Argumenty centrum przekazuje do metody .

W poniższym przykładzie nazwa metody to ReceiveMessage. Nazwy argumentów to user i message:

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

Powyższy kod w programie connection.on jest uruchamiany, gdy kod po stronie serwera wywołuje go przy użyciu SendAsync metody :

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 określa, która metoda klienta ma być wywoływana przez dopasowanie nazwy metody i argumentów zdefiniowanych w elementach SendAsync i connection.on.

Najlepszym rozwiązaniem jest wywołanie metody start w metodzie HubConnection po on. Takie podejście zapewnia, że programy obsługi są rejestrowane przed odebraniem komunikatów.

Obsługa błędów i rejestrowanie

Użyj console.error polecenia , aby wyświetlić błędy w konsoli przeglądarki, gdy klient nie może nawiązać połączenia lub wysłać komunikatu:

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

Skonfiguruj śledzenie dzienników po stronie klienta, przekazując rejestrator i typ zdarzenia do rejestrowania po nawiązaniu połączenia. Komunikaty są rejestrowane przy użyciu określonego poziomu dziennika i wyższego. Dostępne poziomy dziennika są następujące:

Poziom logowania Zarejestrowane dane Description
signalR.LogLevel.Error Komunikaty o błędach Rejestruje tylko komunikaty Error.
signalR.LogLevel.Warning Komunikaty ostrzegawcze dotyczące potencjalnych błędów Rejestruje komunikaty Warning i Error.
signalR.LogLevel.Information Komunikaty o stanie bez błędów Rejestruje komunikaty Information, Warning i Error.
signalR.LogLevel.Trace Śledzenie komunikatów Rejestruje wszystko, w tym dane przesyłane między centralą a klientem.

Użyj metody configureLogging w usłudze HubConnectionBuilder, aby skonfigurować poziom dziennika. Komunikaty są rejestrowane w konsoli przeglądarki:

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

Ponowne łączenie klientów

Aby ponownie nawiązać połączenie z klientami, możesz skonfigurować automatyczne ponowne łączenie lub skonfigurować połączenie ręcznie.

Automatyczne ponowne nawiązywanie połączenia

Klienta JavaScript dla SignalR można skonfigurować do automatycznego ponownego łączenia przy użyciu metody WithAutomaticReconnect w obiekcie HubConnectionBuilder. Domyślnie nie powoduje automatycznego ponownego nawiązywania połączenia.

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

Bez żadnych parametrów program WithAutomaticReconnect konfiguruje klienta do oczekiwania odpowiednio 0, 2, 10 i 30 sekund przed próbą ponownego nawiązania połączenia. Po czterech nieudanych próbach przestaje ponawiać próbę połączenia.

Przed rozpoczęciem ponownego nawiązywania połączenia:HubConnection

Podejście ponownego łączenia zapewnia możliwość ostrzeżenia użytkowników o utracie połączenia i wyłączeniu elementów interfejsu użytkownika.

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

Jeśli klient pomyślnie połączy się ponownie podczas pierwszych czterech prób, HubConnection wraca do stanu Connected i wywołuje swoje wywołania zwrotne onreconnected. Takie podejście umożliwia informowanie użytkowników o ponownym utworzeniu połączenia.

Ponieważ połączenie wydaje się serwerowi całkowicie nowe, do wywołania zwrotnego connectionId przekazywane jest nowe onreconnected.

Parametr onreconnected funkcji wywołania zwrotnego connectionId ma wartość undefined, jeśli element HubConnection jest skonfigurowany tak, aby pominąć negocjację.

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 nie konfiguruje HubConnection do ponawiania prób po początkowych niepowodzeniach uruchomienia, dlatego błędy uruchamiania należy obsługiwać ręcznie:

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

Jeśli klientowi nie uda się ponownie połączyć w ciągu pierwszych czterech prób, element HubConnection przejdzie do stanu Disconnected i wywoła swoje funkcje zwrotne onclose. Takie podejście umożliwia informowanie użytkowników o trwałym utracie połączenia i próbie odświeżenia strony:

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

Aby skonfigurować niestandardową liczbę ponownych prób ponownego połączenia przed rozłączeniem lub zmianą czasu ponownego nawiązywania połączenia, withAutomaticReconnect akceptuje tablicę liczb reprezentujących opóźnienie w milisekundach oczekiwania przed rozpoczęciem każdej próby ponownego nawiązania połączenia.

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

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

W poprzednim przykładzie skonfigurowana jest wartość HubConnection , aby rozpocząć ponowne nawiązywanie połączeń natychmiast po utracie połączenia. Domyślna konfiguracja oczekuje również zero (0) sekund, aby spróbować ponownie nawiązać połączenie.

  • Jeśli pierwsza próba ponownego nawiązania połączenia zakończy się niepowodzeniem, druga próba ponownego połączenia zostanie uruchomiona natychmiast zamiast czekać 2 sekundy zgodnie z definicją w konfiguracji domyślnej.

  • Jeśli druga próba ponownego nawiązania połączenia zakończy się niepowodzeniem, trzecia próba ponownego nawiązania połączenia rozpocznie się w ciągu 10 sekund, co jest tym samym zachowaniem zdefiniowanym w konfiguracji domyślnej.

  • Skonfigurowany czas ponownego nawiązywania połączenia różni się od domyślnego zachowania, zatrzymując się po trzecim niepowodzeniu ponownego nawiązania połączenia. W konfiguracji domyślnej zostanie podjęta jeszcze jedna ponowna próba nawiązania połączenia po kolejnych 30 sekundach.

Aby uzyskać większą kontrolę nad chronometrażem i liczbą automatycznych ponownych prób ponownego łączenia, withAutomaticReconnect akceptuje obiekt implementjący IRetryPolicy interfejs, który ma jedną metodę o nazwie nextRetryDelayInMilliseconds. nextRetryDelayInMilliseconds przyjmuje jeden argument z typem RetryContext. RetryContext ma trzy właściwości: previousRetryCount (typ number), elapsedMilliseconds (typ number) oraz retryReason (typ Error).

  • Przed pierwszą ponowną próbą previousRetryCount nawiązania połączenia i elapsedMilliseconds mają wartość zero (0) i retryReason jest to błąd, który spowodował utratę połączenia.

  • Po każdej nieudanej próbie ponownego nawiązania połączenia wartość previousRetryCount zwiększa się o 1, elapsedMilliseconds jest aktualizowane tak, aby odzwierciedlać czas poświęcony dotychczas na ponowne nawiązywanie połączenia w milisekundach, a retryReason to błąd, który spowodował niepowodzenie ostatniej próby ponownego nawiązania połączenia.

nextRetryDelayInMilliseconds musi zwrócić wartość będącą albo liczbą milisekund oczekiwania przed następną próbą ponownego połączenia, albo null, jeśli HubConnection powinno przestać ponawiać próby połączenia.

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

Alternatywnie możesz napisać kod, aby ponownie połączyć klienta ręcznie, jak pokazano w następnej sekcji.

Ręczne ponowne nawiązywanie połączenia

Poniższy kod pokazuje typowe podejście do ręcznego ponownego nawiązywania połączenia:

  1. Aby uruchomić połączenie, utwórz funkcję. W tym przypadku, funkcja start.

  2. Wywołaj funkcję start w procedurze obsługi zdarzenia onclose połączenia.

async function start() {
    try {
        await connection.start();
        console.log("SignalR Connected.");
    } catch (err) {
        console.log(err);
        setTimeout(start, 5000);
    }
};

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

Implementacje produkcyjne zwykle stosują algorytm wykładniczego wydłużania odstępów między próbami lub ponawiają próbę określoną liczbę razy.

Karta uśpienia przeglądarki

Niektóre przeglądarki mają funkcję zamrażania kart lub trybu uśpienia kart , która zmniejsza użycie zasobów komputera dla nieaktywnych kart. Jednak ta funkcja może powodować zamykanie połączeń SignalR i prowadzić do niepożądanego doświadczenia użytkownika.

Przeglądarki implementują heurystyki, aby określić, czy karta powinna zostać uśpiona, na przykład:

  • Odtwarzanie dźwięku
  • Utrzymywanie blokady sieciowej
  • Trzymanie blokady IndexedDB
  • Nawiązywanie połączenia z urządzeniem USB
  • Przechwytywanie wideo lub dźwięku
  • Jest odzwierciedlane
  • Przechwytywanie okna lub wyświetlania

Heurystyka może się zmieniać w czasie, a także różnić się między przeglądarkami. Zapoznaj się z macierzą obsługi i dowiedz się, jaka metoda działa najlepiej w przypadku Twoich scenariuszy.

Aby uniknąć uśpienia aplikacji, aplikacja powinna wyzwolić jedną z heurystyki używanych przez przeglądarkę.

Poniższy przykład kodu pokazuje, jak użyć Web Lock, aby utrzymać kartę w stanie aktywności i uniknąć nieoczekiwanego zamknięcia połączenia:

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

W poprzednim kodzie:

  • Blokady internetowe są eksperymentalne. Sprawdzanie warunkowe potwierdza, że przeglądarka obsługuje blokady sieci Web.
  • Program rozpoznawania obietnic języka JavaScript (lockResolver) jest przechowywany, dzięki czemu blokada może zostać zwolniona, gdy karta zostanie uśpiona.
  • Po zamknięciu połączenia blokada jest zwalniana przez wywołanie metody lockResolver(). Po zwolnieniu blokady karta może przejść w stan uśpienia.

Odświeżanie uwierzytelniania bez ponownego nawiązywania połączenia

W .NET 11 lub nowszym klient może zastąpić poświadczenia w otwartym połączeniu, zamiast zamykać połączenie po wygaśnięciu tokenu dostępu. Serwer musi włączyć odświeżanie uwierzytelniania dla centrum. Wywołaj withAuthenticationRefresh, aby zaplanować automatyczne odświeżenie przed upływem terminu ważności podawanego przez serwer:

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub", { accessTokenFactory: () => getAccessToken() })
    .withAuthenticationRefresh()
    .build();

Każde odświeżenie ponownie wywołuje accessTokenFactory, aby uzyskać nowy token. Aby odświeżyć natychmiast, na przykład po uzyskaniu przez aplikację zaktualizowanych oświadczeń, wywołaj metodę connection.refreshAuthentication().

Aby uzyskać opcje odświeżania, procedury obsługi powodzenia i niepowodzeń oraz konfigurację serwera, która włącza tę funkcję, zobacz SignalR uwierzytelnianie i autoryzacja.

Autor: Rachel Appel

Biblioteka kliencka JavaScript platformy ASP.NET Core SignalR umożliwia programistom wywoływanie kodu huba po stronie serwera.

Wyświetl lub pobierz przykładowy kod (jak pobrać)

SignalR Instalowanie pakietu klienta

SignalR Biblioteka klienta języka JavaScript jest dostarczana jako pakiet npm. W poniższych sekcjach opisano różne sposoby instalowania biblioteki klienta.

Instalowanie za pomocą narzędzia npm

W przypadku programu Visual Studio uruchom następujące polecenia z konsoli Menedżer pakietów w folderze głównym. W przypadku programu Visual Studio Code uruchom następujące polecenia w zintegrowanym terminalu.

npm init -y
npm install @microsoft/signalr

Narzędzie npm instaluje zawartość pakietu w folderze node_modules\@microsoft\signalr\dist\ browser. Utwórz nowy folder o nazwie signalr w folderze wwwroot\lib. Skopiuj plik signalr.js do folderu wwwroot\lib\signalr.

Dodaj odwołanie do klienta JavaScript SignalR w elemencie <script>. Przykład:

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

Korzystanie z usługi Content Delivery Network (CDN)

Aby użyć biblioteki klienta bez wymagań wstępnych npm, należy odwołać się do kopii biblioteki klienta hostowanej przez sieć CDN. Przykład:

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

Biblioteka klienta jest dostępna w następujących sieciach CDN:

Instalowanie za pomocą biblioteki LibMan

Biblioteka LibMan może służyć do instalowania określonych plików biblioteki klienta z biblioteki klienta hostowanej przez sieć CDN. Na przykład do projektu dodaj tylko minyfikowany plik JavaScript. Aby uzyskać szczegółowe informacje na temat tego podejścia, zobacz Dodawanie SignalR biblioteki klienta.

Połącz z centralą

Poniższy kod tworzy i uruchamia połączenie. Nazwa centrum jest niewrażliwa na wielkość liter:

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

Połączenia między źródłami

Zazwyczaj przeglądarki ładują połączenia z tej samej domeny co żądana strona. Istnieją jednak sytuacje, w których wymagane jest połączenie z inną domeną.

Important

Kod klienta musi używać bezwzględnego adresu URL zamiast względnego adresu URL. Zmień .withUrl("/chathub") na .withUrl("https://myappurl/chathub").

Aby zapobiec odczytywaniu poufnych danych z innej witryny przez złośliwą witrynę, połączenia między źródłami są domyślnie wyłączone. Aby zezwolić na żądanie międzydomenowe, włącz tę opcję w klasie 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");
            });
        }
    }
}

Metody centrum wywołań z klienta

Klienci języka JavaScript wywołują metody publiczne w centrach za pośrednictwem metody invoke w usłudze HubConnection. Metoda invoke akceptuje:

  • Nazwa metody centrum.
  • Dowolne argumenty zdefiniowane w metodzie centrum.

W poniższym przykładzie nazwa metody w centrum to SendMessage. Drugi i trzeci argument przekazane do invoke odpowiadają argumentom user i message metody koncentratora:

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

Note

Wywoływanie metod hubu od klienta jest obsługiwane tylko podczas korzystania z usługi Azure SignalR w trybie domyślnym.

Metoda invoke zwraca kod JavaScript Promise. Element Promise zostaje zakończony z wartością zwracaną (jeśli istnieje), gdy metoda na serwerze zwróci wynik. Jeśli metoda na serwerze zgłasza błąd, obiekt Promise zostaje odrzucony z komunikatem o błędzie. Użyj async i await lub metod Promise i then elementu catch, aby obsłużyć te przypadki.

Klienci języka JavaScript mogą również wywoływać metody publiczne w centrach za pomocą metody send klasy HubConnection. invoke W przeciwieństwie do metody send metoda ta nie czeka na odpowiedź z serwera. Metoda send zwraca kod JavaScript Promise. Promise zostaje rozwiązany, gdy wiadomość została wysłana do serwera. Jeśli wystąpi błąd podczas wysyłania komunikatu, Promise zostanie on odrzucony z komunikatem o błędzie. Użyj async i await lub metod Promise i then elementu catch, aby obsłużyć te przypadki.

Note

Użycie polecenia send nie czeka, aż serwer otrzyma wiadomość. W związku z tym nie można zwracać danych ani błędów z serwera.

Wywoływanie metod klienta z centrum

Aby odbierać komunikaty z centrum, zdefiniuj metodę przy użyciu metody on klasy HubConnection.

  • Nazwa metody klienta JavaScript.
  • Argumenty centrum przekazuje do metody .

W poniższym przykładzie nazwa metody to ReceiveMessage. Nazwy argumentów to user i message:

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

Powyższy kod w programie connection.on jest uruchamiany, gdy kod po stronie serwera wywołuje go przy użyciu SendAsync metody :

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

SignalR określa, która metoda klienta ma być wywoływana przez dopasowanie nazwy metody i argumentów zdefiniowanych w elementach SendAsync i connection.on.

Note

Dobrą praktyką jest wywołanie metody start na obiekcie HubConnection po on. Dzięki temu programy obsługi są rejestrowane przed odebraniem komunikatów.

Obsługa błędów i rejestrowanie

Użyj try i catch z async i await lub metody Promise elementu catch, aby obsługiwać błędy po stronie klienta. Użyj console.error polecenia , aby wyświetlić błędy w konsoli przeglądarki:

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

Skonfiguruj śledzenie dzienników po stronie klienta, przekazując rejestrator i typ zdarzenia do rejestrowania po nawiązaniu połączenia. Komunikaty są rejestrowane przy użyciu określonego poziomu dziennika i wyższego. Dostępne poziomy dziennika są następujące:

  • signalR.LogLevel.Error:Komunikaty o błędach. Rejestruje tylko komunikaty Error.
  • signalR.LogLevel.Warning: Komunikaty ostrzegawcze dotyczące potencjalnych błędów. Rejestruje komunikaty Warning, i Error.
  • signalR.LogLevel.Information: komunikaty o stanie bez błędów. Rejestruje komunikaty Information, Warning i Error.
  • signalR.LogLevel.Trace: Śledzenie komunikatów. Rejestruje wszystko, w tym dane przesyłane między centralą a klientem.

Użyj metody configureLogging w usłudze HubConnectionBuilder, aby skonfigurować poziom dziennika. Komunikaty są rejestrowane w konsoli przeglądarki:

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

Ponowne łączenie klientów

Automatyczne ponowne nawiązywanie połączenia

Klienta JavaScript dla SignalR można skonfigurować tak, aby automatycznie ponownie nawiązywał połączenie za pomocą metody withAutomaticReconnect w klasie HubConnectionBuilder. Domyślnie nie zostanie automatycznie ponownie nawiązane połączenie.

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

Bez parametrów withAutomaticReconnect() konfiguruje klienta tak, aby przed każdą próbą ponownego połączenia odczekał odpowiednio 0, 2, 10 i 30 sekund, a po czterech nieudanych próbach przestał próbować.

Przed rozpoczęciem jakichkolwiek prób ponownego nawiązania połączenia element HubConnection przejdzie do stanu HubConnectionState.Reconnecting i wywoła swoje wywołania zwrotne onreconnecting, zamiast przejść do stanu Disconnected i wywołać swoje wywołania zwrotne onclose, jak ma to miejsce w przypadku elementu HubConnection bez skonfigurowanego automatycznego ponownego nawiązywania połączenia. Zapewnia to możliwość ostrzeżenia użytkowników o utracie połączenia i wyłączeniu elementów interfejsu użytkownika.

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

Jeśli klient pomyślnie połączy się ponownie w ciągu pierwszych czterech prób, element HubConnection powróci do stanu Connected i uruchomi swoje funkcje zwrotne onreconnected. Zapewnia to możliwość poinformowania użytkowników, że połączenie zostało ponownie nawiązane.

Ponieważ połączenie wydaje się serwerowi całkowicie nowe, do funkcji zwrotnej connectionId zostanie przekazane nowe onreconnected.

Warning

Parametr onreconnected wywołania zwrotnego connectionId będzie niezdefiniowany, jeśli element HubConnection skonfigurowano tak, aby pomijał negocjację.

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() nie skonfiguruje elementu HubConnection, aby ponawiać próby po początkowych błędach uruchamiania, dlatego błędy uruchamiania należy obsługiwać ręcznie:

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

Jeśli klient nie połączy się ponownie pomyślnie w ciągu pierwszych czterech prób, HubConnection przejdzie do stanu Disconnected i wywoła swoje funkcje wywołania zwrotnego onclose. Dzięki temu można poinformować użytkowników, że połączenie zostało trwale utracone i zaleca odświeżenie strony:

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

Aby skonfigurować niestandardową liczbę ponownych prób ponownego połączenia przed rozłączeniem lub zmianą czasu ponownego połączenia, withAutomaticReconnect akceptuje tablicę liczb reprezentujących opóźnienie w milisekundach oczekiwania przed rozpoczęciem każdej próby ponownego nawiązania połączenia.

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

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

W poprzednim przykładzie skonfigurowana jest wartość HubConnection , aby rozpocząć ponowne nawiązywanie połączeń natychmiast po utracie połączenia. Dotyczy to również konfiguracji domyślnej.

Jeśli pierwsza próba ponownego nawiązania połączenia zakończy się niepowodzeniem, druga próba ponownego połączenia zostanie również uruchomiona natychmiast zamiast czekać 2 sekundy, tak jak w konfiguracji domyślnej.

Jeśli druga próba ponownego nawiązania połączenia zakończy się niepowodzeniem, trzecia próba ponownego nawiązania połączenia rozpocznie się w ciągu 10 sekund, co jest ponownie podobne do konfiguracji domyślnej.

Zachowanie niestandardowe ponownie odbiega od domyślnego zachowania, zatrzymując się po trzecim niepowodzeniu ponownego nawiązania połączenia zamiast próby ponownego nawiązania połączenia w ciągu kolejnych 30 sekund, tak jak w konfiguracji domyślnej.

Jeśli chcesz mieć jeszcze większą kontrolę nad chronometrażem i liczbą automatycznych ponownych prób ponownego łączenia, withAutomaticReconnect akceptuje obiekt implementjący IRetryPolicy interfejs, który ma jedną metodę o nazwie nextRetryDelayInMilliseconds.

nextRetryDelayInMilliseconds przyjmuje jeden argument z typem RetryContext. Element RetryContext ma trzy właściwości: previousRetryCount, elapsedMilliseconds i retryReason, które są odpowiednio number, number i Error. Przed pierwszą próbą ponownego połączenia zarówno previousRetryCount, jak i elapsedMilliseconds będą równe zeru, a wartość retryReason będzie błędem, który spowodował zerwanie połączenia. Po każdej nieudanej próbie ponownego połączenia wartość previousRetryCount zostanie zwiększona o jeden, wartość elapsedMilliseconds zostanie zaktualizowana tak, aby odzwierciedlać czas spędzony dotychczas na ponownym łączeniu w milisekundach, a wartość retryReason będzie błędem, który spowodował niepowodzenie ostatniej próby ponownego połączenia.

nextRetryDelayInMilliseconds musi zwrócić wartość będącą albo liczbą milisekund oczekiwania przed następną próbą ponownego połączenia, albo null, jeśli HubConnection powinno przestać ponawiać próby połączenia.

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

Alternatywnie możesz napisać kod, który ponownie połączy klienta ręcznie, jak pokazano w temacie Ręczne ponowne nawiązywanie połączenia.

Ręczne ponowne nawiązywanie połączenia

Poniższy kod pokazuje typowe podejście do ręcznego ponownego nawiązywania połączenia:

  1. Tworzy się funkcję (w tym przypadku funkcję start), aby rozpocząć połączenie.
  2. Wywołaj funkcję start w procedurze obsługi zdarzenia onclose połączenia.
async function start() {
    try {
        await connection.start();
        console.log("SignalR Connected.");
    } catch (err) {
        console.log(err);
        setTimeout(start, 5000);
    }
};

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

Implementacje produkcyjne zwykle stosują algorytm wykładniczego wydłużania odstępów między próbami lub ponawiają próbę określoną liczbę razy.

Karta uśpienia przeglądarki

Niektóre przeglądarki mają funkcję zamrażania lub usypiania nieaktywnych kart, aby zmniejszyć zużycie zasobów komputera. Może to spowodować zamknięcie połączeń SignalR i skutkować niepożądanymi wrażeniami użytkownika. Przeglądarki używają heurystyki, aby ustalić, czy karta powinna zostać uśpiona, na przykład:

  • Odtwarzanie dźwięku
  • Utrzymywanie blokady sieciowej
  • Trzymanie blokady IndexedDB
  • Nawiązywanie połączenia z urządzeniem USB
  • Przechwytywanie wideo lub dźwięku
  • Jest odzwierciedlane
  • Przechwytywanie okna lub wyświetlania

Note

Te heurystyki mogą się zmieniać w czasie lub różnić się między przeglądarkami. Sprawdź macierz wsparcia i ustal, która metoda sprawdzi się najlepiej w Twoich scenariuszach.

Aby uniknąć uśpienia aplikacji, aplikacja powinna wyzwolić jedną z heurystyki używanych przez przeglądarkę.

Poniższy przykład kodu pokazuje, jak użyć Web Lock, aby utrzymać kartę w aktywnym stanie i uniknąć nieoczekiwanego zamknięcia połączenia.

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

W poprzednim przykładzie kodu:

  • Blokady internetowe są eksperymentalne. Sprawdzanie warunkowe potwierdza, że przeglądarka obsługuje blokady sieci Web.
  • Funkcja rozwiązująca obietnicę (lockResolver) jest przechowywana, aby można było zwolnić blokadę, gdy karta może przejść w stan uśpienia.
  • Podczas zamykania połączenia blokada jest zwalniana przez wywołanie metody lockResolver(). Po zwolnieniu blokady karta może przejść w stan uśpienia.