Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Optimize your WebApp like a PRO – ASP.NET MVC Boilerplate

0.00/5 (No votes)
27 Apr 2015 2  
How to optimize your webapp like a PRO - ASP.NET MVC Boilerplate

Introduction

We are living in a time when technology is developing with fast pace. Today, most of the users can enjoy the privilege of having an internet connection, using modern web browsers and using modern web applications. As a result of the technology, browsers can now host and serve complex applications. The rule of thumb is that no matter how fast the internet connection is, if it is not properly developed, the web application that your team is building might end up with a not so great user experience at the end.

In order to exceed our customer expectations, we might need to go one step further into optimizing what we’ve developed so far or even better – to develop our next web application with optimization practices in mind.

If you wonder what you need to do, there is a great development check list published by Yahoo! development team. Most of the rules are general development/configuration rules. I will go into technical “How to” for implementing them in ASP.NET MVC/IIS.

First thing first, we will start with the simple rules.

Add Style Sheets in the HEAD

This is a simple rule that combines several sub rules like:

  • Forget about using inline styles and internal style sheets
  • Use external style sheets and put them under the HEAD tag

Add Your Scripts at the Bottom of the <body>

image

User defer tag in <script src=”PATH_TO_SCRIPT” <code>defer/> to prevent blocking from the HTML render.

Add Favicon

All modern browsers will first try to resolve your favicon. Add favicon to speedup page resolve and load. You will notice that faulted requests that try to access the favicon will disappear from your web app log file. Don’t forget to add favicon route exception in you ASP.NET MVC web app.

C#
routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" })

Minimize Request for the Static Data by using CSS Sprites

Put all of your icons and assets that you are using for your design into one file. Create CSS file to access the resources. You will minimize n*request per resource time that the browser would call for the separate assets.

There is a great online tool for creating CSS from a sprite. Check out Sprite cow – http://www.spritecow.com.

Once you’ve got the images that will be used in your web app, use https://tinypng.com/ to compress the sprites. The service will trim all of the metadata in the sprites and minimize the file size.

Configure IIS and Your Webapp for GZIP and Caching

To configure IIS, please use this guide from Microsoft http://technet.microsoft.com/en-us/library/cc730629%28v=ws.10%29.aspx. Once successfully configured under system.webServer configuration section, add:

XML
<system.webServer>
    <staticContent>
      <remove fileExtension=".js" />
      <remove fileExtension=".css" />
      <mimeMap fileExtension=".js" mimeType="text/javascript" />
      <mimeMap fileExtension=".css" mimeType="text/css" />
      <clientCache cacheControlCustom="public" 
      cacheControlMode="UseMaxAge" cacheControlMaxAge="500.00:00:00" />
    </staticContent>
    <urlCompression doStaticCompression="true" doDynamicCompression="true" />
</system.webServer>

Minimize Your CSS and JavaScript Files

In order to minimize your CSS and JavaScript files, you will need to install SquishIt and SquishIt MVC extensions. Personally, I prefer SquishIt over ASP.NET Bundle. The following syntax can be used in your views to minimize the CSS files:

CSS
 @(Bundle.Css()
.Add("~/Content/base.css")
.Add("~/Content/CSS/Plugins/BootstrapDatepicker/daterangepicker-bs3.css")
.MvcRender("~/Content/CSS/min/combined_#.css"))

… And for JavaScript:

JavaScript
@(Bundle.JavaScript()
.Add(@"~/Scripts/lib/jquery-2.0.2.min.js")
.Add(@"~/Scripts/lib/jquery.slimscroll.min.js")
.Add(@"~/Scripts/lib/jquery-ui-1.10.3.custom.min.js")
.Add(@"~/Scripts/lib/typeahead.min.js")
.Add(@"~/Scripts/lib/daterangepicker.js")
.Add(@"~/Scripts/lib/moment.min.js")
.MvcRender("~/Scripts/min/combined_#.js"))

Trickiest of all – Cookie Less Domain

For webapps that use Forms authentication, this optimization is mandatory. You may ask why? When the user is authenticated, the web application will create an authentication cookie. The cookie is encrypted for security and the standard size is somewhere around 2KB. Each request that your browser will create while trying to retrieve the static content like: images, CSS files, JavaScript files will contain the cookie. So we have back and forth 2KB per requests. This sucks! We are left with two options:

  • Pay for a content delivery network
  • Trick the browser

The first option is easy. Let's try the second one. Create an CNAME(alias) named cdn.yourwebsite.com in your zone file. Point the alias to your web application domain.

image

Now we will use a modified version of an MVC cache breaker:

C#
namespace System.Web.Mvc
{
    public static class CdnExtensions
    {
        private static readonly Regex ScriptRegex = new Regex
        ("<script.+?src=[\"'](.+?)[\"'].*?>", 
        RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
        private static readonly Regex CssRegex = new Regex("<link.+
        ?href=[\"'](.+?)[\"'].*?>", 
        RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
 
        public static string CdnContent(this UrlHelper url, string link, bool isDynamicResource = false)
        {
            link = link.ToLower();
 
            // last write date ticks to hex
            string cacheBreaker = string.Empty;
 
            if (!isDynamicResource)
            {
                cacheBreaker = Convert.ToString(File.GetLastWriteTimeUtc
                (url.RequestContext.HttpContext.Request.MapPath(link)).Ticks, 16);
            }
 
            link = link.TrimStart(new[] {'~', '/'});
            link = string.Format("{0}/{1}", "BASE_CDN_URL", link);
 
            // returns the file URL in static domain
            return isDynamicResource ? link : string.Format("{0}?v={1}", link, cacheBreaker);
        }
 
        public static MvcHtmlString CdnContent
        (this UrlHelper url, MvcHtmlString mvcLink, bool isDynamicResource = false)
        {
            var context = mvcLink.ToHtmlString().ToLower();
            // match the scripts
            var linksMatches = ScriptRegex.Matches(context);
            var sb = new StringBuilder();
            foreach (Match linkMatch in linksMatches)
            {
                var link = linkMatch.Groups[1].Value;
                link = CdnContent(url, link, isDynamicResource);
                var script = string.Format
                ("<script type='text/javascript' src='{0}'></script>", link);
                sb.AppendLine(script);
            }
            // match the styles
            linksMatches = CssRegex.Matches(context);
            foreach (Match linkMatch in linksMatches)
            {
                var link = linkMatch.Groups[1].Value;
                link = CdnContent(url, link, isDynamicResource);
                var script = string.Format("<link href='{0}' 
                rel='stylesheet' type='text/css' />", link);
                sb.AppendLine(script);
            }
 
            return new MvcHtmlString(sb.ToString());
        }
    }
}

HTML:

HTML
@(Url.CdnContent(Bundle.Css()
.Add("~/Content/base.css")
.Add("~/Content/CSS/Plugins/BootstrapDatepicker/daterangepicker-bs3.css")
.MvcRender("~/Content/CSS/min/combined_#.css")))

… and for JavaScript:

JavaScript
@(Url.CdnContent(Bundle.JavaScript()
.Add(@"~/Scripts/lib/jquery-2.0.2.min.js")
.Add(@"~/Scripts/lib/jquery.slimscroll.min.js")
.Add(@"~/Scripts/lib/jquery-ui-1.10.3.custom.min.js")
.Add(@"~/Scripts/lib/typeahead.min.js")
.Add(@"~/Scripts/lib/daterangepicker.js")
.Add(@"~/Scripts/lib/moment.min.js")
.MvcRender("~/Scripts/min/combined_#.js")))

What is your experience with Web Apps optimization? Happy hacking!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here