• Home
  • .NET + SignalR – Simple Chat App

.NET + SignalR – Simple Chat App

Today I will show you how to create very simple chat app with .NET + SignalR. This tutorial covers the basics of building a real-time app using SignalR. We will cover how to:

  • Create a web project.
  • Add the SignalR client library.
  • Create a SignalR hub.
  • Configure the project to use SignalR.
  • Add code that sends messages from any client to all connected clients.

For building this demo app I will use

*Note: because I work on Mac this example is written especially for Mac users

Demo code is on my Github here.

First open your VisualStudio and then:

  • From the menu, select File > New Solution.
  • Select .NET Core > App > ASP.NET Core Web App (Don’t select ASP.NET Core Web App (MVC)).
  • Select Next.
  • Name the project SignalRChat (or any other Name that you like ), and then select Create.

Next we need to add SignalR Client library to our project. SignalR Server library is included in the Microsoft.AspNetCore.App metapackage. We will be using LibMan to get the client library from unpkg (CDN).

In the Terminal, run the following command to install LibMan.

dotnet tool install -g Microsoft.Web.LibraryManager.Cli

After successful installation of LibMan you will see similar message in your terminal:

Kristijans-iMac:visualstudionew kristijanklepac$ dotnet tool install -g Microsoft.Web.LibraryManager.Cli
You can invoke the tool using the following command: libman
Tool 'microsoft.web.librarymanager.cli' (version '1.0.163') was successfully installed.

Next we need to go to our folder where is our .csproj

cd SignalRChat

Next, run the following command to get the SignalR client library by using LibMan.

libman install @aspnet/signalr -p unpkg -d wwwroot/lib/signalr --files dist/browser/signalr.js --files dist/browser/signalr.min.js

If everything goes well you will get some message like this in your terminal:

Kristijans-iMac:SignalRChat kristijanklepac$ libman install @aspnet/signalr -p unpkg -d wwwroot/lib/signalr --files dist/browser/signalr.js --files dist/browser/signalr.min.js
wwwroot/lib/signalr/dist/browser/signalr.js written to disk
wwwroot/lib/signalr/dist/browser/signalr.min.js written to disk
Installed library "@aspnet/signalr@1.1.0" to "wwwroot/lib/signalr"
Kristijans-iMac:SignalRChat kristijanklepac$

Go inside your wwwroot folder and you will see newly created signalr folder with necessary js files in it.

Next we need to create Hubs folder in our project. Inside we will create ChatHub.cs file. A ChatHub is a class that serves as a high-level pipeline that handles client-server communication.

using System;
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;

namespace SignalRChat.Hubs
{
    public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }
}

The ChatHub class inherits from the SignalR Hub class. The Hub class manages connections, groups, and messaging.

The SendMessage method can be called by any connected client. It sends the received message to all clients. SignalR code is asynchronous to provide maximum scalability.

The SignalR Server must be configured to pass SignalR requests to SignalR. So we need to modify our Startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SignalRChat.Hubs;

namespace SignalRChat
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });


            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            services.AddSignalR();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseSignalR(routes =>
            {
                routes.MapHub<ChatHub>("/chatHub");
            });

            app.UseMvc();
        }
    }
}

In our simple example we will replace index.cshtml file in Pages folder.

@page
<div class="container">
    <div class="row">&nbsp;</div>
    <div class="row">
        <div class="col-6">&nbsp;</div>
        <div class="col-6">
            User..........<input type="text" id="userInput" />
            <br />
            Message...<input type="text" id="messageInput" />
            <input type="button" id="sendButton" value="Send Message" />
        </div>
    </div>
    <div class="row">
        <div class="col-12">
            <hr />
        </div>
    </div>
    <div class="row">
        <div class="col-6">&nbsp;</div>
        <div class="col-6">
            <ul id="messagesList"></ul>
        </div>
    </div>
</div>
<script src="~/lib/signalr/dist/browser/signalr.js"></script>
<script src="~/js/chat.js"></script>

Here we create two input fields for user (Name) and message that he wants to send to other connected clients. Also we have <ul id=”messagesList”></ul> for displaying messages that are received from the SignalR hub. Also we have references to scripts SignalR and Chat.js ( chat.js needs to be added by us and example code is below )

"use strict";

var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();

connection.on("ReceiveMessage", function (user, message) {
    var msg = message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
    var encodedMsg = user + " says " + msg;
    var li = document.createElement("li");
    li.textContent = encodedMsg;
    document.getElementById("messagesList").appendChild(li);
});

connection.start().catch(function (err) {
    return console.error(err.toString());
});

document.getElementById("sendButton").addEventListener("click", function (event) {
    var user = document.getElementById("userInput").value;
    var message = document.getElementById("messageInput").value;
    connection.invoke("SendMessage", user, message).catch(function (err) {
        return console.error(err.toString());
    });
    event.preventDefault();
});

This .js code will:

  • Create and start a connection.
  • Add to the submit button a handler that sends messages to the hub.
  • Add to the connection object a handler that receives messages from the hub and add them to the list.

Now everything is set and we can test our app.

  • From the menu, select Run > Start Without Debugging.

My app is running on https://localhost:5001/ so I open two tabs in the browser…

  • Copy the URL from the address bar, open another browser instance or tab, and paste the URL in the address bar.
  • Choose either browser, enter a name and message, and select the Send Message button.

When you send a message from one browser instance message is instantly visible in other browser and vice versa.

Demo code is on my Github here.

Thanks for reading…

Tags: , ,

Copyright by Kristijan Klepač 2018