SignalR 404 error when trying to negotiate - c#

So I am very new to SignalR, in fact I've only been using it for a couple of days now. Anyway, I am getting the error below when my application first starts up:
The code for the application in question is located in two projects, a Web API and a Single Page Application (SPA). The first one has my backend code (C#) and the second one my client-side code (AngularJS). I think the problem might be due to the fact that the projects in question run on different ports. The Web API, where my SignalR hub lives, is on port 60161 and the SPA is on 60813. My hub is declared like so:
public class ReportHub : Hub
{
public void SendReportProgress(IList<ReportProgress> reportProgress)
{
this.Clients.All.broadcastReportProgress(reportProgress);
}
public override Task OnConnected()
{
this.Clients.All.newConnection();
return base.OnConnected();
}
}
and then in my Startup.cs file for my Web API I initialize SignalR like this:
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.Services.Replace(typeof(IHttpControllerActivator), new NinjectFactory());
config.MessageHandlers.Add(new MessageHandler());
//set up OAuth and Cors
this.ConfigureOAuth(app);
config.EnableCors();
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
// Setting up SignalR
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
map.RunSignalR(new HubConfiguration { EnableJSONP = true });
});
//set up json formatters
FormatterConfig.RegisterFormatters(config.Formatters);
WebApiConfig.Register(config);
app.UseWebApi(config);
}
For my client-side code I use an Angular SignalR API called angular-signalr-hub (Angular-signalr-hub). The client-side follows:
angular
.module("mainApp")
.factory("reportHubService", ["$rootScope", "Hub", reportHubService]);
/// The factory function
function reportHubService($rootScope, Hub) {
var vm = this;
vm.reportName = "None";
// Setting up the SignalR hub
var hub = new Hub("reportHub", {
listeners: {
'newConnection': function(id) {
vm.reportName = "SignalR connected!";
$rootScope.$apply();
},
'broadcastReportProgress': function (reportProgress) {
vm.reportName = reportProgress.reportName;
$rootScope.$apply();
}
},
errorHandler: function(error) {
},
hubDisconnected: function () {
if (hub.connection.lastError) {
hub.connection.start();
}
},
transport: 'webSockets',
logging: true
//rootPath: 'http://localhost:60161/signalr'
});
I did some googling yesterday and one of the suggestions I came upon was to set the SignalR URL to the one of my Web API, which I did (the commented out line above). When I uncomment the line in question, that does seem to do something because if I now go to http://localhost:60161/signalr/hubs in my browser, it does show me the dynamically generated proxy file:
and when I run my application I no longer get the error above, but now it doesn't seem to connect. It gets to the negotiate line and it stops there:
I think it should look like this (this is from a SignalR tutorial I found):
In addition, none of my listeners (declared in my Angular code above) get called, so something is still now working quite right. There should be more lines in the log to the effect that connection was successfully established, etc. What could be the problem here?
UPDATE: upon further debugging i found out the problem is most likely being caused by the ProtocolVersion property being different between the client and the result here:
Because of that it seems it just exists and fails to establish connection.

I figured out what the problem was. My SignalR dependencies were out of date and because of that my client and server versions differed. All I had to do was update (via NuGet Package Manager) all SignalR dependencies to the latest version and now it works.
As a side note, SignalR was not very good at telling me what was wrong. In fact, no error message was displayed, unless of course there was some additional logging somewhere that had to be found or turned on, in addition to the logging I already had (turned on). Either way, it's either not logging certain errors or it makes it difficult to figure out how to turn on all logging. I had to go and debug the JQuery SignalR api to figure out what the problem was, which was a time consuming endeavour.

Related

Changing the base URL of a Blazor WASM app

I have a Blazor WebAssembly app which is server hosted.
When I deploy it on an internal work network, the address is just the IP address, e.g. http://192.168.1.23. That works, but it's not so user friendly. I want it to be http://192.168.1.23/myapp.
I've done some reading on this here (Microsoft), and also here (Stackoverflow). They suggest a couple of things:
In Client side code, index.html, change <base href="/" /> to <base href="/myapp/" />.
In Server side code, Startup.cs, add app.UsePathBase("player"); early on in the Configure() method.
Microsoft also says how to edit the launch settings so that debug still works normally, and they include adding the following to the launchSettings.json file:
"commandLineArgs": "--pathbase=/myapp",
"launchUrl": "myapp"
They don't say if this is in the Client or Server launchSettings.json file, and I can't get it to work anyway. Even if I add "launchUrl": "https://www.google.com" everywhere, nothing different happens when I run it.
Ignoring number 3 above though, the app will start, and I can get to the correct place by manually adding /myapp to the address. So far so good.
I am using cookie authentication though, and when I try to retrieve the user details from the user details from the HttpContext using this method on a hub:
public static User GetAuthenticatedUser(this HttpContext context)
{
var idClaim = context.User.Claims.FirstOrDefault(c => c.Type.Equals(ClaimTypes.NameIdentifier));
var nameClaim = context.User.Claims.FirstOrDefault(c => c.Type.Equals(ClaimTypes.Name));
return new User
{
Id = int.Parse(idClaim.Value),
Name = nameClaim.Value
};
}
It fails, with each claim being null. It did not before I set the base path. How can I overcome this issue?
Note: It works on a regular controller, it's specifically a hub it is failing on, so it's as if the hub is not receiving the same HttpContext as before. The hub was also setup in Startup.cs, with the following in the Configure() method:
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<MyHub>("/myhub1");
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
});
Any advice is much appreciated.

How to configure web api to respond multiple client at the same time?

I'm setting up a new server for web api, but when I try to make a post request from two individual client, server only responds to the first one. From second one, I always get 500 Internal Server Error.
I tried to make all methods in the server as async but same error has occured.
I call the web service as below:
using(var client = new HttpClient())
{
client.BaseAddress = new Uri("http://serverdomain/ApiName/")
var response = client.PostAsync("controller/method",
keyvaluepairContent);
result = response.Result.Content.ReadAsAsync<List<string>>().Result;
}
And the relevant service code is below:
[Route("controller/method")]
[AcceptVerbs("POST")]
public List<string> Foo([FromBody] someparams)
{
//some logic
}
I wrote the config file as :
config.Routes.MapHttpRoute(
name : "DefaultApi",
routeTemplate : "{controller}/{action}/{id}",
defaults : new { id = RouteParameter.Optinal }
);
For one client at the same time, the server is working very well. I get what I need. However, when two clients make requests even for different methods, the delayed one always gets 500 Internal Error. Debugger says that this code below cannot parse the result, that is beacuse the response is not a string list but the error above.
result = response.Result.Content.ReadAsAsync<List<string>>().Result;
I think my code is fine, but I need to configure my web api. I did searched about it but no result.
As mentioned in a comment first, you are using HttpClient wrong please look at this.
As for your WebAPI to be able to respond to multiple calls asynchronously you need to wrap your code in an appropriate method signature like so:
[Route("controller/method")]
[AcceptVerbs("POST")]
public async Task<IHttpActionResult> Foo([FromBody] someparams)
{
//some logic
}
Some valuable info from another stackoverflow answer here
And another simpler answer here
Like you mentioned in your question you tried making all the methods async but the issue is that you need to use Task

Enable CORS from a specific origin on Topshelf hosted service

I'm sure this is a duplicate but none of the answers I've found appears to be working.
I have a web.api hosted as a windows service and I need to allow CORS to two specific origins.
I have this in my startup.cs
config.EnableCors(
new EnableCorsAttribute("http://customer.mydomain.com,
http://admin.mydomain.com", "*", "*")
);
var policy = new CorsPolicy()
{
AllowAnyHeader = true,
AllowAnyMethod = true,
SupportsCredentials = true
};
policy.Origins.Add("*http://customer.mydomain.com");
policy.Origins.Add("*http://admin.mydomain.com");
app.UseCors(new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = context => Task.FromResult(policy)
}
});
So my understanding here is that I'll only allow access to my service from admin and customer subdomains?
and then on top of my controller I have
[EnableCors("*.mydomain.com","*","*")]
public class MasterDataController
everything builds and is happy but when I run my service and call a method in the controller directly from the browser URL it responds fine, shouldn't it be blocking me as I'm not calling it from mydomain.com ?
I've seen answers saying I need to send my origin in my call but surely that's not the answer as I specifically want to stop calls unless they are from my allowed origins? I feel like I'm going in circles here
[Update]
I've also added
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*.bizcash.co.za" />
</customHeaders>
to my IIS proxy and if I check the response headers when I call the method directly I see the following
*.redacted obviously being my domain. However, I can still call it directly from my browser and it shouldn't allow that should it? It should only allow the calls if made from my domain?
okay, so two problems with the question.
adding specific CORS origins doesn't stop you calling the service directly in the browser, it stops other web applications calling it. So I was testing it wrong.
So you need to make your changes, deploy it to your server and then test it via your normal applications.
to make it work this is what I have in the end
var policy = new CorsPolicy()
{
AllowAnyHeader = true,
AllowAnyMethod = true
};
policy.Origins.Add("https://customer.mydomain.com");
policy.Origins.Add("https://admin.mydomain.com");
app.UseCors(new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = context => Task.FromResult(policy)
}
});
That's it, just that in the startup.cs and it works. Hopefully I save someone else two wasted days of life

signalR change default site in startup

I have an application where i have 2 instances of it running on the same IIS server.
/Site1
/Site2
same source code in both, just different settings. I have signalR code on both sites, but i would like Site2 to use the signalR server from Site1.
I have this working so that if changes are made on Site1, Site2 knows to refresh, and that works fine. THE PROBLEM is that if server changes (not javascript) are made on Site2, Site1 doesn't get them. Below is my code:
Startup.cs
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
#if DEBUG
EnableDetailedErrors = true
#endif
};
map.RunSignalR(hubConfiguration);
});
javascript code:
$.connection.hub.url = "http://servername/Site1/signalr";
...$.connection.start etc
All of that seems to work fine, but what i can't find anywhere is how to push settings in the C# code from Site2 to Site1.
random c# code:
var hub = GlobalHost.ConnectionManager.GetHubContext<myHub>();
this globalhost.connectionmanager.gethubcontext is always pointing to itself. How can i get Site2 to point to Site1 in the last section? I've tried looking into editing Startup.cs to use MapSignalR(..path,...), but the path doesn't take absolute URLs, it has to start with '/', so it won't go up a level.
Any ideas or am i missing something basic?
Apparently what i'm looking for is a backplane. https://learn.microsoft.com/en-us/aspnet/signalr/overview/performance/scaleout-in-signalr . super easy to set up, and fixed my problem immediately (without any of the javascript changes above needed).

Path not found error

I installed SignalR 2.0-rc1, and:
1: Created a hub:
public class Socials : Hub
{
public void PublicChat(string message)
{
Clients.All.PublicChat(new { message });
}
}
2: Created a startup class:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
3: Registered it in web.config:
<add key="owin:AppStartup" value="Scyk.Startup, Scyk"/> //Scyk is my main namespace, also a project name, I placed Startup class in there.
Now, https://myhost.com/signalr/hubs is generating javascript file properly, but when I open developer console in my browser, I see that it has not connected, but:
There is an asp error saying that path /signalr/connect was not found (why is it trying to access /signalr/connect? Is that normal? If so, then this must be purely routing problem, how do I solve it?)
In my console, I see that there is a EventSource's response has a MIME type ("text/html") that is not "text/event-stream". Aborting the connection. error. I am not sure if this is related, but it started to show up today, wasn't there before.
What am I doing wrong?
Any path beginning with /signalr should be routed through OWIN so signalr can handle the request.
It is normal for the client to try to access /signalr/connect after accessing /signalr/negotiate. /signalr/connect is the endpoint where SignalR establishes its WebSockets/Server-Sent Events/Forever Frame/Long Polling connections.

Categories

Resources