33

ASP.NET 4.5 has a great new bundling feature and appears to have some support for use of CDNs. The example given by Microsoft for use of the bundling feature with a CDN is this

public static void RegisterBundles(BundleCollection bundles)
{
  //bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
  //            "~/Scripts/jquery-{version}.js"));

  bundles.UseCdn = true;   //enable CDN support

  //add link to jquery on the CDN
  var jqueryCdnPath = "http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js";

  bundles.Add(new ScriptBundle("~/bundles/jquery",
            jqueryCdnPath).Include(
            "~/Scripts/jquery-{version}.js"));

  // Code removed for clarity.
} 

Which seems to suggest that you need tell it explicitly the path to your file on the CDN.

The CloudFront CDN (and I presume many others) gives you a subdomain which mirrors your own. When you hit http://uniquesubdomain.cloudfront.net/js/myfile.js?v=1 it serves up http://mydomain.com/js/myfile.js?v=1

This way you can simply prefix all your links with http://uniquesubdomain.cloudfront.net/ and your files are server from CloudFront.

Is the ASP.NET 4.5 bundling feature compatible with this type of CDN? Is there a built-in way to have the bundling feature prefix all its links with your CDN domain?

Eg.

bundles.UseCdn = true;
var myBundle= new ScriptBundle("~/bundles/js", "https://uniquedomain.cloudfront.net/");
myBundle.Include("~/js/file1.js");
myBundle.Include("~/js/file2.js");

would cause

    <script src="https://uniquedomain.cloudfront.net/bundles/js?v=6y-qVPSK3RYOYHfPhOBDd92H4LjEjs-D3Hh2Yml6CXA1"></script>
Steven V
  • 15,061
  • 3
  • 56
  • 73
Mr. Flibble
  • 25,035
  • 21
  • 67
  • 96
  • 2
    similar kind of question http://stackoverflow.com/questions/12047981/how-to-upload-bundled-and-minified-files-to-windows-azure-cdn,just replace azure cdn by your custom cdn – Cris Mar 30 '13 at 12:47

4 Answers4

9

This functionality is not built-in, but is possible with a couple of small helper methods. Here's what I'm using right now:

public static class Cdn
{
    private const string CdnRoot = "//cloudfrontdomainhere.com";

    private static bool EnableCdn
    {
        get
        {
            bool enableCdn = false;
            bool.TryParse(WebConfigurationManager.AppSettings["EnableCdn"], out enableCdn);
            return enableCdn;
        }
    }

    public static IHtmlString RenderScripts(string bundlePath)
    {
        if (EnableCdn)
        {
            string sourceUrl = CdnRoot + Scripts.Url(bundlePath);
            return new HtmlString(string.Format("<script src=\"{0}\"></script>", sourceUrl));
        }

        return Scripts.Render(bundlePath);
    }

    public static IHtmlString RenderStyles(string bundlePath)
    {
        if (EnableCdn)
        {
            string sourceUrl = CdnRoot + Styles.Url(bundlePath);
            return new HtmlString(string.Format("<link href=\"{0}\" rel=\"stylesheet\" />", sourceUrl));
        }

        return Styles.Render(bundlePath);
    }
}

Note that I have my own configuration setting called EnableCdn in the appSettings section of my config file. When called from a Razor view, this produces the correct ouput, which appends the CDN domain onto the paths.

In your Razor files just do Cdn.RenderScripts("~/pathtoscriptbundle")

AmirHossein
  • 380
  • 1
  • 6
  • 25
Brian Vallelunga
  • 8,920
  • 14
  • 53
  • 80
1

Might not be exactly what you're looking for, but a lot of CDN's now act as a reverse proxy using DNS so you don't have to link your assets explicitly. I know Cloudflare does this and I'm sure others do too.

orourkedd
  • 5,771
  • 4
  • 39
  • 63
  • I'm using SSL so as far as I'm aware this won't work. ty for the tip tho. – Mr. Flibble Apr 24 '13 at 18:00
  • You can use SSL with cloudflare ($20/month). I'm currently using it on an ecommerce store. – orourkedd Apr 24 '13 at 19:37
  • I do not think this will work with reverse DNS though (unless I am misunderstanding something). If the browser requests https://yourdomain.com/image.jpg and Cloudflare returns the image then it will need *your* SSL cert otherwise the response will be flagged as insecure by the browser. Or do you actually send Cloudflare your own SSL cert which it will use to serve your content? – Mr. Flibble Apr 25 '13 at 11:59
  • Cloudflare generates a cert for you that is used when pulling data from their server. Pretty clever on their part. – orourkedd Apr 25 '13 at 12:14
  • You guys are missing the point from @orourkedd. Your site call cdn for content, cdn in tern will call your site to get content. – mamu Sep 29 '13 at 21:46
  • 2
    SSL is now available even for free plans on Cloudflare. – Vincent Nov 10 '14 at 13:05
1

Another option, is to use the Scripts or Styles RenderFormat method like so. This was particularly useful for me as I customise the tagFormat occassionally to wrap references in conditional html comments, or append additional attributes like media="screen,print". The code is simpler because you can do the awkward Replace on a string, before it becomes a HTML-encoded string.

Alternatively, you could have the tagFormat as an optional parameter to those methods where the default value is the string constants mentioned below.

public class BundleHelper
{
    public static readonly string StyleTagFormat = "<link href=\"{0}\" rel=\"stylesheet\"/>";
    public static readonly string ScriptTagFormat = "<script src=\"{0}\"></script>"

    /// <summary>
    /// Customised script bundle rendering method with CDN support if optimizations and CDN enabled.
    /// </summary>
    public static IHtmlString RenderScriptFormat(string tagFormat, string path)
    {
        // Check for absolute url to ensure the standard framework support for CDN bundles, with a CdnPath still works.
        if (AppSettings.Bundling.EnableCdn && !UriHelper.IsAbsoluteUrl(Scripts.Url(path).ToString()))
        {
            tagFormat = tagFormat.Replace(" src=\"{0}\"", String.Format(" src=\"{0}{{0}}\"", AppSettings.Bundling.BundlesCDNPrefixUrl));
        }
        return Scripts.RenderFormat(tagFormat, path);
    }

    /// <summary>
    /// Customised styles bundle rendering method with CDN support if optimizations and CDN enabled.
    /// </summary>
    public static IHtmlString RenderStyleFormat(string tagFormat, string path)
    {
        // Check for absolute url to ensure the standard framework support for CDN bundles, with a CdnPath still works.
        if (AppSettings.Bundling.EnableCdn && !UriHelper.IsAbsoluteUrl(Styles.Url(path).ToString()))
        {
            tagFormat = tagFormat.Replace(" href=\"{0}\"", String.Format(" href=\"{0}{{0}}\"", AppSettings.Bundling.BundlesCDNPrefixUrl));
        }
        return Styles.RenderFormat(tagFormat, path);
    }
}


public class UriHelper
{
    /// <summary>
    /// Determines whether a url is absolute or not.
    /// </summary>
    /// <param name="url">Url string  to test.</param>
    /// <returns>true/false.</returns>
    /// <remarks>
    /// Examples:
    ///     ?IsAbsoluteUrl("hello")
    ///     false
    ///     ?IsAbsoluteUrl("/hello")
    ///     false
    ///     ?IsAbsoluteUrl("ftp//hello")
    ///     false
    ///     ?IsAbsoluteUrl("//hello")
    ///     true
    ///     ?IsAbsoluteUrl("ftp://hello")
    ///     true
    ///     ?IsAbsoluteUrl("http://hello")
    ///     true
    ///     ?IsAbsoluteUrl("https://hello")
    ///     true
    /// </remarks>
    public static bool IsAbsoluteUrl(string url)
    {
        Uri result;
        return Uri.TryCreate(url, UriKind.Absolute, out result);
    }
}
benmccallum
  • 1,009
  • 11
  • 25
0

It is not possible, but instead of bundle you can use a text template to combine JS files as one js and put that on CDN.

<#@ ... hostspecific="true"  extension=".js">

<#

    Write (System.IO.File.ReadAllText("a.js"));
    Write (System.IO.File.ReadAllText("b.js"));
 #>
Akash Kava
  • 37,127
  • 20
  • 114
  • 162