Razor Pages generate HTML by keeping a page’s Razor markup close to its C# request-handling code. They suit small, self-contained features that do not need a controller to choose among views. The trade-off is flexibility: a page can become unwieldy as a feature grows, and Razor Pages are not intended for web services. They can coexist with MVC controllers in the same application. section 23.2
To add a page to an ASP.NET Core application:
- Call AddRazorPages() when registering services and MapRazorPages() when mapping endpoints. section 8.2.1
- Create a .cshtml file under Pages and put @page first. By convention, Pages/Suppliers/List.cshtml handles /suppliers/list; @page can specify a different route. section 23.2.2 section 23.3
- Use @model to select a page model. Define that class inside @functions or in a separate .cshtml.cs file; its properties supply values to the markup. section 23.2.2 section 23.6
- Put shared imports and tag-helper registration in Pages/_ViewImports.cshtml. Use _ViewStart.cshtml to select a default layout, and partial views where pages share markup. section 8.2.1 section 23.6
Listing 23.4 The contents of the Index.cshtml file in the Pages folder
@page
@model IndexModel
@using Microsoft.AspNetCore.Mvc.RazorPages
@using WebApp.Models;
<!DOCTYPE html>
<html>
<head>
<link href="/lib/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="bg-primary text-white text-center m-2 p-2">
@Model.Product?.Name
</div>
</body>
</html>
@functions {
public class IndexModel : PageModel {
private DataContext context;
public Product? Product { get; set; }
public IndexModel(DataContext ctx) {
context = ctx;
}
public async Task OnGetAsync(long id = 1) {
Product = await context.Products.FindAsync(id);
}
}
}
The Index.cshtml listing shows @page, @model, Razor output, and an inline PageModel together. Its OnGetAsync handler loads a product for display. section 23.2.2
Handler names determine which requests a page handles. OnGet or OnGetAsync prepares a response to GET; OnPost or OnPostAsync processes a submitted form. Handler parameters can receive route and form values through model binding. A POST handler can return a redirect rather than render the page again. Forms need an antiforgery token unless that protection is explicitly disabled. section 23.4.3
From a product list to editable records
A product-list page links to separate Details, Create, Edit, and Delete pages. Each page handles its own operation while a partial view and shared base class limit duplicated markup and logic. Restart the application and visit /pages to follow the links and test the operations. section 31.3 section 31.3.2
Watch the default URL when mixing Razor Pages and MVC: an Index.cshtml page can take precedence over a controller’s default route. Route order can be changed if the controller should handle /. A page model is optional for simple output, such as an order-completion page that binds an order ID from the request. section 23.3 section 9.3.7


