Razor Redux

Microsoft has introduced a new version of Razor with ASP.NET Core 2. Tom Fischer explains why it’s worth taking another look at this methodology, especially those who discounted it before. He walks you through creating a simple application, highlighting the benefits or Razor.

ASP.NET Core 2 reintroduced Razor. If this humble markup first exposed in 2010 allowing developers to embed C# and VB.NET into web pages failed to impress, the redesigned version just might. Imagine all the things developers adored about Web Forms, Model-View-Controller (MVC), and data-bound templating rolled up into one technology.

This article provides a brief Razor introduction that might convince skeptical .NET developers to consider it for their next web application.

MVC Backgrounder

Readers unfamiliar with ASP.NET’s implementation of the Model-View-Controller (MVC) may find this brief overview helpful before continuing with this article. MVC is an architectural pattern splitting a web application page into three components: model, view, and controller. Models capture the application domain, such as, user name information. Views display the model, again, exposing the user name as an HTML label in the upper left corner. Controllers manage the interaction between view and model.

This loose coupling of model, view, and controller facilitates their individual development. For example, the view’s design does not necessarily impact its associated model. The separation also simplifies unit testing for each of the components.

Implementing MVC-based applications is not without its challenges. Some find it unnecessarily more complex than the Web Forms-based predecessor. Nonetheless, coding an MVC view with Razor markup seems easier than a Web Forms page. In all likelihood, this ease inspired the new Razor – markup sans model and controller components.

Getting Started

An ASP.NET Core 2 Razor-based web application requires installing the .NET Core 2.0 SDK and upgrading to Microsoft.AspNetCore 2.0.0. Rather than explicitly installing AspNetCore directly, Visual Studio 2017 users may find upgrading to version 15.4+ a preferable option after installing the SDK. Upgrading Visual Studio exposes several new web project templates.

Once the prerequisites are in place, create a new ASP.NET Core 2.0 project.

After selecting the highlighted template option, Visual Studio creates the simple project which is entitled RazorDemo as shown in the Solution Explorer window.

Like most Visual Studio templated projects, building and executing RazorDemo requires minimum effort on the part of the developer. Perhaps the most fascinating aspect of this starter project resides in the Startup.cs method ConfigureServices. Razor pages rely on the MVC framework even if they don’t explicitly demand its expected components, such as, models and controllers.

Razor by convention assumes a Pages folder exists to house its web pages. These cshtml (or vbhtml for the VB.NET persuasion) files allow for two variations: markup only and code + markup.

A Tale of Two Pages

Razor supports two fundamental implementations of a web page. The first type is somewhat simple, an example in your project is Error.cshtml, which includes only markup. The second involves code and markup, for example Index.cshtml and Index.cshtml.cs.

Markup Only

Adding a static Razor page via Visual Studio is simple. It begins by right clicking on the project folder of interest and clicking Add followed by New Item and clicking Razor Page. This sequence, which might vary some between Visual Studio versions, brings up the following dialog.

Inspecting this newly added page highlights the simplicity of a basic markup-only file. While it may look similar to the View found in an MVC implementation, the mark up differs. The Razor keyword page stands out as the salient element. This enables page requests without a supporting model or controller while exploiting the popular Razor constructs.

Note: This discussion does not dwell on markup syntax for a good reason – it didn’t dramatically change. The most impressive aspect of the new Razor is everything other than the markup!

Developers may also add Razor pages by right clicking Add and selecting Add Razor Page… at the top of the presented item list. This option provides help for incorporating the Entity Framework.

Lastly, if a developer selects the Razor View when selecting a New Item, the added cshtml page may disappoint. It will contain a warning that the project needs MVC enabled. Count that as a gentle reminder that Razor View and Razor Page differ.

Code + Markup

The second type of Razor page contains a markup file with a Web Forms like code-behind file. PageModel, which resembles a hybrid MVC controller, provides the glue linking the two. Creating such a page is as simple as the markup only version except that you must check Generate PageModel class.

C:\Users\tom.fischer\AppData\Local\Microsoft\Windows\INetCache\Content.Word\Add CodeMarkup Page.png

The markup file created when adding the Razor page, CodeMarkup.cshtml, closely resembles the markup only type with one critical difference. After page, another Razor keyword, model, follows declaring the markup’s supporting class RazorDemo.Pages.CodeMarkupModel which inherits from PageModel.

The Code-behind CodeMarkupModel class reveals both the simplicity and power of the new Razor. Exploiting it only requires learning a few concepts. Razor code-behind pages do not equate to the older Web Forms like sounding construct. PageModel more closely resembles an MVC controller.

PageModel Basics

Developers familiar with ASP.NET MVC projects will find fully functional Razor-based web pages easy to implement. This article explores these essentials via a sample order editing page.

Requirements

We satisfy our limited ambitions with a web page to edit an order as shown below.

This simple page allows highlighting a few new and many old features in Razor and MVC.

Preliminaries

Implementing the page only requires a few steps before coding:

  1. Add a Razor page via Visual Studio entitled OrderEdit with
    • Generate PageModel class checked
    • Use a layout page: checked with the _Layout.cshtml value
  2. Add the following using statements to the new OrderEditModel (OrderEdit.cshtml.cs)
    • Microsoft.Extensions.Caching.Memory for temporarily maintaining state
    • System.ComponentModel.DataAnnotations for validation support
  3. Add services.AddMemoryCache() to ConfigureServices enabling the in-memory cache

Time to code.

[BindProperty]

This attribute dramatically alters how user input reaches the server compared to most MVC applications. Instead of passing an object via an action for server-side processing, it leverages two-way data binding. Readers familiar with Model–View–ViewModel (MVVM), another architecture pattern combining the strengths of MVC with the supporting framework’s data binding features, will be comfortable with [BindProperty].

Note: For those still wishing to pass state the old, MVC way, don’t fret. It’s still possible, read Dino Esposito’s Improvements to Model Binding in ASP.NET Core to get started.

OrderEditModel begins with the definition of OrderInformation. This simple model-like class houses all the properties expected to be passed between client and server. Once defined, the markup page can naturally access order information via the OrderInformation typed Order property.

With all data-bound properties defined, accessing them on OrderEdit.cshtml view proves equally simple with a little help from asp-for and asp-validation-for as shown next.

Also note the minimal effort required to expose DisplayDate. All it required was applying @, another Razor keyword.

Handlers

Razor handles (no pun intended) the expected HTTP verbs via default naming conventions:

  • OnGet/OnGetAsync
  • OnPost/OnPostAsync
  • OnDelete/OnDeleteAsync
  • OnPut/OnPutAsync
  • OnPatch/OnPatchAsync

It also supports named handlers via On<HTTP Verb><Custom Handler Name> syntax with or without an Async suffix. And if that’s not enough, consider rolling your own with a DefaultPageApplicationModelProvider.

Before inspecting the two handlers, the code-behind class constructor loads an instance of the Microsoft in-memory cache via built-in dependency management. _cache courteously remembers your order edits as you click about.

Thru the power of convention, Razor pages default any GET to OnGet or OnGetAsync, along with any POST OnPost or OnPostAsync. OrderEdit.cshtml neither contains nor requires explicit references to its supporting OnGet or OnPost methods.

Authorization

While authorization remains intuitive, Razor operates with its own API. Adding the following snippet to the Startup’s ConfigureServices method demonstrates one such page authorization arrangement.

Page level authorization possibilities remain. Fortunately, the API leverages many of the expected names and behaviors.

Almost Like MVC

As most likely apparent by now, Razor lives within the MVC framework. Our sample page leveraged several of its features: application startup, dependency injection, middleware management, Tag Helpers and model validation. Despite this commonality, some differences exist which may alter how developers approach Razor versus MVC controller pages.

API Support

Nothing explicitly precludes using PageModel for an API controller. Some might even argue it’s a perfect option to expose a service with user interface for checking configuration or errors. Nonetheless, doesn’t it seems potentially confusing to drag in the Microsoft.AspNetCore.Mvc.Razor namespace in a world expecting only Microsoft.AspNetCore.Mvc?

Folder Layout

The simplified code folder layout stands out as a shining star of the new Razor. First, the underlying magic avoids the many, many files and folders found in a typical MVC application as suggested below.

Adding a new page doesn’t require adding a file to the Controllers, Models, and Views folders for just a single web page.

Second, Razor avoids those pesky issues when employing hierarchical folder structures or an Area in MVC. For example, incorporation of the sample order management feature only requires the folders shown in the following screenshot and the usual routing tweaks.

Conclusion

This introduction into the Razor technology released with ASP.NET Core 2.0 barely scratched the surface. It hopefully covered enough ground to convince developers acquainted with the older version to reconsider. Razor now combines some of the best ideas from different technologies introduced into ASP.NET over the years. Anticipate seeing it as a compelling alternative to MVC based pages.