-->

MVC CDN fallback for Style Bundle

2020-08-27 06:06发布

问题:

Does MVC have a built in way to specify a CDN fallback for style sheets? I am trying to set up a fallback for the jQuery Mobile Structure style sheet. Here is my code in the RegisterBundles method:

var JQMstyleSheet = new StyleBundle("~/JQMstyle", "http://code.jquery.com/mobile/1.3.1/jquery.mobile.structure-1.3.1.min.css").Include("~/theme/jquery.mobile.structure-1.3.1.css");
JQMstyleSheet.CdnFallbackExpression = "window.jQuery.mobile";
bundles.Add(JQMstyleSheet);

When the page renders it outputs this to the html:

<script>
(window.jQuery.mobile)||document.write('<script src="/JQMstyle"><\/script>');
</script>

When the CDN fails it doesn't dynamically add the style sheet like it does perfectly for my javascript files. I think the problem is that it is trying to render a script, when it should be a style. Is there a different fallback property other than CdnFallbackExpression?

UPDATE

The Microsoft docs for System.Web.Optimization.StyleBundle shows a CdnFallbackExpression as an available property, however in the description it says "Gets the script extension rendered by the Scripts helper class..." http://msdn.microsoft.com/en-us/library/system.web.optimization.stylebundle(v=vs.110).aspx Is this a bug in the System.Web.Optimization.StyleBundle ? shouldn't that property by referencing the Styles helper class?

回答1:

TLDR;

Check out my solution which provides a StyleBundle extension method to solve the problem.

Style Bundle Fallback

Also

Yes there is a bug in the Microsoft ASP.NET Optimization Framework, documented here.

The solution is to modify the CdnFallbackExpression to be a javascript function that both checks for the stylesheet and loads the fallback, thus ignoring the bad script from the Optimization Framework.

There are a couple of tricky parts, especially checking for a stylesheet being loaded when it comes from another domain, like most CDN sources.

I have a solution on GitHub that you can use until the issue is fixed in the framework; however, I'd still watch out for the tricky part of determining when the stylesheet is actually loaded.



回答2:

Here is my RegisterBundles method and it works perfectly. It automatically falls back to the local scripts and styles if the CDN fails. See if it can help.

using System.Web;
using System.Web.Optimization;

    public static void RegisterBundles(BundleCollection bundles)
    {
        var jQueryCdn = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.4.0.min.js";
        var jQueryUICdn = "https://ajax.aspnetcdn.com/ajax/jquery.ui/1.12.1/jquery-ui.min.js";
        var jQueryUICss = "https://ajax.aspnetcdn.com/ajax/jquery.ui/1.12.1/themes/blitzer/jquery-ui.css";
        var jQueryValidateCdn = "https://ajax.aspnetcdn.com/ajax/jquery.validate/1.19.0/jquery.validate.min.js";
        var jQueryValidateUnobtrusiveCdn = "https://ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.min.js";
        var modernizrCdn = "https://ajax.aspnetcdn.com/ajax/modernizr/modernizr-3.5.0.js";
        var respondCdn = "https://ajax.aspnetcdn.com/ajax/respond/1.4.2/respond.min.js";
        var bootstrapCdn = "https://ajax.aspnetcdn.com/ajax/bootstrap/4.3.1/bootstrap.min.js";
        var bootstrapCSSCdn = "https://ajax.aspnetcdn.com/ajax/bootstrap/4.3.1/css/bootstrap.min.css";

        bundles.UseCdn = true;

        bundles.Add(new ScriptBundle("~/bundles/jquery", jQueryCdn).Include(
                    "~/Scripts/jquery-{version}.js"));
        bundles.Add(new ScriptBundle("~/bundles/jqueryui", jQueryUICdn).Include(
                    "~/Scripts/jquery-ui-1.12.1.js"));
        bundles.Add(new ScriptBundle("~/bundles/jqueryval", jQueryValidateCdn).Include(
                    "~/Scripts/jquery.validate*"));
        bundles.Add(new ScriptBundle("~/bundles/jqueryUnobtrusive", jQueryValidateUnobtrusiveCdn));

        bundles.Add(new ScriptBundle("~/bundles/modernizr", modernizrCdn).Include(
                    "~/Scripts/modernizr-*"));
        bundles.Add(new ScriptBundle("~/bundles/bootstrap", bootstrapCdn).Include(
                    "~/Scripts/bootstrap.js"));
        bundles.Add(new ScriptBundle("~/bundles/respond", respondCdn).Include(
                    "~/Scripts/respond.js"));
        //Styles
        bundles.Add(new StyleBundle("~/Content/bootstrapcss", bootstrapCSSCdn).Include(
                    "~/Content/bootstrap.css"));
        bundles.Add(new StyleBundle("~/Content/jqueryuicss",jQueryUICss).Include(
                    "~/Scripts/jquery-ui.css"));
        bundles.Add(new StyleBundle("~/Content/css").Include(
                    "~/Content/site.css"));
        //set to true before deployment to use CDN files
        BundleTable.EnableOptimizations = true;
    }