Signalr hubs 404 not found - c#

I'm trying to integrate Signalr into my web form project.
First, added the references using nugget
Microsoft.AspNet.SignalR.Client;
Microsoft.AspNet.SignalR.Core;
Microsoft.AspNet.SignalR.System.Web;
Microsoft.Owin;
Microsoft.Owin.Host.SystemWeb;
Microsoft.Owin.Security;
Owin
My startup class:
[assembly: OwinStartup(typeof(ns.App_Code.Startup))]
namespace ns
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
My hub:
public class PositionHub : Hub
{
static ConcurrentDictionary<int, List<string>> connectedUsers = new ConcurrentDictionary<int, List<string>>();
public override Task OnConnected()
{
return base.OnConnected();
}
}
Finally, the client-side:
<script type="text/javascript" src="Scripts/jquery-2.2.4.min.js"></script>
<script src="Scripts/jquery.signalR-2.3.0.min.js"></script>
<script src="<%= ResolveUrl("~/signalr/hubs") %>" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
var logger = $.connection.positionHub;
logger.start();
});
</script>
Also, added to web.config the following:
<add key="owin:AutomaticAppStartup" value="true" />
and
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true" />
When I run the app it gives the 404 error:
GET http://localhost:41363/signalr/hubs 404 (Not Found)
This is not my first time using signalr, but it is the first time I have this issue, and been struggling for hours to solve it, with no luck...

I notice you don't have the following NuGet packages installed which are required when integrating SignalR with Javascript.
Microsoft.AspNet.SignalR
This package pulls in the server components and JavaScript client required to use SignalR in an ASP.NET application.
Microsoft.AspNet.SignalR.JS
Script client for ASP.NET SignalR.
(This one is included automatically by Microsoft.AspNet.SignalR above.)
EDIT
Via the comments below we found out that OP's web.configfile contained some url rewrite rules which intercepted the /signalr/hubs url.
Adding a rule to prevent this url from being rewritten solved the problem.
<rule name="signalR" enabled="true" stopProcessing="true">
<match url="^signalr.*" /> <action type="None" />
</rule>

fix startup class namespace
namespace ns
to be
namespace TeltonikaSiteWebApplication.App_Code

The rewrite rule was conflicting it on web config was conflicting with signalr. I needed to add a new one to make it work. . Thanks to #pfx on the guidance.

Related

.NET Core 2.0 MVC odd 404 on controller methods only when deployed to IIS

My home controller sucessfully return index.html from
http://localhost/
and
http://localhost/controller/
but if i try to hit
http://localhost/controller/method/
I get a 404 even though that method works fine in IIS express.
Couldn't find anything online with someone having a similar issue where only the methods on a controller didn't work on one particular deployment but the controller itself is fine.
Things I've Tried that were common among a lot of .Net Core 2.0 issues with IIS Deployments:
Make sure windows authentication is on in project settings and in IIS (I've toggled it on and off to no avail on both I don't have user auth on my web app so I don't think this matters for me).
Switched my application pool to use No managed code for CLR version
Change application pool ID to be LocalSystem
Change permissions on my publish output folder to include %hostname%\IIS_IUSRS
Pretty sure I've also tried a lot of other basic troubleshooting that sometimes fixes issues. I.E. removing and readding app. Turning things on and off again to no avail.
Any suggestions how to troubleshoot this would be very welcome.
I also want to note it was working yesterday and can't remember changing anything other than the publishing output to use Debug instead of Release which of course by now I've changed back to Release but still no luck.
Here is some code
public class MyController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public void Store([FromBody]MyObject obj)
{
Console.WriteLine(Request.Body);
//Some code
}
[HttpGet]
public void Check(string objectUID, string idfv)
{
Console.WriteLine($"ObjectUID: {objectUID}");
Console.WriteLine($"IDFV: {idfv}");
//some other code
}
[HttpGet]
public MyObject Retrieve(string objectUID)
{
Console.Writeline($"ObjectUID: {objectUID}");
//Some Code
}
}
This is my routing.
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=MyController}/{action=Index}/{id?}");
});
If you‘re not sure why it doesn’t work, try Attribute-Routing, explained in the Docs
Then you could try it this way:
[Route("[controller]/[action]")]
public class MyController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public void Store([FromBody]MyObject obj)
{
Console.WriteLine(Request.Body);
//Some code
}
// If you have a parameter from the uri or query-string, you can add it to the Template this way
[HttpGet("{objectUID} ")]
public void Check(string objectUID, string idfv)
{
Console.WriteLine($"ObjectUID: {objectUID}");
Console.WriteLine($"IDFV: {idfv}");
//some other code
}
// Or optional parameter like this
[HttpGet ("{objectUID?} ")]
public MyObject Retrieve(string objectUID)
{
Console.Writeline($"ObjectUID: {objectUID}");
//Some Code
}
}
Since you are getting 404 error, I suspect the aspnet core handler is missing from your website. Assuming that you have .NET Core Hosting Bundle installed,
ensure you have the following handler added in the web.config file. If web.config file is missing add a new web.config. Also, add a logs folder at the website root folder for logging.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- To customize the asp.net core module uncomment and edit the following section.
For more info see https://go.microsoft.com/fwlink/?linkid=838655 -->
<system.webServer>
<handlers>
<remove name="aspNetCore"/>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
</system.webServer>
</configuration>
I got ASP.NET Core 2 app running on my machine following these steps:
App Pool setting run under AppPoolIdentity with No Managed runtime configuration
Publish your ASP.NET Core 2 website to a folder. By default it publishes to bin\Release\PublishOutput
Point your IIS website to published folder
I was having a similar issue with getting a 404 when trying to access my site's Account controller. All of the other Razor pages where working correctly. If I change the inheritInChildApplications="false" to true in the web.config the controller starts working.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="true">
...
</configuration>

How and when does Configuration method in OwinStartup class is called/executed?

Before I ask my question I have already gone through the following posts:
Can't get the OWIN Startup class to run in IIS Express after
renaming ASP.NET project
file and all the posts mentioned in the question.
OWIN Startup Detection
OwinStartupAttribute required in web.config to correct Server Error #884
OWIN Startup class not
detected
Here is my project's folder layout:
Currently there is no controller or view. Just the Owin Startup file.
Startup.cs
using System;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(Bootstrapper.Startup))]
namespace Bootstrapper
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync(GetTime() + " My First OWIN App");
});
}
string GetTime()
{
return DateTime.Now.Millisecond.ToString();
}
}
}
Web.config
<appSettings>
<add key="owin:AutomaticAppStartup" value="true" />
<add key="owin:appStartup" value="Bootstrapper.Startup" />
<add key="webpages:Version" value="2.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="PreserveLoginUrl" value="true" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
I have the following reference in the Bootstrapper project:
Microsoft.Owin
Microsoft.Owin.Host.SystemWeb
Owin
System
System.Core
UPDATE:
Forgot to add the error message:
Now,
WHY is it not working?
What is the step-by-step process of adding and using an Owin
Startup class in a very basic project(like accessing Home/Index)?
How and when does Configuration method in Owin Startup class is
called/executed?
UPDATE: on 10-Dec-2016
Check the Project-Folder-Layout. In Bootstrapper project I have the following file:
IocConfig.cs
[assembly: PreApplicationStartMethod(typeof(IocConfig), "RegisterDependencies")]
namespace Bootstrapper
{
public class IocConfig
{
public static void RegisterDependencies()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
builder.RegisterModule<AutofacWebTypesModule>();
builder.RegisterType(typeof(MovieService)).As(typeof(IMovieService)).InstancePerRequest();
builder.RegisterType(typeof(MovieRepository)).As(typeof(IMovieRepository)).InstancePerRequest();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
}
Now I want to execute IocConfig.RegisterDependencies() in OWIN Startup class. I am doing using Bootstrapper in Startup at the top but, it is not working. I mean I am unable to reference IocConfig in Startup. How to resolve this?
Create an empty web application project
Install the OWIN using NuGet (install-package Microsoft.Owin.Host.SystemWeb)
Add an empty class into the project root called "Startup.cs"
Here I will answer your third question. The startup class is an entry point of OWIN and is being looked up automatically. As stated in official docs:
Naming Convention: Katana looks for a class named Startup in namespace
matching the assembly name or the global namespace.
Note, that you can also choose your own name of Startup class but you have to set this up using decorators or AppConfig. As stated here:
https://www.asp.net/aspnet/overview/owin-and-katana/owin-startup-class-detection
This is everything you need for a basic and working OWIN test:
using Owin;
using System;
namespace OwinTest
{
public class Startup
{
public static void Configuration(IAppBuilder app)
{
app.Use(async (ctx, next) =>
{
await ctx.Response.WriteAsync(DateTime.Now.ToString() + " My First OWIN App");
});
}
}
}
If you wish to use MVC (I guess by "Home/Index" you mean MVC), follow these steps:
Install MVC NuGet (install-package Microsoft.AspNet.Mvc).
Add a "Controllers" folder into your project.
Create a new empty controller under the new "Controlles" folder (right click -> add -> MVC 5 Controller - Empty) and name it "HomeController".
Create a view page under newly created "Views/Home" folder. Right click -> add -> View. Name it "Index" and uncheck the "use layour page".
Make the page inherit from WebViewPage. It should all look like this:
#inherits System.Web.Mvc.WebViewPage
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
<h1>Owin Hello</h1>
</div>
</body>
</html>
Add a global.asax to set up routes. Right click on the project -> add -> New Item -> Global Application Class.
Add the routes definition to the Application_Start method:
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapRoute(name: "Default",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" });
}
Do not forget to comment out the above "..await ctx.Response.WriteAsync..." middleware. It would interfere with the MVC otherwise.
Run the project. Should be working.
It's a little bit late, but I found the solution how to put OWIN Startup class in separate project. Everything you did in your project is correct, you must only apply one change in the properties of your Bootstrapper project. Right click on Bootstrapper project, enter properties, click Build tab and look for Output path. You should see standard output path bin\debug\ which means that your Bootstrapper dll will land in this folder. You must change this to the bin folder, where your whole web app is.
For example, I've created a simple solution with two projects, first is an empty web app, and the second is a library with an OWIN Startup class. In properties of the second project I've changed the output path to ..\OwinTest.Web\bin. This will cause all dlls to land in one folder after the build. You can now run your app and OWIN Startup should work right.
Below is the screen of properties settings of Bootstrapper project:
Three years later, but this might help someone.
The question you ask in your title is answered here:
WebApp.Start Method Type Parameter
The WebApp class uses reflection to get a pointer to the Configuration(IAppBuilder) method then calls it. If the class you provide as the generic type argument does not have a Configuration method with the expected arguments then you get an error at run time.

Hangfire dashboard returns 404

When trying to access the hangfire dashboard on my local IIS at domain/hangfire/ I get a 404 response. This is in a webforms project targeting .Net 4.5.1, Hangfire is version 1.5.3. My startup and authorisationoverride classes are as follows:
[assembly: OwinStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
GlobalConfiguration.Configuration.UseSqlServerStorage("MyConnString");
DashboardOptions opts = new DashboardOptions
{
AuthorizationFilters = new[] { new AuthorisationOverride() }
};
app.UseHangfireServer();
app.UseHangfireDashboard("/hangfire", opts);
}
}
}
public class AuthorisationOverride : Hangfire.Dashboard.IAuthorizationFilter
{
public bool Authorize(IDictionary<string, object> owinEnvironment)
{
return true;
}
}
Jobs are running successfully, but I've run out of ideas for getting the Dashboard to work.
I had something similar but I managed to get it resolved by reading through this post.
Hope you will have a better luck following through that if you haven't yet. The main problem for me was the missing DLL, and then the removing site data from the TemporaryASP.NET folder.
Edit: Someone down voted this answer because I used a link for the solution.
Since I did find a solution to this specific problem, I thought I will give it another try to share. :)
Here are the steps that I have taken to come to a solution.
Confirm you have the Microsoft.Owin.Host.SystemWeb.dll in your bin directory of this project. (In my case, the dll was missing)
Stop your app pool
Navigate to your TemporaryASP.NET folder : C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files and delete the folder inside of your site/application's folder.
Restart you app pool
Navigate to "/admin" or whatever you set your dashboard url to be "/hangfire" by default.
Struggled with this for a few hours today and just fixed it in my project.
Try moving your Hangfire configuration code higher up in your Startup class's Configuration method.
I had my Hangfire configuration code at the very bottom of Startup.Configuration and just happened to discover that the dashboard works again when I move it before some of the other OWIN stuff I was configuring.
Specifically, I moved it above the following code in my project:
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
// my AutoMapper configuration
// some async/await code that was calling .Wait() here.
I didn't take time to figure out exactly which line of code was breaking the Hangfire dashboard, but I hope that helps someone.
Also for the record, the old code was working under IIS Express at https://localhost:44342/hangfire. I was getting the 404 in full IIS at https://localhost/appname/hangfire.
Add this line in your web.config file:
<system.webServer>
<handlers>
<add name="hangfireDashboard" path="hangfire" type="System.Web.DefaultHttpHandler" verb="*" />
</handlers>
</system.webServer>
Since there is no solution so far, I would like to share something that I rectified to get this issue resolved.
If you're facing this issue only in production then, your web.config file is not properly configured.
Firstly, assuming you have already created the Startup class, add the following to the web.config under :
<add key="owin:AutomaticAppStartup" value="true" />
Next, make sure that you have referenced the OWIN assemblies as the below following:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
Although, when you install OWIN via nuget, the setup will automatically update the web.config for you, but just in case it doesn't, you can always add this. Further make sure this above OWIN version matches with the one yo have installed via nuget.
Hope this helps somebody!
Edit: Answering the OP's original question, Hangfire returns 404 error when it is not started. Apart from adding the Startup OWIN class, we need to mention automaticstartup=true in the web config also. The next issue IIS will look for is reference to Hangfire, where is we kick in the assembly details.
application Startup
[assembly: OwinStartupAttribute(typeof(yournamespace.Startup))]
namespace yournamespace
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var storage = new SqlServerStorage("connectionstring");
......
......
app.UseHangfireDashboard("/Scheduler", new DashboardOptions() { AuthorizationFilters = new[] { new HangFireAuthorizationFilter() } }, storage);
}
Authorization Filter
public class HangFireAuthorizationFilter:IAuthorizationFilter
{
public bool Authorize(IDictionary<string, object> owinEnvironment)
{
// In case you need an OWIN context, use the next line.
// `OwinContext` class is defined in the `Microsoft.Owin` package.
var context = new OwinContext(owinEnvironment);
return context.Authentication.User.Identity.IsAuthenticated &&
context.Authentication.User.IsInRole("xyz");
}
}
You Can Ignore the HangFireAuthorizationFilter if you want to.
The problem for me was missing ASP.NET installation on the server

SignalR connectivity issue with reverse proxy / cross domains

I have a SignalR server hosted and running in a Owin Web API application running in Azure (Company Service App). From a separate web application, let's say application A (non Azure) I'm connecting to SignalR using the javascript generated proxies:
Where CompanyServiceBaseUrl is set to my site url: (e.g. "http:// mysite/companyservice")
<script src="#Url.Content(CompanyServiceBaseUrl + "/Scripts/jquery.signalR-2.2.0.js")"></script>
<script src="#Url.Content(CompanyServiceBaseUrl + "/signalr/hubs")" type="text/javascript"></script>
<script type="text/javascript">
var hub = $.connection.perform;
hub.client.onToast = function () {
$("#copyOrganizationSuccessAlert").show();
};
$(document).ready(function() {
var event = document.createEvent('Event');
event.initEvent('onOrganizationLock', true, true);
onOrganizationLockEvent = function() {
document.dispatchEvent(event);
};
hub.client.onOrganizationLock = onOrganizationLockEvent;
});
$.connection.hub.start().done(function () {
hub.server.joinGroup(perform.company.application.clientId);
});
</script>
Here is my generated folder structure in my client:
When I test locally the SignalR connectivity works fine, but when I test in my environment hosted in Azure connectivity fails.
SignalR negotiate request (between App A and CompanyService):
http://<mysiteurl>/signalr/negotiate (notice that companyservice is missing from URL:
it should be:
http://<mysiteurl>/companyservice/signalr/negotiate
Here the Reverse proxy rule:
<rule name="companyservice" stopProcessing="true">
<match url="^companyservice/(.*)" />
<conditions>
<add input="{CACHE_URL}" pattern="^(https?)://" />
</conditions>
<action type="Rewrite" url="https://myazuresite.azurewebsites.net/{R:1}" />
<serverVariables>
<set name="HTTP_ACCEPT_ENCODING" value="" />
</serverVariables>
</rule>
</rules>
Question:
Why is my client connection.Url coming as "/signalr" instead of "/companyservice/signalr" when testing with my Azure site using a reverse proxy?
Finally, looking at this reported SignalR issue, looks like he is experience a very similar thing:
SignalR doesn't work behind a Reverse Proxy
But I'd like to understand what he means by:
"If I use a Reverse-Proxy rule (scan and replace) and set "Url":"/NorthboundWebApiClient/signalr" everything works fine.
I have tried establishing a connection with and without generated proxies specifying my connection url manually var connection = $.hubConnection('/companyservice/signalr', {useDefaultPath: false});, configuring CORS in the client side and no luck.
Here is a related post having problems with cross-domains:
How do I get a SignalR hub connection to work cross-domain?
Four months later a colleague suggested a solution which worked perfectly. His idea was to create a custom custom OwinMiddleware that would add the missing Azure virtual directory to the path in case missing. Something like this:
public class SignalRvDirMiddleware : OwinMiddleware
{
public SignalRvDirMiddleware(OwinMiddleware next)
: base(next)
{ }
public override async Task Invoke(IOwinContext context)
{
var vdir = new PathString("/companyservice");
PathString path = context.Request.PathBase;
if (!path.StartsWithSegments(vdir))
{
context.Request.PathBase = vdir + path;
}
await Next.Invoke(context);
}
}
And then just hook it up in your Owin Configuration method on Startup.cs :
public void Configuration(IAppBuilder appBuilder)
{
// Branch the pipeline here for requests that start with "/signalr"
appBuilder.Map("/signalr", map =>
{
map.Use<SignalRvDirMiddleware>();
map.RunSignalR(hubConfiguration);
});
}
The credit goes to my colleague Jake S.

How to set up embedded resources in an MVC application

I am trying to serve some JS and CSS files that are embedded into a DLL, with a solution based on this approach here: http://weblogs.asp.net/imranbaloch/asp-net-bundling-and-minification-and-embedded-resources
so, javascript and css files are embedded and I create bundles for them.
My problems start because, having quite a few of them, I need some folder structure to keep order. So the original route
RouteTable.Routes.Insert(0,
new Route("Embedded/{file}.{extension}",
new RouteValueDictionary(new { }),
new RouteValueDictionary(new { extension = "css|js" }),
new EmbeddedResourceRouteHandler()
));
is not enough anymore, so I have changed it to this:
RouteTable.Routes.Insert(0,
new Route("Embedded/{*url}",
new RouteValueDictionary(new { }),
new EmbeddedResourceRouteHandler()
));
I also cannot use the extension part because the catch-all part has to be the last one
So now if I try to access anything that looks like a file, my route will never be used so I will just get a 404
I have tried replacing the dot with a slash or adding a slash at the end but what I'm after here is a simple solution that will allow me to map urls that look like files to actual files.
I've also searched the web and there seem to be solutions based on UrlRewrite or altering the web.config but:
- I would like not to modify the IIS settings for every application to accomodate the library
- since it's a library, I would like it to be self contained and developers that use it shouldn't care about these sort of internal issues
So, is there a solution that I can implement in my library for this?
Also worth mentioning is that the original routing had the same issue, it only worked because of
<modules runAllManagedModulesForAllRequests="true" />
in the web.config, which I don't think is a good idea for performance
When you set
<modules runAllManagedModulesForAllRequests="true" />
this enables all available modules to run against the request. Which, as you mentioned, isn't the best for performance. However, you could add only the module you actually need- in this case the UrlRoutingModule.
You could add this module like this:
<system.webServer>
<modules>
<remove name="UrlRoutingModule-4.0" />
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
</modules>
</system.webServer>
If you want an even better way (IMO) to do this, disregard the WebConfig and add it in a AppStart.cs file in your class library.
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(AppStart), "PreStart")]
[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(AppStart), "Start")]
namespace EmbeddedPages
{
public static class AppStart
{
private static bool PreStartFired = false;
public static void PreStart()
{
if (!PreStartFired)
{
PreStartFired = true;
DynamicModuleUtility.RegisterModule(typeof(UrlRoutingModule));
}
}
}
}
This adds the UrlRoutingModule into the module stack, and your URL's should now properly resolve. Note: you will need to add WebActivator to your project through nuget.

Categories

Resources