SignalR is a websocket library for .NET. During setup and local development there are a number of issues I have encountered. In this post I’ll explain how to fix these.
I will assume you are using ASP.NET core, version 2.2 or later. And your client is just plain old vanilla JavaScript.
Obviously your first step should be reproducing your issue with new empty projects, where you can be sure nothing else is interfering. Beyond that, here are a few tips:
Client Scripts Updated
To use SignalR in JavaScript you should be using the official client-side library from Microsoft. This is usually a single file called signalr.js
or signalr.min.js
(the minified version). You can find basic HTML/JS example use here to check you are using it correctly.
If you have got the client scripts from a non-official source, or there is a chance they might be out-of-date, make sure you replace them with the latest version. You can get hold of the latest version of the scripts using Visual Studio or using a use a CDN, as explained in Microsoft's official documentation.
HTTPS client with HTTP server - ‘Mixed Content’ Error
My first mistake was accessing my client web app using ‘https’ and the SignalR server on ‘http’. Web browsers will not like sending an unencrypted ‘http’ request when visiting a ‘https’ page. For web browsers the ‘s’ in the URL is supposed to give the impression of security, so it expects all the linked content, including JavaScript web requests to be retrievable over secure TLS connections.
If you do this, your web brower should tell you something about the site not being secure. And it will likely block all of these requests, like those trying to establish a SignalR connection.
This is generally referred to as HTTPS mixed content.
The fix is simple. Either downgrade your website to use http, or a much better idea - upgrade your server to use https.
Set the correct URLs
This may seem obvious but you have to be very careful. Make sure your script has the correct URL of the SignalR server assigned.
Pay particular attention to whether it is ‘http’ or ‘https’, the path is the same as the route of the Hub in your ASP.NET Core app.
If you are using localhost, make sure you assign the correct port! You’re project settings of your ASP.NET core app will show this. I recommend using http when tesing locally, as development SSL certificates can be a nuisance.
It should look something like this:
var connection = new signalR.HubConnectionBuilder().withUrl("http://localhost:63205/myhub").build();
A Websever for your Website
Opening your website by literally opening a index.html
file in your web browser is not a good idea. Most web browsers will block cross-origin requests when you do this, and normally log it as a CORS error.
If you are doing this, you’re address bar will display something like:
file:///C:/Users/.../index.html
To avoid this, you need to start a local webserver which will serve your files. Then access it using localhost or a local ip address such as 127.0.0.1
or something starting with 192 or 172. And you’ll usually have to provide a port number too like 8080
.
http://127.0.0.1:8080/
How do you do this? My prefered method is to use node.js which you can install here. Then open a terminal and use the following command to install the http-server
package:
npm install --global http-server
And from there it is simple. Every time your want to start a webserver, open a terminal to the directory containing your index.html
, javascript files and whatever else. Simply type http-server
. Then it will give you the address such as 127.0.0.1:8080
which you can open in a web browser to access your website.
Now take note of the address serving your website, you’ll need it to configure CORS correctly …
CORS
CORS can be a nightmare for web developers testing their work locally. But, it is a necessary evil.
You can check for errors these using the F12 developer tools in your web browser. Any red error message in the console which mentions CORS, means you haven’t configured it correctly. But first to explain what it is …
Suppose you are on example.com
and the website wants to fetch something from another domain like jam-es.com
. If that was allowed, anyone who visits example.com
will be pinging jam-es.com
. If example.com
is your site, and it’s quite popular, you could easily get your visitors machines to flood jam-es.com
with requests and perform a DDoS attack. For this reason, web browsers block visitors of one domain (example.com
) from sending requests to another domain (jam-es.com
), unless jam-es.com
explicitly says it allows requests from visitors of example.com
.
This is the exact scenaro you may have. You are visiting your web app on one domain example.com
but it is trying to send requests to a different domain, the domain of your SignalR server jam-es.com
(or even just a different subdomain like signalr.example.com
). Those requests will be blocked unless you configure CORS correctly by ensuring OPTIONS
requests to jam-es.com
return a certain HTTP response header.
This header has the name Access-Control-Allow-Origin
and it must be returned by your SignalR server. This only applies when your client sends requests with an Origin
header (to tell the server which domain the visitor making the request is coming from), which all web browsers include by default (and it is hard to disable).
The Access-Control-Allow-Origin
header may return 3 different values. Either the wildcard *
to allow all domains, or null
(which you should never use), or it should be set to the domain the vistor’s is making the request from like example.com
.
Note: With SignalR you cannot use *
, because SignalR requires the use of credentials, and requests using *
with credentials will all be blocked by your web browser too 🙄.
Therefore, we need to change our ASP.NET Core SignalR server to return this header with the value set to the domain the requests are coming from. If you are testing locally and read the previous section this will be a local IP address with a port like 127.0.0.1:8080
, otherwise it is the domain of your website example.com
.
Within Startup.cs
inside ConfigureServices
add the following line:
services.AddCors();
And within Configure
add:
app.UseCors(builder =>
{
builder.WithOrigins("http://127.0.0.1:8080", "https://example.com")
.AllowAnyHeader()
.WithMethods("GET", "POST")
.AllowCredentials();
});
Notice here, we’ve provided two allowed origins, one which we can use for development, and the other for production. But the method will accept as many as you like, or even just one. Make sure you include the ‘http’ or ‘https’ correctly.
Now build and run your webserver, and hopefully you will not get any more issues with CORS … but you might …
Deployment issues … CORS again?
By this point, you should be able to get things working locally. But you might still have issues when deployed to your site example.com
(when using it as an origin above).
You’re going to have to go through all the settings on your server or cloud dashboard, until you find what is set wrong.
For example, if using a server or cloud VM, make sure your firewall is open to all sources on ports 80 and 443.
For the Azure Web App users, I recommend you check this first. Navigate to your web app (App Service resource) in the Azure Portal and look for the section called ‘CORS’ in the left-hand menu. If enabled this will enable CORS at an App Service level and override everything you set in the set above … and break your SignalR connections. So make sure this is DISABLED, i.e. remove anything from the ‘Allowed Origins’ list, especially remove the wildcard *
if it appears.
Hopefully all your issues are fixed now … or at least those relating to SignalR conenctions 🙂
Comments
Post a Comment