I'm facing an inconsistent CORS issue with SignalR in my ASP.NET Core and Angular application. On my machine, the connection works fine in Chrome but fails with a CORS error in Edge. However, for some other people, the error occurs in both Chrome and Edge, which makes the issue difficult to reproduce consistently.
Access to XMLHttpRequest at 'http://localhost:21023/signalr/negotiate?enc_auth_token=wNYmO41%2F48SHNstaLVXxHCCre29BZQl1NhC6NM3R3rzpXtPQxVzH6jEzA%2FQhXFN5tu6Fk7pO53uppm1mVXMZgxbyRVz26dnepi%2FFyB6axBY%2B6gq1GL%2BuRQgoiFUCjRN2p8w6LevViwKlHyWZZJZO1DGVSjAi1m2U%2Bog9pkHw9%2FTWnx%2BxQedi3enbqm%2FZYOXT%2FBycU4LZ4F6zlZtrxzHwExtSfELJf%2F0pBk4IITKNjDQHBqaqbtRSdePntPkQpHXvZNrJtiNNLL85J%2F82FKzVtA8QFbHGXtNOZ0fxoo2a1PXJ9tDbrwc9J%2BI7IuFSrHNNT3DC28TLcM7kBDr5mCl95lNKMDiM9hflCF7A6D%2BTSCOGKZJGV6uiFiyN1qDqLG95D4BgZchn6jC%2FSTq493ETqjromBYUfLMabD0HGtdrmMAtmjbyHHswplwmBUPw1heNZCPd%2BmVVqg7jpSio9GWs6B0MZWfI3ZTW6Ox%2BGZMPTiv5RdVDpvoNOLyQAarB0Y8sa6zhTa2mQSXFpwgLm1gfUEIY5DDrgAesHYt8DRXab%2B6Jitdbn6nKmVd0aayomDTm0TJmEG0upxcuSg0aIoYnDqauY33PPhb4L4lmOWUOu5ojBHRHAT%2BQynlt9sFSjqXcBoKmUzB2Hg6pEYRHM3Ah3XUOHDtwwxjITCL%2FKV3HFf8hFBgp%2F9rPtYVn5eziCkIoDBfyahUqpUQKQ%2FULKZvHhKIsaoVNSC0DKSKF5teOv1MIp60Te20EDcwJs6YurEXzkWFhFAAVH6%2Fb1bjDY0MQfhOTNeu9JFvHecXdkbqACrzi04714nKTZcu%2F1Zroiikf9HKJ4Ew7QmqrtNgrfLNyThON6hgpJWuz60TzSzaki7dwfYT72u7DjPel0%2B7Qsaz4VcKtVSPHZEoQVWzomMk6QlgiYkM08MUaVEdFNLBENrChUaKgvfG8G%2BhWggKGMGInp%2BuIp4OwbstyyOapAGGDhA%3D%3D' from origin 'http://localhost:4200' has been blocked by CORS policy: Request header field x-requested-with is not allowed by Access-Control-Allow-Headers in preflight response.
I have configured CORS in Startup.cs and tried several different approaches, but it still doesn’t work. I suspect the issue might be related to the middleware order or a missing Authorization header in the CORS configuration.
Backend (ASP.NET Core)
Here is the CORS and SignalR configuration in Startup.cs:
// ConfigureServices method
services.AddCors(
options => options.AddPolicy(
_defaultCorsPolicyName,
builder => builder
.WithOrigins(
// App:CorsOrigins in appsettings.json can contain more than one address separated by comma.
_appConfiguration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray()
)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
.SetIsOriginAllowed(s => true)
)
);
services.AddSignalR(options =>
{
options.EnableDetailedErrors = true;
});
// Configure method
app.UseAbp(options => { options.UseAbpRequestLocalization = false; }); // Initializes ABP framework.
app.UseCors(_defaultCorsPolicyName); // Enable CORS!
app.UseStaticFiles();
app.UseAuthentication();
app.UseAbpRequestLocalization();
app.UseSignalR(endpoints =>
{
endpoints.MapHub<AbpCommonHub>("/signalr", options => {
options.Transports = HttpTransportType.LongPolling | HttpTransportType.WebSockets;
});
endpoints.MapHub<TimekeepingHub>("/signalr-timekeepingHub", options => {
options.Transports = HttpTransportType.LongPolling | HttpTransportType.WebSockets;
});
});
Frontend (Angular)
Here is how I connect to the SignalR hub from Angular:
setupMyTimekeepingHub(callback : Function): void {
const self = this;
abp.signalr.startConnection(abp.appPath + 'signalr-timekeepingHub', function (connection) {
console.log(connection);
self.timekeepingHub = connection; // Save a reference to the hub
connection.on('connectedSuccess', function (prevSyncDays: string[]) {
self.listProcessingDays = prevSyncDays;
self.timekeepingProcess.next({event:"connectedSuccess",data: self.listProcessingDays});
});
// Other event handlers...
}).then(function (connection) {
abp.log.debug('Connected to timekeepingHub server!');
if(typeof callback === "function"){
callback();
}
}).catch((err) => {
console.log(err);
self.timekeepingProcess.next({event:"errorOccured",data: null});
});
}
initSignalR(callback): void {
const encryptedAuthToken = new UtilsService().getCookieValue(AppConsts.authorization.encrptedAuthTokenName);
abp.signalr = {
autoConnect: true,
connect: undefined,
hubs: undefined,
qs: AppConsts.authorization.encrptedAuthTokenName + '=' + encodeURIComponent(encryptedAuthToken),
remoteServiceBaseUrl: AppConsts.remoteServiceBaseUrl,
startConnection: undefined,
url: '/signalr'
};
jQuery.getScript(AppConsts.appBaseUrl + '/assets/abp/abp.signalr-client.js', () => {
this.setupMyTimekeepingHub(callback);
});
}
I have tried the following:
* Changing the middleware order (placing CORS before Authentication)
* Adding a custom middleware to handle preflight OPTIONS requests
* Adding the Authorization header to Access-Control-Allow-Headers
* Removing extra whitespaces in the URL
But it still doesn’t work. Has anyone experienced this issue before? What should I do to resolve this CORS error?
Additional information:
* ASP.NET Core version: 2.2
* SignalR version: 1.1.0
* Angular version: 8.2.14
* ABP Framework version: 4.3.0
x-requested-withis usually used by client-side frameworks to give the server an indication that this is a background request. What happens if you add that to Access-Control-Allow-Headers?