I am using ASP.NET Core 3.1, with SignalR 3.1.9:
I am using the Javscript client v3.1.9 (libman.json file):
{
"provider": "unpkg",
"library": "@microsoft/signalr@3.1.9",
"destination": "wwwroot/lib/signalr/",
"files": [
"dist/browser/signalr.js",
"dist/browser/signalr.min.js"
]
}
On my webserver, the root (example.com) is used by Wordpress for the front-end. Wordpress allows requests to /core with some modifications in the web.config, and the website loads 100% correctly.
To host my .NET Core app, I created an Application that points to the sub-folder core (example.com/core). I don't know if it is important, but all my controllers are under the area "app" (example.com/core/app).
I declared a new Hub:
public class NotificationsHub : Hub
{
private readonly IMainDataService _data;
private readonly ILogger<NotificationsHub> _logger;
public NotificationsHub(IMainDataService data, ILoggerFactory loggerFactory)
{
this._data = data;
this._logger = loggerFactory.CreateLogger<NotificationsHub>();
}
public async Task SendFriendNotification(Guid newFriendId, Guid currentUserId)
{
var currentUser = await this._data.GetUserByIdAsync(currentUserId);
var notificationRecipientId = newFriendId.ToString();
await Clients.User(notificationRecipientId).SendAsync("FriendRequestReceived", newFriendId, currentUserId, currentUser.FirstName, currentUser.LastName);
}
This is my Startup.cs (shortened to the essential):
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
options.ConsentCookie.Expiration = TimeSpan.FromDays(365);
});
services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAdB2C");
services.AddControllersWithViews()
.AddMvcLocalization()
.AddMicrosoftIdentityUI();
services.AddRazorPages();
services.AddSignalR();
services.AddRouting();
services.AddOptions();
services.Configure<OpenIdConnectOptions>(Configuration.GetSection("AzureAdB2C"));
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.Cookie.IsEssential = true;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
if ((env.IsDevelopment() || env.IsStaging() || env.IsProduction()) && !env.IsEnvironment("Localhost"))
{
app.UsePathBase("/core");
}
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapControllerRoute(
name: "areas",
pattern: "{area:exists}/{controller=Map}/{action=Index}/{id?}"
);
endpoints.MapControllerRoute(
name: "default",
pattern: "{area=App}/{controller=Map}/{action=Index}"
);
endpoints.MapControllerRoute(
name: "profile",
pattern: "{area=App}/{controller=Profile}/{action=Index}/{id?}"
);
endpoints.MapRazorPages();
endpoints.MapDbLocalizationAdminUI();
endpoints.MapDbLocalizationClientsideProvider();
if (env.EnvironmentName == "Localhost")
{
endpoints.MapHub<NotificationsHub>("/notificationshub");
}
else
{
endpoints.MapHub<NotificationsHub>("/core/notificationshub");
}
});
}
In local, as I am directly running my .NET Core app at the root, there is no problem. But when I deploy to Dev/Staging/Prod, where the sub-folder /core is used, I always get an error 404 on the following URL: https://www.example.com/core/notificationshub/negotiate?negotiateVersion=1
I also tried:
In both cases, I get a 404 exception. The 404 is always followed by the following line in the F12 console:
Error: Failed to complete negotiation with the server: Error: Not Found
Error: Failed to start the connection: Error: Not Found
Error: Not Found
The 3rd line above points to my JavaScript client:
"use strict";
var currentUrl = window.location.href;
var basePath = '';
if (currentUrl.includes("/core") == true) {
basePath = '/core'
}
var connection = new signalR.HubConnectionBuilder().withUrl(basePath + "/notificationshub").build();
connection.start().catch(function (err) {
return console.error(err.toString());
});
The error is on the line connection.start().
I tried to modified the previous line with ../ before the path, but it didn't help:
let connection = new signalR.HubConnectionBuilder().withUrl("../" + basePath + "/notificationshub").build();
Any idea if I am missing something? It seems straightforward to have SignalR working on the root URL, but not if there is a sub-folder.
It looks like you're mapping your app to "/core" and then mapping your hub to "/core/hub". So your hub is actually at "/core/core/hub".