(616) 371-1037

[email protected]

SignalR in AspNetCore

May 4, 2018 - John Waters

No Comments

We’ve all been waiting for the first production release of SignalR for AspNetCore, and supposedly it’s going to be out there any day now. Impatient as I am, I started using 1.0.0-preview2-final – and it rocks!  Here are some tips and tricks, including use of Dependency Injection and handling Multi Tenant scenarios. But first… a demo! See update at end of blog for a couple of changes in RC1.

 

As I mentioned, this is a multi tenant app, so when something happens in a tenant, I want to broadcast that to ONLY users logged into the same tenant. Also, when the user in Browser A clicks Re-sequence, when the back end is done, I want to push out some messages to all browsers.

The back end is an AspNetCore WebAPI, and the front end is an Angular 5 client. The Angular client uses the same version of SignalR as the server (important for these prereleases that the versions match!). Here is snippet from the Angular code, which sets up the connection with the hub:

Worth noting here is that:

  • The connection call passes in a session token on the URL. This comes from a previous call to the login API, which returns a JWT, and I pass this on the Query String to the connect call.
  • The transport is forced to Web Sockets (as in this version there seem to be some issues with the HTTP based negotiation protocol resulting in some 405s)
  • All incoming messages are routed through a single event emitter, with a data payload, you will see what that means for the server shortly.

Let’s follow that token through to the server. The server is configured to use a JwtBearer for authentication:

Here you also see the addition of SignalR to the services collection, after Authentication and Mvc. And below, you see SignalR added to the HTTP pipeline, after Authentication, and also that we are configuring CORS.

Note the AllowCredentials() for CORS.

OK – so the server is set for JWT bearer tokens – but it looks for these in the HTTP Authorization header. We are passing it on the query string. To solve this, see the UseTokenInQueryString call above? This installs a Middleware that takes care of this for us:

Perfect – now we can get our SignalR hub calls authenticated, a good practice and of course a MUST for multi tenant! Otherwise we don’t know which tenant the connected client belongs to… unless we rely on the client to tell us, which is iffy.

Let’s take a look at the Hub that we configured above with Routes.MapHub<StopsHub> :

Hmm – nothing there! It’s all in a generic base class for all my Hubs:

Don’t forget that [Authorize] attribute to force authenticated calls only!

In OnConnectedAsync, an authenticated client connects, and I extract the TenantId from the Claims embedded in the JWT. GetClaim<T> gets it for me:

I pass in the User that has the claims, which comes form Context.User (since we have authenticated users).

Once I have the Tenant ID, I add this client connection to a SignalR Hub Group named TenantID.ToString(), so later on I can send messages to all clients of this tenant. When the client disconnects, I remove it from the group. That’s all there is to the Hub… so where is the logic for sending messages?

In my middle tier code, I need to be able to message the clients from arbitrary classes. The code looks something like this:

Here, _hubHelper is an injected class that does the sending of messages. Note I am not passing the current Tenant ID to it… how does it know where to send the message? And how does it get a handle to the StopHub, where the Groups are defined?

In previous SignalR versions, you would do that by calling GetHubContext like this:

In AspNetCore, it all happens magically through Dependency Injection! Here is my HubHelper class being configured for DI, along with the all important HttpContextAccessor:

And here is the class definition, where I store the context accessor that is injected into the constructor. Note I also declare an IHubContext<StopsHub> in the constructor. That’s all I need to do to get the StopHub context injected into my helper.

Armed with the HttpContext and HubContext, I can now implement those tenant aware messages:

To find the current tenant, I get the TenantID claim from the User of the HttpContext, just like I did when the client connected.

Then, I send the message to the clients of that Tenant (remember, the group name is the TenantID.ToString()). Pretty simple… you saw the result in the demo!

Oh – and as far as the comment about the Angular client routing all events through a common emitter – here is the server side implication. Instead of passing a series of arguments that are different for each message (messageTicker, stopStatusChange, tripEvent), I just pass an anonymous type as the single parameter:

That’s all there is to it – multi tenant SignalR with an Angular client in AspNetCore – enjoy!

Breaking news – AspNetCore RC1 was released at Build today. There were a couple of naming changes – in the Hubs, Groups.AddAsync and RemoveAsync became AddToGroupAsync and RemoveFromGroupAsync. In your Startup.cs file, when doing UseSignalR, the options.Transports became options.HttpTransportType. On the Angular client side, similar changes were made to naming. Also, the way you create a Hub connection changed to this:

Don’t forget to update your package reference client side to

And do an npm install.

 

 

John Waters

Leave a comment

Your email address will not be published. Required fields are marked *