Razor Pages
|
This section documents ASP.NET Core on .NET 10 (LTS), the current release — the minimal hosting model, the middleware pipeline, dependency injection, Minimal APIs, MVC & Razor Pages, Blazor with the current render modes, SignalR and gRPC, EF Core, ASP.NET Core Identity and policy-based authorization, output caching, rate limiting, and Native-AOT-aware building — as described by the official documentation at Microsoft Learn, which is the reference these pages are written and verified against. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. .NET ships a major release every November and its APIs continue to evolve: the examples here target .NET 10 / C# 14. This section’s bibliography lists the reference material consulted while preparing these pages. |
A Razor Page pairs one .cshtml file with one PageModel code-behind class, so each URL has a single,
self-contained handler instead of sharing a controller with unrelated actions.
Registering Razor Pages
builder.Services.AddRazorPages();
var app = builder.Build();
app.MapRazorPages();
Pages live under Pages/ by convention; Pages/Products/Edit.cshtml is served at /Products/Edit. See
Introduction to Razor Pages.
@page and route templates
@page must be the first directive in the file — it both marks the file as a page and can carry a route
template, exactly like [Route] on a controller:
@page "{id:int?}"
@model EditModel
<form method="post">
<input asp-for="Product.Name" />
<span asp-validation-for="Product.Name"></span>
<button>Save</button>
</form>
Constraints (\{id:int}), optional segments (\{id?}), and catch-alls work the same as in
routing. See
Razor Pages route and app
conventions.
PageModel and handler selection
public sealed class EditModel(IProductService products) : PageModel
{
[BindProperty] public ProductInput Product { get; set; } = new();
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id is int i) Product = await products.GetInputAsync(i);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid) return Page();
await products.SaveAsync(Product);
return RedirectToPage("Index");
}
}
Handlers are selected by HTTP verb from the method name: OnGet/OnGetAsync, OnPost/OnPostAsync, and so
on. A page can define named handlers for more than one action per page:
public async Task<IActionResult> OnPostDeleteAsync(int id) { ... } // handler = "Delete"
<form method="post" asp-page-handler="Delete" asp-route-id="@p.Id">
<button>Delete</button>
</form>
[BindProperty]
[BindProperty] model-binds a property from the request the same way an action parameter would; add
SupportsGet = true to also bind it on GET (off by default, since binding on GET from arbitrary query
values is rarely intended):
[BindProperty(SupportsGet = true)]
public string? Search { get; set; }
See Model Binding and Validation for binding sources and validation, which apply identically to Razor Pages and MVC.
Page conventions
Configure routing, authorization, and folder-wide behavior from AddRazorPages, instead of repeating
attributes on every page:
builder.Services.AddRazorPages(o =>
{
o.Conventions.AuthorizeFolder("/Admin");
o.Conventions.AllowAnonymousToPage("/Admin/Login");
o.Conventions.AddPageRoute("/Products/Details", "products/{id:int}/details");
});
Filters
IPageFilter / IAsyncPageFilter run around page handler execution — the Razor Pages analog of MVC action
filters:
public sealed class AuditPageFilter : IAsyncPageFilter
{
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) => Task.CompletedTask;
public async Task OnPageHandlerExecutionAsync(
PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
{
// runs immediately before the handler
await next();
// runs after the handler, before the result executes
}
}
Register it globally (o.Filters.Add<AuditPageFilter>() in AddRazorPages) or per page via a [TypeFilter]
on the PageModel. See Filters and the MVC Pipeline for
the full filter pipeline shared with MVC.
Razor Pages vs. MVC
| Choose | When |
|---|---|
Razor Pages |
The site is a set of pages/forms; each URL has one clear responsibility; you want the view and its handler colocated. |
MVC |
Many actions share a controller, filters, or a non-page-shaped URL space; building HTTP APIs alongside views (see MVC Controllers and Views). |
Both share the same routing, model binding, validation, and Razor rendering pipeline underneath, so the choice is organizational, not a difference in capability. See Razor Pages vs MVC controllers and views.