Over a million developers have joined DZone.

Optimize Your WebApp Like a Pro: ASP.NET MVC Boilerplate

Evolve your approach to Application Performance Monitoring by adopting five best practices that are outlined and explored in this e-book, brought to you in partnership with BMC.

We are living in a time when technology is developing at a fast pace. Today most 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 speedy the internet connection is, if 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 combine 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” 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.

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 Cacheing

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:

<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:

@(Bundle.Css()
.Add("~/Content/base.css")
.Add("~/Content/CSS/Plugins/BootstrapDatepicker/daterangepicker-bs3.css")
.MvcRender("~/Content/CSS/min/combined_#.css"))
… And for 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 Formsauthentication 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 a content delivery network
  • Trick the browser

The first option is easy. Lets 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:

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:

@(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:

@(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!

Learn tips and best practices for optimizing your capacity management strategy with the Market Guide for Capacity Management, brought to you in partnership with BMC.

Topics:

Published at DZone with permission of Marjan Nikolovski. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}