ASP.NET Core 2.0 Razor Pages

Problem

How to use Razor Pages in ASP.NET Core 2.0.

Solution

Create an empty project and amend Startup.cs to add services and middleware for MVC:

Add a service and domain model (implementation of IMovieService is just in-memory list in sample source code):

Add input and output models (to receive and send data via property bindings):

Add a folder called Pages and add Index, _Layout, _ViewImports and _ViewStart pages to it. These pages are no different than MVC. Also add a folder Movies for our CRUD pages:

Add 4 new RazorPage items to Movies folder called Index, Create, Edit and Delete. These will add .cshtml and .cshtml.cs files:

Each of these pages will have our IMovieService injected via constructor injection e.g.:

Modify the Index.cshtml:

Modify the Index.cshtml.cs:

Modify the Create.cshtml:

Modify the Create.cshtml.cs:

Modify the Edit.cshtml:

Modify the Edit.cshtml.cs:

Modify the Delete.cshtml:

Modify the Delete.cshtml.cs:

Run and browse to /Movies:

On clicking first Edit (notice the URL would be /Movies/Edit/1):

On clicking Delete (notice the URL would be /Movies/Delete/3):

Note: you could download the source code to play with it.

Discussion

Razor Pages are introduced in ASP.NET Core 2.0 to make building simple web applications quicker and is a good way to play with various ASP.NET Core concepts like Razor, Layout Pages and Tag Helpers etc.

Razor Pages use ASP.NET Core MVC under the hood however the programming model is not the same. Unlike MVC where Controllers, Models and Views are distinct and separate components of the architecture, in Razor Pages these concepts are brought together under one roof, Page Model.

Page Model

I like to think of Page Model as a combination of Controller and Models. Like controller because they receive the HTTP requests and like model because they hold the data/properties for views.

For a .cshtml file to act as Page Model, it must contain as its first line the @page directive. The .cshtml.cs (code-behind) class inherits from PageModel abstract class. By convention the code-behind class has Model appended to page’s name e.g. Index page’s code-behind is IndexModel.

Routing

Routing to pages depends on their location in your project directory structure, under the Pages folder (by default). If a page is not specified in URL, the default of Index is used.

In our sample we navigated to URL /Movies to view the page located at /Pages/Movies/Index in our solution. Similarly the URL /Movies/Edit maps to /Pages/Movies/Edit page.

ASP.NET Core 2.0 has introduced new constructs used for generating URLs:

  • Page() method
  • asp-page Tag Helper
  • RedirectToPage() method on PageModel base class

Note that URLs starting with / are absolute paths and point to Pages folder. We can also use relative URLs stating with ./ or ../ or by simply omitting the /. To understand better, here is what happens when navigating to various URLs from Page/Movies/Delete:

We can specify routing constraints as part of @page directive to indicate to runtime to expect route parameters or throw 404 (Not Found) if missing.  In our Edit page we used the constraint like:

If you prefer to use a different name than Pages for your root folder, you could do so by configuring page options:

Handlers

As mentioned earlier, the page receives HTTP requests (i.e. acts as an Action in MVC world) and these are handled by the handler methods.  These handlers return IActionResult and named using convention of On[verb]. Most commonly used are OnGet() and OnPost(). For asynchronous you could append Async to the name, but this is optional.

The PageModel base class has RedirectToPage() method (that returns RedirectToPageResult) to navigate to other pages and Page() method (that returns PageResult) to return the current page. Note that if return type of handler method is void, runtime returns a PageResult.

In order to have multiple handler method for HTTP verbs, we can use named handler methods using asp-page-handler attribute. The name specified here should have a method in page class using convention On[verb][handler]. Let’s add a link on our movies list to delete the movie:

Add a method in the page model class to handle this request (note its name and parameter):

Move your mouse over the Delete link and you’ll notice the URL like /Movies?id=1&handler=delete. If you prefer to replace query parameters with URL segments then add following route constraints to the @page directive and generated URL will be /Movies/delete/1:

Binding

@model directive on pages points to the page model class because as mentioned earlier, this class acts as the model for Razor Pages. This works for reading the properties however in order to populate them when posting data (i.e. when using verbs other than GET) we need to use an attribute [BindProperty] to mark properties to use Model Binding.

Source Code

GitHub: https://github.com/TahirNaushad/Fiver.Mvc.RazorPages

2 comments

  1. Thank you very much. This is a great post on Razor Pages. I would like to see more of Razor Pages posts from you. You are amazing.

    Once more, thank you for this post.

    1. I am glad that you liked the post and found it useful. Razor Pages is an interesting new addition to ASP.NET Core 2.0 and surely as I discover more about it, I will write too.

      Thank you for your feedback.

Leave a Reply