SignalR for ASP.NET Core 2.0 with Aurelia and Webpack

Just today (September 15, 2017) Microsoft announced the first alpha version of SignalR for ASP.NET Core 2.0. Now these are great news! I immediately had to try this out with my favorite way of building web apps: The Aurelia framework. I can already say: It works like a charm. But let me start from the beginning.

TL;DR If you do not want to follow this tutorial and just want the code, go ahead, have a look at my Aurelia Playground over on GitHub!

Setting up a simple application

I used a skeleton I once created to have an easy starting point with Aurelia. Initially the skeleton was set up using the Aurelia template coming with the the ASP.NET Core SPA templates (dotnet new aurelia). I upgraded Bootstrap to the shiny new version 4 but that’s basically all I changed. Feel free to use it as a starting point for your projects!

For this project I am going to use this template as a baseline. Go ahead and clone https://github.com/Spontifixus/aurelia-playground.git to get started and read on to build your first ASP.NET Core 2.0 SignalR application!

SignalR on the server

This section covers the server-side components needed to power the chat application.

Installing the nuget package

To install the new SignalR, everything you need to do, is to open a console and run

dotnet add package Microsoft.AspNetCore.SignalR --version 1.0.0-alpha1-final

That installs all required packages and dependencies needed by SignalR.

Creating a ChatHub

The center of all SignalR activities are so called Hubs. Hubs handle connections and provide methods the clients can invoke remotely. Of course hubs can send events to the clients, too! So we need to create a hub class, that can receive a chat message and distribute it among the clients.

Let’s create a folder named “SignalR” and add a file named “ChatHub.cs” to it:

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

namespace AureliaPlayground.SignalR
{
    public class ChatHub : Hub
    {
        public Task SendMessage(Message message)
        {
            return this.Clients.All.InvokeAsync("IncomingMessageEvent", message);
        }
    }
}

As you can see, the method SendMessage takes a message and distributes it to all clients. Of course the definition of the Message-class is still missing, so let’s add that quickly. Add a file named “Message.cs” to the “SignalR”-folder:

namespace AureliaPlayground.SignalR
{
    public class Message
    {
        public string SenderName { get; set; }
        public string Text { get; set; }
    }
}

Obviously this is a very simple implementation. It should be expanded to have at least a proper error handling, but for now this will do.

Configuring our Application

Now that our ChatHub-class was implemented, we need to make the hub known to the system. We do this by first registering the SignalR components to the dependency injection container and then configuring a route to the chat hub. Adding SignalR to the container can be achieved by adding the following line to the ConfigureServices-method of the Startup-class:

services.AddSignalR();

Don’t forget to add the using directive (using Microsoft.AspNetCore.SignalR;)!

With the new SignalR for ASP.NET Core 2.0 configuring the routes is as easy as we know it from ASP.NET Core MVC - it works very much the same way. Just add the following snippet to the Configure-method of the Startup-class:

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

Again, don’t forget to add the using directive for the ChatHub-class. This code configures SignalR to publish the hub under the /chat URL.

This is all you need to do to get things running on the server side!

SignalR on the client

This sections explains how to setup the client-side components of our application.

Installing the client

Microsoft built an entirely new SignalR client for JavaScript or TypeScript. The new client has a much simpler interface than the older versions and thus can be configured and used much more intuitively.

First things first: To be able to use SignalR on the client side install the npm package by opening a command line and running

npm install @aspnet/signalr-client --save

Our little application uses webpack, and to bundle the client into our vendor bundle, we need to modify the relevant config file. So add @aspnet/signalr-client to the list of packages in the “webpack.config.vendor.js” file:

vendor: [
    'aurelia-event-aggregator',
    'aurelia-fetch-client',
    'aurelia-framework',
    'aurelia-history-browser',
    'aurelia-logging-console',
    'aurelia-pal-browser',
    'aurelia-polyfills',
    'aurelia-route-recognizer',
    'aurelia-router',
    'aurelia-templating-binding',
    'aurelia-templating-resources',
    'aurelia-templating-router',
    'bootstrap',
    'bootstrap/dist/css/bootstrap.css',
    'jquery',
    'popper.js',
    '@aspnet/signalr-client'
]

Then open a console and run webpack to rebundle the “vendor.js” file:

webpack --config webpack.config.vendor.js

Note that although jQuery is still included in our build it is only used by bootstrap, but no longer by SignalR!

Create an Aurelia component

All we need now is a page in our application that we can use to chat. For this create a new folder named “chat” in the “ClientApp/components”-folder of our application. Then add a component named “chat.ts” to this folder:

export class Chat {
}

To establish a connection to the chat hub we need to set up a hub connection. The SignalR client provides a class taking care of this. Let’s create a private field and a constructor to create an instance of the HubConnection-class:

private chatHubConnection: HubConnection;

constructor() {
    this.chatHubConnection = new HubConnection('/chat');
}

Note that in a real-life application you usually would encapsulate the connectivity to the chat hub in a chat service that you then can resolve using dependency injection.

Before we listen for incoming messages we need to add a field to store them, so we can access them from the template:

chatLog: Message[] = [];

Now we can register to the IncomingMessageEvent in the constructor:

this.chatHubConnection.on('IncomingMessageEvent', (incomingMessage: Message) => {
    this.chatLog.push(incomingMessage);
});

You might note that we use a class named “Message” here, so add a new class below the Chat-class:

export class ChatMessage {
    SenderName: string;
    Text: string;
}

To actually listen to new events we need to start the hub connection. Starting a hub connection returns a promise that gets resolved once the connection is established. We will store the promise in a private field so we can access it later on to ensure a working connection. To start the connection when our component gets activated we implement the activate-method:

private connectionPromise?: Promise<void>;

activate() {
    this.connectionPromise = this.chatHubConnection.start();
}

Also we implement the deactivate-method to stop the connection once we navigate away from the chat:

deactivate() {
    this.connectionPromise = undefined;
    this.chatHubConnection.stop();        
}

Now we need a method to send a new message to the chat. For this we add a field containing an empty message that we can bind to from the template and a method actually sending the message. We use the connection promise to ensure that the hub is connected before sending the message:

async sendMessage(): Promise<void> {
    if (!connectionPromise) {
        console.warning('Chat: No connection to the server.')
    }
    await connectionPromise;
    this.chatHubConnection.invoke('sendMessage', this.currentMessage);
    this.currentMessage.Text = '';
}

Finally we need a template for our component. So add a file named “chat.html” to the chat folder:

<template>
    <h1>Chat</h1>
    <p>This is a simple example of a SignalR Chat application. Every message you type here will be displayed at all other clients.</p>
    <form>
        <div class="form-row">
            <div class="col">
                <input type="text" class="form-control" placeholder="Your name..." value.two-way="currentMessage.SenderName">
            </div>
            <div class="col-7">
                <input type="text" class="form-control" placeholder="Your message..." value.two-way="currentMessage.Text">
            </div>
            <div class="col">
                <button type="submit" class="btn btn-primary" click.delegate="sendMessage()">Send</button>
            </div>
        </div>
    </form>
    <p>
        <ul>
            <li repeat.for = "message of chatLog"><strong>${message.SenderName}:</strong> ${message.Text}</li>
        </ul>
    </p>
</template>

This template provides a simple form where we can enter a name and the message and click a button to send it. Incoming messages are just listed below the form.

Add the navigation item

Open the file “ClientApp/components/app/app.ts” and add our component to the navigation menu:

{
    route: 'chat',
    name: 'chat',
    settings: { icon: 'th-list' },
    moduleId: PLATFORM.moduleName('../chat/chat'),
    nav: true,
    title: 'Chat'
}

Build and run the application

To run the application open a console and run

webpack --config webpack.config.vendor.js
webpack
dotnet restore
dotnet build
dotnet run

Summary

The first impression is that the first alpha of SignalR for ASP.NET Core 2.0 already works really well and is fun to use. I literally put this together in about half an hour (writing this up took a bit longer of course…).

I am really pleased where this is going and how well this works together with Aurelia and Webpack. What are your experiences with this?

Geschrieben am 15.09.2017