Elegant Code, Raccoon Blog’s CSS Controller

time to read 4 min | 630 words

The following piece of code is responsible for the CSS generation for this blog:

public class CssController : Controller
{
     public ActionResult Merge(string[] files)
     {
         var builder = new StringBuilder();
         foreach (var file in files)
         {
             var pathAllowed = Server.MapPath(Url.Content("~/Content/css"));
             var normalizeFile = Server.MapPath(Url.Content(Path.Combine("~/Content/css", file)));
             if (normalizeFile.StartsWith(pathAllowed) == false)
             {
                 return HttpNotFound("Path not allowed");
             }
             if (System.IO.File.Exists(normalizeFile))
             {
                 Response.AddFileDependency(normalizeFile);
                 builder.AppendLine(System.IO.File.ReadAllText(normalizeFile));
             }
         }

         Response.Cache.VaryByParams["files"] = true;
         Response.Cache.SetLastModifiedFromFileDependencies();
         Response.Cache.SetETagFromFileDependencies();
         Response.Cache.SetCacheability(HttpCacheability.Public);

         var css = dotless.Core.Less.Parse(builder.ToString(), new DotlessConfiguration());

         return Content(css, "text/css");
     }
}

There are a lot of things going on in a very short amount of code. The CSS for this blog is defined as:

<link rel="stylesheet" type="text/css" href="/blog/css?files=ResetCss.css&files=custom/ayende.settings.less.css&files=base.less.css&files=custom/ayende.less.css">

This means that while we have multiple CSS files that make maintaining things easier, we only make a single request to the server to get all of them.

Next, and important, we have a security check that ensures that only files from the appropriate path can be served. If you aren’t in the CSS directory, you won’t be returned.

Then we have a lot of code that is related to caching, which basically means that we will rely on the ASP.Net output cache to do everything for us. The really nice thing is that unless the files change, we will not be executing this code again, rather, it would be served directly from the cache, without any computation on our part.

All in all, I am more than happy with this code.