Feb 17, 2013Today the ASP.NET and Web Tools 2012.2 update was released. Go download it right now! It contains a lot of new and updated Visual Studio tooling features including: First class LESS editor Knockout.js Intellisense Paste JSON as classes CoffeeScript editor Mustache/Handlebars/JsRender syntax highlighting Page Inspector Live CSS auto-sync as you type JavaScript selection mapping and callstack Some of them are features that started their lives in Web Essentials 2012 and are now ported into an official release. Every time we move features from the experimental Web Essentials extension into the official product, we try to make the transition as smooth as possible. However, this time we moved some substantial features that are mutually exclusive – they register the same MEF components and that leads to this rather ugly exception: This exception occurs when Web Tools 2012.2 is installed and you haven’t updated Web Essentials to version 2.5. The solution is simply to update Web Essentials. Go do that now. If you don’t have Web Essentials installed at all, you won’t get this error because then there is no conflict.
* $4.95/month ASP.NET Hosting with FREE SQL 2012 DB! – Click Here!
Jan 16, 2013Nerd alert: This post is only for crazy website performance freaks. Proceed at your own risk. The holy grail for us crazy website performance freaks is to reach a perfect score of 100/100 in Google Page Speed without sacrificing important features of the website we’re building. One of those important features is Google Analytics. Gotta have Google Analytics, right?! Let’s say that you’ve optimized your website to the perfect score of 100/100 and now decide to add Google Analytics. Too bad, your score is now 98/100. That’s because the ga.js JavaScript file loaded from Google’s servers doesn’t have a far-future expires HTTP header. To get the perfect score back, we need to fix this problem. Getting back to 100/100 Here’s a solution that I use on one of my websites. It involves the addition of a single .ashx file to your web project. It’s isolated, safe to use and works. The role of the .ashx file is to act as a proxy to the Google Analytics ga.js script file by downloading its content and serving it with a sufficient expires header. It caches the script file on the server, so it doesn’t have to download the ga.js file every time a visitor hits your website. Step 1: Add an empty .ashx file (Generic Handler) to the root of you project and call it ga.ashx. Step 2: Copy this code into the .ashx file you created in step 1. Step 3: Modify the Google Analytics tracking script on your page to look like this: <script> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-12345678-9']); _gaq.push(['_trackPageview']); </script> <script src="/ga.ashx" async="async" defer="defer"></script> Voila! That’s it. You now have the perfect score back. Optional step 4: Don’t like the .ashx extension? Then change it to .js by adding this to the web.config: <rewrite> <rules> <rule name="analytics"> <match url="^ga.js" /> <action type="Rewrite" url="ga.ashx" /> </rule> </rules> </rewrite> You need to add it to the <system.webServer> section of web.config. Remember to run the AppPool in Integrated Pipeline Mode for the <system.webServer> section to kick in. Then just change the script tag to this: <script src="/ga.js" async="async" defer="defer"></script> Wait, is this a good idea? I’ll let you be the judge of that. What I can tell you is that this exact code is running on one of my websites and it reports data to Google Analytics just fine. Does it work? Yes Is it cool? Totally Should I do it? N/A
* $4.95/month ASP.NET Hosting with FREE SQL 2012 DB! – Click Here!
Jan 15, 2013In the beginning of January, we released the Web Developer Checklist with great interest from the general web community. The checklist helps raising awareness of common best practices for building websites. It was always the plan to branch the checklist out into technology specific checklists to make it even easier to apply all the best practices. Today we’re excited to announce the first technology specific checklist – the ASP.NET Developer Checklist. It contains links to many ASP.NET specific tools and solutions to common problems. Not only is it a great tool for all ASP.NET developers to learn from, but also to track the progress of implementing the various best practices. We hope it will be received well and add value to the millions of ASP.NET developers worldwide. In the meantime we will be working on finishing another ASP.NET specific checklist that focuses on website performance. ASP.NET is just the first of many web technologies to receive its own checklist, so remember to check the Web Developer Checklist often for updates.
* $4.95/month ASP.NET Hosting with FREE SQL 2012 DB! – Click Here!
Jan 13, 2013Rule #1 in website optimization is to enable GZip compression on the web server. This is very easy using web.config as explained here. However, some web servers have disabled automatic compression of JavaScript files, because they are served with the content type: application/x-javascript. For these web servers we can use a web.config trick to change the content type of JavaScript files to text/javascript. This is a completely valid content type supported by all browsers. Just paste the following XML snippet in to your web.config’s <system.webServer> section. <staticContent> <remove fileExtension=".js"/> <mimeMap fileExtension=".js" mimeType="text/javascript" /> </staticContent> Chances are that you don’t have this issue, since it seems to only apply to some hosters, but now you know how to get around it should you ever end up in the situation with uncompressed JavaScript files.
* $4.95/month ASP.NET Hosting with FREE SQL 2012 DB! – Click Here!
Jan 9, 2013Optimizing for website performance includes setting long expiration dates on our static resources, such s images, stylesheets and JavaScript files. Doing that tells the browser to cache our files so it doesn’t have to request them every time the user loads a page. This is one of the most important things to do when optimizing websites. In ASP.NET on IIS7+ it’s really easy. Just add this chunk of XML to the web.config’s <system.webServer> element: <staticContent> <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365:00:00" /> </staticContent>
.csharpcode, .csharpcode pre
{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{background-color: #f4f4f4;width: 100%;margin: 0em;}
.csharpcode .lnum { color: #606060; } The above code tells the browsers to automatically cache all static resources for 365 days. That’s good and you should do this right now. The issue becomes clear the first time you make a change to any static file. How is the browser going to know that you made a change, so it can download the latest version of the file? The answer is that it can’t. It will keep serving the same cached version of the file for the next 365 days regardless of any changes you are making to the files. Fingerprinting The good news is that it is fairly trivial to make a change to our code, that changes the URL pointing to the static files and thereby tricking the browser into believing it’s a brand new resource that needs to be downloaded. Here’s a little class that I use on several websites, that adds a fingerprint, or timestamp, to the URL of the static file. using System; using System.IO; using System.Web; using System.Web.Caching; using System.Web.Hosting; public class Fingerprint { public static string Tag(string rootRelativePath) { if (HttpRuntime.Cache[rootRelativePath] == null) { string absolute = HostingEnvironment.MapPath("~" + rootRelativePath); DateTime date = File.GetLastWriteTime(absolute); int index = rootRelativePath.LastIndexOf('/'); string result = rootRelativePath.Insert(index, "/v-" + date.Ticks); HttpRuntime.Cache.Insert(rootRelativePath, result, new CacheDependency(absolute)); } return HttpRuntime.Cache[rootRelativePath] as string; } } All you need to change in order to use this class, is to modify the references to the static files. Modify references Here’s what it looks like in Razor for the stylesheet reference: <link rel="stylesheet" href="@Fingerprint.Tag("/content/site.css")" />.csharpcode, .csharpcode pre
{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{background-color: #f4f4f4;width: 100%;margin: 0em;}
.csharpcode .lnum { color: #606060; }…and in WebForms:<link rel="stylesheet" href="<%=Fingerprint.Tag("/content/site.css") %>" />.csharpcode, .csharpcode pre
{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{background-color: #f4f4f4;width: 100%;margin: 0em;}
.csharpcode .lnum { color: #606060; }The result of using the FingerPrint.Tag method will in this case be:<link rel="stylesheet" href="/content/v-634933238684083941/site.css" />.csharpcode, .csharpcode pre
{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{background-color: #f4f4f4;width: 100%;margin: 0em;}
.csharpcode .lnum { color: #606060; }Since the URL now has a reference to a non-existing folder (v-634933238684083941), we need to make the web server pretend it exist. We do that with URL rewriting.
URL rewrite
By adding this snippet of XML to the web.config’s <system.webServer> section, we instruct IIS 7+ to intercept all URLs with a folder name containing “v=[numbers]” and rewrite the URL to the original file path.<rewrite> <rules> <rule name="fingerprint"> <match url="([\S]+)(/v-[0-9]+/)([\S]+)" /> <action type="Rewrite" url="{R:1}/{R:3}" /> </rule> </rules> </rewrite>You can use this technique for all your JavaScript and image files as well.
The beauty is, that every time you change one of the referenced static files, the fingerprint will change as well. This creates a brand new URL every time so the browsers will download the updated files.
FYI, you need to run the AppPool in Integrated Pipeline mode for the <system.webServer> section to have any effect.
* $4.95/month ASP.NET Hosting with FREE SQL 2012 DB! – Click Here!
Jan 7, 2013So you just built a website and are about to make it public to the world. You go through a few checks to make sure that everything works as expected. Perhaps run HTML validation and other similar services. But are you sure you remembered to implement all the best practices? Does it look good on mobile devices, in Windows 8 snapped view and what about basic accessibility? Enter Web Developer Checklist We’ve tried to come up with the complete list of tasks and checks that every web developer should go through when developing any kind of website. The Web Developer Checklist is structured in a way that makes it really easy to follow the progress as well as getting help to perform all the checks. Give it a try and let me know what you think.
* $4.95/month ASP.NET Hosting with FREE SQL 2012 DB! – Click Here!
Oct 18, 2011At //build and today at VS Live I gave a talk about performance optimizing ASP.NET web applications using bundling, minification and other cool tricks.
See the video presentation
The Visual Studio 2010 extension I used were:Web Essentials
Image Optimizer
Web Standards Update
The demo website can now be downloaded here. It runs in ASP.NET 4.0
Optimize Website.zip (4.39 mb)
* $4.95/month ASP.NET Hosting with FREE SQL 2012 DB! – Click Here!
Dec 1, 2010HTML5 and CSS3 introduces some new file types that enables us to create even better websites. We are now able to embed video, audio and custom fonts natively to any web page. Some of these file types are relatively new and not supported by the IIS web server by default. It’s file types like .m4v, .webm and .woff. When a request is made to the IIS for these unsupported file types, we are met with the following error message: HTTP Error 404.3 - Not Found The page you are requesting cannot be served because of the extension configuration. If the page is a script, add a handler. If the file should be downloaded, add a MIME map. The problem is that the IIS doesn’t know how to serve these new files unless we tell it how. This can be easily done in the web.config’s <system.webServer> section by adding the following snippet: <staticContent> <mimeMap fileExtension=".mp4" mimeType="video/mp4" /> <mimeMap fileExtension=".m4v" mimeType="video/m4v" /> <mimeMap fileExtension=".ogg" mimeType="video/ogg" /> <mimeMap fileExtension=".ogv" mimeType="video/ogg" /> <mimeMap fileExtension=".webm" mimeType="video/webm" /> <mimeMap fileExtension=".oga" mimeType="audio/ogg" /> <mimeMap fileExtension=".spx" mimeType="audio/ogg" /> <mimeMap fileExtension=".svg" mimeType="images/svg+xml" /> <mimeMap fileExtension=".svgz" mimeType="images/svg+xml" /> <remove fileExtension=".eot" /> <mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" /> <mimeMap fileExtension=".otf" mimeType="font/otf" /> <mimeMap fileExtension=".woff" mimeType="font/x-woff" /></staticContent>.csharpcode, .csharpcode pre
{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{background-color: #f4f4f4;width: 100%;margin: 0em;}
.csharpcode .lnum { color: #606060; }The above snippet includes support for most video, audio and font file types used by HTML5 and CSS3.
* $4.95/month ASP.NET Hosting with FREE SQL 2012 DB! – Click Here!
Aug 15, 2010In part 1 of this series, we looked at some tricks to optimize the performance of any website running in IIS 7 by only modifying the web.config. In this part we will focus on handling browser caching issues and optimize the number of JavaScript and CSS files loaded from an ASP.NET website.
NB! All the code (a single .cs file of 125 lines) is included in the zip file at the bottom of this post.
Browser caching
In part 1, we looked at how it was possible to set an expiration header to any static file such as JavaScript and CSS files, so the browser would cache them for a long time and thereby optimize both for bandwidth and the number of requested files going from server to browser.
The problem with setting a browser cache expiration date of i.e. a JavaScript file to a year in the future becomes clear when you change the file before it expires in your visitor’s browsers. They simply won’t see the changes until they either clear their cache or hits F5 manually.
Adding the version number
The only viable way to maintain a far-in-the-future expiration date is to change the URL of the file when the file changes. So instead of including script files like so:
<script type="text/javascript" src=”/scripts/global.js"></script>…we really want to get a version number included in the src attribute, like so:
<script type="text/javascript" src=/scripts/v_634174870689341736/global.js"></script>The problem with this is that ASP.NET doesn’t have any feature that will inject a version number, so we have to create that our selves. It is very simple to do so by looking at when the file was last changed and then retrieve the ticks from that date. In the zip file below you’ll find a method that does exactly that and it can be used like so:
<script type="text/javascript" src="<%=BundleHelper.InsertFile("/scripts/global.js") %>"></script>The BundleHelper.InsertFile method is one you want to use for Stylesheets as well, like so:
<link rel="Stylesheet" href="<%=BundleHelper.InsertFile("includes/style.css") %>" type="text/css" />Ok, now all our JavaScript and stylesheet references have the version number in the path. Next thing to look at is getting it working with the updated non-existing path.
The HTTP handler
To be able to serve the correct file even with the version number in the path, we need to register an HTTP handler in the web.config’s <system.webServer> section like so:
<add name="ScriptBundler" verb="GET,HEAD" path="*.js" type="FileBundleHandler" /><add name="CssBundler" verb="GET,HEAD" path="*.css" type="FileBundleHandler" />The handler we just registered is called FileBundleHandler and knows how to filter out the version number to find the right file. It supports both .css and .js files. The handler also makes sure to both output cache and browser cache correctly. Just add the FileBundleHandler.cs file from the zip file to your website and you are up and running.
Now the browser cache issue has been resolved by adding a version number to the path of the included file and by adding an HTTP handler that knows how to remove it again when serving the file.
Bundle multiple files
Another common website performance issue is that there are many JavaScript and CSS files included on a page. This scenario results in the browser have to download a lot of extra files and that all slows down the performance of a website. The solution to this is also very simple when you’ve first completed the above steps to register the HTTP handler in web.config and called the BundleHelper.InsertFile method when inserting JavaScript and CSS files.
The folder structure convention
There are many ways of bundling files into a single request, like Justin Etheredge’s Squisher. For this example I have chosen a convention based approach because that doesn’t require any code to implement.
Any given ASP.NET website might have a folder structure similar to this:The folder convention supported in the FileBundleHandler lets you reference a folder instead of just a file. Both the HTTP handler and the BundleHelper.InsertFile understand when a folder is referenced and automatically bundles all the .js or .css files to a single response. So in order to bundle all the files in a given folder, simply reference the folder name and add the extension of the types of files you want bundled. Having the folder structure above, you can add a bundle like so:
<script type="text/javascript" src="<%=BundleHelper.InsertFile("/scripts/common.js") %>"></script>Notice that the file /scripts/common.js doesn’t exist, but the folder /scripts/common does. By adding .js at the end, we tell the HTTP handler to look for all files with the same file extension – in this case .js files. It bundles all the files in alphabetical order and serve the as a single response. For security reasons, the HTTP handler will only serve .css and .js extensions.
Minification
Since we are now running all JavaScript and stylesheet files in bundles and through the HTTP handler, it makes sense to also look at the content of the files to optimize even further.
For this example I’m using the Microsoft Ajax Minifier (MAM), which is a single .dll file capable of minifying both JavaScript and stylesheets. The MAM is my favorite JavaScript minifier since it not only removes whitespace, it also rewrites variable and function names and a lot of other things as well. For me it has proven a better choice than the YUI Compressor and Google Closure Compiler. The stylesheet minifier feature of MAM also looks very nice, but I have honestly never used it before except for this example.
Basically what MAM does is that it optimizes and removes unwanted whitespace from both JavaScript and stylesheets. The HTTP handler makes use of MAM for both single files and bundled ones, so you get full benefit no matter your scenario.
Summary
No matter if you use the website model, the web application model or ASP.NET MVC you are now able to utilize the browser cache to the fullest. Furthermore, by bundling your files using the folder convention you can minimize the number of requests sent by the browser. Both JavaScript and stylesheet files are also minified and optimized for even smaller file sizes sent over the wire.
It's worth noticing that the output caching respects file changes and therefore refreshes evertime changes are made to the JavaScript and CSS files tunnelled through this code.
Following the techniques in part 1 combined with this example will improve any website’s server-to-browser performance substantially.
Implementation
Download the zip file below and place the AjaxMin.dll in your bin folder.
Then place the FileBundleHandler.cs in your App_Code folder if you use the website model – otherwise place it where ever it makes sense in your structure.
Now register the HTTP handler in your web.config under the <system.webServer> section like so:<add name="ScriptBundler" verb="GET,HEAD" path="*.js" type="FileBundleHandler" /><add name="CssBundler" verb="GET,HEAD" path="*.css" type="FileBundleHandler" />The last thing you need is to start using the BundleHelper.InsertFile method on your pages for both JavaScript and stylesheets like so:<script type="text/javascript" src="<%=BundleHelper.InsertFile("/scripts/common.js") %>"></script><link rel="Stylesheet" href="<%=BundleHelper.InsertFile("styles/global.css") %>" type="text/css" />Download
FileBundler.zip (89,95 kb)
* $4.95/month ASP.NET Hosting with FREE SQL 2012 DB! – Click Here!
Jul 7, 2010In this first installment of performance tuning tricks for ASP.NET and IIS 7 we will look at some of the easy, yet powerful possibilities in the web.config file. By taking advantage of these few tricks we can increase the performance of any new or existing website without changing anything but the web.config file.
[More]* $4.95/month ASP.NET Hosting with FREE SQL 2012 DB! – Click Here!