SignalRASP.NET Core JavaScript 用戶端

作者:Rachel Appel

ASP.NET Core SignalR JavaScript 用戶端程式庫可讓開發人員呼叫伺服器端 SignalR 中樞程式碼。

SignalR安裝用戶端套件

SignalRJavaScript 用戶端程式庫會以npm套件的形式傳遞。 下列各節概述安裝用戶端程式庫的不同方式。

使用 npm 安裝

套件管理員主控台執行下列命令:

npm init -y
npm install @microsoft/signalr

npm 會在 node_modules\@microsoft\signalr\dist\browser 資料夾中安裝套件內容。 建立 wwwroot/lib/signalr 資料夾。 將 signalr.js 檔案複製到 wwwroot/lib/signalr 資料夾。

SignalR參考 專案中的 <script> JavaScript 用戶端。 例如:

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

使用內容傳遞網路 (CDN)

若要在沒有 npm 必要條件的情況下使用用戶端程式庫,請參考用戶端程式庫的 CDN 裝載複本。 例如:

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

用戶端程式庫可在下列 CDN 上使用:

使用 LibMan 安裝

LibMan 可用來從 CDN 裝載的用戶端程式庫安裝特定用戶端程式庫檔案。 例如,只將縮訂的 JavaScript 檔案新增至專案。 如需該方法的詳細資訊,請參閱 新增 SignalR 用戶端程式庫

連線到中樞

下列程式碼會建立並啟動連線。 中樞的名稱不區分大小寫:

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

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

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

// Start the connection.
start();

(CORS) 的跨原始來源連線

一般而言,瀏覽器會從與要求頁面相同的網域載入連線。 不過,有時候需要連線到另一個網域。

提出 跨網域要求時,用戶端程式代碼 必須使用 絕對 URL,而不是相對 URL。 針對跨網域要求,請將 變更 .withUrl("/chathub").withUrl("https://{App domain name}/chathub")

若要防止惡意網站從另一個網站讀取敏感性資料,預設會停用 跨原始來源連線 。 若要允許跨原始來源要求,請啟用 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 必須先呼叫 ,才能呼叫 MapHub

從用戶端呼叫中樞方法

JavaScript 用戶端會透過HubConnectioninvoke方法,在中樞上呼叫公用方法。 方法 invoke 接受:

  • 中樞方法的名稱。
  • 在中樞方法中定義的任何引數。

在下列醒目提示的程式碼中,中樞上的方法名稱為 SendMessage 。 傳遞至 invoke 中樞方法和引數的第二個和第三個引數會對應至中樞方法 user 的 和 message 引數:

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

只有在 以預設模式使用 Azure SignalR 服務 時,才支援從用戶端呼叫中樞方法。 如需詳細資訊,請參閱 azure-signalr GitHub 存放庫 (常見問題)

方法會 invoke 傳回 JavaScript Promise 。 如果伺服器上的方法傳回時有任何) ,則會 Promise 使用傳回值 (解析 。 如果伺服器上的 方法擲回錯誤,則會 Promise 以錯誤訊息拒絕 。 使用 asyncawaitPromisethencatch 方法來處理這些案例。

JavaScript 用戶端也可以透過 的 HubConnection傳送方法,在中樞上呼叫公用方法。 invoke不同于 方法, send 此方法不會等候來自伺服器的回應。 方法會 send 傳回 JavaScript Promise 。 當 Promise 訊息已傳送至伺服器時,就會解析 。 如果傳送訊息時發生錯誤, Promise 就會因為錯誤訊息而遭到拒絕。 使用 asyncawaitPromisethencatch 方法來處理這些案例。

使用 send不會等到伺服器收到訊息為止。 因此,無法從伺服器傳回資料或錯誤。

從中樞呼叫用戶端方法

若要從中樞接收訊息,請使用 的 on 方法定義方法 HubConnection

  • JavaScript 用戶端方法的名稱。
  • 中樞傳遞至 方法的引數。

在下列範例中,方法名稱為 ReceiveMessage 。 引數名稱為 usermessage

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

中的 connection.on 上述程式碼會在伺服器端程式碼使用 方法來呼叫它時執行 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會比對 和 connection.onSendAsync 定義的方法名稱和引數,判斷要呼叫哪一個用戶端方法。

最佳做法是在 之後 on 的 上 HubConnection 呼叫start方法。 這麼做可確保在收到任何訊息之前,先註冊處理常式。

錯誤處理和記錄

當用戶端無法連線或傳送訊息時,使用 console.error 將錯誤輸出至瀏覽器的主控台:

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

藉由在建立連線時傳遞記錄器和事件種類來設定用戶端記錄追蹤。 訊息會以指定的記錄層級和更高層級記錄。 可用的記錄層級如下所示:

  • signalR.LogLevel.Error:錯誤訊息。 僅記錄 Error 訊息。
  • signalR.LogLevel.Warning:警告有關潛在錯誤的訊息。 記錄 Warning 、 和 Error 訊息。
  • signalR.LogLevel.Information:沒有錯誤的狀態訊息。 記錄 InformationWarningError 訊息。
  • signalR.LogLevel.Trace:追蹤訊息。 記錄所有專案,包括中樞與用戶端之間傳輸的資料。

使用HubConnectionBuilder上的configureLogging方法來設定記錄層級。 訊息會記錄到瀏覽器主控台:

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

重新連線用戶端

自動重新連線

的 JavaScript 用戶端 SignalR 可以設定為使用HubConnectionBuilder上的WithAutomaticReconnect方法自動重新連線。 預設不會自動重新連線。

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

如果沒有任何參數, WithAutomaticReconnect 會分別設定用戶端等候 0、2、10 和 30 秒,然後再嘗試每次重新連線嘗試。 嘗試四次失敗之後,它會停止嘗試重新連線。

開始任何重新連線嘗試之前,: HubConnection

  • 轉換至 HubConnectionState.Reconnecting 狀態並引發其 onreconnecting 回呼。
  • 不會轉換至 Disconnected 狀態,並觸發其 onclose 回呼,例如 HubConnection 未設定自動重新連線。

重新連線方法可讓您:

  • 警告使用者連線已遺失。
  • 停用 UI 元素。
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);
});

如果用戶端在前四次嘗試中成功重新連線,則會 HubConnection 轉換回 Connected 狀態並引發其 onreconnected 回呼。 這可讓您通知使用者已重新建立連線。

由於連線看起來完全不熟悉伺服器,因此會提供新的 connectionIdonreconnected 呼。

如果 設定為略過交涉HubConnection 則回 onreconnected 呼的參數 connectionId未定義的

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 不會將 HubConnection 設定為重試初始啟動失敗,因此必須手動處理啟動失敗:

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

如果用戶端未在其前四次嘗試內成功重新連線,則會 HubConnection 轉換至 Disconnected 狀態並引發其 內部回 呼。 這提供通知使用者的機會:

  • 連線已永久遺失。
  • 請嘗試重新整理頁面:
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);
});

若要在中斷連線或變更重新連線時間之前設定自訂的重新連線嘗試次數,請接受數位陣列, withAutomaticReconnect 表示在開始每次重新連線嘗試之前等待的延遲以毫秒為單位。

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

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

上述範例會將 HubConnection 設定為在連線遺失之後立即開始嘗試重新連線。 預設組態也會等候零秒來嘗試重新連線。

如果第一次重新連線嘗試失敗,第二次重新連線嘗試也會立即啟動,而不是使用預設設定等候 2 秒。

如果第二次重新連線嘗試失敗,第三次重新連線嘗試會在 10 秒內啟動,這與預設組態相同。

設定的重新連線時間與預設行為不同,方法是在第三次重新連線嘗試失敗後停止,而不是在另一個 30 秒內再嘗試一次重新連線。

若要進一步控制自動重新連線嘗試的時間和次數, withAutomaticReconnect 請接受實 IRetryPolicy 作 介面的物件,其具有名為 nextRetryDelayInMilliseconds 的單一方法。

nextRetryDelayInMilliseconds 採用具有 型 RetryContext 別的單一引數。 RetryContext有三個屬性: previousRetryCountelapsedMillisecondsretryReason 和 分別為 numbernumberError 。 第一次重新連線嘗試之前, previousRetryCountelapsedMilliseconds 都會是零,而 retryReason 會是造成連線遺失的錯誤。 每次重試嘗試失敗之後, previousRetryCount 都會遞增一次, elapsedMilliseconds 並更新以反映到目前為止重新連線所花費的時間,以毫秒為單位,而 retryReason 會是導致上次重新連線嘗試失敗的錯誤。

nextRetryDelayInMilliseconds 必須傳回一個數位,表示下次重新連線嘗試之前要等候的毫秒數,或者 null 如果 HubConnection 應該停止重新連線,則為 。

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

或者,您也可以撰寫程式碼,手動重新連線用戶端,如下一節所示。

手動重新連線

下列程式碼示範典型的手動重新連線方法:

  1. 在此情況下, start 會建立函式) 來啟動連線, (函式。
  2. start在連接的 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();
});

生產實作通常會使用指數輪詢,或重試指定的次數。

瀏覽器睡眠索引標籤

某些瀏覽器具有索引標籤凍結或睡眠功能,以減少非使用中索引標籤的電腦資源使用量。 這可能會導致 SignalR 連線關閉,而且可能會導致不必要的使用者體驗。 瀏覽器會使用啟發學習法來找出索引標籤是否應該進入睡眠狀態,例如:

  • 播放音訊
  • 保留 Web 鎖定
  • IndexedDB按住鎖定
  • 連線到 USB 裝置
  • 擷取視訊或音訊
  • 正在鏡像
  • 擷取視窗或顯示

瀏覽器啟發學習法可能會隨著時間而變更,而且瀏覽器之間可能會有所不同。 檢查支援矩陣,並找出最適合您案例的方法。

為了避免讓應用程式進入睡眠狀態,應用程式應該觸發瀏覽器使用的其中一個啟發學習法。

下列程式碼範例示範如何使用 Web 鎖定 讓索引標籤保持喚醒,並避免非預期的連線關閉。

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

針對上述程式碼範例:

  • Web 鎖定是實驗性的。 條件式檢查會確認瀏覽器支援 Web 鎖定。
  • 承諾解析程式 lockResolver 會儲存,以便在索引標籤可接受進入睡眠狀態時釋放鎖定。
  • 關閉連線時,會藉由呼叫 lockResolver() 來釋放鎖定。 釋放鎖定時,允許索引標籤進入睡眠狀態。

其他資源

作者:Rachel Appel

ASP.NET Core SignalR JavaScript 用戶端程式庫可讓開發人員呼叫伺服器端中樞程式碼。

檢視或下載範例程式碼 (如何下載)

SignalR安裝用戶端套件

SignalRJavaScript 用戶端程式庫會以npm套件的形式傳遞。 下列各節概述安裝用戶端程式庫的不同方式。

使用 npm 安裝

若為 Visual Studio,請在根資料夾中,從 套件管理員主控台 執行下列命令。 針對Visual Studio Code,請從整合式終端機執行下列命令。

npm init -y
npm install @microsoft/signalr

npm 會在 node_modules\@microsoft\signalr\dist\browser 資料夾中安裝套件內容。 在wwwroot\lib資料夾下建立名為signalr的新資料夾。 將 signalr.js 檔案複製到 wwwroot\lib\signalr 資料夾。

SignalR參考 專案中的 <script> JavaScript 用戶端。 例如:

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

使用內容傳遞網路 (CDN)

若要在沒有 npm 必要條件的情況下使用用戶端程式庫,請參考用戶端程式庫的 CDN 裝載複本。 例如:

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

用戶端程式庫可在下列 CDN 上使用:

使用 LibMan 安裝

LibMan 可用來從 CDN 裝載的用戶端程式庫安裝特定用戶端程式庫檔案。 例如,只將縮訂的 JavaScript 檔案新增至專案。 如需該方法的詳細資訊,請參閱 新增 SignalR 用戶端程式庫

連線到中樞

下列程式碼會建立並啟動連線。 中樞的名稱不區分大小寫:

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

跨原始來源連線

一般而言,瀏覽器會從與要求頁面相同的網域載入連線。 不過,有時候需要連線到另一個網域。

重要

用戶端程式代碼必須使用絕對 URL,而不是相對 URL。 將 .withUrl("/chathub") 變更為 .withUrl("https://myappurl/chathub")

若要防止惡意網站從另一個網站讀取敏感性資料,預設會停用 跨原始來源連線 。 若要允許跨原始來源要求,請在 類別中 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");
            });
        }
    }
}

從用戶端呼叫中樞方法

JavaScript 用戶端會透過HubConnectioninvoke方法,在中樞上呼叫公用方法。 方法 invoke 接受:

  • 中樞方法的名稱。
  • 在中樞方法中定義的任何引數。

在下列範例中,中樞上的方法名稱為 SendMessage 。 傳遞至 invoke 中樞方法和引數的第二個和第三個引數會對應至中樞方法 user 的 和 message 引數:

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

注意

只有在以預設模式使用 Azure SignalR 服務時,才支援從用戶端呼叫中樞方法。 如需詳細資訊,請參閱 azure-signalr GitHub 存放庫 (常見問題)

方法會 invoke 傳回 JavaScript Promise 。 如果伺服器上的方法傳回時有任何) ,則會 Promise 使用傳回值 (解析 。 如果伺服器上的 方法擲回錯誤,則會 Promise 以錯誤訊息拒絕 。 使用 asyncawaitPromisethencatch 方法來處理這些案例。

JavaScript 用戶端也可以透過 的 HubConnection傳送方法,在中樞上呼叫公用方法。 invoke不同于 方法, send 此方法不會等候來自伺服器的回應。 方法會 send 傳回 JavaScript Promise 。 當 Promise 訊息已傳送至伺服器時,就會解析 。 如果傳送訊息時發生錯誤, Promise 就會因為錯誤訊息而遭到拒絕。 使用 asyncawaitPromisethencatch 方法來處理這些案例。

注意

使用 send 不會等到伺服器收到訊息為止。 因此,無法從伺服器傳回資料或錯誤。

從中樞呼叫用戶端方法

若要從中樞接收訊息,請使用 的 on 方法定義方法 HubConnection

  • JavaScript 用戶端方法的名稱。
  • 中樞傳遞至 方法的引數。

在下列範例中,方法名稱為 ReceiveMessage 。 引數名稱為 usermessage

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

中的 connection.on 上述程式碼會在伺服器端程式碼使用 方法來呼叫它時執行 SendAsync

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

SignalR會比對 和 connection.onSendAsync 定義的方法名稱和引數,判斷要呼叫哪一個用戶端方法。

注意

最佳做法是在 之後 on 的 上 HubConnection 呼叫start方法。 這麼做可確保在收到任何訊息之前,先註冊您的處理常式。

錯誤處理和記錄

使用 trycatch 搭配 asyncawaitPromisecatch 方法來處理用戶端錯誤。 用來 console.error 將錯誤輸出至瀏覽器的主控台:

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

藉由在建立連線時傳遞記錄器和事件種類來設定用戶端記錄追蹤。 訊息會以指定的記錄層級和更高層級記錄。 可用的記錄層級如下所示:

  • signalR.LogLevel.Error:錯誤訊息。 僅記錄 Error 訊息。
  • signalR.LogLevel.Warning:警告有關潛在錯誤的訊息。 記錄 Warning 、 和 Error 訊息。
  • signalR.LogLevel.Information:沒有錯誤的狀態訊息。 記錄 InformationWarningError 訊息。
  • signalR.LogLevel.Trace:追蹤訊息。 記錄所有專案,包括中樞與用戶端之間傳輸的資料。

使用HubConnectionBuilder上的configureLogging方法來設定記錄層級。 訊息會記錄到瀏覽器主控台:

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

重新連線用戶端

自動重新連線

的 JavaScript 用戶端 SignalR 可以設定為使用 withAutomaticReconnectHubConnectionBuilder上的 方法自動重新連線。 預設不會自動重新連線。

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

如果沒有任何參數, withAutomaticReconnect() 請將用戶端分別設定為等候 0、2、10 和 30 秒,再嘗試每次重新連線,並在四次嘗試失敗之後停止。

在開始任何重新連線嘗試之前,會 HubConnection 轉換至 HubConnectionState.Reconnecting 狀態並引發其 onreconnecting 回呼,而不是轉換至 Disconnected 狀態,並觸發其 onclose 回呼,就像未設定自動重新連線一樣 HubConnection 。 這可讓您警告使用者連線已遺失,並停用 UI 元素。

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

如果用戶端在其前四次嘗試內成功重新連線,則會 HubConnection 轉換回 Connected 狀態並引發其 onreconnected 回呼。 這可讓您通知使用者已重新建立連線。

由於連線看起來完全不熟悉伺服器,因此會將新的 connectionId 提供給回 onreconnected 呼。

警告

如果 設定為略過交涉HubConnection 則回 onreconnected 呼的參數 connectionId 將會是未定義的。

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() 不會將 HubConnection 設定為重試初始啟動失敗,因此必須手動處理啟動失敗:

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

如果用戶端在前四次嘗試內未成功重新連線,則會 HubConnection 轉換至 Disconnected 狀態並引發其 內部回 呼。 這可讓您通知使用者連線已永久遺失,並建議重新整理頁面:

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

若要在中斷連線或變更重新連線時間之前設定自訂的重新連線嘗試次數,請接受數位陣列, withAutomaticReconnect 表示在開始每次重新連線嘗試之前等待的延遲以毫秒為單位。

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

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

上述範例會將 HubConnection 設定為在連線遺失之後立即開始嘗試重新連線。 這也適用于預設組態。

如果第一次重新連線嘗試失敗,第二次重新連線嘗試也會立即啟動,而不是等候 2 秒,就像在預設組態中一樣。

如果第二次重新連線嘗試失敗,第三次重新連線嘗試將會在 10 秒內啟動,就像預設設定一樣。

然後,自訂行為會在第三次重新連線嘗試失敗後停止,而不是在另一個 30 秒內再嘗試一次重新連線嘗試,就像在預設組態中一樣,再次從預設行為發散。

如果您想要更充分掌控自動重新連線嘗試的時機和次數, withAutomaticReconnect 請接受實 IRetryPolicy 作 介面的物件,其具有名為 nextRetryDelayInMilliseconds 的單一方法。

nextRetryDelayInMilliseconds 採用具有 型 RetryContext 別的單一引數。 RetryContext有三個屬性: previousRetryCountelapsedMillisecondsretryReason 和 分別為 numbernumberError 。 第一次重新連線嘗試之前, previousRetryCountelapsedMilliseconds 都會是零,而 retryReason 會是造成連線遺失的錯誤。 每次重試嘗試失敗之後, previousRetryCount 都會遞增一次, elapsedMilliseconds 並更新以反映到目前為止重新連線所花費的時間,以毫秒為單位,而 retryReason 會是導致上次重新連線嘗試失敗的錯誤。

nextRetryDelayInMilliseconds 必須傳回一個數位,表示下次重新連線嘗試之前要等候的毫秒數,或者 null 如果 HubConnection 應該停止重新連線,則為 。

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

或者,您也可以撰寫程式碼,以手動方式重新連線用戶端,如 手動重新連線所示。

手動重新連線

下列程式碼示範典型的手動重新連線方法:

  1. 在此情況下, start 會建立函式) 來啟動連線, (函式。
  2. start在連接的 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();
});

生產實作通常會使用指數輪詢,或重試指定的次數。

瀏覽器睡眠索引標籤

某些瀏覽器具有索引標籤凍結或睡眠功能,以減少非使用中索引標籤的電腦資源使用量。 這可能會導致 SignalR 連線關閉,而且可能會導致不必要的使用者體驗。 瀏覽器會使用啟發學習法來找出索引標籤是否應該進入睡眠狀態,例如:

  • 播放音訊
  • 保留 Web 鎖定
  • IndexedDB按住鎖定
  • 連線到 USB 裝置
  • 擷取視訊或音訊
  • 正在鏡像
  • 擷取視窗或顯示

注意

這些啟發學習法可能會隨著時間而變更,或在瀏覽器之間有所不同。 請檢查您的支援矩陣,並找出最適合您案例的方法。

為了避免讓應用程式進入睡眠狀態,應用程式應該觸發瀏覽器使用的其中一個啟發學習法。

下列程式碼範例示範如何使用 Web 鎖定 讓索引標籤保持喚醒,並避免非預期的連線關閉。

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

針對上述程式碼範例:

  • Web 鎖定是實驗性的。 條件式檢查會確認瀏覽器支援 Web 鎖定。
  • 承諾解析程式 (lockResolver) 會儲存,以便在索引標籤可接受進入睡眠狀態時釋放鎖定。
  • 關閉連線時,會藉由呼叫 lockResolver() 來釋放鎖定。 釋放鎖定時,允許索引標籤進入睡眠狀態。

其他資源