Razor Pages in ASP.NET Core

Razor Pages is a page-centric programming model for building web UIs with ASP.NET Core. It simplifies building Razor-based applications by enabling developers to bring together HTML, CSS, JavaScript, and server-side code into a single unit. This model is ideal for scenarios where you want to focus on individual pages rather than the controller-action structure of MVC.

Introduction

Razor Pages offers a clear and organized way to develop web applications. Each page consists of a Razor file (.cshtml) for the UI and an optional C# code-behind file (.cshtml.cs) for handling logic. This separation of concerns makes it easier to manage and maintain your web application.

Components of a Razor Page

A typical Razor Page consists of the following components:

The Page Model

The Page Model is a C# class that inherits from PageModel. It acts as a backend for your Razor Page, providing properties and methods to support the UI. Key features include:

Tip: You can also create Razor Pages without a separate code-behind file by placing C# code directly within the Razor view using the @functions block or top-level statements, though a separate code-behind is generally recommended for better organization and testability.

The Razor View

The Razor View (.cshtml) uses Razor syntax to embed server-side code within HTML. This allows you to dynamically generate HTML content based on data from the Page Model.

The @model directive at the top of the Razor file specifies the Page Model type it's associated with:

@page
@model MyWebApp.Pages.IndexModel
@{
    ViewData["Title"] = "Home page";
}

@Model.Message

Welcome to our ASP.NET Core application!

Routing for Razor Pages

Razor Pages use a convention-based routing system. By default, the route for a Razor Page is determined by its file path relative to the Pages folder. For example, a file named Pages/About.cshtml will be accessible at the /About URL.

You can customize routing using the @page directive with route templates or by defining routes in the Startup.cs (or Program.cs in .NET 6+) file.

@page "{id:int}"
@model MyWebApp.Pages.Products.DetailsModel

Product Details

Product ID: @Model.Product.Id

Data Binding

Model binding simplifies the process of mapping incoming request data to properties on your Page Model. ASP.NET Core automatically handles binding for form fields, query strings, and route parameters to properties marked with [BindProperty].

// In ProductDetails.cshtml.cs
public class ProductDetailsModel : PageModel
{
    [BindProperty(SupportsGet = true)]
    public int ProductId { get; set; }

    public Product Product { get; set; }

    public void OnGet()
    {
        // Load product details based on ProductId
        Product = GetProductById(ProductId);
    }
}

Form Handling

Handling form submissions in Razor Pages is straightforward. Use HTTP POST methods to process form data. The OnPost() method in your Page Model is called when a form is submitted using the POST method.

// In Create.cshtml.cs
public class CreateModel : PageModel
{
    [BindProperty]
    public Product NewProduct { get; set; }

    public IActionResult OnPost()
    {
        if (!ModelState.IsValid)
        {
            return Page(); // Re-render the page with validation errors
        }

        // Add NewProduct to your data store
        // ...

        return RedirectToPage("./Index"); // Redirect to the index page
    }
}

In the Razor View (Create.cshtml):

@page
@model CreateModel
@{
    ViewData["Title"] = "Create Product";
}

Layouts and Partial Views

Razor Pages leverage the layout system defined in ASP.NET Core. A default layout file (usually _Layout.cshtml in the Pages/Shared folder) is applied to all pages, providing consistent headers, footers, and navigation.

You can use _ViewImports.cshtml to import common directives and namespaces across all Razor Pages within a directory or the entire application.

Partial Views

Partial views are reusable chunks of UI that can be rendered within Razor Pages or other partial views. They are useful for breaking down complex UIs into smaller, manageable components.

// In Index.cshtml
<partial name="_ProductSummary" model="Model.FeaturedProduct" />

Tag Helpers

Tag Helpers allow you to write server-side code in Razor files using the familiar syntax of HTML attributes. They transform HTML tags into .NET objects that can render server-side markup. Common Tag Helpers include <form>, <input>, <label>, and <a>.

Dependency Injection

Razor Pages fully support dependency injection. You can inject services into your Page Model constructors or properties using the [FromServices] attribute to access services like database contexts, logging, or configuration.

// In Index.cshtml.cs
public class IndexModel : PageModel
{
    private readonly IProductService _productService;

    public IndexModel(IProductService productService)
    {
        _productService = productService;
    }

    public void OnGet()
    {
        Products = _productService.GetAllProducts();
    }
}

Security Considerations

Razor Pages integrate with ASP.NET Core's authentication and authorization mechanisms. You can protect pages or specific handlers using the [Authorize] attribute.

// In Admin/Index.cshtml.cs
[Authorize]
public class AdminIndexModel : PageModel
{
    public void OnGet() { ... }
}

Testing Razor Pages

The page-centric model of Razor Pages makes unit and integration testing more straightforward. You can test your Page Models independently of the web server and use test hosts to simulate HTTP requests.

Note: For more advanced scenarios, consider using ASP.NET Core MVC if your application requires a more structured approach with distinct controllers and actions.