Cross domain requests not working in SignalR 2.0.0-rc1 - c#

I recently upgraded a project from SignalR 2.0.0-beta1 to 2.0.0-rc1. I understand that in RC1, configuration of support for cross domain requests changed. I've updated my project to use the new syntax however I'm now getting the following error when attempting to communicate with my hub:
XMLHttpRequest cannot load
=1377623738064">http://localhost:8080/negotiate?connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&clientProtocol=1.3&=1377623738064.
Origin http://localhost:7176 is not allowed by
Access-Control-Allow-Origin.
The client site is running at http://localhost:7176 and the hub is listening via a console application at http://localhost:8080. Am I missing something here? Cross domain requests were working before I upgraded to RC1.
CONSOLE APP ENTRY POINT
static void Main(string[] args)
{
var chatServer = new ChatServer();
string endpoint = "http://localhost:8080";
chatServer.Start(endpoint);
Console.WriteLine("Chat server listening at {0}...", endpoint);
Console.ReadLine();
}
CHATSERVER CLASS
public class ChatServer
{
public IDisposable Start(string url)
{
return WebApp.Start<Startup>(url);
}
}
STARTUP CONFIGURATION
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
map.RunSignalR(new HubConfiguration { EnableJSONP = true });
});
}
}

Something is wrong with your client configuration.
XMLHttpRequest cannot load =1377623738064">http://localhost:8080/negotiate?connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&clientProtocol=1.3&=1377623738064. Origin http://localhost:7176 is not allowed by Access-Control-Allow-Origin.
The negotiate request should be made to http://localhost:8080/signalr/negotiate?... not http://localhost:8080/negotiate?.... To fix this you can try the following before you call $.connection.hub.start:
$.connection.hub.url = http://localhost:8080/signalr;

Not sure if this question has been adequately answered, but I made the following changes to the sample provided by Microsoft:
public void Configuration(IAppBuilder app)
{
var config = new HubConfiguration();
config.EnableJSONP = true;
app.MapSignalR(config);
}
And I added the following to the JS sample:
$.connection.hub.start({ jsonp: true }).done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.send($('#displayname').val(), $('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
And now the Cross domain scripting is enabled. Hope this helps someone else, I was really puzzling with it for a while.

For Microsoft.Owin 2.x and above:
Add Microsoft.Owin.Cors package via NuGet by this command in Package Manager console:
PM> Install-Package Microsoft.Owin.Cors
and then using this package in Startup class file:
using Microsoft.Owin;
using Microsoft.Owin.Cors;
then change your source code like this:
// app.MapHubs(new HubConfiguration { EnableCrossDomain = true });
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();

I think the best way to handle Cross Domain is documented here
CrossDomain Signal R

Related

Ignore WCF certificate errors in ASP.NET Core 3.1

I have a solution like this :
-MySolution
|--MyWCFWrapper
|--MyaspnetcoreWebApp
|--ConsoleTestApp
MyWCFWrapper is a .NET Standard library consumes the WCF service added as a WCF reference using the Visual Studio import wizard.
The MyaspnetcoreWebApp application provides controllers for a front end to consume. In this controller, I am instantiating and making calls to MyWCFWrapper library.
The ConsoleTestApp is a console application that also makes calls to MyWCFWrapper library for testing.
I get an error:
System.ServiceModel.Security.SecurityNegotiationException: 'Could not establish trust relationship for the SSL/TLS secure channel with authority 'exampleabc.com'
when I make WCF service calls.
The reason for this error is my WCF service at exampleablc.com is a test server and has a self signed certificate (name different to the webservice) and is also expired.
Workaround that works in ConsoleTestApp :
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
(se, cert, chain, sslerror) =>
{
return true;
};
MyWCFWrapperClass api = new MyWCFWrapperClass(..);
api.SendNewInfo("NewInfo");
This is not recommended, but this is ok for me for now because it's a test server.
The same workaround does not work in the MyaspnetcoreWebApp controller. What I have tried to make it work :.
In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("SeitaCertificate").ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = (request, certificate, certificateChain, policy) =>
{
return true;
}
};
});
services.AddControllersWithViews();
----
}
Added the certificate to Trusted Root Certificate Authorities on my local PC. This fails probably because the certificate has expired.
The certificate error is raised in the WCF call library and I have not been able to find a way to ignore the cert error.
What I can do is at least have the certificate updated so that it is not expired. (I have started this process and it is likely to take some time)
I would like to learn a way how to capture and ignore these certificate errors selectively and appropriately for calling a WCF library in a asp .net core 3.1 web application. How can I do that?
You can refer to the following code to bypass certificate verification:
BasicHttpsBinding binding = new BasicHttpsBinding();
binding.Security.Mode = BasicHttpsSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
ChannelFactory<IService> factory = new ChannelFactory<IService>(binding, new EndpointAddress(uri));
factory.Credentials.ServiceCertificate.SslCertificateAuthentication = new System.ServiceModel.Security.X509ServiceCertificateAuthentication()
{
CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None,
RevocationMode = System.Security.Cryptography.X509Certificates.X509RevocationMode.NoCheck
};
You can resolve this issue by overriding the certificate validation method X509CertificateValidator
class Program
{
static void Main(string[] args)
{
var client = new YourServiceReference.Cient();
// Bypass certificate verification
client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication
{
CertificateValidationMode = X509CertificateValidationMode.Custom,
CustomCertificateValidator = new CustomCertificateValidator()
};
// --------
}
internal class CustomCertificateValidator : X509CertificateValidator
{
// Override certificate validation
public override void Validate(X509Certificate2 certificate) { }
}
}

How to integrate push notification in Angular 7 using signalR and API in C#?

I have a Angular 7 application in which I want to implement push notification using SignlaR (2.4.4 version) with API in C#. Can anyone suggest how to solve my problem?
I have tried everything that I can, but I am not able to establish a connection between Angular SignalR client and SignalR Hub created in C# API.
1) This is my Startup.cs
HubConfiguration cfg = new HubConfiguration();
app.MapSignalR<PersistentConnection>("/ClientHub");
app.MapSignalR(cfg);
2) This is my Hub
public class ClientHub : Hub
{
public Task SendNotificationCount(int chatUserCnt)
{
var context =
GlobalHost.ConnectionManager.GetHubContext<ClientHub>();
return context.Clients.All.SendAsync("Send", chatUserCnt);
}
public Task SendHelloToAll(string message)
{
var context = GlobalHost.ConnectionManager.GetHubContext<ClientHub>();
return context.Clients.All.SendMsgAsync("SendToAll", message);
}
}
3) This is Angular Code where I am trying to Connect to the Hub.
hubUrl = http://localhost:60067/ClientHub
connectToHub(hubUrl:string) {
this.connection = new HubConnectionBuilder().withUrl(hubUrl).build();
this.connection.start().then(() => {
console.log("connected to hub");
this.connection.on('SendToAll', (message) => {
alert(message);
});
}).catch((error) => {
console.log(error);
});
}
I am expecting, whenever there is a change in the database for notification to get alerts through the Hub in realtime. Also I want to send database changes using signalR hub to Angular. But I am not able to connect with hub from Angular.
This is where I would start looking:
https://learn.microsoft.com/en-us/aspnet/core/tutorials/signalr-typescript-webpack?view=aspnetcore-2.2&tabs=visual-studio

No client method with the name 'x' found with SignalR

I have an ASP.NET Boilerplate v3.6.2 project (.NET Core / Angular) in which I need to call a client function from a backend method, so I'm using the ASP.NET Core SignalR implementation.
I followed the official documentation, so:
In the backend
In my module, I added the dependency to AbpAspNetCoreSignalRModule:
[DependsOn(typeof(AbpAspNetCoreSignalRModule))]
public class MyModule : AbpModule
And added this NuGet package to the module's project.
Then I extended the AbpCommonHub class to take advantage of the built-in implementation of the SignalR Hub, adding a SendMessage() method used to send the message:
public interface IMySignalRHub : ITransientDependency
{
Task SendMessage(string message);
}
public class MySignalRHub: AbpCommonHub, IMySignalRHub {
protected IHubContext<MySignalRHub> _context;
protected IOnlineClientManager onlineClientManager;
protected IClientInfoProvider clientInfoProvider;
public MySignalRHub(
IHubContext<MySignalRHub> context,
IOnlineClientManager onlineClientManager,
IClientInfoProvider clientInfoProvider)
: base(onlineClientManager, clientInfoProvider) {
AbpSession = NullAbpSession.Instance;
_context = context;
}
public async Task SendMessage(string message) {
await _context.Clients.All.SendAsync("getMessage", string.Format("User {0}: {1}", AbpSession.UserId, message));
}
}
I changed the mapping of the '/signalr' url to MySignalRHub:
public class Startup {
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) {
[...]
# if FEATURE_SIGNALR
app.UseAppBuilder(ConfigureOwinServices);
# elif FEATURE_SIGNALR_ASPNETCORE
app.UseSignalR(routes => {
routes.MapHub<MySignalRHub>("/signalr");
});
# endif
[...]
}
}
Then I use the hub to send a message in a service implementation:
public class MyAppService: AsyncCrudAppService<MyEntity, MyDto, int, PagedAndSortedResultRequestDto, MyCreateDto, MyDto>, IMyAppService {
private readonly IMySignalRHub _hub;
public MyAppService(IRepository<MyEntity> repository, IMySignalRHub hub) : base(repository) {
_hub = hub;
}
public override Task<MyDto> Create(MyCreateDto input) {
_hub.SendMessage("test string").Wait();
[...]
}
}
In the client
All the configurations and inclusions are already in place from the original template. When I open the Angular app I can see this console logs:
DEBUG: Connected to SignalR server!
DEBUG: Registered to the SignalR server!
When I try to call the backend service which sends the message to the client, I get this warning in console:
Warning: No client method with the name 'getMessage' found.
I tried many solutions found in the official documentation and on the Internet but none of them worked. I'm not able to define the 'getMessage' handler in the client code.
Some non-working examples I tried:
Implementation 1:
// This point is reached
abp.event.on('getMessage', userNotification => {
debugger; // Never reaches this point
});
Implementation 2:
// This point is reached
abp.event.on('abp.notifications.received', userNotification => {
debugger; // Never reaches this point
});
Implementation 3:
// This is taken from the official documentation and triggers the error:
// ERROR TypeError: abp.signalr.startConnection is not a function
abp.signalr.startConnection('/signalr', function (connection) {
connection.on('getMessage', function (message) {
console.log('received message: ' + message);
});
});
Have you ever found yourself in this situation? Do you have a simple working example of the handler definition in the Angular client?
UPDATE
I tried this alternative solution, changing the implementation of the SignalRAspNetCoreHelper class (a shared class which is shipped with the base template):
export class SignalRAspNetCoreHelper {
static initSignalR(): void {
var 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', () => {
// ADDED THIS
abp.signalr.startConnection(abp.signalr.url, function (connection) {
connection.on('getMessage', function (message) { // Register for incoming messages
console.log('received message: ' + message);
});
});
});
}
}
Now in the console I can see both the messages:
Warning: No client method with the name 'getMessage' found.
SignalRAspNetCoreHelper.ts:22 received message: User 2: asd
So it is working, but not fully. Somewhere the 'getMessage' handler is not visible.
What is the proper way to implement the messages handler in Angular with ASP.NET Boilerplate?
You should use Clients.Others.SendAsync or Client.AllExcept.SendAsync instead of Clients.All.SendAsync.
Clients.All.SendAsync is designed for scenarios where the client wants to send AND receive messages (like a chat room). Hence all connected clients are supposed to implement connection.on('getMessage', in order to receive the notifications. If they don't, they raise the warning No client method with the name 'x' found when receiving back the notification they just pushed.
In your scenario, I understand you have one kind of client pushing notifications and another kind receiving them (like a GPS device sending positions to a tracking application). In that scenario, using Clients.Others.SendAsync or Client.AllExcept.SendAsync will ensure pushing clients will not be broadcasted back the notification they (or their kind) just pushed.
Set autoConnect: false to start your own connection:
abp.signalr = {
autoConnect: false,
// ...
};
Better yet...
Don't extend AbpCommonHub. You'll find that real-time notification stops working and you need to replace IRealTimeNotifier because SignalRRealTimeNotifier uses AbpCommonHub.
Do you have a simple working example of the handler definition in the Angular client?
What is the proper way to implement the messages handler in Angular with ASPNet Boilerplate?
Follow the documentation and create a separate hub.
I was facing the same error in my Angular application where I use the package "#aspnet/signalr": "1.1.4".
The cause of this issue was I wasn't calling the subscribe method after join the channel.
public async getWorkSheetById(worksheetId: string): Promise < Worksheet > {
const worksheet: Worksheet = await this._worksheetService.getWorksheet(worksheetId);
this.displayWorksheet(worksheet);
await this._worksheetEventsService.joinWorksheetChannel(this._loadedWorksheet.id);
return worksheet;
}
So, to fix this I called the subscribe method after join await this.subscribeToSendMethod(this._loadedWorksheet))
public subscribeToSendMethod(loadedWorksheet: Worksheet): Worksheet {
let newWorksheet: Worksheet;
this._hubConnection.on('Send', (groupId: string, payloadType: string, payload: string, senderUserId: string)=> {
newWorksheet=this._worksheetHandler.handlePayload(payloadType, payload, loadedWorksheet);
this.displayWorksheet(newWorksheet);
}
);
return newWorksheet;
}

Returning an HttpResponseMessage contents from a pass through API in ASP.NET Core [duplicate]

I'm developing an ASP.Net Core web application where I need to create a kind of "authentication proxy" to another (external) web service.
What I mean by authentication proxy is that I will receive requests through a specific path of my web app and will have to check the headers of those requests for an authentication token that I'll have issued earlier, and then redirect all the requests with the same request string / content to an external web API which my app will authenticate with through HTTP Basic auth.
Here's the whole process in pseudo-code
Client requests a token by making a POST to a unique URL that I sent him earlier
My app sends him a unique token in response to this POST
Client makes a GET request to a specific URL of my app, say /extapi and adds the auth-token in the HTTP header
My app gets the request, checks that the auth-token is present and valid
My app does the same request to the external web API and authenticates the request using BASIC authentication
My app receives the result from the request and sends it back to the client
Here's what I have for now. It seems to be working fine, but I'm wondering if it's really the way this should be done or if there isn't a more elegant or better solution to this? Could that solution create issues in the long run for scaling the application?
[HttpGet]
public async Task GetStatement()
{
//TODO check for token presence and reject if issue
var queryString = Request.QueryString;
var response = await _httpClient.GetAsync(queryString.Value);
var content = await response.Content.ReadAsStringAsync();
Response.StatusCode = (int)response.StatusCode;
Response.ContentType = response.Content.Headers.ContentType.ToString();
Response.ContentLength = response.Content.Headers.ContentLength;
await Response.WriteAsync(content);
}
[HttpPost]
public async Task PostStatement()
{
using (var streamContent = new StreamContent(Request.Body))
{
//TODO check for token presence and reject if issue
var response = await _httpClient.PostAsync(string.Empty, streamContent);
var content = await response.Content.ReadAsStringAsync();
Response.StatusCode = (int)response.StatusCode;
Response.ContentType = response.Content.Headers.ContentType?.ToString();
Response.ContentLength = response.Content.Headers.ContentLength;
await Response.WriteAsync(content);
}
}
_httpClient being a HttpClient class instantiated somewhere else and being a singleton and with a BaseAddressof http://someexternalapp.com/api/
Also, is there a simpler approach for the token creation / token check than doing it manually?
If anyone is interested, I took the Microsoft.AspNetCore.Proxy code and made it a little better with middleware.
Check it out here: https://github.com/twitchax/AspNetCore.Proxy. NuGet here: https://www.nuget.org/packages/AspNetCore.Proxy/. Microsoft archived the other one mentioned in this post, and I plan on responding to any issues on this project.
Basically, it makes reverse proxying another web server a lot easier by allowing you to use attributes on methods that take a route with args and compute the proxied address.
[ProxyRoute("api/searchgoogle/{query}")]
public static Task<string> SearchGoogleProxy(string query)
{
// Get the proxied address.
return Task.FromResult($"https://www.google.com/search?q={query}");
}
I ended up implementing a proxy middleware inspired by a project in Asp.Net's GitHub.
It basically implements a middleware that reads the request received, creates a copy from it and sends it back to a configured service, reads the response from the service and sends it back to the caller.
This post talks about writing a simple HTTP proxy logic in C# or ASP.NET Core. And allowing your project to proxy the request to any other URL. It is not about deploying a proxy server for your ASP.NET Core project.
Add the following code anywhere of your project.
public static HttpRequestMessage CreateProxyHttpRequest(this HttpContext context, Uri uri)
{
var request = context.Request;
var requestMessage = new HttpRequestMessage();
var requestMethod = request.Method;
if (!HttpMethods.IsGet(requestMethod) &&
!HttpMethods.IsHead(requestMethod) &&
!HttpMethods.IsDelete(requestMethod) &&
!HttpMethods.IsTrace(requestMethod))
{
var streamContent = new StreamContent(request.Body);
requestMessage.Content = streamContent;
}
// Copy the request headers
foreach (var header in request.Headers)
{
if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()) && requestMessage.Content != null)
{
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
}
requestMessage.Headers.Host = uri.Authority;
requestMessage.RequestUri = uri;
requestMessage.Method = new HttpMethod(request.Method);
return requestMessage;
}
This method covert user sends HttpContext.Request to a reusable HttpRequestMessage. So you can send this message to the target server.
After your target server response, you need to copy the responded HttpResponseMessage to the HttpContext.Response so the user's browser just gets it.
public static async Task CopyProxyHttpResponse(this HttpContext context, HttpResponseMessage responseMessage)
{
if (responseMessage == null)
{
throw new ArgumentNullException(nameof(responseMessage));
}
var response = context.Response;
response.StatusCode = (int)responseMessage.StatusCode;
foreach (var header in responseMessage.Headers)
{
response.Headers[header.Key] = header.Value.ToArray();
}
foreach (var header in responseMessage.Content.Headers)
{
response.Headers[header.Key] = header.Value.ToArray();
}
// SendAsync removes chunking from the response. This removes the header so it doesn't expect a chunked response.
response.Headers.Remove("transfer-encoding");
using (var responseStream = await responseMessage.Content.ReadAsStreamAsync())
{
await responseStream.CopyToAsync(response.Body, _streamCopyBufferSize, context.RequestAborted);
}
}
And now the preparation is complete. Back to our controller:
private readonly HttpClient _client;
public YourController()
{
_client = new HttpClient(new HttpClientHandler()
{
AllowAutoRedirect = false
});
}
public async Task<IActionResult> Rewrite()
{
var request = HttpContext.CreateProxyHttpRequest(new Uri("https://www.google.com"));
var response = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, HttpContext.RequestAborted);
await HttpContext.CopyProxyHttpResponse(response);
return new EmptyResult();
}
And try to access it. It will be proxied to google.com
A nice reverse proxy middleware implementation can also be found here: https://auth0.com/blog/building-a-reverse-proxy-in-dot-net-core/
Note that I replaced this line here
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
with
requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToString());
Original headers (e.g. like an authorization header with a bearer token) would not be added without my modification in my case.
I had luck using twitchax's AspNetCore.Proxy NuGet package, but could not get it to work using the ProxyRoute method shown in twitchax's answer. (Could have easily been a mistake on my end.)
Instead I defined the mapping in Statup.cs Configure() method similar to the code below.
app.UseProxy("api/someexternalapp-proxy/{arg1}", async (args) =>
{
string url = "https://someexternalapp.com/" + args["arg1"];
return await Task.FromResult<string>(url);
});
Piggy-backing on James Lawruk's answer https://stackoverflow.com/a/54149906/6596451 to get the twitchax Proxy attribute to work, I was also getting a 404 error until I specified the full route in the ProxyRoute attribute. I had my static route in a separate controller and the relative path from Controller's route was not working.
This worked:
public class ProxyController : Controller
{
[ProxyRoute("api/Proxy/{name}")]
public static Task<string> Get(string name)
{
return Task.FromResult($"http://www.google.com/");
}
}
This does not:
[Route("api/[controller]")]
public class ProxyController : Controller
{
[ProxyRoute("{name}")]
public static Task<string> Get(string name)
{
return Task.FromResult($"http://www.google.com/");
}
}
Hope this helps someone!
Twitchax's answer seems to be the best solution at the moment. In researching this, I found that Microsoft is developing a more robust solution that fits the exact problem the OP was trying to solve.
Repo: https://github.com/microsoft/reverse-proxy
Article for Preview 1 (they actually just released prev 2): https://devblogs.microsoft.com/dotnet/introducing-yarp-preview-1/
From the Article...
YARP is a project to create a reverse proxy server. It started when we noticed a pattern of questions from internal teams at Microsoft who were either building a reverse proxy for their service or had been asking about APIs and technology for building one, so we decided to get them all together to work on a common solution, which has become YARP.
YARP is a reverse proxy toolkit for building fast proxy servers in .NET using the infrastructure from ASP.NET and .NET. The key differentiator for YARP is that it is being designed to be easily customized and tweaked to match the specific needs of each deployment scenario. YARP plugs into the ASP.NET pipeline for handling incoming requests, and then has its own sub-pipeline for performing the steps to proxy the requests to backend servers. Customers can add additional modules, or replace stock modules as needed.
...
YARP works with either .NET Core 3.1 or .NET 5 preview 4 (or later). Download the preview 4 (or greater) of .NET 5 SDK from https://dotnet.microsoft.com/download/dotnet/5.0
More specifically, one of their sample apps implements authentication (as for the OP's original intent)
https://github.com/microsoft/reverse-proxy/blob/master/samples/ReverseProxy.Auth.Sample/Startup.cs
Here is a basic implementation of Proxy library for ASP.NET Core:
This does not implement the authorization but could be useful to someone looking for a simple reverse proxy with ASP.NET Core. We only use this for development stages.
using System;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
namespace Sample.Proxy
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(options =>
{
options.AddDebug();
options.AddConsole(console =>
{
console.IncludeScopes = true;
});
});
services.AddProxy(options =>
{
options.MessageHandler = new HttpClientHandler
{
AllowAutoRedirect = false,
UseCookies = true
};
options.PrepareRequest = (originalRequest, message) =>
{
var host = GetHeaderValue(originalRequest, "X-Forwarded-Host") ?? originalRequest.Host.Host;
var port = GetHeaderValue(originalRequest, "X-Forwarded-Port") ?? originalRequest.Host.Port.Value.ToString(CultureInfo.InvariantCulture);
var prefix = GetHeaderValue(originalRequest, "X-Forwarded-Prefix") ?? originalRequest.PathBase;
message.Headers.Add("X-Forwarded-Host", host);
if (!string.IsNullOrWhiteSpace(port)) message.Headers.Add("X-Forwarded-Port", port);
if (!string.IsNullOrWhiteSpace(prefix)) message.Headers.Add("X-Forwarded-Prefix", prefix);
return Task.FromResult(0);
};
});
}
private static string GetHeaderValue(HttpRequest request, string headerName)
{
return request.Headers.TryGetValue(headerName, out StringValues list) ? list.FirstOrDefault() : null;
}
public void Configure(IApplicationBuilder app)
{
app.UseWebSockets()
.Map("/api", api => api.RunProxy(new Uri("http://localhost:8833")))
.Map("/image", api => api.RunProxy(new Uri("http://localhost:8844")))
.Map("/admin", api => api.RunProxy(new Uri("http://localhost:8822")))
.RunProxy(new Uri("http://localhost:8811"));
}
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}

Signal R Server Client "Access-Control-Allow-Origin" missing error

I'm trying out the Signal R and built a server dll (windows service library/c#) that runs as a Windows Services. I have build also a client application (asp.net web application) to communicate with the server.
But i'm getting always the error(Firefox) "Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/signalr/negotiate?clientProtocol=1.5&connectionData=%5B%5D&_=1482829095207. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing)."
Chrome error "
Failed to load resource: the server responded with a status of 400 (Bad Request)"
XMLHttpRequest cannot load http://localhost:8080/signalr/negotiate?clientProtocol=1.5&connectionData=%5B%5D&_=1482830200155. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:50259' is therefore not allowed access. The response had HTTP status code 400.
Note: Edge and also IE gives me errors
I have read almost every post about this subject on Stackoverflow, but non of these solutions seems to work.
The code i'm using for the server side:
namespace SignalRService
{
public class StartupConfiguration
{
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors = true,
EnableJSONP = true,
};
map.RunSignalR(hubConfiguration);
});
}
}
}
Services.cs
public void StartService()
{
LogMessage("SignalRService started", true);
Running = true;
WebApp.Start<StartupConfiguration>(ConfigurationManager.AppSettings["SignalRServerUrl"]);
}
EnvironmentSettings.config:
<add key="SignalRServerUrl" value="http://localhost:8080"/>
Hubs.cs
namespace SignalRService.Hubs
{
[HubName("TestHub")]
public class TestHub: Hub
{
public static Dictionary<string, List<HubClient>> clients = new Dictionary<string, List<HubClient>>();
[HubMethodName("Subscribe")]
public async Task Subscribe(string Id)
{...... }}
ClientSide (Javascript/Jquery)
var signalrHubConnection;
var signalrHubConnectionProxy;
var signalRServerUrl = "http://localhost:8080";
var currentTimeout;
var count = 0;
var startSignalRConnection = function () {
console.log("Start");
signalrHubConnection = $.hubConnection(signalRServerUrl);
console.log("Running");
signalrHubConnection.logging = true;
signalrHubConnectionProxy = signalrHubConnection.createHubProxy('TestHub');
console.log("--Subscribe starting");
signalrHubConnection.start()
.done(function () {
signalrHubConnectionProxy.invoke('Subscribe', Id.toString());
console.log("Subscribe ending");
})
.fail(function (test) {
if (count < 5) {
console.log(test.toString());
clearTimeout(currentTimeout);
currentTimeout = setTimeout(function () {
count++;
startSignalRConnection();
}, 300000); // retry after 5 minutes
}
}
);
signalrHubConnectionProxy.on('IncomingMessage',
function (message) {
console.log("Message = " + message.toString());
}
);
};
Test.aspx
<script src="https://code.jquery.com/jquery-3.1.1.min.js" type="text/javascript"></script>
<script src="http://ajax.aspnetcdn.com/ajax/signalr/jquery.signalr-2.2.1.min.js"></script>
Did I something wrong?
The error implied that the SignalR url is different from the requesting url (origin). So, SignalR is on localhost, but your main website (the site that holds the client side example) obviously is accessed using "localhost".
Maybe you're accessing it using an IP (eg http://127.0.0.1/) or your PC name (eg http://badassPC/), whereas they must match under the default SignalR setting. I am pretty certain it doesn't matter if the port is different, and also doesn't matter if they are on the same domain (eg www.mysite.com and signalr.mysite.com)
Note there is a workaround that I wouldn't recommend unless you really really know what you're doing as there is a quite serious security risk otherwise: https://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-javascript-client#crossdomain

Categories