Manual Minification In ASP.NET

IIS can perform compression on the fly and probably works much better than performing minification manually.

When people render their WebForms and MVC Views, they send their finished HTML back down to the page. The HTML contains all the markup required to run in the browser. Every developer knows all this though (and if not... well...).

Part of the whole grand ASP.NET pipeline is the Response.Filter. A seemingly innocent property with a lot of potential. Enough boasting about this guy, let's get onto the code and see what it can do.

First, you're going to need to create a custom stream. I'm hiding all the inherited members because they aren't going to mean much in this example, but don't forget that you need them.

With our custom stream, add a hidden property called the UnderlyingStream (honestly call it whatever you want). Lastly, accept a stream in the constructor and assign it to the underlying stream when you instantiate the new stream you're building.

Lastly, we're going to override .Write() routine to actually minify our output. A couple simple Regular Expressions and we can chop out most of the whitespace.

public class MinifyFilter : Stream {

    //inherited members from Stream class - Don't forget
    //See download for more information
    //...

    //The real output stream
    private Stream _UnderlyingStream;

    //Make sure to pass in the current Response.Filter
    public MinifyFilter(Stream stream) {
        this._UnderlyingStream = stream;
    }

    //Override our write event
    public override void Write(byte[] buffer, int offset, int count) {

        //clean out the whitespace characters
        string html = Encoding.UTF8.GetString(buffer);
        html = Regex.Replace(html, @"\n|\t", " ");
        html = Regex.Replace(html, @">\s+<", "><").Trim();
        html = Regex.Replace(html, @"\s{2,}", " ");

        //write the new html
        byte[] output = Encoding.UTF8.GetBytes(html);
        this._UnderlyingStream.Write(output, 0, output.Length);

    }
}

This is a very streamlined version of the code that doesn't take everything into account. You might want to put some special attention into preventing formatting of text in the pre tags or code tags, but it's a great start.

Now using it is as simple as...

//MVC Example: At the start of a controller action
public ActionResult Index() {
  this.Response.Filter = new MinifyFilter(this.Response.Filter);
  //...rest of the action
  return this.View();
}

//WebForms Example: At the Page_Init
protected void Page_Init(object sender, EventArgs e) {
  this.Response.Filter = new MinifyFilter(this.Response.Filter);
  //rest of the Page_Init code
}

You could also assign this to a more global event that runs for all requests, but at that point you will need to write code to make sure you don't accidentally minify something like an image or a file (things that can't be compressed by just removing white-spaces).

You might wonder how much a little script like this would save. The answer is simple - It depends. Clearly, pages with a lot of white spaces will find a lot more gain. Sometimes it's 1 or 2 KBs (which will add up over the course of a month). Sometimes though, especially on larger pages, you can see some real savings.

Keep in mind that this code isn't bullet-proof, but instead it's here to get you started. You can continue to tweak the .Write() method to make sure that your code behaves the way you would like.

May 8, 2009

Manual Minification In ASP.NET

Post titled "Manual Minification In ASP.NET"