Author : MD TAREQ HASSAN | Updated : 2020/07/03
What is razor pages?
- A page-focused framework for building dynamic, data-driven web sites
- Razor Pages framework is lightweight and very flexible
- Razor Pages can be considered as “MVVM for web” (M = Model, V = View, VM = ViewModel)
- Clean separation of concerns
- MVVM Data binding
- Razor Pages based on a page-centric development model, offering a familiarity to web developers with experience of other page-centric frameworks (PHP, Classic ASP, Java Server Pages, ASP.NET Web Forms etc.)
- Razor Pages can make coding page-focused scenarios easier and more productive than using controllers and views
- Links:
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
- Razor Pages: much like am MVVM (simple, clean, easy to maintain). Razor Pages for
- simple pages (read-only)
- basic data input
- also suitable for big/complex applications
- ASP.Net core Identity is provided as Razor Pages (razor library)
- MVC: seperation of concern (fit for big/complex application). MVC for
- dynamic server views
- single page apps
- REST APIs
- AJAX calls
- Links:
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
- Page:
- PageModel class name is
<PageName>Model
and is in the same namespace as the page
- All pages will be under “Pages” folder (i.e.
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 pageOnPost
/OnPostAsync
to handle form submissions
Multiple handlers
asp-page
=> target page to submit formasp-page-handler
=> target handler in target page (defined byasp-page
)- In PageModel:
GetXxxAsync()
/PostXxxAsync()
whenasp-page-handler="Xxx"
- https://docs.microsoft.com/en-us/aspnet/core/razor-pages/#multiple-handlers-per-page
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
- Same as MVC TempData but
[TempData]
annotation is in case of Razor Pages - Stores data until it’s read
- Useful for redirection
- https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state#tempdata
PageModel
[TempData]
public string Message { get; set; }
Page
<h3>Message: @Model.Message</h3>