in Programming

Create a push notification system with SignalR and Toastr

Do you have events in your application that you would like to trigger a notification to other users? How about if a user places an order on a website and you want the website administrator to see a notification, or a sale on Apple products is initiated and you want to notify customers?

Note: If you wish to download the code to follow along instead of going step by step, you may download it on Github here.

SignalR is a library for .NET that enables real-time communication. It does this via web-sockets, which is typically available in modern browsers, but also degrades gradually to other methods that are supported by the older browsers. You just need to worry about the SignalR framework not the underlying architecture. Toastr is javascript framework for non-blocking notifications. With just a few lines of code you can get a nice popup system in place.

Let’s walk through the steps, it’s not too difficult.

1. Create a new MVC project in your favorite language, I’ll be using C#. I disable user authentication and name it ‘SignalrToastr’ but you can name it whatever you would like.

2. Open ‘package manager console’ for your application, and install the SignalR package.

PM> Install-Package Microsoft.AspNet.SignalR

3. Create a class file named ‘Startup.cs’ and place it in the root of your application. Give the following contents to your new file. This file is an Owin startup file, which is outside the scope of this discussion, but is worth noting as Owin will become a larger part of ASP.NET vNext. It is a middleware framework that resides outside of IIS. Microsoft also uses Owin for the ‘Individual Accounts’ user manager, so if you left user authentication enabled in step #1, you can simply add the ‘MapSignalR’ line below to the Startup.cs class it created for you.

using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(SignalrToastr.Startup))]
namespace SignalrToastr
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // Any connection or hub wire up and configuration should go here
            app.MapSignalR();
        }
    }
}

4. Create a folder called ‘Hubs’ and add a file named ‘ContactHub.cs’. Enter the following contents into the file.

using Microsoft.AspNet.SignalR;

namespace SignalrToastr.Hubs
{
    public class ContactHub : Hub
    {
        public void ContactFormSubmitted(string name, string message)
        {
            // Calls the 'notifyUsers' method on the client side.
            //  Note:  You can change 'All' to target only certain clients.
            //  Note:  Code after 'All' is dynamic, and you can specify whatever name you wish.
            Clients.All.notifyUsers(name);
        }
    }
}

SignalR uses ‘Hub’ objects to communicate between the client and the server. We will go over more on what is actually happening here in a later step. For now, you should note that ‘ContactFormSubmitted’ is a method being called on the server, from the client. The client passes this server method two parameters, name and message. This server side method then notifies ‘All’ clients to execute a JS method named ‘notifyUsers’ which takes one parameter, name. Outside the scope of this tutorial is how you can narrow down which clients you want to send the message to.

5. Modify the ASP.NET templated view that comes installed ‘~/Views/Home/Contact.cshtml’ and replace it’s contents entirely with the following:


<form class="form-horizontal">
    <fieldset>

        <!-- Form Name -->
        <legend>Contact</legend>

        <!-- Text input-->
        <div class="control-group">
            <label class="control-label" for="txtName">Name</label>
            <div class="controls">
                <input id="txtName" name="txtName" type="text" class="input-medium">

            </div>
        </div>

        <!-- Textarea -->
        <div class="control-group">
            <label class="control-label" for="txtComments">Comments</label>
            <div class="controls">
                <textarea id="txtComments" name="txtComments"></textarea>
            </div>
        </div>

        <!-- Button -->
        <div class="control-group">
            <label class="control-label" for="btnSubmit"></label>
            <div class="controls">
                <button id="btnSubmit" name="btnSubmit" class="btn btn-primary">Submit</button>
            </div>
        </div>

    </fieldset>
</form>


@section scripts
{
    <script src="~/Scripts/jquery.signalR-2.0.3.js"></script>
    <script src="~/signalr/hubs"></script>    
    <script type="text/javascript">
        $(function() {
            var contact = $.connection.contactHub;

            // Callbacks to the client should be defined here.
            contact.client.notifyUsers = function (name) {
                
            };

            $.connection.hub.start().done(function() {
                // Calls to server should be included within the hub.start() method.
                $('#btnSubmit').click(function() {
                    // Call the ContactFormSubmitted method on the hub.
                    contact.server.contactFormSubmitted($('#txtName').val(), $('#txtComments').val());
                    // Clear text boxes and reset focus for next comment.
                    $('#txtName').val('');
                    $('#txtComments').val('');
                    return false;  // prevent 'form' from submitting.
                });
            });
        });
    </script>
}

This creates a simple two field bootstrap form, and also adds our required JS includes to make SignalR work (jquery.signalR-*, and ~/signalr/hubs). It also creates a placeholder for our client callback notifyUsers, and also adds an event handler for our contactFormSubmitted method to push to the server. If you build your solution at this point and open your browser to your projects location for /signalr/hubs/ (i.e. http://localhost:62279/signalr/hubs/) you’ll see some generated JS markup that SignalR created based on your hub. Note how it lower cases your hub name, and methods. This is what the SignalR framework is creating for you automatically based on your Hub classes.

        proxies.contactHub = this.createHubProxy('contactHub'); 
        proxies.contactHub.client = { };
        proxies.contactHub.server = {
            contactFormSubmitted: function (name, message) {
                return proxies.contactHub.invoke.apply(proxies.contactHub, $.merge(["ContactFormSubmitted"], $.makeArray(arguments)));
             }
        };

6. Repeat step #2, but install the Toastr framework.

PM> Install-Package toastr

7. Add the toastr.css file to either the top of Contact.cshtml or to your Layout.cshtml file:

<link href="~/Content/toastr.css" rel="stylesheet" />

8. Modify your scripts section of Contact.cshtml to look as follows. Note how we have added a reference to taostr.js and also placed the toastr.info call inside our notifyUsers callback method.

 <script src="~/Scripts/toastr.js"></script>
    <script src="~/Scripts/jquery.signalR-2.0.3.js"></script>
    <script src="~/signalr/hubs"></script>    

    <script type="text/javascript">
        $(function() {
            var contact = $.connection.contactHub;

            // Callbacks to the client should be defined here.
            contact.client.notifyUsers = function (name) {
                toastr.info('New contact form submitted by ' + name);
            };

            $.connection.hub.start().done(function() {
                // Calls to server should be included within the hub.start() method.
                $('#btnSubmit').click(function() {
                    // Call the ContactFormSubmitted method on the hub.
                    contact.server.contactFormSubmitted($('#txtName').val(), $('#txtComments').val());
                    // Clear text boxes and reset focus for next comment.
                    $('#txtName').val('');
                    $('#txtComments').val('');
                    return false;  // prevent 'form' from submitting.
                });
            });
        });
    </script>

9. You are done at this point, so let’s try it out. Open two different browsers side-by-side, I’ll be using Chrome and Firefox. This is done to show you how the server is making a callback to ‘All’ clients as shown in step #4. You can narrow this down in the future if you want, to only administrative users for example. If all goes well, you should be able to fill the form out in one browser, and see the popup in both.

toast-notification