
Áp dụng SignalR trong xây dựng Chat Application thời gian thực
June 29, 2025Trong các bài viết trước, chúng ta đã cùng nhau khám phá nhiều kỹ thuật khác nhau để đạt được tính năng giao tiếp thời gian thực giữa client và server trong .NET.
Mặc dù việc hiểu rõ các kỹ thuật nền tảng là rất quan trọng, nhưng khi xây dựng hệ thống ở cấp độ doanh nghiệp, lựa chọn một giải pháp ổn định, được hỗ trợ lâu dài như SignalR của Microsoft là hướng đi hợp lý cả về kỹ thuật lẫn chi phí bảo trì.
Bài viết này sẽ giúp bạn hiểu cách hoạt động của SignalR và mở rộng từ ứng dụng chat sử dụng WebSocket để triển khai thêm các tính năng nâng cao dễ dàng hơn rất nhiều.
Xây dựng SignalR Server
Cài đặt gói NuGet:
dotnet add package Microsoft.AspNetCore.SignalR
Code phía server như sau:
using Microsoft.AspNetCore.SignalR;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(o =>
{
o.AddPolicy("AllowAnyOrigin", p => p
.WithOrigins("null") // Để chạy HTML trực tiếp trên trình duyệt
.AllowAnyHeader()
.AllowCredentials());
});
builder.Services.AddSignalR();
var app = builder.Build();
app.UseCors("AllowAnyOrigin");
app.MapHub<ChatHub>("/chatHub");
app.Run();
public class ChatHub : Hub
{
public async Task SendMessage(string message)
{
await Clients.All.SendAsync("ReceiveMessage", message);
}
}
Tạo SignalR Client
Dưới đây là một ví dụ đơn giản sử dụng SignalR client trong HTML:
<!DOCTYPE html>
<html>
<head>
<title>SignalR Client</title>
<script src="<https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.0/signalr.min.js>"></script>
</head>
<body>
<h1>SignalR Client</h1>
<div id="messages"></div>
<input id="chatbox">
<script>
const connection = new signalR.HubConnectionBuilder()
.withAutomaticReconnect()
.withUrl("<http://localhost:5008/chatHub>")
.build();
connection.start().then(function () {
document.getElementById("chatbox").addEventListener("keyup", function (event) {
if (event.key === "Enter") {
connection.invoke("SendMessage", event.target.value);
event.target.value = "";
}
});
}).catch(function (err) {
return console.error(err.toString());
});
connection.on("ReceiveMessage", function (message) {
const messages = document.getElementById("messages");
messages.innerHTML += `<p>${message}</p>`;
});
</script>
</body>
</html>
Kết quả: bạn có một client tự động kết nối lại khi mất mạng, có thể gửi và nhận tin nhắn từ server.
Hỗ trợ User và Room (Phòng Chat)
Để nâng cao ứng dụng, bạn có thể mở rộng để hỗ trợ người dùng và chia theo phòng chat.
public record User(string Name, string Room);
public record Message(string User, string Text);
public class ChatHub : Hub
{
private static ConcurrentDictionary<string, User> _users = new();
public override async Task OnDisconnectedAsync(Exception? exception)
{
if (_users.TryGetValue(Context.ConnectionId, out var user))
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, user.Room);
await Clients.Group(user.Room).SendAsync("UserLeft", user.Name);
}
}
public async Task JoinRoom(string userName, string roomName)
{
_users.TryAdd(Context.ConnectionId, new User(userName, roomName));
await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
await Clients.Group(roomName).SendAsync("UserJoined", userName);
}
public async Task SendMessageToRoom(string roomName, string content)
{
var message = new Message(_users[Context.ConnectionId].Name, content);
await Clients.Group(roomName).SendAsync("ReceiveMessage", message);
}
}
Client hỗ trợ Room và User
document.getElementById("joinRoom").addEventListener("click", function () {
const roomName = document.getElementById("roomName").value;
const userName = document.getElementById("userName").value;
connection.invoke("JoinRoom", userName, roomName);
});
document.getElementById("messageInput").addEventListener("keyup", function (event) {
if (event.key === "Enter") {
const message = document.getElementById("messageInput").value;
const roomName = document.getElementById("roomName").value;
if (message && roomName) {
connection.invoke("SendMessageToRoom", roomName, message);
}
}
});
connection.on("ReceiveMessage", function (msg) {
const messages = document.getElementById("messages");
messages.innerHTML += `<p>${msg.user}: ${msg.text}</p>`;
});
connection.on("UserJoined", function (msg) {
const messages = document.getElementById("messages");
messages.innerHTML += `<p>${msg} đã tham gia phòng.</p>`;
});
connection.on("UserLeft", function (msg) {
const messages = document.getElementById("messages");
messages.innerHTML += `<p>${msg} đã rời khỏi phòng.</p>`;
});

Chúng ta đã cùng nhau xây dựng một ứng dụng chat đơn giản sử dụng ASP.NET Core SignalR, từ việc kết nối cơ bản đến hỗ trợ người dùng và chia phòng.
Mặc dù SignalR còn nhiều chủ đề nâng cao như scaling với Redis, authentication với ASP.NET Core Identity, hoặc tích hợp với Azure SignalR Service, nhưng bài viết này là nền tảng vững chắc để bạn bắt đầu.
Hãy tiếp tục khám phá SignalR để xây dựng các ứng dụng thời gian thực mạnh mẽ, hiệu suất cao trong .NET.
Bạn muốn học thêm về các chủ đề như WebSocket, Server-Sent Events, hoặc scaling SignalR? Đừng ngại để lại bình luận hoặc đăng ký nhận bài viết tiếp theo!