Writing output in Razor helpers using code

The new ASP.Net WebPages view engine (formerly Razor) allows you to create reusable parameterized blocks of HTML called helpers

For example:

@helper Fibonacci(int count) {
    int current = 1, prev = 0;
    for (int i = 0; i < count; i++) {
        @:@current, 
        int t = current;
        current += prev;
        prev = t;
    }
}

This helper will write out the first count Fibonacci numbers.  It can be called by writing @Fibonacci(30) in the page that defines the helper.

Using Razor syntax in this code looks strange.  Razor syntax is designed to write HTML tags.  Since I’m printing plain text, I need to use the  @: escape (or the <text> tag) in order to output my text.  This small bit of code looks confusing and can get lost inside larger blocks of server-side code.

Instead, I can use an undocumented hack.  Razor helpers are implemented by compiling a lambda expression as an Action<TextWriter>.  The lambda expression receives a TextWriter parameter named __razor_helper_writer.  (You can see this by writing a Razor page with a compilation error and clicking Show Complete Compilation Source)  There is nothing preventing me from using this parameter yourself.  (it even shows up in IntelliSense!)

Therefore, I can rewrite the helper as follows:

@helper Fibonacci(int count) {
    int current = 1, prev = 0;
    for (int i = 0; i < count; i++) {
        __razor_helper_writer.Write(current + ", ");
        int t = current;
        current += prev;
        prev = t;
    }
}

Remember to correctly escape your text by calling Html.Encode.  Since this writes directly to the output stream, it doesn’t get the benefit of Razor’s automatic escaping.

Note: This relies on undocumented implementation details of the Razor compiler, and may change in future releases. I would not recommend doing this.
Instead, you can write a function:

@functions{ 
    string Fibonacci(int count) {
        var builder = new System.Text.StringBuilder();
        int current = 1, prev = 0;
        for (int i = 0; i < count; i++) {
            builder.Append(current).Append(", ");

            int t = current;
            current += prev;
            prev = t;
        }
        return builder.ToString();
    }
}
This can be called the same way as the helper.  Instead of using the special helper support, the call will just print a string, the same you print any other string.  Therefore, the function’s output will be HTML-escaped.  To prevent that, you can change the function to return an HtmlString.

0 comments:

Post a Comment