- Home
- .NET tutorials
- Add a SignalR hub to ASP.NET Core & connect using JavaScript
Add a SignalR hub to ASP.NET Core & connect using JavaScript
Published: Monday 22 July 2024
SignalR is a powerful software library that allows you to send and receive messages from a client and server in real-time.
We are going to add SignalR to an ASP.NET Core app and use the client to connect to the SignalR hub. We'll also look at how to send messages and how we can receive them.
Create a SignalR hub
The first steps involve creating a SignalR hub.
C# coding challenges
In the ASP.NET Core app, we create a new class called ChatHub
. To make this a SignalR hub, we need to inherit the Hub
class which is imported from the Microsoft.AspNetCore.SignalR
namespace.
The Hub
class has a virtual method called OnConnectedAsync
. We are going to override this and send a messages to all connected clients when a new client has connected.
The method name will be called ReceiveMessage
and we'll pass in a parameter with the message. The client will listen out for this method to be invoked.
In-addition, we are going to set up a SendMessage
method. This will be invoked from the client and it will send a message back to that particular client saying that the message has been received.
// ChatHub.cs
using Microsoft.AspNetCore.SignalR;
public class ChatHub : Hub
{
public override async Task OnConnectedAsync()
{
await Clients.All.SendAsync("ReceiveMessage", $"Received the message: Another connection has been added.");
}
public async Task SendMessage(string message)
{
await Clients.Client(Context.ConnectionId).SendAsync("ReceiveMessage", $"Received the message: {message}");
}
}
Sending messages to clients
If we want to send a message to all clients, we can call Clients.All
and then the SendAsync
method to invoke a method with parameters.
However, we may only want to send a message to a specific client. Each client connected to a SignalR hub has a connection ID assigned to them. We can get the connection ID from the hub context and pass it in as a parameter for Clients.Client
.
So to send a message to the connected client, we would call Clients.Client(Context.ConnectionId)
, followed by the SendAsync
method and then the method and parameters that we wish to invoke.
Add SignalR to services and map the hub
SignalR needs to be added to the services in the ASP.NET Core app. As well as that, the SignalR hub needs to be mapped with an endpoint.
This is done in Program.cs
and we call the AddSignalR
extension method that is part of the IServiceCollection
instance.
To map the SignalR hub, we call the MapHub
extension method from the WebApplication
instance. We pass in the SignalR hub as the generic type. In this instance, that would be ChatHub
. Then it needs to be mapped to a pattern or endpoint. This will be specified as /chathub
and will be used to connect to the SignalR hub from the client.
// Program.cs
using RoundTheCode.SignalR.Hubs;
var builder = WebApplication.CreateBuilder(args);
...
builder.Services.AddSignalR(); // Adds SignalR to the IServiceCollection instance
var app = builder.Build();
...
app.MapHub<ChatHub>("/chathub"); // Maps the ChatHub with a endpoint of /chathub.
...
app.Run();
Front-end set up
We have an HTML page that will be responsible for sending and receiving messages back from the server.
The page has an input box that allows you to add your message. This has an ID of Message
. As well as that, there is a Send Message button to send the message to the server which has an ID of SendMessage
.
Finally, there is a <ul>
tag that will store all the messages and has an ID of Messages
.
The file also contains a reference to the SignalR JavaScript library as well as a custom SignalR JavaScript file (called signalr.js
) which we will use to code the SignalR functionality.
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-12">
<input id="Message" value="" />
<button class="btn btn-primary" id="SendMessage"
type="button">Send message</button>
</div>
</div>
<div class="row">
<div class="col-12">
<ul id="Messages">
</ul>
</div>
</div>
</div>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js">
</script>
<script src="/js/signalr.js"></script>
</body>
</html>
Connecting to SignalR hub
As stated, the index.html
page has a reference to the signalr.js
JavaScript file. We'll use this JavaScript file to connect to the SignalR hub.
We need to create a new signalR.HubConnectionBuilder
and specify a URL. This will be set as /chathub
so it's the same URL that we mapped the SignalR hub in the ASP.NET Core app.
We can also configure logging before we build the connection.
To start the connection, we can call the start
method that is in the connection
instance.
We may wish to try and re-established the SignalR connection if it's closed just say because we lose the connection. To do that, we can set the onclose
method in the connection instance, and restart it from there.
// signalr.js
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
var Start = async () => {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(Start, 5000);
}
};
// Start the connection.
Start();
connection.onclose(async () => {
await Start();
});
A message stating SignalR Connected should be logged to the console application. If it doesn't, the client has been unable to connect to SignalR meaning that something is not set up correctly.
Sending a message to the SignalR hub
With the connection established, we can now send a message to the SignalR hub.
To do that, we get the connection
instance and call the invoke
method. The invoke
method allows you to pass in the method name as a parameter, followed by any other parameters for that method.
In the ChatHub
class, we created the SendMessage
method:
// ChatHub.cs
public class ChatHub : Hub
{
...
public async Task SendMessage(string message)
{
await Clients.Client(Context.ConnectionId).SendAsync("ReceiveMessage", $"Received the message: {message}");
}
}
We are now going to invoke this method by calling connection.invoke
, adding SendMessage
as the method name and the message we inputted into the input box as the message.
This will be added to the method and to call it, we get the SendMessage
ID element in JavaScript and add a click
listener. We get the Message
ID element which is the input box and check it has a value. If it does, we send the message.
// signalr.js
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
var Start = async () => {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(Start, 5000);
}
};
// Start the connection.
Start();
connection.onclose(async () => {
await Start();
});
// Sends the message to SignalR
var SendMessage = async (message) => {
await connection.invoke("SendMessage", message);
}
// Sends the message when the SendMessage ID element has been clicked
document.getElementById('SendMessage').addEventListener('click', async () => {
var message = document.getElementById('Message');
if (message && message.value) {
await SendMessage(message.value);
message.value = '';
}
});
Receiving a message back from the server
We also want to be able to receive a message back from the server.
In the ChatHub
class, we called SendAsync("ReceiveMessage", message)
which invokes the ReceiveMessage
method to the clients specified. We need to set up the client so it can listen out for calls from the ReceiveMessage
method.
This is done by calling the on
method in the connection
instance, specifying the method name and any parameters we are expecting.
We'll do a look up for our messages element where we store each message as a bullet point. If it exists, we create a new li
element and add the message as the inner text. Then we append the li
element to the Messages
ID element which adds it as a bullet point.
// signalr.js
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
var Start = async () => {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(Start, 5000);
}
};
// Start the connection.
Start();
connection.onclose(async () => {
await Start();
});
var SendMessage = async (message) => {
await connection.invoke("SendMessage", message);
}
document.getElementById('SendMessage').addEventListener('click', async () => {
var message = document.getElementById('Message');
if (message && message.value) {
await SendMessage(message.value);
message.value = '';
}
});
connection.on("ReceiveMessage", (message) => {
var messages = document.getElementById('Messages');
if (messages) {
var bulletPoint = document.createElement('li');
bulletPoint.innerText = message;
messages.appendChild(bulletPoint);
}
});
Watch the video
Watch the video where we show you how to create a SignalR hub, add it to ASP.NET Core and connect to it from the client so we can send and receive messages.
As well as that, we'll give you a demo of SignalR connected successfully and show you how it can send and receive messages.