Introduction
This will be a short post which will tie in with the next post about branching authentication in ASP.NET Core using middleware.
The Problem
Recently I encountered a situation in which I needed to pass to a SignalR hub additional data with every request, kinda like environment metadata from external systems. In my case, it was the username from an external system to correlate it.
Of course, the obvious answer would have been that every time I invoked a method on the hub, to also pass along the metadata as a parameter, but that would become cumbersome since this was related to users, so it needed to be in each and every request, cookies were off the table since the requests would be coming from a different domain, and with web sockets involved, it’s not like we can build the request each time like for Ajax calls.
I started looking into additional ways to send information to the hub in such a way that it would always be present from that caller, and also not mess up my method calls.
Some of you might know that the current approach to connect to a SignalR hub now is by using the following snippet of code in JavaScript:
const connection = new signalR.HubConnectionBuilder().withUrl("/hub").build();
connection.on("hubMethod", (messages) => {
alert(messages);
});
connection.start().catch(err => console.error(err.toString()));
The Solution
In my case, I wanted to add a username
associated with each call so that I can handle it in some custom middleware (which has access to the HttpContext
which holds information about the current request). The change for that was actually so minor that it could be easily overlooked, so what I changed to that snippet of code was the following:
const username = "Vlad";
const connection = new signalR.HubConnectionBuilder().withUrl
(`/hub?UserName=${username}`).build(); // this builds the connection for the hub
connection.on("hubMethod", (messages) => { // this is a handler for when the hub
// reaches the client on the "hubMethod" client endpoint.
alert(messages);
});
connection.start().catch(err => console.error(err.toString())); // here we just start
// the connection and log out any errors that might occur.
With this change, if you were to have a look at the request coming in, it will always have that query string attached, and that will be present for all and any future calls from that particular client to the hub.
But How Can I Use That in the Hub?
In this particular case, there was no need to access the HttpContext
from the hub since it was going to get used in the middleware before it reaches the hub, but if you have a look at the Context
property of a SignalR Hub, you will notice that context isn’t an HttpContext
so we will need an extra step to enable us to access the metadata in our hub methods as well.
To get the HttpContext
inside of a Hub
method, all we need to do is add the following line to the method:
HttpContext httpContext = Context.Features.Get().HttpContext;
Pro Tip
If your metadata is getting too big (which it really shouldn’t), please keep in mind that query strings have a limit on the number of characters that can be sent, so in those cases, it might be worthwhile looking into compressing that string parameter client side and decompressing it server side (maybe we will cover that in a future post ).
Cheers and happy coding.
CodeProject