Author : HASSAN MD TAREQ | Updated : 2020/07/03

What is razor pages?

Notes:

  • Razor pages are built on top of AspNetCore MVC
  • AspNetCore Identity is implemented as Razor Pages. Scaffold Identity & take a look to better understand razor pages (see how Microsoft implemented it)

MVC vs Razor Pages

Overview

  • Similarity to MVC view
    • same razor view engine & razor systax
    • same Tag Helpers
    • Same model binding, validation, and action results
    • same .cshtml extension
  • @page directive: to indicate that it’s a Razor Page
    • @page makes the file into an MVC action - which means that it handles requests directly, without going through a controller
    • @page must be the first Razor directive on a page
  • @model directive: to bind ViewModel
  • Conventions:
    • All pages will be under “Pages” folder (i.e. Pages/Foo/xxx.cshtml)
    • URL routing are based on location in Pages folder (the path to the file on disk is used to calculate the URL)
      • Pages/Foo/Index.cshtml => ‘/foo
      • Pages/Foo/Bar.cshtml => ‘/foo/bar
    • Each razor page .cshtml file has a corresponding .cshtml.cs file containing the PageModel
      • Page: Bar.cshtml
      • PageModel: Bar.cshtml.cs
      • Visual Studio will hide the PageModel behind the page and we have expand to see PageModel
    • PageModel class name is <PageName>Model and is in the same namespace as the page

Pages/Foo/Bar.cshtml

@page
@using Hover.Foo // the namespace where BarModel belongs
@model BarModel


<h2>Hovermind</h2>

<p>
    @Model.Message
</p>

Pages/Foo/Bar.cshtml.cs

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;

namespace Hover.Foo
{
    public class BarModel : PageModel
    {
        public string Message { get; private set; } = "Hover Now ->";

        public void OnGet()
        {
            Message += $" { DateTime.Now }";
        }
    }
}

Configuration

configurations-in-startup#razor-pages

Layout

Same as MVC: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/layout

Page model

  • Page model acts as both a mini-controller and the view model for the view

Anatomy of PageModel

public class XxxModel : PageModel
{
	// bindable properties (property type => POCO i.e. public Baz baz)
	
	// DI injection properties
	
	// constructor for DI injection

	// handler methods for corresponding http verbs
}

Example: Bar.cshtml.cs

public class BarModel : PageModel
{
    [BindProperty]
    public Customer Customer { get; set; }
	
    private readonly CustomerDbContext _context;

    public BarModel(CustomerDbContext context)
    {
        _context = context;
    }

    public IActionResult OnGet()
    {
        return Page();
    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Customers.Add(Customer);
        await _context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

Handler methods

  • Handler method is the cllback function for corresponding HTTP verb
  • Handler methods for any HTTP verb can be added
    • OnPost[handler]Async i.e. OnPostDeleteAsync
    • <button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
  • The most common handlers are:
    • OnGet / OnGetAsync to initialize state needed for the page
    • OnPost / OnPostAsync to handle form submissions
Multiple handlers

Property binding and model expression

  • [BindProperty] annotation is used to bind PageModel properties to Page
  • By default binding for GET (url parameters) is not supported, to allow use: [BindProperty(SupportsGet = true)]

Routing

By conmvention, path in “Pages” forlder & page file name becomes route url

  • Pages/Foo.cshtml => “/foo
  • Pages/Foo/Index.cshtml => “/foo
  • Pages/Foo/Bar.cshtml => “/foo/bar
  • Use route template after @page directive if you want more URL segments after page name in the url (See: route-template-and-constraint)

To overrive default routing convention, provide absolute URL after @page directive
Example: Pages/Foo/Bar.cshtml

@page "/xxx"

<!-- ... ... ... -->
  • hardcoded absolute URL “/xxx” is being used
  • Page url would be “/foo/xxx” instead of “/foo/bar

Details: https://docs.microsoft.com/en-us/aspnet/core/razor-pages/razor-pages-conventions

Route template and constraint

  • Same MVC route template can be used: “{controller}/{action}/{param}
  • Same MVC route constraint can be used: “{id:int}
  • If you already know MVC routing template then imagine:
    • @page => is like “{controller=<page-file-name>}/{action=<OnGet>}
    • @page "/absolute-url" => overrides default routing
    • @page "{route-template}" => “foo/<path-segments-according-to-template>
      • @page "{id}" => “foo/5
      • @page "{year}/{month}/{day}" => “foo/2020/7/4
    • @page "{y}/{z}": @page = “/foo” + "{y}/{z}" = “/y/z” (URL: “foo/y/z”)

Pages/Foo/Bar.cshtml

@page "{id:int}"

<!-- ... ... ... -->

URL would be: “foo/bar/5

Accessing route url segments

Pages/Bar.cshtml

@page "{id:int}"

<!-- ... ... ... -->

URL: “/bar/5

Pages/Bar.cshtml.cs

public class BarModel : PageModel
{
    // ... ... ...

    public IActionResult OnGet(int id) // id = 5
    {
        // ... ... ...
    }
	
	// ... ... ...
}

ViewData

  • Same as MVC ViewData/ViewBag but [ViewData] annotation is in case of Razor Pages
  • Used to:
    • Pass data from PageModel to Page
    • Set title in Layout
  • Acceessed in page
    • @Model.Xxx
    • @ViewData["Xxx"]

PageModel

public class AboutModel : PageModel
{
    [ViewData]
    public string Title { get; } = "About";

    public void OnGet()
    {
    }
}

Page

<h1>@Model.Title</h1>

<!-- ... ... ... -->

Layout

<!DOCTYPE html>
<html lang="en">
<head>

    <title>@ViewData["Title"] - WebApplication</title>
	
    <!-- ... ... ... -->

TempData

PageModel

[TempData]
public string Message { get; set; }

Page

<h3>Message: @Model.Message</h3>