一個簡單的 SignalR Hub 示例

2025-01-17 00:03:41 | .NET | 81 Reads

下面是一個簡單的 SignalR Hub 示例,包括服務器端和客戶端的基本配置與使用說明。


1. 服務器端

a. 建立 Hub 類別

首先,在 v2knowBlazor(Server)專案中新增一個 SignalR Hub 類別,例如 ChatHub.cs

using Microsoft.AspNetCore.SignalR;

public class ChatHub : Hub
{
    // 一個簡單的方法:收到消息後轉發給所有連接的客戶端
    public async Task SendMessage(string user, string message)
    {
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}

這個 Hub 定義了一個 SendMessage 方法,當客戶端呼叫該方法時,服務器會透過 Clients.All.SendAsync 把消息發送給所有連接的客戶端。

b. 在 Program.cs 中註冊並映射 Hub

v2knowBlazorProgram.cs 裡面,新增 SignalR 服務並映射 Hub 端點:

var builder = WebApplication.CreateBuilder(args);

// 註冊 SignalR 服務
builder.Services.AddSignalR();

// 其它服務與中間件配置
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents()
    .AddInteractiveWebAssemblyComponents();

// ... 其他註冊與配置代碼

var app = builder.Build();

// 中間件配置,如錯誤處理、靜態文件、路由等
if (app.Environment.IsDevelopment())
{
    app.UseWebAssemblyDebugging();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

// 映射 SignalR Hub 端點
app.MapHub<ChatHub>("/chathub");

// 映射 Razor 組件(Blazor 頁面)等
app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode()
    .AddInteractiveWebAssemblyRenderMode()
    .AddAdditionalAssemblies(typeof(v2knowBlazor.Client._Imports).Assembly);

app.Run();

這裏透過 builder.Services.AddSignalR(); 添加 SignalR 相關服務,並使用 app.MapHub<ChatHub>("/chathub"); 將 Hub 映射到指定端點(這裡是 /chathub)。


2. 客戶端

v2knowBlazor.Client 專案中,你可以使用 SignalR 客戶端庫來建立連線。假設你在某個 Razor 組件中(例如 Pages/Chat.razor)使用 SignalR 連線:

a. 安裝 NuGet 套件

確保你的客戶端專案中安裝了 SignalR 客戶端套件:

dotnet add package Microsoft.AspNetCore.SignalR.Client

b. 示例 Razor 組件(Chat.razor)

@page "/chat"
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager Navigation
@rendermode InteractiveAuto

<h3>Chat</h3>

<input @bind="user" placeholder="Your name" />
<input @bind="message" placeholder="Message" />
<button @onclick="Send">Send</button>

<ul>
    @foreach (var msg in messages)
    {
        <li>@msg</li>
    }
</ul>

@code {
    private HubConnection? hubConnection;
    private string user = string.Empty;
    private string message = string.Empty;
    private List<string> messages = new();

    protected override async Task OnInitializedAsync()
    {
        // 建立 Hub 連線(注意使用 Navigation.BaseUri 拼出完整 URL)
        hubConnection = new HubConnectionBuilder()
            .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
            .WithAutomaticReconnect()
            .Build();

        // 註冊從服務器接收消息的處理函數
        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            messages.Add(encodedMsg);
            InvokeAsync(StateHasChanged);
        });

        await hubConnection.StartAsync();
    }

    private async Task Send()
    {
        if (hubConnection is not null)
        {
            await hubConnection.SendAsync("SendMessage", user, message);
            message = string.Empty;
        }
    }

    public bool IsConnected =>
        hubConnection is not null && hubConnection.State == HubConnectionState.Connected;

    public async ValueTask DisposeAsync()
    {
        if (hubConnection is not null)
        {
            await hubConnection.DisposeAsync();
        }
    }
}

在這個範例中:

  • 我們先建立一個 HubConnection 並指定連線的 URL (/chathub),該 URL 與服務器端在 Program.cs 中映射的端點一致。
  • 使用 hubConnection.On<string, string>("ReceiveMessage", ...) 註冊從服務器接收消息的事件處理程式。
  • 當用戶在 UI 中輸入資料並點擊 Send 按鈕時,呼叫 SendMessage 方法,將訊息發送到服務器端,後者再分發給所有連線的用戶。

總結

這個簡單示例展示了如何在服務器端建立一個 SignalR Hub,並在客戶端(Blazor WASM)連線、接收與發送消息。這種模式可應用於聊天應用、即時通知、遊戲或其它需要實時通信的場景。

直接訪問/chathub會出現Connection ID required, 這個URL相當於API?會被濫用嗎?需要令牌嗎?

當你直接在瀏覽器中訪問 /chathub 時,看到“Connection ID required”訊息,這其實是正常現象。這是因為 SignalR Hub 的端點並非傳統意義上的 HTTP API,而是一個用來建立持久連線(如 WebSocket 或長輪詢)的通信管道。以下是針對你的幾個問題的詳細解釋:


1. /chathub 是 API 嗎?

  • 不是傳統 API/chathub 端點不是用來直接發送 HTTP 請求並獲取資料的 API,而是專門用來建立持久連線的。當 SignalR 客戶端(例如在 Blazor 應用中)嘗試建立連線時,它會發起握手(handshake)請求,並根據不同的協議(例如 WebSocket、長輪詢等)來維護連線。
  • 直接訪問無法使用:直接在瀏覽器中輸入 /chathub 會得到類似「Connection ID required」的提示,這是因為請求並未經過正確的 SignalR 握手流程。

2. 會被濫用嗎?

  • 暴露端點風險:在理論上,所有公開的端點都有可能遭受惡意請求。不過 SignalR Hub 端點並不是傳統靜態 API,而是需要經過握手與協議協商才能使用。若沒有正確的連線握手,請求會被拒絕或返回錯誤訊息。
  • 防範措施:如果應用中存在較高的安全需求或擔心惡意濫用,可以考慮如下做法:
    • 身份驗證與授權:在 Hub 中實現授權機制,例如使用 HubAuthorize 屬性來限制只有經過授權的用戶才能連線。
    • 令牌驗證:透過配置 SignalR 連線時,客戶端可以附加 JWT 或其它令牌,服務器端在建立連線時進行驗證。這樣即使有人直接訪問 /chathub,也無法通過身份驗證。
    • 限流與防火牆:若應用面臨大流量或潛在攻擊風險,可能需要進一步的 IP 限流或應用層防火牆來保護 SignalR 端點。

3. 是否需要令牌?

  • 根據需求決定:預設情況下,SignalR Hub 並不自動要求令牌。如果你的應用存在敏感操作或數據,建議採用身份驗證與授權機制:

     

    • 你可以在 SignalR Hub 類別上加上 [Authorize] 屬性,這樣只有已通過授權的用戶才能建立連線。

    • 客戶端在建立連線時,可以配置附加令牌。例如(以 JWT 為例):

      hubConnection = new HubConnectionBuilder()
          .WithUrl(Navigation.ToAbsoluteUri("/chathub"), options =>
          {
              options.AccessTokenProvider = () => Task.FromResult("your-jwt-token-here");
          })
          .WithAutomaticReconnect()
          .Build();
      

服務器端在授權時會檢查這個令牌,確保連線合法。


總結

  • /chathub 端點實際上是供 SignalR 客戶端建立持久連線的管道,而不是傳統意義上的 API。
  • 直接訪問會因為缺少握手信息而返回錯誤(如 "Connection ID required")。
  • 這樣一來,你既能利用 SignalR 實現實時雙向通信,又能保障應用的安全性。

  • 安全性:如果擔心被濫用,可以在 Hub 上實施身份驗證與授權機制,並考慮使用令牌進行進一步保護。

This article was last edited at 2025-01-17 00:28:12