Since about 1996, writing websites on the ASP.Net platform has involved working with the “angle-bracket percent” (<%) syntax (sometimes called “WebForms syntax”) I’m sure we’re all familiar with. From Classic ASP right up to ASP.Net 4.0, if you worked with the Microsoft Web Platform, you probably worked with this syntax. However, with the release of the MVC framework in 2009 (more than 13 years after Classic ASP 1.0), the community started to explore new languages and new ways of writing web pages. Last year, we started investigating ways in which we could revamp the ASP.Net syntax, and the result of that process is Razor. In this article, I will describe the motivations behind developing Razor, and give an overview of the syntax.
What is Razor?
When we started designing Razor, we took a simple snippet of pseudo-code as an example:
<ul>
foreach(var product in Products) {
<li><a href=”/Products/product.Id”>product.Name</a></li>
}
</ul>
Listing 1: What the developer means
This is what the developer means; what they want their code to do. However, in most web languages, like the traditional ASP.Net syntax, you have to write something like this:
<ul>
<% foreach(var product in Products) { %>
<li><a href=”/Products/<%=product.Id%>”><%=product.Name%></a></li>
<% } %>
</ul>
Listing 2: What the developer has to write, in WebForms syntax.
So, we stripped all the extra control characters out and started again, back to the first example. We wanted to find a way to blend server-side code and client-side markup with as few characters as possible. The answer was a single character.
The answer was a single character.
What makes Razor so different from other languages is that it only has one major control character, the at-sign (@). Going back to the earlier example, writing it in Razor would require only 3 control characters instead of 18 control characters:
<ul>
@foreach(var product in Products) {
<li><a href=”/Products/@product.Id”>@product.Name</a></li>
}
</ul>
Listing 3: The @ at work.
However, Razor does more than just cut down on extra characters. The magic of Razor lies in how it unifies two languages, the server-side code and the client-side markup. Razor has a fairly deep understanding of C#, VB and HTML and it uses that understanding to cut back on the extra syntax users need to write. For example, in Listing 3, you’ll notice that there are @ characters indicating where we switch to code, but nothing to indicate that we’ve gone back to markup. The Razor parser understands that immediately after the first @, there’s a foreach statement, so it assumes that when that statement is finished, you’re ready to go back to markup. Similarly, the parser is able to detect the <li> tag and treat it’s contents as markup. Razor also allows continued nesting of markup within code and vice-versa.
Getting Started with Razor
Razor files have one of two extensions, indicating which language the server-side code is written in. If you are using C#, the file extension is CSHTML. If you are using VB.Net, the file extension is VBHTML. For the most part, the server-side languages work the same way in Razor, but there are a few cases where one language works a little differently than the other. I’ll highlight those cases when we get to them.
A Razor Document
A Razor document starts with HTML. Until you need to start inserting some server-side code, a Razor file is just like a plain HTML file. When you’re ready to go in to code, you start a code block
Code Blocks
Code Blocks in Razor are started with a single @, followed immediately by a C# or VB block construct (like foreach, or If). If you want code that isn’t within the body of a block statement (for example, you want to declare some variables or calculate some values), you can create a generic code block using @{ in CSHTML, or @Code in VBHTML.
@{ var foo = GetFoo(); }
<p>This is just regular markup!</p>
@if(foo != null) { foo.Bar(); }
Listing 4a: C# - Code Blocks
@Code
Dim foo = GetFoo()
End Code
<p>This is just regular markup!</p>
@If foo IsNot Nothing
foo.Bar()
End If
Listing 4b: VB - Code Blocks
Notice that there are no Razor constructs necessary in order to mark the end of a code block, the code block ends when it would naturally be expected to end.
Mixing Code and Markup
On the web, code isn’t very useful unless it can be used to control the output of client-side markup. In Razor, any code block can contain markup blocks within it. In fact, those markup blocks can then contain further code block which can contain further markup blocks. You can nest them as deeply as you need!
In CSHTML, mixing markup in to your code is as easy as just typing some HTML! However, there are two rules to keep in mind. First, markup is only allowed at the start of a C# statement, you can’t place markup in the middle of a statement. Second, you must start your markup with a tag and all the tags must have a matching closing tag (or be self closed, like <br/>). For those cases where you don’t want to have an outer tag (like <p>) rendered around your text, you can use a special tag called <text>. The <text> tag is only used to define the boundaries of your markup, and it will not actually be rendered.
Once inside the tag, everything until the matching close tag is considered HTML, including additional tags. You can also use the @ character to start another code block. This is why it’s so essential to close your tags. If you don’t, the parser can’t determine where the markup block ends, so it doesn’t know when to switch back to code.
Unfortunately, this is also one place where CSHTML and VBHTML differ. Due to the complexity of the VB language, and the XML literals feature, we aren’t able to be quite as smart when parsing VB. So in VBHTML, when you want to switch from Code back to markup, you must use the @ character again. You can still use the <text> tag as before and
The following example shows some valid and invalid ways of putting markup inside C# and VB code:
@if(foo) {
<p>This is valid</p>
<text>This is also valid, and it won’t have any tags around it!</text>
string p = <p>This is NOT valid!</p>;
string p = <text>Nor is this!</text>;
}
Listing 5a: C# - Mixing Markup and Code
@If Foo
@<p>This is valid</p>
<p>This is NOT valid</p>
@<text>This is also valid, and it won’t have any tags around it!</text>
<text>This is still NOT valid!</text>
Dim p = <p>This is actually an XML Literal, and not considered markup!</p>
Dim p = <text>Same for this!</text>
}
Listing 5b: VB - Mixing Markup and Code
Partials, Layouts and Sections
Often, when writing a web application, you will find it necessary to extract out common view logic into a component. In ASP.Net WebForms, these are called UserControls and in ASP.Net MVC and WebPages, we call them Partials. Razor supports rendering partials using the RenderPage method. For example, consider a partial like the following (shown only in C#, but the VB code is similar):
The current time is @PageData[0]
Listing 6: _Time.cshtml Partial Page
Let’s note a few things about this file. First, the file-name starts with an “_”. In both ASP.Net WebPages and MVC, this indicates to the Razor engine that the file cannot be served on it’s own. With traditional ASP.Net syntax, UserControl have a different file extension (ascx) which prevents them from being served. However, all Razor pages written in the same language have the same extension, so we use the “_” to denote pages which are not to be served on their own. Second, is the PageData collection. When rendering a partial page, this collection will contain all of the extra arguments that were provided to the RenderPage method. So to use this partial, we would write code like this:
<p>@RenderPage(“_Time.cshtml”, DateTime.Now)</p>
Listing 7: Using the Time Partial
The first argument to RenderPage is the virtual path to the page we want to render, and the remaining arguments are passed on to that page.
One of the most popular features of ASP.Net is master pages. Razor has a concept similar to master pages, called layout pages. A layout page is a template, much like a master page, which has placeholders where each specific page’s content will be rendered. As with partial pages, layout pages are not directly requested, they are linked to a content page. When the user requests the content page, the Razor engine runs the page and collects up all the content to be rendered. It then checks to see if the page defines a layout page and if so, passes the content to that page and renders it.
Here’s a simple example of a content page:
@{
Layout = “_Layout.cshtml”;
}
@section Header {
This is the header
}
@section Footer {
This is the footer
}
This is the body
Listing 8: Content Page – Content.cshtml
Let’s review this code from the top. First, we need to attach a layout page to our content page. We do this by simply setting the Layout property in a code block. You can use any code you want to set this property, even load it from config files or a database. Next, we define a Header section. Just like master pages allow for multiple content placeholders, Razor content pages can define multiple sections, as well as a special “body” section. Each named section is defined using the @section keyword (in both C# and VB). In C#, we use “{“ and “}” to delimit the boundaries of the section. In VB, the “End Section” keyword is used, much like existing VB constructs like “If”. Any content which is not contained in a section is considered part of the “body”, which is a special unnamed section. Then, after our header section, we define some body content and then a footer section.
There are also some cases where it doesn’t make sense to have a body. For example, if you have a three column layout, you may want to have three sections “Col1”, “Col2” and “Col3” and no body. In Razor, as long as you have at least one @section block, you can omit the body completely.
Now that we have a content page, let see what the layout page would look like:
<html>
...
<body>
<div id=”header”>@RenderSection(“Header”)</div>
<div id=”body”>@RenderBody()</div>
<div id=”footer”>@RenderSection(“Footer”, required: false)</div>
</body>
</html>
Listing 9: Layout Page - _Layout.cshtml
The layout page contains all the outer HTML markup we need to define our page, then it uses the RenderSection and RenderBody methods to render the sections defined by our content page. The RenderSection method injects the section with the specified name, and the RenderBody method injects the content which was not part of a section. Here, we’ve also specified that the footer section is not required. Normally, if we try to render a section the content page does not define, we get an run-time error, but by setting the “required” parameter to false, we tell the run-time that this section is optional. Layout pages can also be nested, so our _Layout.cshtml file above could have specified a further layout page simply by setting the Layout property.
Other Bits of Syntax
Sometimes it is necessary to define common functions that will be used throughout the page. For this purpose, Razor has a “functions” keyword which allows you to insert C# or VB function declarations. For example:
@functions {
public DateTime CurrentTime() {
return DateTime.Now;
}
}
The time is: @CurrentTime()
Listing 9a: C# - Functions Block
@Functions
Public Function CurrentTime() as DateTime
Return DateTime.Now
End Function
End Functions
The time is: @CurrentTime()
Listing 9b: VB - Functions Block
It is also often necessary to import classes and types from other namespaces into your Razor pages, and for this we provide the @using/@Imports keyword:
@using System.IO
@{
// Now I can use FileStream!
Stream s = new FileStream();
}
Listing 10a: C# - Using Statement
@Imports System.IO
@Code
‘ Now I can use FileStream!
Dim s as Stream = New FileStream()
End Code
Listing 10b: VB – Imports Statement
Finally, Razor also includes its own server-side commenting feature. This allows you to write comments in both code and markup block, as well as comment out chunks of Razor code. This syntax is the same in both languages:
This will be rendered!
@* But this will not, because it is a comment *@
Listing 11: Razor Comments
Conclusion