ASP.Net Combres and Combres MVC with Cloudfront

1.7k views Asked by At

Currently I am using Amazon Cloudfront to service static objects on my ASP.Net MVC3 C# site. So all static resources have http://cdn.domainname.com/ appended before the resource.

At the same time I am using combres and combred mvc to compress and combine my CSS and Javascript.

The tag to output the minimized combined files are as follows.

@Html.Raw(WebExtensions.CombresLink("siteCss"))
@Html.Raw(WebExtensions.CombresLink("siteJs"))

This produces links on my site to

<link rel="stylesheet" type="text/css" href="/combres.axd/siteCss/-63135510/"/>
<script type="text/javascript" src="/combres.axd/siteJs/-561397631/"></script> 

As you can see my cloudfront cdn isn't in front of it so I am not getting the benefits of cloudfront with these to files.

Is there anyone out there who knows how to insert my cdn without changing the source code of the actuall combress dll file?

2

There are 2 answers

2
Buu On BEST ANSWER

I'm not familiar with Cloudfront, but with Combres (latest release) you can change the host name (which gets appended as prefix before the /combres.axd... by setting the host attribute in . For example:

 <resourceSets url="~/combres.axd"
                host="static.mysite.com"
                defaultDuration="365"
                defaultVersion="auto"
                defaultDebugEnabled="false"
                defaultIgnorePipelineWhenDebug="true"
                localChangeMonitorInterval="30"
                remoteChangeMonitorInterval="60"
                > 

Please let me know if this approach works with CloudFront?

0
JeffR On

I ran in the same issue a few months ago and just ran across this post. I was able to get around it by making my own Combres filter (FixUrlsInCSSFilter) that will read a "Base Url" value from the web.config or a database setting and apply it to all combres image urls.

Hope it helps someone out there...

combres.xml:

<combres xmlns='urn:combres'>
  <filters>
    <filter type="MySite.Filters.FixUrlsInCssFilter, MySite" />
  </filters>

FixUrlsInCssFilter - most of this was copied from the original reflected file

    public sealed class FixUrlsInCssFilter : ISingleContentFilter
{
    /// <inheritdoc cref="IContentFilter.CanApplyTo" />
    public bool CanApplyTo(ResourceType resourceType)
    {
        return resourceType == ResourceType.CSS;
    }

    /// <inheritdoc cref="ISingleContentFilter.TransformContent" />
    public string TransformContent(ResourceSet resourceSet, Resource resource, string content)
    {
        string baseUrl = AppSettings.GetImageBaseUrl();

        return Regex.Replace(content, @"url\((?<url>.*?)\)", match => FixUrl(resource, match, baseUrl),
            RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture);
    }

    private static string FixUrl(Resource resource, Match match, string baseUrl)
    {
        try
        {
            const string template = "url(\"{0}\")";
            var url = match.Groups["url"].Value.Trim('\"', '\'');

            while (url.StartsWith("../", StringComparison.Ordinal))
            {
                url = url.Substring(3); // skip one '../'
            }

            if (!baseUrl.EndsWith("/"))
                baseUrl += "/";

            if (baseUrl.StartsWith("http"))
            {
                return string.Format(CultureInfo.InvariantCulture, template, baseUrl + url);
            }
            else
                return string.Format(CultureInfo.InvariantCulture, template, (baseUrl + url).ResolveUrl());
        }
        catch (Exception ex)
        {
            // Be lenient here, only log.  After all, this is just an image in the CSS file
            // and it should't be the reason to stop loading that CSS file.
            EventManager.RaiseExceptionEvent("Cannot fix url " + match.Value, ex);
            return match.Value;
        }
    }
}

#region Required to override FixUrlsInCssFilter for Combres

public static class CombresExtensionMethods
{
    /// <summary>
    ///   Returns the relative HTTP path from a partial path starting out with a ~ character or the original URL if it's an absolute or relative URL that doesn't start with ~.
    /// </summary>
    public static string ResolveUrl(this string originalUrl)
    {
        if (string.IsNullOrEmpty(originalUrl) || IsAbsoluteUrl(originalUrl) || !originalUrl.StartsWith("~", StringComparison.Ordinal))
            return originalUrl;

        /* 
         * Fix up path for ~ root app dir directory
         * VirtualPathUtility blows up if there is a 
         * query string, so we have to account for this.
         */
        var queryStringStartIndex = originalUrl.IndexOf('?');
        string result;
        if (queryStringStartIndex != -1)
        {
            var baseUrl = originalUrl.Substring(0, queryStringStartIndex);
            var queryString = originalUrl.Substring(queryStringStartIndex);
            result = string.Concat(VirtualPathUtility.ToAbsolute(baseUrl), queryString);
        }
        else
        {
            result = VirtualPathUtility.ToAbsolute(originalUrl);
        }

        return result.StartsWith("/", StringComparison.Ordinal) ? result : "/" + result;
    }

    private static bool IsAbsoluteUrl(string url)
    {
        int indexOfSlashes = url.IndexOf("://", StringComparison.Ordinal);
        int indexOfQuestionMarks = url.IndexOf("?", StringComparison.Ordinal);

        /*
         * This has :// but still NOT an absolute path:
         * ~/path/to/page.aspx?returnurl=http://www.my.page
         */
        return indexOfSlashes > -1 && (indexOfQuestionMarks < 0 || indexOfQuestionMarks > indexOfSlashes);
    }
}

#endregion

AppSettings class - to retrieve the value from the web.config. I also use this to build the path for images not handled by combres...

    public class AppSettings
{
    /// <summary>
    /// Retrieves the value for "ImageBaseUrl" if the key exists
    /// </summary>
    /// <returns></returns>
    public static string GetImageBaseUrl()
    {
        string baseUrl = "";

        if (ConfigurationManager.AppSettings["ImageBaseUrl"] != null)
            baseUrl = ConfigurationManager.AppSettings["ImageBaseUrl"];

        return baseUrl;
    }
}

web.config values

<appSettings>
   <add key="ImageBaseUrl" value="~/Content/Images/" /> <!--For Development-->
   <add key="ImageBaseUrl" value="https://d209523005EXAMPLE.cloudfront.net/Content/Images/" /> <!--For Production-->
</appSettings>