EKsumic's Blog

let today = new Beginning();

Click the left button to use the catalog.

OR

一個簡單的 SignalR Hub 示例

下面是一個簡單的 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 09:28:12

* *