SignalR – Tổng quan & sử dụng trong Blazor

Trang này trình bày SignalR theo hướng học từ tổng quan đến thực hành: SignalR là gì, Hub, đăng ký trong Program.cs, kết nối từ Blazor, gửi và nhận message realtime.


1️⃣ Tổng quan về SignalR

SignalR là thư viện của ASP.NET Core dùng để xây dựng chức năng realtime giữa server và client.

Khác với HTTP thông thường (client gửi request rồi chờ response), SignalR giữ kết nối mở giữa client và server để:

  • Server có thể chủ động đẩy dữ liệu xuống client
  • Client có thể gửi dữ liệu lên server theo thời gian thực
  • Nhiều client có thể nhận cùng một message ngay lập tức

SignalR rất phù hợp cho các chức năng như: chat realtime, thông báo realtime, dashboard realtime, game, tracking trạng thái, collaborative app...

Xem sơ đồ luồng hoạt động

Client A ─┐
          ├── SignalR Hub (Server) ──> Broadcast / Send / Group / User
Client B ─┤
          └── Server có thể đẩy message realtime xuống các client

2️⃣ Hub là gì?

Hub là trung tâm giao tiếp realtime trong SignalR.

Bạn có thể hiểu Hub giống như một “điểm kết nối” để client và server gọi lẫn nhau:

  • Client gọi method trên Hub
  • Hub xử lý logic phía server
  • Hub gửi message xuống một hoặc nhiều client

Hầu hết các ứng dụng SignalR đều bắt đầu bằng việc tạo một class kế thừa từ Hub.

Xem code Hub cơ bản

using Microsoft.AspNetCore.SignalR;

public class ChatHub : Hub
{
}

3️⃣ Đăng ký SignalR trong Program.cs

Để dùng SignalR, cần làm 2 bước:

  • AddSignalR() để đăng ký service
  • MapHub<THub>("/hub-url") để ánh xạ endpoint Hub

Đây là bước tương tự như Web API dùng AddControllers()MapControllers().

Xem code Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

// Đăng ký SignalR
builder.Services.AddSignalR();

var app = builder.Build();

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

app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

// Map Hub
app.MapHub<ChatHub>("/chathub");

app.Run();

4️⃣ Tạo Hub hoàn chỉnh

Một Hub thường sẽ có các method để:

  • Nhận dữ liệu từ client
  • Gửi message đến tất cả client
  • Gửi đến client hiện tại, user cụ thể hoặc group

Ví dụ đơn giản nhất là chat realtime: client gửi tên và nội dung, server nhận rồi broadcast lại cho mọi người.

Xem code ChatHub đầy đủ

using Microsoft.AspNetCore.SignalR;

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

    public override async Task OnConnectedAsync()
    {
        await Clients.All.SendAsync("ReceiveSystemMessage",
            $"{Context.ConnectionId} connected");

        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        await Clients.All.SendAsync("ReceiveSystemMessage",
            $"{Context.ConnectionId} disconnected");

        await base.OnDisconnectedAsync(exception);
    }
}

5️⃣ Kết nối từ Blazor đến Hub

Phía Blazor, ta dùng HubConnectionBuilder để tạo kết nối đến Hub.

Quy trình thường là:

  • Tạo HubConnection
  • Đăng ký các handler nhận message bằng On<T>(...)
  • Gọi StartAsync() để kết nối

Khi không dùng nữa, cần DisposeAsync() để giải phóng kết nối.

Xem code kết nối Hub trong Blazor

using Microsoft.AspNetCore.SignalR.Client;

private HubConnection? _hubConnection;

protected override async Task OnInitializedAsync()
{
    _hubConnection = new HubConnectionBuilder()
        .WithUrl(Nav.ToAbsoluteUri("/chathub"))
        .WithAutomaticReconnect()
        .Build();

    _hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
    {
        _messages.Add($"{user}: {message}");
        InvokeAsync(StateHasChanged);
    });

    await _hubConnection.StartAsync();
}

6️⃣ Gửi message từ client lên server

Sau khi kết nối thành công, client có thể gọi method trên Hub bằng InvokeAsync().

Ví dụ: gọi method SendMessage để gửi tên và nội dung lên server.

Xem code gửi message

private async Task Send()
{
    if (_hubConnection is not null)
    {
        await _hubConnection.InvokeAsync("SendMessage", _userName, _message);
    }
}

7️⃣ Nhận message từ server xuống client

Để nhận message, client đăng ký callback bằng On<...>().

Ví dụ:

  • Server gọi Clients.All.SendAsync("ReceiveMessage", ...)
  • Client đã đăng ký On<string, string>("ReceiveMessage", ...)
  • Callback sẽ chạy ngay khi message đến

Đây là phần tạo nên tính realtime của SignalR.

Xem code nhận message

_hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
{
    _messages.Add($"{user}: {message}");
    InvokeAsync(StateHasChanged);
});

8️⃣ Demo chat realtime trong Blazor

Status: Disconnected

ConnectionId: -

Xem code component demo hoàn chỉnh
@page "/signalr-demo"
@implements IAsyncDisposable
@inject NavigationManager Nav

using Microsoft.AspNetCore.SignalR.Client;

private HubConnection? _hubConnection;
private List<string> _messages = new();
private string _userName = "PLC & SCADA";
private string _message = "";
private bool _isConnected = false;
private string _connectionInfo = "-";

private async Task ConnectHub()
{
    if (_hubConnection is not null)
        return;

    _hubConnection = new HubConnectionBuilder()
        .WithUrl(Nav.ToAbsoluteUri("/chathub"))
        .WithAutomaticReconnect()
        .Build();

    _hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
    {
        _messages.Add($"{user}: {message}");
        InvokeAsync(StateHasChanged);
    });

    _hubConnection.On<string>("ReceiveSystemMessage", (message) =>
    {
        _messages.Add($"[System] {message}");
        InvokeAsync(StateHasChanged);
    });

    await _hubConnection.StartAsync();

    _isConnected = true;
    _connectionInfo = _hubConnection.ConnectionId ?? "-";
}

private async Task SendMessage()
{
    if (_hubConnection is null || string.IsNullOrWhiteSpace(_message))
        return;

    await _hubConnection.InvokeAsync("SendMessage", _userName, _message);
    _message = "";
}

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

Logs:


    9️⃣ Group, User và Broadcast

    SignalR hỗ trợ nhiều kiểu gửi message:

    • Clients.All → gửi cho tất cả client
    • Clients.Caller → gửi cho client hiện tại
    • Clients.Others → gửi cho mọi client trừ client hiện tại
    • Clients.User(userId) → gửi cho một user cụ thể
    • Clients.Group(groupName) → gửi cho một group

    Đây là lý do SignalR rất mạnh khi làm chat room, notification room, dashboard theo từng nhóm người dùng.

    Xem code ví dụ Group
    
    public async Task JoinGroup(string groupName)
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
        await Clients.Group(groupName).SendAsync("ReceiveSystemMessage",
            $"{Context.ConnectionId} joined {groupName}");
    }
    
    public async Task SendGroupMessage(string groupName, string user, string message)
    {
        await Clients.Group(groupName).SendAsync("ReceiveMessage", user, message);
    }
    

    🔟 Tổng kết

    • SignalR dùng để giao tiếp realtime giữa server và client.
    • Hub là trung tâm nhận và gửi message realtime.
    • Dùng AddSignalR()MapHub<THub>() để đăng ký trong ASP.NET Core.
    • Phía Blazor dùng HubConnectionBuilder để kết nối đến Hub.
    • Dùng InvokeAsync() để client gọi method trên server.
    • Dùng On<...>() để client nhận message từ server.
    • SignalR rất phù hợp cho chat, notification, dashboard realtime và collaborative app.
    An error has occurred. This application may no longer respond until reloaded. Reload 🗙
    Web hosting by Somee.com