Razor Pages Concepts in ASP.NET Core

Last updated: October 26, 2023

What are Razor Pages?

Razor Pages is a page-focused programming model for building dynamic web UIs with ASP.NET Core. It provides a simpler, higher-productivity alternative to MVC controllers and actions for application pages. Razor Pages simplifies common web development tasks, making it easier to build robust, feature-rich web applications.

It's built on top of the ASP.NET Core framework and leverages Razor syntax for embedding server-side code within HTML.

Key Benefits

  • Simplicity: Offers a cleaner, more direct way to build page-based applications compared to traditional MVC.
  • Productivity: Reduces boilerplate code and simplifies common web development scenarios.
  • Testability: Page models are easily testable classes.
  • Organization: Encourages a clear separation of concerns for each page.
  • Full Framework Support: Benefits from all the features of ASP.NET Core, including dependency injection, middleware, and configuration.

File Structure

Razor Pages are organized in a convention-based structure within your project, typically in a folder named Pages. Each Razor Page consists of two primary files:

  • Razor View (.cshtml): Contains the HTML markup and Razor syntax for rendering the UI.
  • Page Model (.cshtml.cs): A C# class that inherits from PageModel and handles the logic for the page, such as data retrieval, business logic, and request handling.

For example, a page named Contact.cshtml would typically have a corresponding Contact.cshtml.cs file in the same directory.

Project Structure Example:
Pages/
├── _ViewImports.cshtml
├── _ViewStart.cshtml
├── Index.cshtml
├── Index.cshtml.cs
├── About.cshtml
├── About.cshtml.cs
└── Contact.cshtml
    └── Contact.cshtml.cs

The Razor Page Model

The Page Model class is the heart of a Razor Page. It inherits from Microsoft.AspNetCore.Mvc.RazorPages.PageModel and provides a C# representation of the page's logic.

Key features of the Page Model include:

  • Properties: Used to pass data between the UI and the server.
  • Event Handlers: Methods like OnGet(), OnPost(), etc., to handle HTTP requests.
  • Services: Can inject services (e.g., from dependency injection) to access application functionality.
  • Validation: Supports data annotations for client-side and server-side validation.
Contact.cshtml.cs Example:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;

public class ContactModel : PageModel
{
    [BindProperty]
    [Required]
    [EmailAddress]
    public string Email { get; set; }

    [BindProperty]
    [Required]
    [StringLength(500, ErrorMessage = "Message cannot be longer than 500 characters.")]
    public string Message { get; set; }

    public string SuccessMessage { get; private set; }

    public void OnGet()
    {
        // Logic for GET request (e.g., load initial data)
    }

    public IActionResult OnPost()
    {
        if (!ModelState.IsValid)
        {
            return Page(); // Return to the same page if validation fails
        }

        // Process the form data (e.g., send an email)
        SuccessMessage = "Thank you for your message!";

        return Page(); // Re-render the page with the success message
    }
}

Razor Syntax

Razor syntax allows you to embed server-side C# code within your HTML markup. It uses the @ symbol to denote code blocks.

  • Expressions: @Model.PropertyName to display a property's value.
  • Code Blocks: @{ ... } for multi-line code.
  • Directives: @page, @model to specify page properties and models.
  • HTML Helpers: Built-in helpers for forms, inputs, etc.
Contact.cshtml Example:
@page
@model ContactModel
@{
    ViewData["Title"] = "Contact Us";
}

Contact Us

@if (!string.IsNullOrEmpty(Model.SuccessMessage)) { <div class="alert alert-success" role="alert"> @Model.SuccessMessage </div> } <form method="post"> <div class="mb-3"> <label for="email" class="form-label">Email address</label> <input type="email" class="form-control" id="email" asp-for="Email"> <span asp-validation-for="Email" class="text-danger"></span> </div> <div class="mb-3"> <label for="message" class="form-label">Message</label> <textarea class="form-control" id="message" rows="5" asp-for="Message"></textarea> <span asp-validation-for="Message" class="text-danger"></span> </div> <button type="submit" class="btn btn-primary">Send Message</button> </form> @section Scripts { <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script> <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script> }

Handling Requests

Razor Pages use specific methods in the Page Model to handle different HTTP request verbs.

  • OnGet(): Handles HTTP GET requests. Use this to load data and initialize the page.
  • OnPost(): Handles HTTP POST requests. Use this to process form submissions.
  • OnPut(), OnDelete(), etc.: For other HTTP verbs.

These methods can return an IActionResult, such as Page() (to render the current page), RedirectToPage(), or RedirectToRoute().

Data Binding

Data binding simplifies the process of mapping client-side input (e.g., form fields) to properties in your Page Model. The [BindProperty] attribute is used to enable binding for a property.

When a form is submitted, ASP.NET Core attempts to bind the incoming request data to the properties marked with [BindProperty]. This binding happens automatically for POST requests when you call Page() in your handler.

Model Validation

Razor Pages integrate seamlessly with ASP.NET Core's built-in validation mechanisms, primarily using Data Annotations.

  • Decorate Page Model properties with Data Annotation attributes (e.g., [Required], [EmailAddress], [StringLength]).
  • Use ModelState.IsValid to check if the model is valid.
  • Use the asp-validation-for tag helper in your Razor view to display validation error messages.
  • Ensure you have the necessary JavaScript validation libraries included in your project (e.g., jQuery Validate and Unobtrusive Validation).
Tip: Client-side validation provides immediate feedback to the user, while server-side validation is crucial for security and data integrity. Razor Pages support both.

Routing

By default, Razor Pages use a file-system-based routing convention. The URL path for a page corresponds to its file path within the Pages folder, relative to the root of the application.

  • Pages/Index.cshtml maps to /.
  • Pages/About.cshtml maps to /About.
  • Pages/Contact.cshtml maps to /Contact.

You can customize routing using attributes like [Route] on the Page Model, but the convention-based routing is often sufficient.

Layout Pages

Layout pages (e.g., _Layout.cshtml) are used to define the overall structure and shared elements of your application, such as headers, footers, and navigation menus. They are rendered around individual Razor Page content.

Individual Razor Pages can specify which layout to use in their _ViewStart.cshtml file or by setting the Layout property in the Page Model.

Note: For complex applications with highly interactive UIs, consider integrating with JavaScript frameworks like Blazor, React, or Vue.js.