Compacting CSS on-the-fly
I’m currently doing quite a lot of work on my ny MVC-version of Imager Gallery. I still have a lot to do before I have anything to release (or even use myself) though but I thought I could release a few snippets that I’ve found useful.
First out is a HttpHandler that will process .css-files and remove any unused whitespace from the file before sending it to the client. I’ve been meaning to write this for a long time but never really had the time until someone posted a link about it over at ASPSidan.se showing their own implementation. Reading up on his post about it I found that it was based on a solution by Sam Collett. Both implementations lacked a few things that I wanted to have so I sat down with Sams code and started improving it, mainly making it a work as a handler for .css-files instead of a Generic Handler wanting a querystring but also adding caching and compiling the regular expression as a static member.
To use this, just pop it in a .cs-file in your your App_Code folder and add it to your web.config (as described in the comments for the class). You also need to configure your IIS to route .css-files through the asp.net process to make it run (not needed under IIS7 or IIS6 configured for MVC). Hope you find it useful!
using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;
namespace HttpHandlers
{
/// <summary>
/// Handler for compacting css-files on the fly
/// Based on solution by Sam Collett found at
/// http://webdevel.blogspot.com/2007/09/csscompact-webhandler-for-shrinking-css.html
///
/// Needs to be added to web.config under System.Web/httpHandlers like this
/// <add verb="GET,HEAD" path="*.css" type="HttpHandlers.CssCompactHandler"/>
///
/// For debuing purposes you can append a querystring whenbrowsing to your .css-file
/// to skip the compacting. It would look like this:
/// http://mySite/Content/Site.css?noCompact=1
/// </summary>
public class CssCompactHandler : IHttpHandler
{
// Staticly compiled regex to avoid compiling it each request
private static Regex RegexRemoval = new Regex(@"^\s+|/\*([^*\\\\]|\*(?!/))+\*/|\r|\n|\t", RegexOptions.Multiline | RegexOptions.Compiled);
public void ProcessRequest(HttpContext context)
{
HttpResponse Response = context.Response;
HttpRequest Request = context.Request;
Cache Cache = context.Cache;
FileInfo fileInfo = new FileInfo(Request.PhysicalPath);
if (!fileInfo.Exists)
{
Response.StatusDescription = "The server has not found anything that matches the requested URI.";
Response.StatusCode = 404;
Response.End();
}
Response.ContentType = "text/css";
Response.Cache.SetLastModified(fileInfo.LastWriteTime);
// If we are in debugmode (set in web.config) then
// we dont want to cache the css since we are probably
// still in development
if (context.IsDebuggingEnabled)
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
}
else
{
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetExpires(DateTime.Now.AddDays(1));
}
// HEAD requests only need the headers so we skip the
// content here
if (Request.HttpMethod != "HEAD")
{
string cacheName = string.Format("CssCompactHandlerCache_{0}", fileInfo.FullName);
string cacheValue = Cache[cacheName] as string;
if (cacheValue != null && Request.QueryString["noCompact"] == null)
{
Response.Write(cacheValue);
Response.End();
}
using (StreamReader cssStream = fileInfo.OpenText())
{
string cssContent = cssStream.ReadToEnd();
if (Request.QueryString["noCompact"] == null)
{
cssContent = RegexRemoval.Replace(cssContent, "");
// Set up a CacheDependency on the css-file,
// this way the cache will be invalidated if
// we change the file
CacheDependency dependency = new CacheDependency(fileInfo.FullName);
Cache.Add(string.Format("CssCompactHandlerCache_{0}", fileInfo.FullName), cssContent, dependency, DateTime.Now.AddYears(1), Cache.NoSlidingExpiration, CacheItemPriority.AboveNormal, null);
}
Response.Write(cssContent);
}
}
}
public bool IsReusable
{
get
{
return true;
}
}
}
}
Recent Comments