Category Archives: SignalR

Signal R and React

I haven’t touched Signal R in a while. I wanted to see how to work with Signal R in a React app.

Let’s start by creating a simple ASP.NET Core Web API server

  • Create an ASP.NET Core Web API application, I’m going to use minimal API
  • Add the NuGet package Microsoft.AspNetCore.SignalR.Client
  • Add the following to the Program.cs
    builder.Services.AddSignalR();
    builder.Services.AddCors();
    
  • We’ve added CORS support as we’re going to need tthis for testing locally, we’ll also need the following code
    app.UseCors(options =>
    {
      options.AllowAnyHeader()
        .AllowAnyMethod()
        .AllowCredentials()
        .SetIsOriginAllowed(origin => true);
    });
    

Before we can use Signal R we’ll need to add a hub, I’m going to add a file NotificationHub.cs with the following code

public class NotificationHub : Hub;

Now return to Program.cs and add the following

  • We need to map our hub into the application using
    app.MapHub<NotificationHub>("/notifications");
    
  • Finally let’s map an endpoint to allow us to send messages via Swagger to our clients. After the line above, add the following
    app.MapGet("/test", async (IHubContext<NotificationHub> hub, string message) =>
      await hub.Clients.All.SendAsync("NotifyMe",$"Message: {message}"));
    

Now we need a client, so create yourself a React application (I’m using TypeScript as usual with mine).

  • Add the package @microsoft/signalr, i.e. from yarn yarn add @microsoft/signalr
  • In the App.tsx we’re going to create the HubConnectionBuilder against out ASP.NET Core API server. We’ll then start the connection and finally watch for messages on the “NotifyMe” name as previously set up in the ASP.NET app, the code looks like this
    import { HubConnectionBuilder } from '@microsoft/signalr';
    
    function App() {
      const [message, setMessage] = useState("");
    
      useEffect(() => {
        const connection = new HubConnectionBuilder()
          .withUrl("http://localhost:5021/notifications")
          .build();
      
        connection.start();  
        connection.on("NotifyMe", data => {
          setMessage(data);
        });
      }, [])  
    
      return (
        <div className="App">
          {message}
        </div>
      );
    }
    

Make sure you start the ASP.NET server first, then start your React application. Now from the Swagger page we can send messages into the server and out to the React client’s connected to SignalR.

SignalR 2

Well it’s about time I revisited SignalR. I’m working on a little side project to act as a intranet dashboard for one of the applications I support.

The idea is to produce those commonly asked for pieces of information about the current state of the application, infrastructure etc. into a usable web page.

So first up I want a way to update the dashboard when changes are detected (or found when polling at specific periods) to the dashboard. As I have written something a little like this in the past with SignalR I thought it’d be good to see where the technology stood now.

So we’re going to create a bare minimum ASP.NET MVC application with SignalR periodically causing updates to the page – ofcourse there’s plenty of chat samples for this, so if you’re looking for something a little more in depth, please go and check those out.

Getting started

I’m using Visual Studio 2015 and will be creating an ASP.NET MVC5 web application for this (ofcourse you don’t need to got full ASP.NET MVC for this, but I want to integrate this into my MVC app).

  • Create a new project in VS2015, select Visual C#/Web/ASP.NET Application (available as a .NET 4.5 or above template) – my project is called SignalRTest
  • After you press OK, select MVC from the next screen and press OK
  • Add a new folder named Hubs (not required but keeps the code partitioned nicely)
  • Right mouse click on the Hubs folder and select Add | New Item
  • Select Visual C#/Web/SignalR
  • Select SignalR Hub Class (v2) and give it a meaningful name, mine is ServerStatusHub
  • If you have a Startup.cs file (this will be supplied if you have authorization enabled), then simple add the following line to the Configuration method
    app.MapSignalR();
    
  • If you chose no authentication then you’ll need to create a Startup.cs file in the root folder (alongside the Web.Config in your solution), it should look like this
    using Microsoft.Owin;
    using Owin;
    
    [assembly: OwinStartupAttribute(typeof(SignalRTest.Startup))]
    namespace SignalRTest
    {
        public partial class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                app.MapSignalR();
            }
        }
    }
    

Let’s pause and take stock, we now have a Hub derived object named ServerStatusHub which will be callable from JavaScript (in the application we’re writing) and will equally be able to call out to client JavaScript code as and when server updates come in.

We’re going to change the code in the hub to this

using System;
using System.Threading;
using Microsoft.AspNet.SignalR;

namespace SignalRTest.Hubs
{
    public class ServerStatusHub : Hub
    {
        private readonly Timer timer;

        public ServerStatusHub()
        {
            timer = new Timer(state => Refresh());
            timer.Change(TimeSpan.FromSeconds(10), 
               TimeSpan.FromSeconds(30));
        }

        public void Refresh()
        {
            Clients.All.refresh(DateTime.Now.ToString());
        }
   }
}

Note: this is not great code as the timer will just keep going until the application closes.

So this will both simulate the equivalent of events coming from some external trigger (in this case the timer) which will be received and processed on the web application (client) and it also allows the code to be called as a server to initiate a refresh of the status, i.e. via a button click (for example).

Open the Index.cshtml file in the Views/Home folder of the solution and remove all the divs leaving the following

@{
    ViewBag.Title = "Home Page";
}

Now add to the Index.cshtml, the following

<div>
    <div id="serverStatus"></div>
</div>
<button id="serverRefresh">Refresh</button>

@section scripts {
    <script src="~/Scripts/jquery.signalR-2.1.2.min.js"></script>
    <script src="~/signalr/hubs"></script>
    <script>
        $(function() {

            try {
                var serverStatus = $.connection.serverStatusHub;

                serverStatus.client.refresh = function(status) {
                    $('#serverStatus').html(htmlEncode(status));
                };

                $('#serverRefresh')
                    .click(function() {
                        serverStatus.server.refresh();
                    });

            } catch (sourceError) {
                $('#serverStatus').html(htmlEncode(sourceError.message));
            }

            $.connection.hub.start()
                .done(function() {
                })
                .fail(function() {
                    $('#serverStatus').html('Failed to start server hub');
                });
        });

        function htmlEncode(value) {
            return $('<div />').text(value).html();
        }
    </script>
}

Don’t worry about the blue squiggle line say thar ~/signalr/hibs could not be found, the proxies will created when the application runs. If you do want to see what’s created you can run the application and navigate to the folder (i.e. http://localhost:56433/signalr/hubs) and see the proxies.

So we’re creating the SignalR JavaScript code against the proxies and hence have an object named serverStatusHub.

Whilst the methods and types are Pascal case in the C# code we use Camel case for the JavaScript.

The code above simply creates a connection to the server status hub and then we create a client method (equivalent to a callback) where we’ll recieve updates from the hub as they come in. We’ll simply output these to the HTML page.

We also hook up to the button serverRefresh so the user can call the hub to get the latest status of the servers in our imaginary application. The rest of this section of code is error handling code, but it’s following (after the catch block) with the code to connect to the hub and start the SignalR hub up.

And that’s all there is to it.

Self hosting SignalR

I’ve covered the steps needed to host SignalR within a web application, but we’ve also seen we can connect to SignalR through a .NET client (in my case a simple Console app.). So next we’ll cover self-hosting SignalR.

Requirements

At the time of writing you’ll need to install the following NuGet packages from Package Manager Console in Visual Studio

Install-Package Microsoft.AspNet.SignalR
Install-Package Microsoft.Owin.Hosting -Pre
Install-Package Microsoft.Owin.Host.HttpListener -Pre

You can delete the scripts folder added by SignalR, we’re really just after the assemblies

Microsoft.AspNet.SignalR.Core
Microsoft.AspNet.SignalR.Owin
Microsoft.Owin.Host.HttpListener
Microsoft.Owin.Hosting
Owin

So if you feel like only deploying the minimal assemblies you can remove anything else installed from those packages.

Edit: Since writing the above a couple of things seems to have changed on those packages, you’ll now need Microsoft.Owin assembly and WebApplication (in the below code) is now WebApp.

The Server

public class MyHub : Hub
{
   public void Send(string message)
   {
      Clients.All.sendMessage(message);
   }
}

internal class MyStartUp
{
   public void Configuration(IAppBuilder app)
   {
      // the line below works fine with the console client but not the browser client
      // app.MapHubs();
      // use the following to enable the browser client to work with the server
      HubConfiguration config = new HubConfiguration();
      config.EnableCrossDomain = true;
      config.EnableDetailedErrors = true;
      app.MapHubs(config);   
   }
}

class Program
{
   // just for demo purposes, will "beep" every 5 seconds
   private static Timer timer = new Timer(o =>
   {
      var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
      context.Clients.All.sendMessage(DateTime.Now.ToLongTimeString() + " beep");
   });

   static void Main(string[] args)
   {
      using (WebApplication.Start<MyStartUp>("http://localhost:9100"))
      {
         // just for this demo
         TimeSpan ts = TimeSpan.FromSeconds(5);
 	 timer.Change(ts, ts);

	 Console.WriteLine("Server is running, Press <Enter> to stop");
	 Console.ReadLine();
      }
   }
}

I’ve listed all the code above, it’s all pretty self-explanatory. The URL in the start method is the URL for the server and port etc. The timer is solely used to send out a message every 5 seconds for out client to see. I’ve listed the code for a simple client below (see my post on creating a .NET client for SignalR if you need more info. on this).

Client

A simple .NET Console client…

static void Main(string[] args)
{
   HubConnection connection = new HubConnection("http://localhost:9100");
   IHubProxy proxy = connection.CreateHubProxy("myHub");
   connection.Start().ContinueWith(task =>
   {
      proxy.Invoke("Send", ".NET Client Connected");

      proxy.On<string>("sendMessage", (message) =>
      {
         Console.WriteLine(message);
      });
   });
   Console.Read();
}

If, you wish to access the server hub via JavaScript then the following is a simple example of the required JavaScript, note we create the proxy to the hub in the same way to the way we do this in the console client.

<script>
   $(function () {
      var connection = $.hubConnection("http://localhost:9100");
      var proxy = connection.createHubProxy('myHub');

      proxy.on("sendMessage", function (message) {
         $('#discussion').append('<li><strong>' + message + '</strong></li>');
      });

      connection.start().done(function () {
         proxy.invoke("Send", "Webpage client");
      }).fail(function () {
         $('#discussion').append('<li><strong>Failed to start</strong></li>')
      });
   });
</script>

Note: The at least one proxy.on event handler must be set before the connection.start or you will be able to send messages but not receive them (see http://www.asp.net/signalr/overview/hubs-api/hubs-api-guide-javascript-client#establishconnection for more information).

SignalR Minimal Server

Here’s the minimal steps required to create a SignalR server.

  • Create a new ASP.NET Empty Web Applications
  • Use NuGet to add Microsoft ASP.NET SignalR to the project
  • Right mouse click on the project select Add | Global Application Class
  • Edit the Global.asax.cs file to look like the following
    public class Global : System.Web.HttpApplication
    {
       protected void Application_Start(object sender, EventArgs e)
       {
          RouteTable.Routes.MapHubs();
       }
    }
    
  • A a new class file named MyHub and edited it to look like this
    public class MyHub : Hub
    {
       public void Send(string message)
       {
          Clients.All.sendMessage(message);
       }
    }
    
  • Finally we need an index.html pages so right mouse click on the project and select Add | HTMLPage – name it index and press ok. Then edit the file as follows
    <!DOCTYPE html>
    <html>
    <head>
        <title>Minimal SignalR</title>
    
        <script src="../Scripts/jquery-1.6.4.js"></script>
        <script src="../Scripts/jquery.signalR-1.1.2.js"></script>
        <script src="../signalr/hubs"></script>
    
        <script>
            $(function () {
               var monitor = $.connection.myHub;
               monitor.client.sendMessage = function (message) {
                  $('#discussion').append('<li><strong>' + message + '</strong></li>');
              }
              $.connection.hub.start().done(function () {
                  monitor.server.send("Starting...");
              });
            });
        </script>
    </head>
    <body>
        <h2>Minimal SignalR</h2>
    
        <div class="container">
           <ul id="discussion"></ul>
       </div>
    </body>
    
    </html>
    

The above has all the parts required to create a SignalR capable server and script in the HTML file but it doesn’t do anything, that’s for the user to implement, but just as an example to give something to see, let’s edit Global.asax.cs again and change the code to the following

private Timer timer = new Timer(o =>
{
   var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
   context.Clients.All.sendMessage(DateTime.Now.ToLongTimeString() + " beep");
});

protected void Application_Start(object sender, EventArgs e)
{
   RouteTable.Routes.MapHubs();

   TimeSpan ts = TimeSpan.FromSeconds(5);
   timer.Change(ts, ts);
}

The code above will simply send a message every 5 seconds to any listening clients.

Note: You’ll notice how the JavaScript has the MyHub methods etc. in camel case, the proxy automatically does this for us. Also notice the JavaScript call, monitor.server.send uses the Hub method to push a message out when the script starts whereas we subscribe our function to a “known” name, one used in the MyHub (Clients.All.sendMessage). As this is dynamic there’s no compile time check on this, so make sure you get the name and case correct.

SignalR .NET client

I’ve been using SignalR from a browser but decided I wanted to try the .SignalR .NET Client code as well to see how to implement such an app. I’m not going to cover the server exception to show the Hub

public class MonitorServiceHub : Hub
{
   public void ProjectUpdate(string message)
   {
      Clients.All.projectUpdated(message);
   }
}

It’s very simple as you see. This pushed messages out to anything subscribed to the “projectUpdated” function.

So in JavaScript we have the following for the browser

<script>
var monitor = $.connection.monitorServiceHub;
monitor.client.projectUpdated = function (message) {
   $('#latest').append('<li>' + message + '</li>');
}
$.connection.hub.start().done(function () {
   monitor.server.projectUpdate("Starting Monitor");
});
</script>

So how to we create a SignalR client ?

  • Create a new Console application in Visual Studio 2012
  • Use NuGet to add the Microsoft ASP.NET SignaalR Client
  • Now, simple add the following code
    static void Main(string[] args)
    {
       HubConnection connection = new HubConnection("http://localhost/test/");
       IHubProxy proxy = connection.CreateHubProxy("monitorServiceHub");
       connection.Start().ContinueWith(task =>
       {
          proxy.Invoke("ProjectUpdate", ".NET Client Connected");
    
          proxy.On<string>("projectUpdated", (message) =>
          {
             Console.WriteLine(message);
          });
       });
       Console.Read();
    }
    

Obviously replace the http://localhost/test/ with the location of your server. This line creates the connection (as the name suggests) and then we create the proxy. Notice we use the camel case name as created by the proxy. Next we start the connection and set up a ContinueWith where the communications takes place. I’ve added code to send a message out (just to demonstrate how) using Invoke, so all clients will see when the .NET client connects. Notice this uses the method name on the class not the camel case naming. Finally we in essence subscribe to the projectUpdated messages/function and simply output whatever message we get to the Console.