Thursday, 3 October 2013

Executing Razor Views outside of a web application with NonHttpRunTimeRazorSupport

NonHttpRunTimeRazorSupport (catchy) is a small library that allows you to use ASP.Net MVC Razor views to render content outside of the context of a running ASP.Net application  with full support for Layouts, _ViewStart inclusion, Partials, built-in HtmlHelpers etc.


Introduction

Razor views are a really nice mechanism for generating web page content within an ASP.Net MVC application. They're so great in fact, that it's a shame to use them just to render HTML to display in a browser. What if we could use them to generate other content within our application, like email messages?

Many applications need to send emails. For something simple, like a one-off message to a site administrator, you might be able to get away with using a StringBuilder to generate the content of the email. If you need to send a range of rich HTML emails and want developers to be able to modify them easily, a full-blown template system like Razor is a much more flexible and maintainable mechanism.

Another common requirement is to send emails in an "offline" scenario. By "offline", I mean outside of the context of an HTTP request in a running ASP.Net application. Maybe you want to send emails within a console app, Windows service or an NServiceBus / MassTransit / Rebus message handler.

NonHttpRunTimeRazorSupport lets you use all of Razor's features outside of a running web app.

There are loads of other things you could use this library for - but email is probably one of the most common requirements it supports.

How it Works

The library is on GitHub at https://github.com/danmalcolm/NonHttpRunTimeRazorSupport. If you know about git, go ahead and clone the repo on your development machine, otherwise you can download a zip file of the source or browse the code directly on GitHub.

This solution uses RazorGenerator, a great tool that allows you to pre-compile Razor views at design time. As you edit your Razor views in Visual Studio, a pre-compiled view (an accompanying C# class file a bit like a web forms Something.aspx.Designer.cs file). Pre-compiled views can be used instead of .cshtml files that are compiled on-the-fly by RazorViewEngine. One thing that the RazorGenerator project is missing is an example of how to render pre-compiled Razor views in the "offline" context we introduced above*.

Razor View templates, whether compiled at design-time by RazorGenerator or at runtime by RazorViewEngine, are instances of WebViewPage. Views of this type are designed to execute in the context of an HTTP request. The standard built-in RazorViewEngine assumes a web request and other supporting infrastructure offered by HttpRuntime to be present when locating and rendering Razor views. 

The main purpose of NonHttpRunTimeRazorSupport is to work around this limitation. A customised ViewEngine and fake implementations of HttpContext (and it's HttpRequest, HttpResponse members) are used to support the execution of Razor Views.

Needless to say, you have to write your templates with this "offline" context in mind. Putting something like @HttpContext.Current.Request.ServerVariables[...] in the middle of your template would result in an exception. Note that the library attempts to provide help here by throwing an exception with an informative message if  any non-supported HttpContext (and family) members are accessed.

Getting Started

See the README for details on how to get up and running.

The repository includes a simple EmailDemo console app, that demonstrates how you might handle sending emails in your application.

The EmailNotificationService class uses Razor templates to render email content, using a convention to locate views based on the name of the model type. Here's how the views are structured:


This foundation would allow you to support a range of emails without adding a great deal of code. For each message type, you would simply need to add a model and Razor templates to render the subject, text and html body content. Here's the code for EmailNotificationService in full:

Look at EmailNotificationService.cs on github if you can't see the code in-line above.

Alternatives

There are other libraries that allow you to use Razor templates to generate email content, such as MvcMailer and ActionMailer, but, they don't fully support sending Razor-templated emails in an "offline" scenario ("Email sending from a background process" is still listed as an upcoming feature on the MvcMailer Wiki page and the standalone version of ActionMailer doesn't appear to support layouts and other Razor features.

What Next?

I'll consider adding a NuGet package once I've got feedback from using this approach on some other projects. Any comments or feedback would be great.

I came up with this solution while working on a project that was already using RazorGenerator. I'm interested in exploring whether it would be possible to bypass RazorGenerator and compile the views on the fly. Compiling Razor views from templates stored in a data store could also be interesting....

Happy mailing!

* I should point out that views compiled with the RazorGenerator "Template" generator can run outside of the context of an ASP.Net application. They are not the same thing as standard MVC Razor views - the declarative Layout property, _ViewStart pages, partials, HtmlHelper / UrlHelper etc are not available.