I love ASP.NET MVC and I feel it is the future of ASP.NET Web Development, but what I don’t like is trying to get your pages to look nice and I’m not talking about visually for the end user, that actually is very easy and enjoyable. If you are like me, then you probably spend hours looking at the source of web pages. I consider myself a source snob, always wanting to see properly indented tags and elements. Below you’ll see what I’m talking about, a nightmare of bad HTML output. ASP.NET MVC doesn’t modify your HTML, which is great for web developers but it can also frustrate the same people if they suffer from being source snobs. So how do we address this issue without spending hours indenting views and thinking of nesting scenarios. There are two approaches we could take.

        <div id="main">

            <h2>About</h2>
                    <div>
<div>
                                    <div>
<div>
            <div>
<p>
    Put content here.
</p>
            </div>
</div>
                                </div>
                </div>
</div>

HttpModule

This was the standard way of doing it in a ASP.NET Web Form application. Before any html is spit out to the Response stream, you would modify it to meet your needs. This is partly the right approach for the MVC solution, but HttpModules are indiscriminate and will fire every time a request comes in. You can put logic in our httpmodule but what you will find is that it is very difficult to tell the difference between a ViewResult, JSonResult, or FileResult in an HttpModule.

ActionFilter

We get all the advantages of an HttpModule with the ability to discriminate, either by placing the filter only on actions that return a ViewResult or by accessing properties in the context passed to the filter. So this is the approach to go with, now let’s look at some code.

The Solution

For this solution I am using TidyNet, a great library that let’s you clean up HTML. There are a ton of features in this library but what I am concerned with is indenting, clean html, and making sure there are not stupidly placed tags (look at the above code, we don’t really need all those divs).

So let’s start by thinking about our ActionFilter. We need to access the response stream and modify the HTML right before output. We also need to take into consideration that ViewResults (output that is in html) is our only concern. Other results like JsonResult will be ignored since they aren’t HTML. We also want this to happen after the action is executed (although the approach I’ll take, it probably doesn’t matter). Beep Boop Beep, after some computing we have our action filter. Let’s look at the meat of the filter.

    public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (filterContext.Result is ViewResult)
            {
                var tidy = new Tidy
                               {
                                   Options =
                                       {
                                           DocType = DocType,
                                           DropFontTags = DropFontTags,
                                           LogicalEmphasis = LogicalEmphasis,
                                           XmlOut = XmlOut,
                                           Xhtml = Xhtml,
                                           IndentContent = IndentContent,
                                           HideEndTags = HideEndTags,
                                           MakeClean = MakeClean,
                                           TidyMark = TidyMark,
                                       }
                               };

                filterContext.RequestContext.HttpContext.Response.Filter =
                    new HtmlTidyFilter(filterContext.RequestContext.HttpContext.Response.Filter, tidy);
            }
        }

For this solution, we attach a filter to the response which will run at the time the response is written. Cool right? Let’s look at the meat of the filter itself, which is stream heavy but pretty straight forward. We get the HTML, clean the HTML, and then write the HTML.

            public override void Write(byte[] buffer, int offset, int count)
            {
                var data = new byte[count];
                Buffer.BlockCopy(buffer, offset, data, 0, count);
                string html = Encoding.Default.GetString(buffer);

                using (var input = new MemoryStream())
                {
                    using (var output = new MemoryStream())
                    {
                        byte[] byteArray = Encoding.UTF8.GetBytes(html);
                        input.Write(byteArray, 0, byteArray.Length);
                        input.Position = 0;
                        _tidy.Parse(input, output, new TidyMessageCollection());

                        string result = Encoding.UTF8.GetString(output.ToArray());

                        byte[] outdata = Encoding.Default.GetBytes(result);
                        _stream.Write(outdata, 0, outdata.GetLength(0));
                    }
                }
            }

So now that we have this super sweet ActionFilter, let’s use it. I place the attribute like such.

    [TidyHtml]
    [HandleError]
    public class HomeController : Controller
    {     // Controller Actions
           ...
     }

When all is run and the response is written to the page, we have the following output.

    <div id="main">
        <h2>
          About
        </h2>
        <div>
          <p>
            Put content here.
          </p>
        </div>
        <div id="footer">
        </div>
      </div>

Awesome!!! Clean HTML that makes me feel all warm and fuzzy inside and makes the web designers smile. If you are worried about this running everytime you hit an action then I suggest you use the OutputCacheActionFilter so that your pretty HTML is cached. Download the example and feel free to modify it. I exposed certain properties for TidyNet, but you can remove or add the ones you can see using in your projects. Have fun.

Aqua Bird Clean Html ActionFilter (318 kb)

Update : There is a bug in Tidy.Net that puts a break in your textarea tags. Your textareas will still work, but it will have an indent at the beginning. This can be very annoying.