Generate HTML in the backend

Olav Nybø

Recently I was working on a Azure Web Job that was reading from an Azure Queue. The requirements of this job was that it should read the queue messages, do some processing and then generate and send an HTML-email based on the incoming message.
Since the request isn't a regular HTTP request to a ASP.Net site it's not easy to use the regular razor view engine to generate the HTML for the email.

RazorEngine is one possible solution, but a cursory look at the documentation that talked about temporary files and the need to clean up these made me a bit skeptical.

Then I found DotLiquid which is exactly what I needed. A small simple library that given a template and some data will output a string containing the HTML.

DotLiquid seems to be geared towards letting users author and upload their own templates. Because of this the library is very focused on ensuring that no information is accidentally exposed through the generated HTML.
The developer is required to explicitly mark each property of all classes as safe for exposure through dotliquid before referencing them in templates.
Security is of course important when needed, but in my case I had a view model that only contained the data I wanted to show, but fortunately it is quite easy to extend dotliquid to allow for safe view models.

Read the rest of the post to see how this is done.

The view model

The example we are going to use has a view model as shown in the diagram below.

test

We are going to display information about tv-shows. Each show has a cast, which is a list of actors who are described by their first- and last name and the date of birth.

The template

For this example we are just creating a page that has the show name in an header over a table of the cast like this.

....
<h1>Show: {{name}}</h1>
<h4>Actor information</h4>
<table>
    {% for actor in cast %}
    <tr>
        <td>First Name</td>
        <td>&nbsp;{{actor.first_name}}</td>
    </tr>
    ....
    {% endfor %}
 </table>

This is not a complete template file, I have marked where I have skipped details with ....
For a complete example see the full sample code on my dotliquid viewmodel github repo.
The template language is quite similar to moustache and angularJS syntax using double curly brackets for expressions that may be replaced by text and tags inside {% %} for special functions such as the for loop over the actors shown in the example above.
See liquid documentation for full documentation of the template syntax.

The code

The code consist of 3 main steps.

  1. Register all types and their properties that should be available for use in the template
  2. Create a template object by parsing a template
  3. Pass the view model into the Render method of the template object created in the previous step.

This is show in the code below:

// Register the types and their properties as safe
Template.RegisterSafeType(typeof(Show),
            new[] { "Name", "Cast" });
Template.RegisterSafeType(typeof(Actor),
            new[] { "FirstName", "LastName", "Born" });

// parse the HTML template
var template = Template.Parse(
     new StreamReader("EmailTemplate.html").ReadToEnd());

var show = GetTvShow(); // build the view model

// render the view model using the parsed template
var result = template.Render(
            Hash.FromAnonymousObject(show));

Make it simpler

As we saw in the previous section, there will be quite a lot of registering of safe types and listing of properties for anything but the smallest models.
Also calling the Render function is a bit more complicated than I like.
But this can all be very easily fixed using a bit of reflection and an extension method.

Fixing the registration

The RegisterViewModel below will register a complete view model hierarchy including all properties of all the classes in the view model.

public static void RegisterViewModel(Type rootType)
{
   rootType
      .Assembly
      .GetTypes()
      .Where(t => t.Namespace == rootType.Namespace)
      .ToList()
      .ForEach(RegisterSafeTypeWithAllProperties);
}

public static void RegisterSafeTypeWithAllProperties(Type type)
{
   Template.RegisterSafeType(type,
      type
      .GetProperties()
      .Select(p => p.Name)
      .ToArray());
}

This extension method fixes the Render call.

public static string RenderViewModel(this Template template, object root)
{
   return template.Render(
      Hash.FromAnonymousObject(root));
}

Using these bits we end up with a very simple solution to generate HTML from a view model.

var show = GetTvShow();

LiquidFunctions.RegisterViewModel(typeof(Show));

var template = Template.Parse(
   new StreamReader("EmailTemplate.html")
.ReadToEnd());

var result = template.RenderViewModel(show);

The code has become a lot simpler. This comes with a cost of course. If this is used in combination with templates authored by users it becomes extremely important that we know that the view model does not contain any data can't be displayed and that it has no references to any such data.
The RegisterViewModel function will register all types in the same namespace as the root type that we pass in. This means that we must be very careful that all types in this namespace are safe for registration with the templating engine.

Keeping that final warning in mind, KISS FTW!

Complete example at: https://github.com/onybo/DotLiquid-ViewModel

dotliquid, HTML5, templating, asp.net">
comments powered by Disqus