4

I'm using the "HotTowel" Single Page App Template, but I'm not being able to get SignalR working. I'm getting the following error:

Failed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:8184/signalr/hubs

I had a kind of boilerplate using the standard MVC 4 Single Page App, with a very simple hub in place (users online counter). Everything was working fine.

After I switched to "HotTowel" it stopped working. I checked with John Papa and he gave me a suggestion to check in the Durandal side, as he mentioned he knew some fix was about to be done on some issues interfering with some routing.

main.js:

require.config({
  paths: {
    "text": "durandal/amd/text",
    "signr": "../scripts/jquery.signalR-1.0.1.min"
  }
});
define(['durandal/app', 'durandal/viewLocator', 'durandal/system', 'durandal/plugins/router', 'services/logger', "signr"],
  function (app, viewLocator, system, router, logger, sigr) {

  // Enable debug message to show in the console
  system.debug(true);

  app.start().then(function () {
    toastr.options.positionClass = 'toast-bottom-right';
    toastr.options.backgroundpositionClass = 'toast-bottom-right';

    router.handleInvalidRoute = function (route, params) {
      logger.logError('No Route Found', route, 'main', true);
    };

    // When finding a viewmodel module, replace the viewmodel string 
    // with view to find it partner view.
    router.useConvention();
    viewLocator.useConvention();

    // Adapt to touch devices
    app.adaptToDevice();
    //Show the app by setting the root view model for our application.
    app.setRoot('viewmodels/shell', 'entrance');
  });
});

Global.asax.cs:

protected void Application_Start()
{
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    // Register the default hubs route: ~/signalr/hubs
    RouteTable.Routes.MapHubs();
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
}

HotTowel\index.cshtml:

<body>
  <div id="applicationHost">
    @Html.Partial("_splash")
  </div>

  @Scripts.Render("~/scripts/vendor")
    @if(HttpContext.Current.IsDebuggingEnabled) {
      <script type="text/javascript" src="~/App/durandal/amd/require.js" data-main="@Url.Content("~/App/main")"></script>
    } else {
      <!-- Remember to run the Durandal optimizer.exe to create the main-built.js  -->
      <script type="text/javascript" src="~/App/main-built.js"></script>
    }    
  <script type="text/javascript" src="~/signalr/hubs"></script>    
  @*<script src="~/App/services/hubs.js"></script>*@
</body>

(I know this probably isn't the place to put it, but I couldn't make it appear on my sources, until I've put it here) - by the way, if you can show me the correct way to do it, I'll appreciate

What else can I tell you to help me troubleshoot this? I have all references to SignalR, etc... I have a working example using another project template...

Can someone guide me ?

It should be easy enough to replicate:

  1. Just use the "HotTowel" template
  2. Add all SignalR references
  3. Test with my code

I'm thinking either Durandal or Require.Js are getting in the way with this. Can someone save the day? :)

Alexander
  • 2,320
  • 2
  • 25
  • 33
Tiago Reis
  • 1,461
  • 1
  • 10
  • 8

1 Answers1

10

The issue is that SignalR needs its routes to be registered first. The HotTowel template attempts to 'be first' by using a PreApplicationStartMethod with WebActivator.

Couple ways to fix this.. We have WebActivator available, so you can create a new class (say SignalRConfig.cs) in App_Start like this:

[assembly: WebActivator.PreApplicationStartMethod(
typeof(SignalRConfig), "RegisterRoutes", Order = 1)]
public static class SignalRConfig
{
    public static void RegisterRoutes()
    {
        RouteTable.Routes.MapHubs();
    }
}

This will register SignalR's routes before HotTowel. Remove your call to MapHubs in global.asax as this will now be handled for you.

Alternatively, open HotTowelRouteConfig.cs and remove/comment out this attribute:

[assembly: WebActivator.PreApplicationStartMethod(
typeof(Empire.Web.App_Start.HotTowelRouteConfig), "RegisterHotTowelPreStart", Order = 2)]

Then go into global.asax and, after calling RouteTable.Routes.MapHubs(), add:

HotTowelRouteConfig.RegisterHotTowelPreStart();

After this, they should play nicely.

stirno
  • 598
  • 4
  • 10
  • Hi Brandon! Works GREAT! :) Thank you so so much! I wasn't still too familiar with the WebActivator concept. – Tiago Reis Mar 10 '13 at 01:55
  • 3
    Yes, the reason we use web activator is to help avoid messing up global asax on you. Once you install hot towel you can remove the web activator code and add in your own routes. THe alternative is i add the global asax code, but that means if you use HotTowel as a NuGet package it would conflict with existing global.asax. In other words, it was not perfect either way. Anyway - the best thing to do is add your routes in app_start/global.asax and remove or move the web activator code. – John Papa Mar 17 '13 at 14:02