ASP.Net MVC/Web API Custom Dynamic Bundle Working in Production but not Dev

4k views Asked by At

In my development environment, I'm receiving 404 responses when I try to load a dynamically generated bundle. Strangely, this isn't occurring in our production environment, so it's not really catastrophic, but it makes development a bit frustrating.

What should happen is:

  1. At application pre start (WebActivator PreStart), set up Dependency Resolver, Web API and MVC config stuff.
  2. At application post start (WebActivator PostStart), load a service via dependency resolver (let's say IMyService).
  3. Instantiate custom IBundleTransform (JsonBundleTransform - see below for code).
  4. Call IEnumerable<string> IMyService.ListSupportedGroups().
  5. Loop through supported groups and build custom bundle (CustomBundle - see below...).
  6. Add custom bundles to BundleTable.Bundles.
  7. Add static JS/CSS files to BundleTable.Bundles.
  8. Reference various bundles in pages and see content in browser.

It fails at step 8 for the custom bundles with 404 errors. When debugging, the custom bundle transformer is never called. As I mentioned above, however, everything works fine in production - though I've compared the config files and can't see anything missing from my dev configs which should have any effect. Also, in production, the bundled content is rendered correctly regardless of the value for compilation->debug in the web.config.

Other notes:

  • I'm using IIS 7.5 in production, IIS 8 in development.
  • When I set debug="false" in dev, i get 404's on all of my bundles.
  • This was working but stopped at some point and I can't identify when or why that happened.

The code I'm using is as follows (redundant code removed, names changed to protected the innocent, etc...):

The Transform

public class JsonBundleTransform: IBundleTransform
{
    public void Process( BundleContext context, BundleResponse response )
    {
        var bundle = context.BundleCollection.FirstOrDefault(b => b.Path == context.BundleVirtualPath) as CustomBundle;

        response.Content = string.Format( ";var obj = {0};", JsonConvert.SerializeObject( bundle.KeyValues ) );
        response.ContentType = "application/javascript";
        response.Cacheability = HttpCacheability.Server;
    }
}

The Custom Bundle

public class CustomBundle: Bundle
{
    public CustomBundle( string virtualPath, IBundleTransform transform, IMyService myService, string groupId ) : base( virtualPath, transform )
    {
        keyValues = myService.GetKeyValuesByGroupId( groupId );
    }

    public IDictionary<string, string> KeyValues { get; private set; }
}

The Config

public class BundleConfig
{
    public static void RegisterBundles( BundleCollection bundles, IMyService myService )
    {
        var transform = new JsonBundleTransform();

        var jsonBundles = myService
            .ListSupportedGroups()
            .Select( groupId => 
                new CustomBundle( 
                    string.Format( "~/resource/script/keyValues-{0}.js", groupId ), 
                    transform, 
                    myService, 
                    groupId
                )
            );

        foreach ( var jsonBundle in jsonBundles ) {
            bundles.Add( jsonBundle );
        }

        // Static bundles added here...
    }
}

Rendering the Script

@Scripts.Render( Url.Content( string.Format( "~/resource/script/keyValues-{0}.js", Model.GroupId ) ) ) )

Any idea what I'm missing here to get this working?

Thanks in advance for any help or advice offered.

Edit

Given that it's working in production, I'm leaning strongly towards thinking that the problem is an environmental problem and not a code issue - though, for the life of me, I can't figure out what it is yet. I think the most likely candidate is something do with configuration or references (IIS versions seem an unlikely cause, to be honest).

1

There are 1 answers

1
Andy Wilson On

If you append ".js" to the virtual path of your bundles, IIS will interpret any request for those bundles as a request for a static file, unless:

  • You have instructed IIS to run all managed modules for all requests
  • You have explicitly configured IIS to process your bundle urls using the routing handler

If you compare the production and dev configurations, I suspect you'll find that the production environment is has "runAllManagedModulesForAllRequests" set to "true".

The easiest fix for this issue is simply to remove the ".js" extension from the virtual paths of your bundles. If you prefer to keep the extension, you can configure IIS to pass the requests to the routing handler:

  <system.webServer>
    <handlers>      
      <add name="UrlRoutingHandler" 
           type="System.Web.Routing.UrlRoutingHandler, 
                 System.Web, Version=4.0.0.0, 
                 Culture=neutral, 
                 PublicKeyToken=b03f5f7f11d50a3a" 
           path="/resource/script/*" 
           verb="GET"/>      
    </handlers>
  </system.webServer>

You could also leave "runAllManagedModulesForAllRequests" enabled, but that has performance concerns, as there is no reason to run static files through the entire managed pipeline.