SignalR uses the concept of Hub on the server side
—an in-memory object that all clients connect up to for real time communications. The Hub allows SignalR to send and receive messages across machine boundaries, thus allowing clients to call methods on the server and vice versa.
Useful Links:
-
SignalR Documentation: Overview of ASP.NET Core SignalR | Microsoft Learn
-
SignalR package: NuGet Gallery | Microsoft.AspNetCore.SignalR.Client 7.0.4
- Create a Blazor Server App
- Add a SignalR Hub
- Add NuGet Package:
Microsoft.AspNetCore.SignalR.Client
- Add new folder to the solution,
Hub
. Create classChatHub.cs
for Sending Messages to the client.
ChatApp/ChatApp/Hubs/ChatHub.cs
using Microsoft.AspNetCore.SignalR;
namespace ChatApp.Hubs
{
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
}
using ChatApp.Data;
using ChatApp.Hubs;
using Microsoft.AspNetCore.ResponseCompression;
internal class Program
{
private static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
//compressing the messages
builder.Services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/octet-stream" });
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapHub<ChatHub>("/chathub");
app.MapFallbackToPage("/_Host");
app.Run();
}
}
namespace ChatApp.Models
{
public class UserMessage
{
public string UserName { get; set; }
public string Message { get; set; }
public bool CurrentUser { get; set; }
public DateTime DataSent { get; set; }
}
}
@page "/"
@using Microsoft.AspNetCore.SignalR.Client
@using Models
@inject NavigationManager NavigationManager
@implements IAsyncDisposable
Messaging Area
<div class="container overflow-auto shadow-sm p-3 mb-5 bg-white rounded" style="height: 500px;">
@if (!userMessages.Any())
{
<p>No messages yet, start chatting!</p>
}
@foreach (var userMessage in userMessages)
{
<div class="row mb-3 d-flex @(userMessage.CurrentUser ? "justify-content-end" : "")">
<div class="card shadow @(userMessage.CurrentUser ? "color-green mr-5" : "ml-5")" style="width: 18rem;">
<div class="card-header">
@(userMessage.CurrentUser ? "You" : userMessage.Username)
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item @(userMessage.CurrentUser ? "color-green" : "")">@userMessage.Message</li>
</ul>
<div class="card-footer">
<span class="small">@userMessage.DateSent.ToString("HH:mm | MMM dd")</span>
</div>
</div>
</div>
}
</div>
Texting Area
<div class="container">
<div class="row">
<div class="col-3">
<input @bind="usernameInput" type="text" class="form-control" placeholder="Your name" readonly="@isUserReadonly"/>
</div>
<div class="col-6">
<textarea @bind="messageInput" class="form-control" placeholder="Start typing..."></textarea>
</div>
<div class="col-3">
<button type="button" @onclick="Send" disabled="@(!IsConnected)" class="btn btn-primary">Send</button>
</div>
</div>
</div>
Functional Property
@code{
private HubConnection hubConnection;
private List<UserMessage> userMessages = new();
private string usernameInput;
private string messageInput;
private bool isUserReadonly = false;
public bool IsConnected => hubConnection.State == HubConnectionState.Connected;
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/chathub"))
.Build();
hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
{
userMessages.Add(new UserMessage { Username = user, Message = message, CurrentUser = user == usernameInput, DateSent = DateTime.Now });
StateHasChanged();
});
await hubConnection.StartAsync();
}
private async Task Send()
{
if (!string.IsNullOrEmpty(usernameInput) && !string.IsNullOrEmpty(messageInput))
{
await hubConnection.SendAsync("SendMessage", usernameInput, messageInput);
isUserReadonly = true;
messageInput = string.Empty;
}
}
public async ValueTask DisposeAsync()
{
if (hubConnection is not null)
{
await hubConnection.DisposeAsync();
}
}
}
.color-green {
background-color: #5CDB94;
}