Using SignalR in ASP.NET Core & React to send messages

Published: Thursday 2 July 2020

We did a live stream where we integrated SignalR with ASP.NET Core and React.

We went ahead and built a system where you can send messages in a React app. From there, it used SignalR to send the message to the ASP.NET Core app. Once the ASP.NET Core app got the message, it sent it's own message back to the React app.

We are going to recap on the steps we went through in that demo to successfully integrate SignalR with React and ASP.NET Core.

Creating The Applications

Firstly, we built a ASP.NET Core MVC API application. Next, we built a React application in the same directory as the ASP.NET Core application and integrated the two together. Thereafter, we integrated SignalR into both applications to enable us to communicate between ASP.NET Core and React.

Create The ASP.NET Core MVC API Application

We used Visual Studio to create our ASP.NET Core MVC API application. Using Visual Studio Templates, we selected ASP.NET Core Web Application and choose the API template.

Create an ASP.NET Core API in Visual Studio 2019

Create an ASP.NET Core API in Visual Studio 2019

Now, we could of used the React.js template at this stage, but there are a couple of reasons why we didn't:

  • We are not too sure how to use this template, and create the React application with TypeScript. This template always creates the React app with JavaScript.
  • It's good to have an understanding on how to integrate a React app with a ASP.NET Core app.

Create React App

We opened up the terminal in Visual Studio Code, run a CD command to the directory where the ASP.NET Core application exists. Then, we ran the following command to create the React app:

yarn create react-app clientapp --template typescript

This enabled us to create the React app using TypeScript. The React app will be created inside the "clientapp" folder.

ASP.NET Core Configurations

Now that we have created both applications, we need to make some configuration changes to our ASP.NET Core application. But first, we need to install the following NuGet packages into our ASP.NET Core application:

  • Microsoft.AspNetCore.SpaServices
  • Microsoft.AspNetCore.SpaServices.Extensions

From there, we can open up our Startup class and make the configuration changes necessary.

Inside the ConfigureServices method, add the following line of code:

// Startup.cs
services.AddSpaStaticFiles(configuration =>
{
	configuration.RootPath = "ClientApp/build";
});

This will point the root path to the build folder for our React app.

Next, we need to add the following lines of code at the bottom of the Configure method:

// Startup.cs
app.UseSpa(spa =>
{
	spa.Options.SourcePath = "ClientApp";
	if (env.IsDevelopment())
	{
		spa.UseReactDevelopmentServer(npmScript: "start");
	}
});

This points the source path to our React app. In addition, it allows us to run the React Development Server when in Development. So, when we make a change in React, the React application is automatically rebuilt without having to restart the ASP.NET Core application.

It's worth checking at this stage that your ASP.NET Core is integrated properly with React. Run the application to check. The React app should reside in https://localhost:{port}/clientapp.

SignalR Integration

Assuming your application is running fine, it's time to integrate SignalR. SignalR needs to be integrated in both the ASP.NET Core and React applications.

React

Firstly, we will integrate SignalR into our React app by running acommand line in the terminal in Visual Studio Code. Just make sure that you are pointing to the "clientapp" folder.

CD [PathToASPNETCoreApp]/clientapp

Then you can run this command line to install SignalR into React:

yarn add @microsoft/signalr

ASP.NET Core Integration

Next, we need to integrate it into our ASP.NET Core application. You will need to add the following NuGet package in-order to do this:

  • Microsoft.AspNetCore.SignalR.Client

From there, we need to make a change in our Startup class. Inside the ConfigureServices method, add the following:

services.AddSignalR();

Now, we have to create a new SignalR Hub which is very straight forward. Create a new class called MessageHub. We need to ensure that this class inherits the Hub class.

// MessageHub.cs
public class MessageHub : Hub
{
}

Finally, we need to map our hub to a route. The React app will use this route to connect to the right hub. Back in the Startup class, and the Configure method, there should be a app.UseEndPoints method. Add the following code to the top of this method:

endpoints.MapHub<MessageHub>("/message");

This routes the MessageHub to /message.

API Endpoint

We need to create an endpoint where we can fire our messages to. For this, we have created a MessageController. The MessageController will route to /api/message. Inside this controller, we will integrate a Create method which will accept a HTTP Post verb.

The Create method will have a parameter of MessagePost. Inside the MessagePost, there will be a Message property. When we send the payload over to this action, we will send it as JSON. Inside the JSON, there will be a Message key. This Message key will bind with the Message property in the MessagePost class.

Inside the Create method, we use the MessageHub to send a message to all clients connected through SignalR. The MessageHub is injected through dependency injection through the IHubContext interface. Clients can hook into a virtual "sendToReact" method that will triggered when we invoke it.

// MessageController.cs
[Route("/api/message")]
[ApiController]
public class MessageController : Controller
{
	protected readonly IHubContext<MessageHub> _messageHub;
	public MessageController([NotNull] IHubContext<MessageHub> messageHub)
	{
		_messageHub = messageHub;
	}
	 
	[HttpPost]
	public async Task<IActionResult> Create(MessagePost messagePost)
	{
		await _messageHub.Clients.All.SendAsync("sendToReact", "The message '" +
		messagePost.Message + "' has been received");
		return Ok();
	}
}
 
public class MessagePost
{
	public virtual string Message { get; set; }
}

It's worth at this point testing this method in Postman to check that you are getting a 200 response:

Use Postman to send a HTTP POST with JSON

Use Postman to send a HTTP POST with JSON

Use React App To Send The Messages

We now go ahead and change the code in App.tsx. The point here is to build up a SignalR connection to /message (which translates to the MessageHub).

Once we've done that, we create a string array. This array will list all the messages that we have received.

Then we create a separate function component (FC). Called Messages, this will hook into the "sendToReact" method that we use in our ASP.NET Core application through the MessageController. When this method receives a message, we push it to our string array. Then, we use the useState method to update the list of messages received to the client.

Finally, we create a new function component to send the message. This will have a text box and a submit button. When the submit button is clicked, it will throw an API POST call to /api/message. If you remember, it's the Create method in our MessageController. It will then return a message back to any clients connected.

// App.tsx
import React, { useState, useEffect } from 'react';
import logo from './logo.svg';
import './App.css';
import * as signalR from "@microsoft/signalr";
 
const App: React.FC = () => {
 
	const hubConnection = new signalR.HubConnectionBuilder().withUrl("/message")
	.build();

	hubConnection.start();

	var list: string[] = [];

	interface MessageProps {
	HubConnection: signalR.HubConnection
	}

	const Messages: React.FC<MessageProps> = (messageProps) => {

	const [date, setDate] = useState<Date>();

	useEffect(() => {
		messageProps.HubConnection.on("sendToReact", message => {
			list.push(message);
			setDate(new Date());
		})
	}, []);

	return <>{list.map((message, index) => <p key={`message${index}`}>{message}</p>)}</>
	}

	const SendMessage: React.FC = () => {

	const [message, setMessage] = useState("");

	const messageChange = (event: React.ChangeEvent<HTMLInputElement>) => {
		if (event && event.target) {
			setMessage(event.target.value);
		}
	}

	const messageSubmit = (event: React.MouseEvent) => {
		if (event) {
			fetch("/api/message", {
				"method": "POST",
				"headers": {
					Accept: "application/json",
					"Content-Type": "application/json"
				},
				body: JSON.stringify({
					message: message
				})
			});

			setMessage("");
		}
	}

	return <><label>Enter your Message</label><input type="text" onChange={messageChange} value={message} /><button onClick={messageSubmit}>Add Message</button></>;

	}

	return <><SendMessage /><Messages HubConnection={hubConnection}></Messages></>
}
 
export default App;
Integrated SignalR with React and ASP.NET Core

Integrated SignalR with React and ASP.NET Core

You can try the application yourself by downloading the code example.

To see the working demo in action, you can watch back our live stream. This will take you through the processes required. And by watching the video, you can follow along and code to get the application working.

Going Forward...

We integrated SignalR to send messages to all clients. However, with SignalR, you can focus on a particular user. That would be handy if you wanted to integrate a chat application. You can also set up multiple SignalR hubs if you need to. There is a lot of flexibility around it.