1

Background: I'm using the HTML 5 Offline App Cache and dynamically building the manifest file. Basically, the manifest file needs to list each of the static files that your page will request. Works great when the files are actually static, but I'm using Bundling and Minification in System.Web.Optimization, so my files are not static.

When in the DEBUG symbol is loaded (i.e. debugging in VS) then the actual physical files are called from the MVC View. However, when in Release mode, it calls a virtual file that could look something like this: /bundles/scripts/jquery?v=FVs3ACwOLIVInrAl5sdzR2jrCDmVOWFbZMY6g6Q0ulE1

So my question: How can I get that URL in the code to add it to the offline app manifest?

I've tried:

        var paths = new List<string>()
        {
            "~/bundles/styles/common",
            "~/bundles/styles/common1024",
            "~/bundles/styles/common768",
            "~/bundles/styles/common480",
            "~/bundles/styles/frontend",
            "~/bundles/scripts/jquery",
            "~/bundles/scripts/common",
            "~/bundles/scripts/frontend"
        };

        var bundleTable = BundleTable.Bundles;
        foreach (var bundle in bundleTable.Where(b => paths.Contains(b.Path)))
        {
            var bundleContext = new BundleContext(this.HttpContext, bundleTable, bundle.Path);
            IEnumerable<BundleFile> files = bundle.GenerateBundleResponse(bundleContext).Files;
            foreach (var file in files)
            {
                var filePath = file.IncludedVirtualPath.TrimStart(new[] { '~' });
                sb.AppendFormat(formatFullDomain, filePath);
            }
        } 

As well as replacing GenerateBundleResponse() with EnumerateFiles(), but it just always returns the original file paths.

I'm open to alternative implementation suggestions as well. Thanks.

UPDATE: (7/7/14 13:45)

As well as the answer below I also added this Bundles Registry class to keep a list of the required static files so that it works in debug mode in all browsers. (See comments below)

    public class Registry
    {
        public bool Debug = false;
        public Registry()
        {
            SetDebug();
        }
        [Conditional("DEBUG")]
        private void SetDebug()
        {
            Debug = true;
        }

        public IEnumerable<string> CommonScripts
        {
            get
            {
                if (Debug)
                {
                    return new string[]{
                        "/scripts/common/jquery.validate.js",
                        "/scripts/common/jquery.validate.unobtrusive.js",
                        "/scripts/common/knockout-3.1.0.debug.js",
                        "/scripts/common/jquery.timepicker.js",
                        "/scripts/common/datepicker.js",
                        "/scripts/common/utils.js",
                        "/scripts/common/jquery.minicolors.js",
                        "/scripts/common/chosen.jquery.custom.js"
                    };
                }
                else
                {
                    return new string[]{
                        "/scripts/common/commonbundle.js"
                    };
                }
            }
        }
    }

I'm by no means happy with this solution. Please make suggestions if you can improve on this.

hofnarwillie
  • 3,563
  • 10
  • 49
  • 73

1 Answers1

0

I can suggest an alternative from this blog post create your own token.

In summary the author suggests using web essentials to create the bundled file and then creating a razor helper to generate the token, in this case based on the last changed date and time.

public static class StaticFile
{
    public static string Version(string rootRelativePath)
    {
        if (HttpRuntime.Cache[rootRelativePath] == null)
        {
            var absolutePath = HostingEnvironment.MapPath(rootRelativePath);
            var lastChangedDateTime = File.GetLastWriteTime(absolutePath);

            if (rootRelativePath.StartsWith("~"))
            {
                rootRelativePath = rootRelativePath.Substring(1);
            }

            var versionedUrl = rootRelativePath + "?v=" + lastChangedDateTime.Ticks;

            HttpRuntime.Cache.Insert(rootRelativePath, versionedUrl, new CacheDependency(absolutePath));
        }

        return HttpRuntime.Cache[rootRelativePath] as string;
    }
}

Then you can reference the bundled file like so...

@section scripts {
<script src="@StaticFile.Version("~/Scripts/app/myAppBundle.min.js")"></script>}

Then you have control of the token and can do what you want with it.

WooHoo
  • 1,912
  • 17
  • 22
  • Thanks for the answer. It does look easy enough, but what about when I'm debugging? I'd like the individual files to be requested separately and unminified when in debug mode. Any suggestions? – hofnarwillie Jul 07 '14 at 10:22
  • I see it adds js source maps, but this is only helpful if the client debugger supports source maps - which, as far as I can tell, Firebug does not. I can stop using firebug and just use the built in firefox debugger, but that seems a bit of a leap in order to fix one problem. Is there a way to modify the `StaticFile.Version()' method to only call the bundle if not in debug mode? I'll work on this for a bit and post any updates. – hofnarwillie Jul 07 '14 at 10:39
  • Ok this seems to be the best solution available. Although it does seem weird that you can't get access in the Optimizations API. I would have preferred that solution, because then if I ever need to pass this project over to someone else they do not need to install Web Essentials. I posted an update above to show what I ended up with. – hofnarwillie Jul 07 '14 at 12:44