| Signalr / React / Dotnet Core / Development

Wiring up ReactJS.Net with Asp.Net Core MVC via SignalR Core

Recently, I’ve worked on a project that involved React code embedded in Asp.Net Core, that needed to talk to third party technologies via SignalR.

Due to the fact that at the time of writing SignalR Core is in alpha2, there’s little documentation available on how integrating with it can be done, so I decided to share what I’ve learned. And what better way to do that than building something?

What we’re building

To keep it as brief as possible, we’re going to build a very simple web app to demonstrate the communication between SignalR and its clients.

The idea is this:

Application Flow Diagram

The React component sends a message to the JavaScript controller, which in turn sends it to the Hub. The Hub then distributes it among the connected clients. We’ll also look at how it’d work the other way around. Sending a message from the Web app, which is eventually going to be received by the React component.

Setting up the project

You are going to need 3 things:

  • An Asp.Net Core Web Application
  • The ReactJS.Net NuGet package: React.AspNet
  • The SignalR Core NuGet package

You’ll need the Microsoft.AspNetCore.SignalR package. Keep in mind that it is in alpha, therefore, it can be found under the pre-releases.

Adding the Hub

The Hub is the central part of SignalR that allows for communication with multiple clients, usually via WebSockets (if available).

We’re going to make it implement a single method, for invoking displayMessage() on the connected clients:

public class MyHub : Hub
{
    public async Task SendMessage(string message)
    {
        await Clients.All.InvokeAsync("displaymessage", message);
    }
}

Configuring the Web App

Let’s tell the web app that we’re using SignalR and React:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton();
    services.AddSignalR();
    services.AddReact();
    services.AddMvc();
}

… and let’s configure them:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    /*...*/
    app.UseReact(config =>
        config
            .SetReuseJavaScriptEngines(true)
            .SetUseDebugReact(true));

    app.UseStaticFiles();
    app.UseFileServer();

    app.UseSignalR(routes =>
    {
        routes.MapHub("myHub");
    });
    /*...*/
}

You could add all your JSX files here as well with:

.AddScript(~/js/webApp.jsx”)

but since we’re not planning on having any server-side rendering, we’ll leave it out.

Important note: The app.UseReact() method call MUST come before the UseStaticFiles() call!

Setting up SignalR

First, we’ll need the SignalR JavaScript client.

Run npm i @aspnet/signalr-client or get it directly from MSDN.

If you’re going the npm route, you’ll need to configure Webpack (or the tool of your choice) to deploy the packages from node_modules to the wwwroot.

Note: I had some issues with the standard client, specifically when receiving invocations. If you’re experiencing the same, switch to the ES5 client.

Once you have it, you can start directly referencing the client file in either your _Layout.cshtml or the view you’re planning on using it in.

Adding the controller script

The controller is going to serve as a bridge between the hub and the client. Its responsibilities are:

  • It starts and maintains the SignalR connection
signalRConnection = new signalR.HubConnection("/myHub");

signalRConnection.start()
  .catch(err => {
      console.log('Connection error');
});
  • It sends the messages by invoking the SendMessage method on the Hub
signalRController.sendMessage = function (message) {
    return signalRConnection.invoke("SendMessage", message)
        .catch(function(data) {
            alert('Oops, we cannot connect to the server...');
        });
}
  • It listens for messages coming from the Hub, and forwards them
signalRConnection.on('displayMessage', function(msg) {
  window.dispatchEvent(new CustomEvent('messageReceived', { detail: { message: msg } }))
})

Now that we pretty much have SignalR in place, let’s move on to ReactJs.Net.

Setting Up ReactJs.Net

In our _ViewImports.cs, let’s add:

@using React.AspNet;

This is going to tell MVC that our .jsx script includes are JSX files that need to be compiled into JavaScript by ReactJs.Net’s internal transpiler.

Normally you’d need an external transpiler service to precompile your JSX to standard JavaScript, however, ReactJs.Net internally takes care of that for you.

Still, if you’d like, you’re welcome to disable the internal transpiler, and configure (eg.: Babel) to take care of the work.

Let’s add the webApp.jsx file to the project, and include it in the referenced scripts in the view.

I wrote a simple React component that:

  • starts a connection to SignalR
window.signalRController.startConnection()
  • listens to a custom message event, and displays received messages
window.addEventListener('messageReceived', this.messageReceived.bind(this))
  • displays a standard Form control to send messages
class Form extends React.Component {
    handleSubmit = (event) => {
        event.preventDefault();
        window.signalRController.sendMessage(this.message.value);
    }
    render() {
        return (
            this.message = input} placeholder="Message"/>
            Submit Message
        );
    }
}

The component is relatively simple, it consists of a single input field and a submit button.

Let’s add the following line to the end of Index.cshtml after we’re done with the JavaScript and JSX imports:

[@Html](http://twitter.com/Html "Twitter profile for @Html").ReactInitJavaScript();

This will force React to render the code to initialise the component on the client.

Finally, let’s hook our React component up onto our View:

ReactDOM.render(<WebApp />, document.getElementById('webApp'))

Let’s run it!

Exception

Ouch!

What’s happening here?

Whenever a service is requested, the dependency injection framework is performing call-site validation. The way UseReact() works, is that it calls GetService() from the origin of the call, to ensure that the requested services are available, which make the two clash.

To get around this issue, we’ll disable the validation in BuildWebHost:

.UseDefaultServiceProvider(options => options.ValidateScopes = false)

Now when we send the message:

SendMessage

… we get the result!

Sending messages from the Web App

Alright, what about the other way around? What if we need to send messages from our web application to other connected clients where the react components would be displaying the messages?

We can most certainly do that!

Application flow

In this case, we’ll be invoking the Hub method from the MVC controller.

In order to do so, we’ll need the Hub injected into our selected controller. SignalR is already configured in ConfigureServices, let’s add the Hub there as well:

services.AddSingleton<MyHub>();

This will put up our Hub for DI, whenever we ask for the singleton instance for it to be injected.

In our controller, let’s inject and save a reference to our Hub:

public HomeController(MyHub hub)
{
    _myHub = hub;
}

Also, let’s create a simple controller method for sending messages to the other clients:

[HttpPost]
public async Task SendMessage(MessageModel messageModel)
{
    await _myHub.SendMessage(messageModel.Message);
}

And, that’s it!

Hopefully this mini guide helps you with SignalR’s alpha client integration. I did not, or rather, couldn’t cover the whole process, but I tried to outline the most important parts.

Akos Radler

Akos Radler

Full Stack .Net / React developer

Read More