19

Is it somehow possible to get an exception thrown if a javascript or css file is missing ?

I have tried with the code below but it never throws an exception ...

    public class BundleConfig {
    public static void RegisterBundles(BundleCollection bundles) {  
        ....

        bundles.Add(new ScriptBundle("~/bundles/something").Include("~/Scripts/nonExistingFile.js"));
        // I would like the above usage of Include method with 
        // a non-existing file  to throw an exception to make it 
        // obvious that an expected file is missing ...
        // but since it does not I tried to implement a method as below ...
        ...
        AssertThatAllFilesExist(bundles);
        // but unfortunately an exception is never thrown but when 
        // iterating the files in the method below, 
        // the non-existing file seems to never become retrieved ...
    }   

    private static void AssertThatAllFilesExist(BundleCollection bundles) {
        HttpContext currentHttpContext = HttpContext.Current;
        var httpContext = new HttpContextWrapper(currentHttpContext);
        foreach (var bundle in bundles) {
            var bundleContext = new BundleContext(httpContext, bundles, bundle.Path);
            IEnumerable<FileInfo> files = bundle.EnumerateFiles(bundleContext);
            foreach (var file in files) {
                if (!file.Exists) {
                    // if this problem would occur then it should 
                    // indicate that one of the arguments of the 
                    // method "Bundle.Include" does not 
                    // correspond to an existing file ...
                    throw new ArgumentException("The following file does not exist: " + file.FullName);
                }
            }
        }
    }
}   
tereško
  • 58,060
  • 25
  • 98
  • 150
user310457
  • 511
  • 5
  • 14

5 Answers5

6

I use this class instead of ScriptBundle:

public class ScriptBundleWithException : ScriptBundle
{
    public ScriptBundleWithException( string virtualPath ) : base( virtualPath ) {}
    public ScriptBundleWithException( string virtualPath, string cdnPath ) : base( virtualPath, cdnPath ) {}

    public override Bundle Include( params string[] virtualPaths )
    {
        foreach ( var virtualPath in virtualPaths ) {
            Include( virtualPath );
        }
        return this;
    }

    public override Bundle Include( string virtualPath, params IItemTransform[] transforms )
    {
        var realPath = System.Web.Hosting.HostingEnvironment.MapPath( virtualPath );
        if ( !File.Exists( realPath ) ) {
            throw new FileNotFoundException( "Virtual path not found: " + virtualPath );
        }
        return base.Include( virtualPath, transforms );
    }
}

Then in configuration:

bundles.Add( new ScriptBundleWithException( "~/bundles/something" ) //...
nrodic
  • 3,026
  • 3
  • 33
  • 38
  • 1
    Good one... Would have to do that for StyleBundle too though (and any other bundle type) – Wiebe Tijsma Jul 18 '14 at 08:10
  • 3
    Instead of inheriting all descendants of `Bundle` class, I suggest adding an extension method to the `Bundle` itself. See my solution here: http://stackoverflow.com/questions/23802800/asp-net-mvc-bundling-best-way-to-detect-missing-file/25784663#25784663. – Herman Kan Sep 11 '14 at 10:23
2

If you look at the source of Bundle class, you'll find that .Include just silently doesn't add file if it doesn't exist. Hence your asserts don't work.

I ended up with using an array of script files names and checking them manually before adding to the bundle.

ovolko
  • 2,777
  • 2
  • 21
  • 26
2

I came across this and thought I'd submit my solution. As others have mentioned adding files that don't exist to a bundle are silently ignored.

Example code:

string[] jsFiles = { "~/js/file1.js", "~/js/file2.js" };
string[] cssFiles = { "~/css/file1.css", "~/css/file2.css" };
List<string> allFiles = new List<string>();
allFiles.AddRange(jsFiles);
allFiles.AddRange(cssFiles);
var server = HttpContext.Current.Server;
foreach(var file in allFiles) {
    if(!File.Exists(server.MapPath(file))
        throw new FileNotFoundException("File " + file + " was not found.");
}

Then define your bundles as normal, using the arrays for .Include

Sam
  • 946
  • 7
  • 19
  • Your approach has two drawbacks: 1) you have to specify bundle files either twice (in the arrays and `Include()`, or reference them by indices `Include(jsFiles[0])` which is error-prone; 2) it does not support ASP.NET wildcards `*` and `{version}`. – Herman Kan Sep 11 '14 at 10:14
1

Please see my answer here: Asp.Net MVC Bundling, best way to detect missing file. It features support for ASP.NET's wildcards in path.

Community
  • 1
  • 1
Herman Kan
  • 2,253
  • 1
  • 25
  • 32
0

I use T4MVC:

var bundleJsLayout = new ScriptBundle("~/bundle/js").Include(
    "~" + Links.Scripts.jquery_1_9_0_js,
    "~" + Links.Scripts.modernizr_2_6_2_js,
    "~" + Links.Scripts.jquery_unobtrusive_ajax_min_js
    );

bundleJsLayout.Transforms.Add(new JsMinify());
bundles.Add(bundleJsLayout);
Dmitry Efimenko
  • 10,973
  • 7
  • 62
  • 79
  • I had hoped that someone could provide an easier solution with usage of some class/method I was not aware of. Anyway, I now marked your answer as useful, to give you some credit for taking your time to answer, since I assume it is useful for someone already familiar with T4 and T4MVC, though for me myself it feels like overkill to have to learn these things just to achieve what I wanted with my question. – user310457 Feb 18 '13 at 14:46
  • Perhaps just for the sake of this question, implementing T4MVC is an overkill. However, I do suggest you look into it as it helps to get rid of all the magic strings in your code. Plus, there isn't all that much to learn about it. Just watch a video on the home page and you are all done. – Dmitry Efimenko Feb 18 '13 at 17:29
  • This code won't work if you deploy your application to virtual directory. For example if you put your website in "/app" directory then `Links.Scripts.jquery_1_9_0_js` will be `"/app/Scripts/jquery.1.9.0.js" and `"~" + Links.Scripts.jquery_1_9_0_js` will result in file that doesn't exists. – Mariusz Pawelski Sep 09 '14 at 12:48
  • There is a configuration file in T4MVC where you can configure defaults like base path. I'm not sure if that'll fix your issue since I didn't try that, but look into docs for T4MVC – Dmitry Efimenko Sep 09 '14 at 17:23