— Tech+Life+Music

Using Razor in Javascript and CSS files

A sample project for this discussion is available over on my Github.

There are countless use cases where I found myself needing to use Razor syntax (in ASP.NET MVC 3+) inside Javascript, CSS and other asset files. While there are many different ways to get Razor output into external JS files (for example, getting a Url.Action() call into a JS file’s jQuery.ajax() call), I find that most feel like beating around the bush. After all, views in MVC don’t always have to be HTML files, at least in the traditional spirit of MVC.

So why not create a Javascript view?

If you think about it, there’s nothing really stopping you from creating a controller, adding in a view, then writing up the view in Javascript syntax (save for the less-than-tasty syntax highlighting and Intellisense in Visual Studio, since it kind of expects that views are HTML files).

The key thing is to modify the Content-Type of the response to the appropriate value, based on what you’re returning. So Javascript files will be text/javascript, CSS files will be text/css. It’s the same basic idea with, for example, returning image data through the wire.

For example, this controller action:

public class AssetController : Controller {
 
    public ActionResult Styles () {
 
        Response.ContentType = "text/css";
 
        var model = Context.GetEntity();
        return View(model);
    }
}

… can have the following view:

@model Entity;
 
#entity {
    color : @Model.Color;
    background : url(@Model.BackgroundImage.Url);
}

… and can be wired into your markup just like any other stylesheet:

<link rel="stylesheet" href="@Url.Action("Styles","Asset")" />

Taking all that a bit further, we can write up an Attribute to easily maintain the corresponding Content-Type to each action:

public class ContentType : FilterAttribute, IActionFilter 
{
    private string contentType;
    public ContentType (string ct) {
        this.contentType = ct;
    }
 
    public void OnActionExecuted(ActionExecutedContext context) { /* nada */ }
    public void OnActionExecuting(ActionExecutingContext context) {
        context.HttpContext.Response.ContentType = this.contextType;
    }
}

… and just mark our actions with it:

[ContentType("text/css")]
public ActionResult Styles() {
    // ...
    return View();
}

All that notwithstanding though, you’ll still have to deal with two problems when you do this:

  1. Visual Studio assumes that all views are HTML. You don’t get accurate color highlighting and Intellisense in your asset views. I don’t know about you, but the countless ways that VS pushes back when I do this kind of irks me off.
  2. You don’t get bundling (in ASP.NET MVC 4), and unless you wire it up somehow with WebGrease, you don’t get minification either. Caching is possible, but the whole point of dynamic assets are just that: they’re dynamic. You still can cache via normal ASP.NET MVC methods if, for example, you can manage to scope your use cases into unique URLs (via query strings probably), or you could opt to write your own caching mechanism for this.

If you want to try this out, I’ve prepared a very simple VS 2012 MVC 4 project ready to go over on my Github. Feel free to clone, fork, and/or contribute!

With all that said and done though, I believe that this is the way to correctly use views in an MVC framework, and that Visual Studio should perhaps allows for better leniency for use cases such as this. I’d love to hear your ideas on the topic though.

Feel free to start a discussion.
Submit comment