Dissecting Razor, part 7: Helpers

We’ll continue our trek into Razor’s class-level features with helpers.

Helpers are one of Razor’s unique features.  They encapsulate blocks of HTML and server-side logic into reusable page-level methods. 

You can define a helper by writing @helper MethodName(parameters) { ... }.  Inside the code block, you can put any markup or server-side code.  The contents of a helper are parsed as a code block (like the contents of a loop or if block), so any non-HTML-like markup must be surrounded by <text> tags or prefixed by the @: escape.

Here is a simple example:

<!DOCTYPE html>
<html>
    <body>
        @helper NumberRow(int num) {
            var square = num * num;
            <tr>
                <td>@num</td>
                <td>@square</td>
                <td>@(num % 2 == 0 ? "Even" : "Odd")</td>
            </tr>
        }
        <table>
            <thead>
                <tr>
                    <th>Number</th>
                    <th>Square</th>
                    <th>Eveness</th>
                </tr>
            </thead>
            <tbody>
                @for (int i = 0; i < 10; i++) {
                    @NumberRow(i)
                }
            </tbody>
        </table>
    </body>
</html>

Note that code statements (such as the square declaration) can go directly inside the helper body without being wrapped in code blocks – the direct contents of the helper is a code block, not markup.  Like any other code block, HTML-like markup is automatically treated as markup instead of code.

Like @functions blocks, helper methods can go anywhere in the source file; the physical location of the block is ignored.

Here is the generated source for the above example: (with blank lines and #line directives stripped for clarity)

public class _Page_Razor_Helpers_cshtml : System.Web.WebPages.WebPage {

    public System.Web.WebPages.HelperResult NumberRow(int num) {
        return new System.Web.WebPages.HelperResult(__razor_helper_writer => {

            var square = num * num;

            WriteLiteralTo(@__razor_helper_writer, "            <tr>\r\n                <td>");
            WriteTo(@__razor_helper_writer, num);

            WriteLiteralTo(@__razor_helper_writer, "</td>\r\n                <td>");
            WriteTo(@__razor_helper_writer, square);

            WriteLiteralTo(@__razor_helper_writer, "</td>\r\n                <td>");
            WriteTo(@__razor_helper_writer, num % 2 == 0 ? "Even" : "Odd");

            WriteLiteralTo(@__razor_helper_writer, "</td>\r\n            </tr>\r\n");

        });
    }
    public _Page_Razor_Helpers_cshtml() {
    }
    protected ASP.global_asax ApplicationInstance {
        get {
            return ((ASP.global_asax)(Context.ApplicationInstance));
        }
    }
    public override void Execute() {
        WriteLiteral("<!DOCTYPE html>\r\n<html>\r\n    <body>\r\n        ");
        WriteLiteral("\r\n        <table>\r\n            <thead>\r\n                <tr>\r\n                   " +
        " <th>Number</th>\r\n                    <th>Square</th>\r\n                    <th>E" +
        "veness</th>\r\n                </tr>\r\n            </thead>\r\n            <tbody>\r\n");

        for (int i = 0; i < 10; i++) {
            Write(NumberRow(i));
        }

        WriteLiteral("            </tbody>\r\n        </table>\r\n    </body>\r\n</html>\r\n");
#error

    }
}

Helpers are compiled as class-level methods that take a parameter set and return a System.Web.WebPages.HelperResult.  (This class name is configured by the RazorHostFactory)

Notice the the contents of the helper method are inside a lambda expression that takes a parameter named __razor_helper_writer.  This construction allows the helper to write directly to the HTTP response stream instead of assembling a giant string and then writing the string all at once.

The HelperResult constructor takes an Action<TextWriter> which contains the contents of the helper block.  The class implements IHtmlString and calls the action from the constructor to generate HTML.  However, under normal circumstances, this IHtmlString implementation is never called.

Calls to helper methods (@NumberRow(i)) are passed to the Write(HelperResult) overload.  This overload calls HelperResult.WriteTo(writer), which passes the page’s TextWriter directly to the helper’s lambda expression.  Thus, the lambda expression can write directly to the page’s output stream, without passing the output as a parameter to the helper method.

Looking inside the helper, we see that all content is passed to WriteTo and WriteLiteralTo methods, as opposed to the Write and WriteLiteral methods used by the rest of the page.

Helper methods cannot call the normal Write* methods since they aren’t necessarily writing to the current output (even though they usually do).  Therefore, they call these Write*To methods, which accept the TextWriter as a parameter.  These static methods are inherited from the WebPageExecutingBase class; their names are also configured by the RazorHostFactory.  The @ in the parameter is a little-used C# syntactictal feature that allows keywords to be used as identifiers; it has nothing to do with Razor’s use of the @ character.

Since helpers are compiled as normal methods, they can do almost anything that a normal method can.  However, because their contents are compiled inside a lambda expression, they have some limitations.  For example, helpers cannot use ref or out parameters, since they cannot be used inside lambda expressions.  Helpers can take params arrays or optional parameters.

Also, Razor’s C# code parser doesn’t support generic helper methods, although there is no reason that they couldn’t be supported in a later release.

The VB.Net code parser also doesn’t explicitly support generic helpers.  However, because VB.Net generics use parentheses, a VB.Net helper can declare a generic type parameter instead of a normal parameter list.

For example:

<!DOCTYPE html>
<html>

    <body>
        @Helper PrintType(Of T)
            @GetType(T)
        End Helper
        @PrintType(Of Dictionary(Of Integer, List(Of String)))()
    </body>

</html>

This trick is not very useful.

Next Time: Static Helpers

Dissecting Razor, part 6: Function Blocks

After looking at how Razor’s Execute() method is generated, we will turn to class-level features.

C# Razor pages can define class members inside of @functions { ... } blocks.  These are Razor’s equivalent of <script runat="server"> blocks in ASPX pages.  VBHTML pages use @Functions ... End Functions instead.

Functions blocks are emitted directly into top of the generated class, regardless of their location in the original source.  Unlike code blocks, function blocks cannot contain markup.

Here is a simple example:

<!DOCTYPE html>
<html>
    <body>
        @functions{
            public int GetPageLength() {
                //Don't try this in production.
                return ((StringWriter)this.Output).ToString().Length;
            }
        }
        @GetPageLength() characters have been written so far.
    </body>
</html>
@{ #error } 

Note that functions blocks can be defined anywhere, even in the middle of the markup.  The location of the block is totally irrelevant.

Here is the generated C# source, with a comment indicating where the block used to be.

public class _Page_Razor_Functions_cshtml : System.Web.WebPages.WebPage {

#line hidden
#line 4 "...\Functions.cshtml"

    public int GetPageLength() {
        //Don't try this in production.
        return ((StringWriter)this.Output).ToString().Length;
    }

#line default
#line hidden

    public _Page_Razor_Functions_cshtml() {
    }

    protected ASP.global_asax ApplicationInstance {
        get {
            return ((ASP.global_asax)(Context.ApplicationInstance));
        }
    }

    public override void Execute() {
        WriteLiteral("<!DOCTYPE html>\r\n<html>\r\n    <body>\r\n        ");
//Here was the functions block
        WriteLiteral("\r\n        ");

#line 10 "...\Functions.cshtml"
        Write(GetPageLength());
#line default
#line hidden
        WriteLiteral(" characters have been written so far.\r\n    </body>\r\n</html>\r\n");

#line 13 "...\Functions.cshtml"
#error

#line default
#line hidden
        WriteLiteral("     ");
    }
}

The contents of the functions block is inserted at the very top of the class, with the familiar #line directives to pretend that it comes from the CSHTML.

Notice that none of the whitespace around the functions block is stripped; you can see the newline and indentation in the @functions line in the first WriteLiteral string, and the newline and indentation after the closing } in the second WriteLiteral string.

Sure enough, the rendered HTML contains an extra blank like:

<!DOCTYPE html>
<html>
    <body>
        
        55 characters have been written so far.
    </body>
</html>

This means that putting a functions block before the <!DOCTYPE> will cause the HTML to start with an ugly blank line.  Therefore, it’s best to put functions blocks at the end of the source, where the blank lines won’t matter.

Next Time: Helpers

Dissecting Razor, part 5: Use the Source, Luke

Last time, we saw how basic Razor constructs are translated into C#.

We can see the generated class by adding @{ #error } to the page source. This creates a compiler error in the Execute method, and the resulting Yellow Screen of Death contains a Show Complete Compilation Source: link which will show the generated C# class. 

Let’s start with a very simple page:

<!DOCTYPE html>
<html>
    <body>
        1 + 2 = @(1 + 2)<br />
        @{ var source = "<b>bold &amp; fancy</b>"; }
        <code>@source</code> is rendered as
        @(new HtmlString(source))
    </body>
</html>
@{ #error }

This page is rendered like this: (after removing @{ #error })

<!DOCTYPE html>
<html>
    <body>
        1 + 2 = 3<br />
        <code>&lt;b&gt;bold &amp;amp; fancy&lt;/b&gt;</code> is rendered as
        <b>bold &amp; fancy</b>
    </body>
</html>

As expected, the expression @source is automatically escaped.  Also notice that the newline and indentation around the code block (@{ var ... })  was not rendered – the Razor parser strips all whitespace surrounding code blocks.  This is a welcome improvement over the ASPX view engine.

Now let’s look at how this HTML is generated.  This page is transformed into the following C# source:

namespace ASP {
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Web;
    using System.Web.Helpers;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.WebPages;
    using System.Web.WebPages.Html;
    using WebMatrix.Data;
    using WebMatrix.WebData;
    public class _Page_Razor_SimplePage_cshtml : System.Web.WebPages.WebPage {

#line hidden
        public _Page_Razor_WriteTo_cshtml() {
        }

        protected ASP.global_asax ApplicationInstance {
            get {
                return ((ASP.global_asax)(Context.ApplicationInstance));
            }
        }

        public override void Execute() {
            WriteLiteral("<!DOCTYPE html>\r\n<html>\r\n    <body>\r\n        1 + 2 = ");

#line 4 "...\SimplePage.cshtml"
            Write(1 + 2);
#line default
#line hidden
            WriteLiteral("<br />\r\n");

#line 5 "...\SimplePage.cshtml"
            var source = "<b>bold &amp; fancy</b>";
#line default
#line hidden
            WriteLiteral("        <code>");

#line 6 "...\SimplePage.cshtml"
            Write(source);
#line default
#line hidden
            WriteLiteral("</code> is rendered as\r\n        ");

#line 7 "...\SimplePage.cshtml"
            Write(new HtmlString(source));
#line default
#line hidden
            WriteLiteral("\r\n    </body>\r\n</html>\r\n");

#line 10 "...\SimplePage.cshtml"
#error
#line default
#line hidden

        }
    }
}

The WebPageRazorEngineHost injects the ApplicationInstance property into the CodeDOM tree; this property allows code in the page to access any custom properties in Global.asax.

As mentioned earlier, the page source is compiled into the Execute() method.

It uses #line directives to pretend that its code is actually in the CSHTML page.  This means that code or line numbers appearing in error pages come from the original CSHTML source, making the code easier to find when debugging.  The #line hidden directives indicate generated source that did not come from actual code in the CSHTML.

As mentioned last time, literal HTML source is passed to the WriteLiteral method, which is inherited from the WebPageBase class.  This method writes its argument to the current output stream (which can vary when making sections).  These calls are wrapped in #line hidden because they come from literal text, not code.

The two code blocks (the variable declaration and the #error directive) are copied straight into  Execute(), wrapped in #line directives that map them to the actual code lines in the CSHTML.

The code nuggets are passed to the Write method, and are similarly wrapped in #line directives.

Here is a more sophisticated Razor page:

<!DOCTYPE html>
<html>
    <body>
        @{ const int count = 10; }
        <table>
            @for (int i = 0; i < count; i++) {
                <tr>
                    <td>@i</td>
                    <td>@(i * i)</td>
                </tr>
            }
        </table>
    </body>
</html>
@{ #error }

The @for loop is a code block in the form of a control flow statement.  Razor’s C# parser is aware of C# control flow structures and parses them as code blocks.  (The VB parser does the same thing)

Here is the generated Execute() method, with #line directives removed for clarity:

public override void Execute() {
    WriteLiteral("<!DOCTYPE html>\r\n<html>\r\n    <body>\r\n");

    const int count = 10;
    WriteLiteral("        <table>\r\n");

    for (int i = 0; i < count; i++) {
        WriteLiteral("                <tr>\r\n                    <td>");

        Write(i);
        WriteLiteral("</td>\r\n                    <td>");

        Write(i * i);
        WriteLiteral("</td>\r\n                </tr>\r\n");

    }
    WriteLiteral("        </table>\r\n    </body>\r\n</html>\r\n");
#error
}

Here too, we see that all contiguous chunks of literal HTML are passed to WriteLiteral.

This example has two code blocks – the const declaration and the loop.  The for loop code block has HTML inside of it – any HTML markup inside a code block is parsed as normal HTML and passed to WriteLiteral.

Next Time: Function blocks

Dissecting Razor, part 4: Anatomy of a Razor Page

After looking at the various assemblies in the WebPages framework, we will drill into the inner workings of Razor pages.

Razor Side

An ordinary CSHTML page is transformed into a class which inherits the WebPage class.  The generator overrides the abstract Execute() method from the to render the page to the HTTP response stream.

Except for class-level directives and constructs (which will be discussed later), all ordinary content in a Razor page end up in the Execute method. 

There are three types of normal content: Literals, Code Blocks, and Code Nuggets.

Literals include any normal text. Razor compiles literal text into calls to the WriteLiteral method, with the text as a (correctly-escaped) string parameter.  Razor expects this method to write its parameter to the page.

Code Blocks include @{ ... } blocks, as well as control structures.  They’re Razor’s equivalent of <% ... %> blocks.  The contents of a code block are emitted as-is in the Execute method.  Code blocks must contain complete statements, or they’ll result in C# syntax errors.

VBHTML pages use @Code ... End Code blocks instead.

Code Nuggets are @-blocks.  They’re Razor’s equivalent of <%: ... %> blocks in an ASPX page.  Scott Guthrie describes how these blocks are tokenized.  The contents of a code nugget are passed to the Write method, which is expected to HTML-escape its parameter and print it.

WebPages Side

The WebPages framework’s  Write method (which comes from the WebPageBase class) takes a parameter of type Object, allowing one to put any expression in a code nugget.  It passes its parameter to HttpUtility.HtmlEncode, which will call ToString() and HTML-escape the output.  If the parameter is an IHtmlString, HtmlEncode will return its ToHtmlString() method without escaping.

The base class, method names, and default namespaces can be configured in the RazorEngineHost.  In addition, custom RazorEngineHosts can override the PostProcessGeneratedCode method to make arbitrary modifications to the generated code.

The WebRazorHostFactory in System.Web.WebPages.Razor.dll can also read default namespaces, a default base type, and a custom host from the <system.web.webPages.razor> section in Web.config:

<system.web.webPages.razor>
    <host factoryType="MyProject.MyWebPageRazorHost" />
    <pages pageBaseType="MyProject.CustomWebPage">
        <namespaces>
            <add namespace="MyProject.SomeNamespace" />
        </namespaces>
    </pages>
</system.web.webPages.razor>

Next Time: Looking at the generated page

Dissecting Razor, part 3: Razor and MVC

Last time, we saw how standalone Razor pages are served.

MVC3 maintains the strict separation between the WebPages framework and the Razor engine.1

Razor Side

Like the WebPages framework, MVC3 interacts with Razor indirectly, by relying on RazorBuildProvider from System.Web.WebPages.Razor.dll.   However, MVC3 requires that Razor views inherit its own base class, System.Web.Mvc.WebViewPage.

MVC3 adds a new @model directive, which can be used instead of @inherits to specify a strongly-typed model.  This syntax is implemented by customized RazorCodeParsers and RazorCodeLanguages in the System.Web.MVC.Razor namespaces.  These classes are invoked by MvcRazorEngineHosts from a custom RazorHostFactory registered in Views/Web.Config:

<system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
        <namespaces>
            <add namespace="System.Web.Mvc" />
            <add namespace="System.Web.Mvc.Ajax" />
            <add namespace="System.Web.Mvc.Html" />
            <add namespace="System.Web.Routing" />
        </namespaces>
    </pages>
</system.web.webPages.razor>

MVC Side

On the MVC side, MVC3 includes a new RazorViewEngine which creates RazorView instances.  RazorView inherits the existing BuildManagerCompiledView class, which passes the view’s virtual path to the build manager.  RazorView will take the WebViewPage from the build manager, find any matching start pages, and execute the view.

As with the WebPages framework, one can substitute other templating engines.  One can register a build provider which compiles classes that inherit WebViewPage, then add a RazorViewEngine to ViewEngines.Engines with additional extensions in its FileExtensions property.

Next time: Inside Razor Pages

Dissecting Razor, part 2: Gluing the pieces together

Last time, we saw that ASP.Net Web Pages are implemented in two independent assemblies. These assemblies are not directly connected to each-other.

Razor Side

System.Web.WebPages.Razor.dll contains the RazorBuildProvider class, which allows ASP.Net’s build system to compile Razor pages.  This class uses a WebRazorHostFactory to create WebPageRazorHosts used to process CSHTML (or VBHTML) files into CodeDOM trees.  It compiles the CodeDOM tree and returns the generated type(which will typically inherit System.Web.WebPages.WebPage) to the build system.

WebPageRazorHost is coupled to the WebPages framework; it handles the non-standard base types for special pages (StartPage and ApplicationStartPage).

RazorBuildProvider can be configured to use a different WebRazorHostFactory that creates custom WebPageRazorHosts.  (more on this later)

WebPages Side

The WebPages framework contains an internal WebPageHttpModule class which runs when an ASP.Net AppDomain is started.  It runs any _AppStart files, and hooks the request lifecycle to handle requests for CSHTML (or VBHTML) pages.

Requests for Razor pages are handled by System.Web.WebPages.WebPageHttpHandler.  This class passes the page’s virtual path to the build manager and gets a WebPage instance (It assumes that RazorBuildProvider will build the page and give a WebPage instance). 

The handler calls the page’s ExecutePageHierarchy method to serve the page to the client.  This method runs any _PageStart pages in the page’s parent directories, then executes the page.

The WebPageHttpHandler will also add a custom HTTP header, X-AspNetWebPages-Version: 1.0.  This can be disabled by setting WebPageHttpHandler.DisableWebPagesResponseHeader to false.

As mentioned, System.Web.WebPages.dll is not directly tied to the Razor language and engine.  One can create a custom build provider which compiles classes that inherit WebPage, then call WebPageHttpHandler.RegisterExtension to tell the WebPages framework to handle requests to the extension, without using the Razor parser.

The source code for this project is part of the same bundle.

Next time: MVC Razor views