Real-Time Web Application in ASP.Net Core SignalR

Hello everyone, welcome back to .Net Core Central. Today I am going to continue with streaming using ASP.Net Core SignalR, which I started by integrating with Kafka, then Reactive Extensions, and today I will be working with SignalR.

If you have not gone through my previous posts ASP.Net Core Streaming Application Using Kafka – Part 1, ASP.Net Core Streaming Application Using Kafka – Part 2 and Reactive Extensions in .Net Core, I would strongly suggest you do. Though it is not necessary to understand the concepts which I am going to cover here.

ASP.Net Core SignalR

What is ASP.Net Core SignalR

Simply put SignalR allows pushing messages from HTTP server into the browser (or any other clients like a WinForm or console applications). So you can push message to browser-based on events on the server, as and when they occur. Which is very powerful when we write SPA (Single Page Application). SignalR uses WebSocket if the web browser supports it, otherwise, it uses other compatible techniques.

Installing SignalR Nuget Package

I am going to add the SignalR NuGet packages to the TimeManagement.Service project. Since the TimeManagement.Service project is already used for Web API. Hence it makes this project the logical host for SIgnalR.

First of all, I will open the TimeManagement solution in Visual Studio 2017. After the solution is opened, I am going to right-click on the TimeManagement.Service project and select “Manage Nuget Packages” option. This will open up the Nuget Package Manager window.

In the Browse tab, I will search for Microsoft.AspNetCore.SignalR. This will show results for all SignalR related NuGet packages.

From the Nuget Package Manager, I will be installing Microsoft.AspNetCore.SignalR package. And I will install the version 1.0.0-alpha2-final.

ASP.Net Core SignalR Nuget Packages

Creating SignalR Hub

Hub is a base class provided by SignalR which is extended to get all the functionality we need. For the implementation, all we have to do is to create a class and extend it from Hub. A Hub is the high-level pipeline that allows the client and server to call methods on each other directly.

public class BookingHub : Hub { }

Next, I will create a new class BookingMessageRelay, which will be used to send a message to the client using BookingHub.

public class BookingMessageRelay 
{ 
    public BookingMessageRelay(IHubContext hubContext) 
    { 
        Task.Factory.StartNew(() => 
        { 
            while (true) 
            { 
                hubContext.Clients.All.InvokeAsync("booking", DateTime.Now.Ticks); Thread.Sleep(1000); 
            } 
        }); 
    } 
}

Wiring SignalR in Startup

The next thing I will do is wire the SignalR infrastructure in the Startup class. First of all, I will update the ConfigureServices method to add the CORS support using AddCors method. And after that, I will add support for SignalR using AddSignalR method. The reason we have to add support for CORS is that the SignalR hub call from the client uses the OPTIONS method. Hence in the CORS option, I will add AllowAllMethods option.

public void ConfigureServices(IServiceCollection services) 
{ 
    services.AddMvc(); 
    
    var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json"); 
    var config = builder.Build(); 
    
    services.AddTransient<IEmployeeProvider>(p => new EmployeeProvider(config["ConnectionString:TimeManagement"])); 
    services.AddTransient<IEmployeeProcessor>(p => new EmployeeProcessor(config["ConnectionString:TimeManagement"])); 
    
    services.AddCors(options => 
    { 
        options.AddPolicy("CorsPolicy", b => { 
            b.AllowAnyMethod(); 
        }); 
    }); 
    
    services.AddSignalR(); 
}

Furthermore, I will update the Configure method inside Startup class to use CORS and SignalR. For using CORS, I will call the UseCors method passing the same name (“CorsPolicy”) I provided when adding the CORS in the ConfigureServices method. And for using SignalR, I will call UseSignalR method, and configure the route for the BookingHub. Finally, I will call GetService to get an instance of BookingRelay. And I need that so that the singleton instance of BookingRelay is created and starts sending messages to all connected clients.

public void Configure(IApplicationBuilder app, IHostingEnvironment env) 
{ 
    if (env.IsDevelopment()) 
    { 
        app.UseDeveloperExceptionPage(); 
    } 
    app.UseCors("CorsPolicy"); 
    app.UseSignalR(routes => { 
        routes.MapHub<BookingHub>("bookingHub"); 
    }); 

    app.ApplicationServices.GetService<BookingMessageRelay>(); 
}

Configuring HTML client

After the server configuration completed I would need a client to test that the implementation is working as expected. Hence I will create an index.html file inside the wwwroot folder inside of the ASP.Net project TimeManagement.Service. And the next thing I will need is the SignalR javascript files.

Getting SignalR JavaScript files

I will download SignalR JavaScript from NPM (Node Package Manager). And for that first, I will install Node from the node download link. After the installation is completed I will navigate to the installed path in the command prompt. And use the command “npm install @aspnet/signalr-client” to download SignalR JavaScript files needed for my client code.

After NPM package download is completed I will copy the downloaded files and paste them inside of wwwroot folder.

ASP.Net Core SignalR JavaScript files

Writing the client code

Now that I have downloaded the JavaScript files necessary for SignalR to work on the browser, I will add the code in the index.html file.

First I will add the link to the SignalR Client JavaScript file to the section of the HTML file. And I will use the signalr-client-1.0.0-alpha2-final.js file.

<script src="signalr-client-1.0.0-alpha2-final.js"></script>

And after that, I will add the code to connect and log the message from the server on the console.

In the code, first I will create a HubConnection. And then I will start the HubConnection using the start method.

Finally, I will create a callback handler for the booking method triggered by the server. And on callback, I will log the incoming message into the console.

<script>
    var bookingHub = new signalR.HttpConnection(`/bookingHub`);
    var bookingConnection = new signalR.HubConnection(bookingHub);

    bookingConnection.start().catch(err => {
        console.log('connection error' + err);
    });
    
    bookingConnection.on('booking', (message) => {
        console.log('received message - ' + message);
    });
</script>

Update Startup to allow static files

To allow static files into the browser, I will update the Configure method and call UseDefaultFiles and UseStaticFiles methods on the IApplicationBuilder instance. UseDefaultFiles method will allow the index.html file and UseStaticFiles will allow the SignalR JavaScript file.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
            
    app.UseCors("CorsPolicy");
            
    app.UseSignalR(routes => {
        routes.MapHub<BookingHub>("bookingHub");
    });
    
    app.UseDefaultFiles();
    app.UseStaticFiles();
    
    app.ApplicationServices.GetService<BookingMessageRelay>();
}

Finally, I will run the application and check the output in the browser log.

output in browser

Integrate Kafka and Reactive Extensions with SignalR

Finally, I will integrate the Reactive Extension and Kafka streaming that I built. For that, I will change the ConfigureServices method of the Startup class and add BookingConsumer and BookingStream to the ASP.Net Core DI.

services.AddSingleton<IBookingStream, BookingStream>();
services.AddSingleton<Action<string>>((m) => Console.WriteLine(m));
services.AddSingleton<IBookingConsumer, BookingConsumer();

Furthermore, I will update the Configure method of the Startup class to start the BookingConsumer in a separate thread to listen to Kafka stream.

Task.Factory.StartNew(() => app.ApplicationServices.GetService<IBookingConsumer>().Listen());

Now I will update the implementation of BookingMessageRelay constructor, ton accept IBookingStream. And register to the Reactive Extension stream calling the Subscribe method. Finally, on the callback, write the message into SignalR hub.

public class BookingMessageRelay 
{
    public BookingMessageRelay(IHubContext<BookingHub> hubContext, IBookingStream bookingStream)
    {
        bookingStream.Subscribe("Booking_Relay", (m) => hubContext.Clients.All.InvokeAsync("booking", m.Message));
    }
}

Test integrated end to end solution

I will publish a message on the Kafka producer and see it showing in the browser console.
ASP.Net Core SignalR with Kafka and Reactive Extensions integration

Conclusion

Implementing real-time web applications using ASP.Net Core SignalR is extremely simple and easy. The way I have created the HTML application, usually, I will not do that. I will usually use a SPA framework like Angular or React. But for simplicity, I have kept it this way. In my next post, I will implement an Angular SPA application and integrate SignalR into that.

Furthermore, I have created a YouTube video. And in this video, I have recorded the steps I followed for creating the .Net Core SignalR server and client.

YouTube video: https://youtu.be/EFILQUIpKGU

References:

SignalR: https://www.asp.net/signalr
Node: https://nodejs.org/en/