Udostępnij za pośrednictwem


ASP.NET Core SignalR JavaScript client

Autor: Rachel Appel

Biblioteka kliencka ASP.NET Core SignalR Języka JavaScript umożliwia deweloperom wywoływanie kodu centrum po stronie SignalR serwera.

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

Uruchom następujące polecenia z konsoli Menedżer pakietów:

npm init -y
npm install @microsoft/signalr

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

Odwołanie do SignalR klienta JavaScript w elemencie <script> . Na 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. Na przykład:

<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/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.

Nawiązywanie połączenia z koncentratorem

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ą.

Podczas wykonywania żądań między domenami kod klienta musi używać bezwzględnego adresu URL zamiast względnego adresu URL. W przypadku żą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();

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.
  • Wszystkie argumenty zdefiniowane w metodzie piasty.

W poniższym wyróżnionym kodzie nazwa metody w centrum to SendMessage. Drugie i trzecie argumenty przekazane do mapowania na invoke argumenty user i message metody piasty:

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

Wywoływanie metod centrum od klienta jest obsługiwane tylko w przypadku korzystania z usługi platformy Azure w trybie domyślnym.SignalR Aby uzyskać więcej informacji, zobacz Często zadawane pytania (azure —signalr repozytorium GitHub).

Metoda invoke zwraca kod JavaScript Promise. Metoda Promise jest rozpoznawana przy użyciu wartości zwracanej (jeśli istnieje), gdy metoda na serwerze zwraca. Jeśli metoda na serwerze zgłasza błąd, Promise element zostanie odrzucony z komunikatem o błędzie. Użyj async metod i i Promiseawait catch , then 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. Element Promise jest rozpoznawany, gdy wiadomość została wysłana na serwer. Jeśli wystąpi błąd podczas wysyłania komunikatu, Promise zostanie on odrzucony z komunikatem o błędzie. Użyj async metod i i Promiseawait catch , then aby obsłużyć te przypadki.

Użycie send polecenia nie czeka na odebranie komunikatu przez serwer. 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 :

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. Dzięki temu 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:

  • signalR.LogLevel.Error:Komunikaty o błędach. Rejestruje Error tylko komunikaty.
  • signalR.LogLevel.Warning: Komunikaty ostrzegawcze dotyczące potencjalnych błędów. Rejestruje Warningkomunikaty i .Error
  • signalR.LogLevel.Information: komunikaty o stanie bez błędów. Dzienniki Information, Warningi Error komunikaty.
  • signalR.LogLevel.Trace: Śledzenie komunikatów. Rejestruje wszystko, w tym dane transportowane między koncentratorem 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 języka JavaScript dla SignalR programu można skonfigurować do automatycznego ponownego nawiązywania połączenia przy użyciu metody WithAutomaticReconnect w programie HubConnectionBuilder. Domyślnie nie zostanie automatycznie ponownie nawiązane połączenie.

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 zatrzymanie próby ponownego nawiązania połączenia.

Przed rozpoczęciem wszelkich ponownych prób nawiązania połączenia, polecenie HubConnection:

  • Przechodzi do HubConnectionState.Reconnecting stanu i uruchamia wywołania onreconnecting zwrotne.
  • Nie przechodzi do Disconnected stanu i wyzwala wywołania onclose zwrotne, takie jak bez skonfigurowania HubConnection automatycznego ponownego nawiązywania połączenia.

Podejście ponownego łączenia zapewnia możliwość:

  • Ostrzegaj użytkowników, że połączenie zostało utracone.
  • Wyłącz elementy 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 ramach pierwszych czterech prób, HubConnection przejście z powrotem do Connected stanu i wyzwolenie wywołań onreconnected zwrotnych. Zapewnia to możliwość poinformowania użytkowników, że połączenie zostało ponownie nawiązane.

Ponieważ połączenie wygląda zupełnie nowe na serwerze, nowe connectionId połączenie jest udostępniane wywołaniu zwrotnemu onreconnected .

Parametr onreconnected wywołania zwrotnego connectionId jest niezdefiniowany, jeśli HubConnection parametr jest skonfigurowany do pomijania negocjacji.

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 Program nie skonfiguruje elementu , aby ponowić HubConnection próbę początkowych niepowodzeń uruchamiania, dlatego błędy uruchamiania muszą być obsługiwane 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 zostanie pomyślnie ponownie nawiązany w ramach pierwszych czterech prób, HubConnection przejście do Disconnected stanu i wyzwolenie jego wywołań zwrotnych. Zapewnia to możliwość informowania użytkowników:

  • Połączenie zostało trwale utracone.
  • Spróbuj odświeżyć stronę:
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. Domyślna konfiguracja oczekuje również zero sekund na próbę ponownego nawiązania połączenia.

Jeśli pierwsza próba ponownego nawiązania połączenia zakończy się niepowodzeniem, druga próba ponownego połączenia również zostanie uruchomiona natychmiast zamiast czekać 2 sekundy przy użyciu 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 konfiguracja domyślna.

Skonfigurowany czas ponownego nawiązywania połączenia różni się od domyślnego zachowania, zatrzymując się po trzecim niepowodzeniu ponownego połączenia zamiast próby ponownego nawiązania połączenia w ciągu kolejnych 30 sekund.

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. Obiekt RetryContext ma trzy właściwości: previousRetryCount, elapsedMilliseconds i retryReason , które są numberodpowiednio , i number Error . Przed pierwszą ponowną próbą nawiązania połączenia zarówno, jak previousRetryCount i elapsedMilliseconds będzie zero, a retryReason element będzie błędem, który spowodował utratę połączenia. Po każdej nieudanej próbie previousRetryCount ponawiania próby zostanie zmniejszona o jeden, zostanie zaktualizowana, elapsedMilliseconds aby odzwierciedlić ilość czasu spędzonego na ponowne połączenie do tej pory w milisekundach i retryReason będzie to błąd, który spowodował ostatnią ponowną próbę ponownego nawiązania połączenia.

nextRetryDelayInMilliseconds Musi zwrócić liczbę reprezentującą liczbę milisekund oczekiwania przed kolejną ponowną próbą nawiązania połączenia lub null jeśli HubConnection element powinien przestać ponownie się łączyć.

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żna napisać kod, który ponownie łączy klienta ręcznie, jak pokazano w poniższej sekcji.

Ręczne ponowne nawiązywanie połączenia

Poniższy kod przedstawia typowe podejście ręcznego ponownego łączenia:

  1. Funkcja (w tym przypadku funkcja) jest tworzona start w celu uruchomienia połączenia.
  2. Wywołaj start funkcję w procedurze obsługi zdarzeń połączenia onclose .
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 używają wycofywania wykładniczego lub ponawiania próby określonej liczby razy.

Karta uśpienia przeglądarki

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

  • Odtwarzanie dźwięku
  • Przechowywanie blokady internetowej
  • Trzymanie blokady IndexedDB
  • Nawiązywanie połączenia z urządzeniem USB
  • Przechwytywanie wideo lub dźwięku
  • Dublowanie
  • Przechwytywanie okna lub wyświetlania

Heurystyka przeglądarki może ulec zmianie w czasie i może się różnić 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żywać blokady sieci Web, aby zachować blokadę tabulacji 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.
  • Program rozpoznawania obietnicy, lockResolver, jest przechowywany tak, aby blokada mogła zostać zwolniona, gdy jest akceptowalna dla karty uśpienia.
  • Podczas zamykania połączenia blokada jest zwalniana przez wywołanie metody lockResolver(). Po zwolnieniu blokady karta może spać.

Dodatkowe zasoby

Autor: Rachel Appel

Biblioteka kliencka ASP.NET Core SignalR Języka JavaScript umożliwia deweloperom wywoływanie kodu centrum 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 . signalr.js Skopiuj plik do folderu wwwroot\lib\signalr.

Odwołanie do SignalR klienta JavaScript w elemencie <script> . Na 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. Na 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.

Nawiązywanie połączenia z koncentratorem

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ą.

Ważne

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ędzy źródłami, włącz je w Startup klasie:

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.
  • Wszystkie argumenty zdefiniowane w metodzie piasty.

W poniższym przykładzie nazwa metody w centrum to SendMessage. Drugie i trzecie argumenty przekazane do mapowania na invoke argumenty user i message metody piasty:

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

Uwaga

Wywoływanie metod centrum od klienta jest obsługiwane tylko w przypadku korzystania z usługi platformy Azure SignalR w trybie domyślnym . Aby uzyskać więcej informacji, zobacz Często zadawane pytania (azure —signalr repozytorium GitHub).

Metoda invoke zwraca kod JavaScript Promise. Metoda Promise jest rozpoznawana przy użyciu wartości zwracanej (jeśli istnieje), gdy metoda na serwerze zwraca. Jeśli metoda na serwerze zgłasza błąd, Promise element zostanie odrzucony z komunikatem o błędzie. Użyj async metod i i Promiseawait catch , then 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. Element Promise jest rozpoznawany, gdy wiadomość została wysłana na serwer. Jeśli wystąpi błąd podczas wysyłania komunikatu, Promise zostanie on odrzucony z komunikatem o błędzie. Użyj async metod i i Promiseawait catch , then aby obsłużyć te przypadki.

Uwaga

Użycie send polecenia nie czeka na odebranie komunikatu przez serwer. 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.

Uwaga

Najlepszym rozwiązaniem jest wywołanie metody start w metodzie HubConnection after on. Dzięki temu programy obsługi są rejestrowane przed odebraniem komunikatów.

Obsługa błędów i rejestrowanie

Użyj try metod i i i catch async lub Promiseawait metody , 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 Error tylko komunikaty.
  • signalR.LogLevel.Warning: Komunikaty ostrzegawcze dotyczące potencjalnych błędów. Rejestruje Warningkomunikaty i .Error
  • signalR.LogLevel.Information: komunikaty o stanie bez błędów. Dzienniki Information, Warningi Error komunikaty.
  • signalR.LogLevel.Trace: Śledzenie komunikatów. Rejestruje wszystko, w tym dane transportowane między koncentratorem 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 SignalR Języka JavaScript dla programu można skonfigurować do automatycznego ponownego withAutomaticReconnect nawiązywania połączenia przy użyciu metody w programie HubConnectionBuilder. Domyślnie nie zostanie automatycznie ponownie nawiązane połączenie.

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

Bez żadnych parametrów withAutomaticReconnect() klient konfiguruje odpowiednio odczekanie 0, 2, 10 i 30 sekund przed próbą ponownego nawiązania połączenia, zatrzymanie po czterech nieudanych próbach.

Przed rozpoczęciem wszelkich ponownych HubConnection prób ponownego nawiązania połączenia nastąpi przejście do HubConnectionState.Reconnecting stanu i wyzwolenie wywołań onreconnecting zwrotnych zamiast przejścia do Disconnected stanu i wyzwolenie onclose wywołań zwrotnych, takich jak HubConnection bez 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 ramach pierwszych czterech prób, HubConnection nastąpi powrót do Connected stanu i wyzwolenie wywołań onreconnected zwrotnych. Zapewnia to możliwość poinformowania użytkowników, że połączenie zostało ponownie nawiązane.

Ponieważ połączenie wygląda zupełnie nowe na serwerze, connectionId nowe zostanie udostępnione wywołaniu zwrotnemu onreconnected .

Ostrzeżenie

onreconnected Parametr wywołania zwrotnego connectionId będzie niezdefiniowany, jeśli HubConnection parametr został skonfigurowany do pomijania negocjacji.

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() Program nie skonfiguruje elementu , aby ponowić HubConnection próbę początkowych niepowodzeń uruchamiania, dlatego błędy uruchamiania muszą być obsługiwane 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 zostanie pomyślnie ponownie nawiązany w ramach pierwszych czterech prób, HubConnection nastąpi przejście do Disconnected stanu i wyzwolenie jego wywołań zwrotnych. 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. Obiekt RetryContext ma trzy właściwości: previousRetryCount, elapsedMilliseconds i retryReason , które są numberodpowiednio , i number Error . Przed pierwszą ponowną próbą nawiązania połączenia zarówno, jak previousRetryCount i elapsedMilliseconds będzie zero, a retryReason element będzie błędem, który spowodował utratę połączenia. Po każdej nieudanej próbie previousRetryCount ponawiania próby zostanie zmniejszona o jeden, zostanie zaktualizowana, elapsedMilliseconds aby odzwierciedlić ilość czasu spędzonego na ponowne połączenie do tej pory w milisekundach i retryReason będzie to błąd, który spowodował ostatnią ponowną próbę ponownego nawiązania połączenia.

nextRetryDelayInMilliseconds Musi zwrócić liczbę reprezentującą liczbę milisekund oczekiwania przed kolejną ponowną próbą nawiązania połączenia lub null jeśli HubConnection element powinien przestać ponownie się łączyć.

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 przedstawia typowe podejście ręcznego ponownego łączenia:

  1. Funkcja (w tym przypadku funkcja) jest tworzona start w celu uruchomienia połączenia.
  2. Wywołaj start funkcję w procedurze obsługi zdarzeń połączenia onclose .
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 używają wycofywania wykładniczego lub ponawiania próby określonej liczby razy.

Karta uśpienia przeglądarki

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

  • Odtwarzanie dźwięku
  • Przechowywanie blokady internetowej
  • Trzymanie blokady IndexedDB
  • Nawiązywanie połączenia z urządzeniem USB
  • Przechwytywanie wideo lub dźwięku
  • Dublowanie
  • Przechwytywanie okna lub wyświetlania

Uwaga

Te heurystyki mogą się zmieniać w czasie lub różnić się między przeglądarkami. Sprawdź 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żywać blokady sieci Web, aby zachować blokadę tabulacji 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.
  • Program rozpoznawania obietnic (lockResolver) jest przechowywany w taki sposób, aby blokada mogła zostać zwolniona, gdy jest akceptowalna dla karty uśpienia.
  • Podczas zamykania połączenia blokada jest zwalniana przez wywołanie metody lockResolver(). Po zwolnieniu blokady karta może spać.

Dodatkowe zasoby