Introducing Razor – A New View Engine for ASP.Net

UPDATE: Fixed broken examples (I hope :)).

Earlier this morning, Scott Guthrie blogged about a new View Engine we’re developing for ASP.Net.  As many of my readers know, I joined the ASP.Net team back in October in 2009, and I’m really excited to finally be able to share what I have been working on for the past 8 months.  When I joined Microsoft, I was shown some early prototypes for this new syntax and over the course of the next 8 months, we developed it into the Beta we’re going to be releasing very soon.

Writing the parser for Razor has essentially been my job for the last 8 months, so I’d like to describe a little about some of the design ideas that went in to it as well as some of the interesting ways we implemented things.  This is the first in a few blog posts I’ll do about the Razor syntax as well as the Parser design.

Razor syntax is designed around one primary goal: Make code and markup flow together with as little interference from control characters as possible.  For example, let’s take the following ASPX:

<ul>
    <% foreach(var p in Model.Products) { %>
    <li><%= p.Name %> ($<%= p.Price %>)</li>
    <% } %>
</ul>

Now, let's boil it down to the parts that we actually care about, removing all of the extra ASPX control characters:

<ul>
    foreach(var p in Model.Products) {
    <li>p.Name ($p.Price)</li>
    }
</ul>

Obviously there isn't enough data here to unambiguously determine what's code and what's markup. When we were designing Razor, we started from here and added as little as we could to make it absolutely clear what is code and what is markup. We wanted Razor pages to be Code+Markup, with a little extra stuff as possible. We even used that goal as inspiration for the file extension for C# and VB Razor pages: cshtml and vbhtml.

So, using the C# Razor syntax, the above example becomes:

<ul>
    @foreach(var p in Model.Products) {
    <li>@p.Name ($@p.Price)</li>
    }
</ul>

If you ask me, that's pretty darn close to the previous example. Razor takes advantage of a deep knowledge of C# (or VB) and HTML syntaxes to infer a lot about what you intended to write. Let’s take this sample and break it down chunk by chunk to see how Razor parses this document.

<ul>

When Razor starts parsing a document anything goes until we see an "@". So this line just gets classified as Markup and we move on to the next

@foreach(var p in Model.Products) {

Here's where things get interesting. Now, Razor has found an "@". The "@" character is the magic character in Razor. One character, not 5 “<%=%>”, and we let the parser figure out the rest. If you skip ahead a bit, you’ll notice that there's nothing that indicates the end of the block of code (like the "%>" sequence in ASPX). Rather than having it's own syntax for delimiting code blocks, Razor tries to add as little as possible and just uses the syntax of the underlying language to determine when the code block is finished. In this case, Razor knows that a C# foreach statement is contained within "{" and "}" characters, so when you reach the end of the foreach block, it will go back to markup

<li>@p.Name ($@p.Price)</li>

Now, things get even more interesting. Didn't I just say we were in Code until the ending of the foreach? This looks a lot like Markup, and we’re still inside the foreach! This is another case where Razor is using the syntax of the underlying language to infer your intent. We know that after the "{" C# is expecting some kind of statement. But, instead of a statement, we see an HTML tag "<li>", so Razor infers that you intended to switch back to Markup. So we've essentially got a stack of 3 contexts: When we started out we were in Markup, then we saw @foreach so we went to Code, now we've see <li> so were back in Markup. At the closing </li> tag, we know you've finished the inner Markup block, so we go back to the body of the foreach.

}

Then we see the end of the foreach block, so we go back to the top-level Markup context.

</ul>

And we continue parsing markup until the next "@", or the end of the file. You may have noticed I skipped over a bit in the middle of the <li> tag. I'll save the details of that for my next post, but the essential logic is the same: "@" starts code, and we use C# syntax to tell us when that code block is finished.

It's great to finally be able to share the result of our hard work with everyone. We've worked really hard to try to create a really clean syntax for mixing code and markup and I know I’d love to hear your feedback. Post in the comments on my blog, send me a tweet at "@anurse" or send me email at "andrew AT andrewnurse DOT net".

And I know you're all probably eagerly awaiting a chance to try this out. Don’t worry, we'll have a public beta soon that you can try out!

Saturday, July 03, 2010 10:10:39 AM (Pacific Standard Time, UTC-08:00)
Just out of curiosity, i'm sure you guys covered this scenario, but what will happen when i want to output a "}" inside a code block (like a foreach). What will get precedence "</li>" or "}". Do we need escaping like in a string.format with "}}"?
Saturday, July 03, 2010 10:44:54 AM (Pacific Standard Time, UTC-08:00)
How can I request to test "Razor"?
John
Saturday, July 03, 2010 12:39:57 PM (Pacific Standard Time, UTC-08:00)
@Geoffrey Braaf - When you start a code block with something like "foreach () {", you are in code until you tell us otherwise. If you want to render markup, you can use a tag like "<li>" to start markup. Once you enter markup, you are in markup until the end of that tag, so the "</li>" takes precedence over the "}". When you switch to markup from within a code block, you must end the markup block before you can end the code block. Think of the Markup block as a new kind of C# statement.

This does require some new thinking compared with ASPX, where code and markup were just two independent languages: you wrote some Code, then some Markup, the some Code, etc. In Razor, we think of a document as Markup, which <strong>contains</strong> Code, and then that code <strong>contains</strong> more Markup, which can <strong>contain</strong> more Code and so on and so on. So rather than switching between the two, you're nesting one inside the other.

I'm hoping to go into much more detail on the Parser over the next few weeks, so stay tuned if you're interested in how it all works!

@John - No need to request, there's a public beta coming very very soon and I'll blog about the release when it comes out.
Saturday, July 03, 2010 3:37:42 PM (Pacific Standard Time, UTC-08:00)
Hi Andrew, first let me say this rocks!!!! I like the code approach over the spark approach. I find it to be somewhere between spark and nhaml. Not too chatty, but not so sparse that it makes it hard to read. Beautifully blended syntax.

Count me in!!!!

Great job!
Sunday, July 04, 2010 7:22:17 AM (Pacific Standard Time, UTC-08:00)
Thank you Andrew! This engine looks very promising to me. Can't wait to test it.
Couple of questions:
1) How the template gets associated with the Model's type? Is there a directive for that?
2) Would it be possible to use Razor outside MVC, just like a powerful text templating engine (for generating HTML e-mails, for example)?
Vitaly Brusenysev
Sunday, July 04, 2010 9:47:49 AM (Pacific Standard Time, UTC-08:00)
@Vitaly - 1) yes, there will be an @inherits directive which let's you set the base type (including a model type). We are also investigating a @model directive which let's you skip the base class and just declare the Model type. 2) yes! Razor will have public APIs and will be hostable outside ASP.Net
Sunday, July 04, 2010 10:44:01 AM (Pacific Standard Time, UTC-08:00)
This is the parser i was waiting since i learned ASP.net.
Excellent work!
liviu
Sunday, July 04, 2010 8:05:42 PM (Pacific Standard Time, UTC-08:00)
01.<ul>
02. @foreach(var p in Model.Products) {
03. <li>@p.Name ($@p.Price)</li>
04. } ---> Missing @ ?
05.</ul>

Andrew
Sunday, July 04, 2010 10:57:48 PM (Pacific Standard Time, UTC-08:00)
The engine looks very interesting. Some questions:
1) Will it be possible to store view in other projects? or to add new ones dynamically?
2) How do you control whitespace? Is there a way to remove automatically whitespace between tags in the generated markup while keeping the template indented?
3) How do you generate Javascript within the HTML (that may contain { and })?
Zano
Sunday, July 04, 2010 11:43:24 PM (Pacific Standard Time, UTC-08:00)
<li>@p.Name ($@p.Price)</li>
The switch from code to markup between '@p.Name' and ' ($' is an interesting one, I guess.
How do you know the dev didnt meant to call a function Name? Or that maybe the $@ is just a typo, so you should have emitted an error?

// Ryan
Ryan Heath
Monday, July 05, 2010 8:26:48 AM (Pacific Standard Time, UTC-08:00)
@liviu - Glad to hear it!

@Andrew - Nope, the example is correct. We know the "}" is code because it's outside the "<li>" tag

@Zano - 1) Yes, the Razor View Engine works through the existing Virtual Path Provider system which allows you to redirect virtual paths to a database, etc.
2) Whitespace handling in Razor is a little complicated, but is essentially designed such that lines containing only code completely disappear (including their whitespace and newline sequences).
3) You'll need to wrap it in a tag of some form, either "<script>", or "<text>" (which doesn't actually get rendered). But once you're inside the tag, you can put "{" and "}" in wherever you want without colliding with the code block

@Ryan - Those code blocks are parsed as "Implicit Expressions" which are basically a subset of C# expressions. They only allow ".", "()" and "[]" and _no_ whitespace outside of "()" and "[]". I'll go into more detail later, but essentially, the whitespace between "Name" and "(" indicates that the user didn't intend to call a method called "Name". If you want to disambiguate this in a different way, you could use "@()" which indicates that the contents of the "()" are code, no matter what they contain.
Monday, July 05, 2010 9:32:56 AM (Pacific Standard Time, UTC-08:00)
Seems really good. Is Razor supports MVC 2 templates (display and edit templates based on a type) ?
Monday, July 05, 2010 11:19:24 AM (Pacific Standard Time, UTC-08:00)
I guess Zano referred to the whitespace in markup not in the code.
I have asked more or less the same question to ScottGu which he answered.

I realize it will not be a feature on your shortlist, but please do consider removal of whitespace/htmlcomments at compiletime. :)

// Ryan
Ryan Heath
Monday, July 05, 2010 11:25:06 AM (Pacific Standard Time, UTC-08:00)
@"Implicit Expressions", hmm so the whitespace between 'Name' and '(' has a meaning. That's a surprise ...
Anyhow, I am looking forward to your next blogposts about the subject.

// Ryan
Ryan Heath
Monday, July 05, 2010 1:17:20 PM (Pacific Standard Time, UTC-08:00)
A bit OT here, but has there ever been any discussions on using 'XAML' for asp.net ? It seems all the view engines are the same fundamentally and it will turn into soup. ie. I could see as contractor, you goto job 1 - it's haml, then job 2 is nvelocity, then job 3 is Razor, job 4 is default. For what value - a developers preference of using @ instead of <% ?

Although this is neat, I'd like to see MS do XAML for the web - so that your WPF, SL XAML skills naturally move to the web world, and with it we could do some MVVM goodness :)

Just a thought (i used Monorail and though NVelocity was more than adequate - and managed to not involve too much 'logic')
Steve
Tuesday, July 06, 2010 9:10:01 AM (Pacific Standard Time, UTC-08:00)
@mberube - MVC 2 Templates are actually just helper methods, and you can absolutely call them from Razor. So while we didn't do anything to explicitly support them, they just work already :)

@Zano/Ryan Heath - Yes, I will have more details on whitespace handling later. Essentially, lines with only code disappear (as I mentioned) and other than that, Markup takes most of the whitespace, since it has more meaning there.

@Ryan Heath - We aren't going to automatically remove whitespace or HTML comments because users may really want it, but the Razor parser should be extensible enough that someone could, in theory, add this functionality as an option later. We're still talking through our extensibility story though, so don't quote me on that :). As for "implicit expressions", it's important to note that these are a short-hand and that there's always an escape hatch where you can put any C# expression you want. I'll cover expressions next, so hang tight :).

@Steve - I don't think XAML has been discussed, seeing as it would be difficult to see where it fits. Does it replace HTML? If so, we have to add some kind of XAML to HTML rendering for non-Silverlight compatible browsers and accessibility scenarios. Is it just the code language? Then we end up with an XML-based code language and that gets messy fast. As for the view engine soup, I think it's similar to the fears of programming language soup we get in to. In the end, it's all about mixing markup with code in some way, and learning one syntax just requires doing exactly that, learning a syntax. A lot of the skills can easily be transferred from one engine to the next. I'm a firm believer in programming language diversity, and I try to learn new languages all the time, and view engines are just languages :)
Thursday, July 22, 2010 2:24:50 PM (Pacific Standard Time, UTC-08:00)
Thanks Andrew for this Hard work. I like to see the Code+html without extra sing like @, ? or #. As you know the Code syntax is different than the html syntax and the parser should know the differences as simple as that.

thanks
Omar Al-Taezi
Saturday, August 21, 2010 9:52:08 AM (Pacific Standard Time, UTC-08:00)
Beautiful work...any chance we'll get some support for F# sometime?
Dennis
Comments are closed.