Now, let’s start exploring the concepts of SignalR and try to explain, simply, how to build a chat interface in a single page application.
SignalR Definitions
Hub

SignalR uses hubs to communicate between clients and servers.
SignalR handles the dispatching across machine boundaries automatically, allowing clients to call methods on the server and vice versa.
Protocols

The binary messages are unreadable when looking at network traces and logs unless the bytes are passed through a MessagePack parser. SignalR has built-in support for the MessagePack format and provides APIs for the client and server to use.
Transports

WebSockets performs best, but can only be used if both the client and the server support it. If that is not the case, SSE or Long Polling should be used instead.
Backend
Let’s start by creating a blank web API .Net Core 3.1 Solution in visual studio.


Now let’s create a ChatHub class and the IChatHub interface that will represent the proxy between the client and server.
public class ChatHub : Hub
{
public async Task BroadcastAsync(ChatMessage message)
{
await Clients.All.MessageReceivedFromHub(message);
}
public override async Task OnConnectedAsync()
{
await Clients.All.NewUserConnected("a new user connectd");
}
}
public interface IChatHub
{
Task MessageReceivedFromHub(ChatMessage message);
Task NewUserConnected(string message);
}
As you can see the ChatMessage class will be a simple Data Transfer Object that will be used to transfer the messages.
public class ChatMessage
{
public string Text { get; set; }
public string ConnectionId { get; set; }
public DateTime DateTime { get; set; }
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSignalR().AddMessagePackProtocol();
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder
.WithOrigins(
"http://localhost")
.AllowCredentials()
.AllowAnyHeader()
.SetIsOriginAllowed(_ => true)
.AllowAnyMethod();
});
});
}
Now, let’s map the ChatHub class we previously wrote in the Configure method.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub("/signalr");
});
And to finish the server part, let’s write the ChatController.
[Route("api/[controller]")]
[ApiController]
public class ChatController : ControllerBase
{
private readonly IHubContext hubContext;
public ChatController(IHubContext hubContext)
{
this.hubContext = hubContext;
}
[HttpPost]
public async Task SendMessage(ChatMessage message)
{
//additional business logic
await this.hubContext.Clients.All.SendAsync("messageReceivedFromApi", message);
//additional business logic
}
}
If you want extra config options, take a look at this doc.
Frontend
We will use a simple Angular app without much styling effort since we are only interested in demonstrating how SignalR works. You can see the UI in the following image.

Write the following CLI commands to create the UI for the chat app.
- *_ng new chat-ui_* in any folder to scaffold and create a new angular app
- *_npm i @microsoft/signalr@latest --save_* to install the required SignalR libraries.
- *_ng g s signalr_* to scaffold the SignalR service
- *_npm i @microsoft/signalr-protocol-msgpack --save_* to use the MessagePack protocol in the frontend
First, let’s write the SignalR service which will be used from the UI Component.
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { HubConnection, HubConnectionBuilder, LogLevel } from '@microsoft/signalr'
import { from } from 'rxjs';
import { tap } from 'rxjs/operators';
import { chatMesage } from './chatMesage';
import { MessagePackHubProtocol } from '@microsoft/signalr-protocol-msgpack'
@Injectable({
providedIn: 'root'
})
export class SignalrService {
private hubConnection: HubConnection
public messages: chatMesage[] = [];
private connectionUrl = 'https://localhost:44319/signalr';
private apiUrl = 'https://localhost:44319/api/chat';
constructor(private http: HttpClient) { }
public connect = () => {
this.startConnection();
this.addListeners();
}
public sendMessageToApi(message: string) {
return this.http.post(this.apiUrl, this.buildChatMessage(message))
.pipe(tap(_ => console.log("message sucessfully sent to api controller")));
}
public sendMessageToHub(message: string) {
var promise = this.hubConnection.invoke("BroadcastAsync", this.buildChatMessage(message))
.then(() => { console.log('message sent successfully to hub'); })
.catch((err) => console.log('error while sending a message to hub: ' + err));
return from(promise);
}
private getConnection(): HubConnection {
return new HubConnectionBuilder()
.withUrl(this.connectionUrl)
.withHubProtocol(new MessagePackHubProtocol())
// .configureLogging(LogLevel.Trace)
.build();
}
private buildChatMessage(message: string): chatMesage {
return {
connectionId: this.hubConnection.connectionId,
text: message,
dateTime: new Date()
};
}
private startConnection() {
this.hubConnection = this.getConnection();
this.hubConnection.start()
.then(() => console.log('connection started'))
.catch((err) => console.log('error while establishing signalr connection: ' + err))
}
private addListeners() {
this.hubConnection.on("messageReceivedFromApi", (data: chatMesage) => {
console.log("message received from API Controller")
this.messages.push(data);
})
this.hubConnection.on("messageReceivedFromHub", (data: chatMesage) => {
console.log("message received from Hub")
this.messages.push(data);
})
this.hubConnection.on("newUserConnected", _ => {
console.log("new user connected")
})
}
}
In this case, and since we are in an SPA, we have 2 options to send messages from the UI to the HUB:
A – We can send messages directly to the HUB.

B – We can send Http requests to the API Controller and let the controller call the hub.

This is the Component in charge:
import { Component, OnInit } from '@angular/core';
import { SignalrService } from './signalr.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {
title = 'chat-ui';
text: string = "";
constructor(public signalRService: SignalrService) {
}
ngOnInit(): void {
this.signalRService.connect();
}
sendMessage(): void {
// this.signalRService.sendMessageToApi(this.text).subscribe({
// next: _ => this.text = '',
// error: (err) => console.error(err)
// });
this.signalRService.sendMessageToHub(this.text).subscribe({
next: _ => this.text = '',
error: (err) => console.error(err)
});
}
}
We can see here that the connect() method is called on OnInit() to initialize the connection.
The SendMessage() method is called whenever the user clicks the Send Button. For demo purposes, you have both options.


About the Author
Javier Acrich a multi-talented .NET Developer capable of managing both front-end and back-end development. Successfully creates and modifies web features to improve functioning for clients. Skilled in architecture and design of web applications and solutions.