Featured resource
2025 Tech Upskilling Playbook
Tech Upskilling Playbook

Build future-ready tech teams and hit key business milestones with seven proven plays from industry leaders.

Check it out
  • Lab
    • Libraries: If you want this lab, consider one of these libraries.
    • Core Tech
Labs

Guided: Building an Order Form with ASP.NET Core 10 Razor Pages

In this code lab you’ll learn how to use ASP.NET Razor Pages to build an interactive order form. You'll represent the form data with a strongly typed model, using data annotations to define validation rules. You will use tag helpers to render form controls in a consistent and accessible way, and implement page handlers to process GET and POST requests. Finally, you'll see how to add custom server-side validation logic to handle business rules.

Lab platform
Lab Info
Level
Intermediate
Last updated
Mar 13, 2026
Duration
47m

Contact sales

By clicking submit, you agree to our Privacy Policy and Terms of Use, and consent to receive marketing emails from Pluralsight.
Table of Contents
  1. Challenge

    Introduction and Setup

    Introduction to Razor Pages

    Razor Pages is a page-based web UI framework built into ASP.NET. It was introduced as a simpler way of building web user interfaces compared to the full MVC pattern, especially for page-focused scenarios.

    If you need complex routing or are building an API, the MVC pattern provides more control. Razor Pages works well when you have to build a page-centric UI with straightforward request and response flows. The two patterns can also coexist in the same project.

    Each page in Razor Pages consists of two files:

    • A Razor template with a .cshtml extension
    • A C# class that defines the page's logic

    In an MVC project, you will often work across multiple folders for controllers, models and views. With Razor Pages, these two files are colocated, which simplifies development and organization.

    Combining presentation and logic in one place still allows you to build dynamic features, as you will see in this lab. ### Lab Scenario

    In this lab you will build an order form for a fictitious technical conference, where attendees can select the free item of merchandise they prefer.

    A basic starter project has been prepared for you to extend by adding form fields, validation, processing logic, and confirmation display.

    In the project folder Pluralsight.OrderForm, there is an ASP.NET Razor Pages application that you will modify to implement these features. ### Build and Run the Application

    Run the following commands in the terminal to navigate to the application folder and start the application:

    cd Pluralsight.OrderForm
    dotnet run
    

    Click on the Web Browser tab, and then on the button Open in new browser tab↗. You will then see the web application running.

    Whenever you make changes to the code while completing the tasks, you must stop and restart the application for the changes to take effect. Press CTRL+C in the terminal, and then run dotnet run again.

    If you only want to verify that the project still compiles, you can run dotnet build instead of starting the application. ### Working with the Project

    Explore the files in the Pluralsight.OrderForm folder to see what makes up a Razor Pages application.

    • In the Models folder, you will find Order.cs. This is a C# model that represents the order form. It currently contains a single property. You will add additional properties to this class to capture and validate the information required to fulfill an order.
    • In the Pages folder, locate Index.cshtml. This Razor template renders the order form. It contains placeholder markup with TODO comments for each form field you will implement.
    • In the Pages folder, you will also find Index.cshtml.cs. This file contains the associated code for the page. It includes two empty methods: OnGet() and OnPost(). These methods run when the page receives a GET request and a POST request, respectively. You will use them to customize the behavior of the page when it loads and when a form is submitted.

    There are other files necessary for bootstrapping the web application and rendering the pages that you can review. None are essential to understand for the purposes of the lab nor will you be modifying them.

  2. Challenge

    Define the Order Model

    To render and process the order form, you first need a strongly typed representation of the data you will collect. This is what has been started in the Order.cs file. Each piece of data will be defined as a property on this class.

    Currently it contains a single string property for the user's first name.

    Your stakeholders have confirmed that the following information needs to be collected:

    • First name
    • Last name
    • Email address
    • Selected merchandise product
    • Address
    • Comments
    • Indication that the attendee has agreed to their data being collected

    Each piece of information will be captured using a single primitive type, such as string or bool.

    All information is required other than the comments. Optional values can be represented using nullable types, such as string?. The model needs to define not just the information you collect from the form. You can also include any details you need to present to the user when they are filling in the form.

    Most form fields will accept free-text input. However, the product selection should constrain the user's input to a set of valid choices.

    In this scenario, the available products are fixed, so you can define them directly in the model. You have now added all the necessary properties to the model class. However, you can go further by encoding validation constraints into the model.

    Some properties are required and some are optional. For example, the email address should follow a valid email format rather than accepting any arbitrary string.

    In a real-world application, the collected information would be stored in a database with field size limits. Adding validation constraints to the model ensures that invalid data is rejected before it reaches storage.

    These constraints are defined using data annotations.

    Before adding them, verify that Order.cs includes the following directive at the top of the file, as this namespace is required for the validation attributes: using System.ComponentModel.DataAnnotations;

    For example, the FirstName property can be marked as required and limited to a maximum of 50 characters by decorating the property like this:

    [Required]
    [StringLength(50)]
    public string FirstName { get; set; } = string.Empty;
    

    Finally, you can give each property a friendlier display label for the form. When the user is completing the form, you would prefer they see properly presented field names like "First Name" (rather than "FirstName" which is the name of the property in code).

    You can apply that with another data annotation attribute: [Display(Name = "...")].

    For example, your fully annotated FirstName property can look like this:

    [Required]
    [StringLength(50)]
    [Display(Name = "First Name")]
    public string FirstName { get; set; } = string.Empty;
    ``` That completes the model updates. You will now move on to build the order form itself.
  3. Challenge

    Build the Order Form

    By defining the Order model and decorating it with attributes, you can use tag helpers to handle most of the form rendering. Tag helpers are a Razor Pages feature that let you attach server-side behavior to HTML elements via attributes. Instead of mixing C# code into your markup, you add attributes to standard HTML elements.

    For example, you can render an accessible label and input control inside a <form> element with the following markup:

    <label asp-for="Order.FirstName" class="form-label"></label>
    <input asp-for="Order.FirstName" class="form-control" />
    

    The asp-for attribute indicates the model property to bind to the element. Providing this binding allows ASP.NET to automatically populate HTML attributes like value and maxlength, which you can verify by viewing the page source once the form is complete. The [BindProperty] attribute enables model binding. When the form is submitted, ASP.NET automatically populates this property with the submitted from values.

    With this attribute applied, the page exposes the Order model as a bindable property that you can reference in tag helpers. The product selection should be presented as a drop-down list rather than a text box, to ensure the user can only select valid products.

    You will use a <select> element bound to Order.Product via the asp-for tag helper. A second tag helper, asp-items, populates the drop-down with the available products from the model. There are two more fields to add to the form: a free text comments field and an agreement for data processing.

    The comments field will use a <textarea> element. The agreement field will use an <input type="checkbox"> element.

    Both elements will be bound to the model using tag helpers. If you re-run the web application now with dotnet run you should see all the form fields presented with friendly and accessible labels.

  4. Challenge

    Handle Form Submission and Validation

    With the form presentation complete, you now need to handle what happens when the user submits it. You should not blindly accept the input. Instead, you need to ensure the validation rules defined on the model are enforced.

    ASP.NET provides a robust validation pipeline that works on both the server and the client, and you will implement both.

    Why is it necessary to validate both on the server and the client? Server-side validation is essential. It runs inside your page handler and is the only validation you can fully trust, since a user can always bypass the browser. Client-side validation gives the user a better experience by providing immediate feedback without a round trip to the server.

    However, this does not mean you need to duplicate effort. The validation works by reading the data annotation attributes you already added to the model, so you get consistent rules in both places without having to maintain logic in two places (or in two languages).

    You will start with server-side validation, which requires the wiring up of model binding in your page code file. When the form is posted, ASP.NET automatically validates the bound model against its data annotation attributes and records the results in ModelState.

    The code checks ModelState.IsValid, and if validation fails, calls Page() to re-render the current page, preserving the form data and any validation errors. The updates to Index.cshtml.cs provide the security you need, but it is not very friendly for the user as they cannot currently see which particular piece of information they provided is invalid.

    To fix that, you will add tag helpers to the form that surface validation errors to the user.

    ASP.NET provides two tag helpers for presenting validation errors, one for presenting an overall summary and one for field-level details. Re-run the web application with dotnet run.

    If you submit the form with invalid data, you should see it re-displayed with the appropriate validation messages.

    As discussed earlier, client-side validation provides a better experience by surfacing errors immediately. This feature is powered by the jQuery unobtrusive validation library, but for your requirements, you only need to reference the appropriate files.

    The default ASP.NET Razor Pages project includes a partial view — a reusable fragment of Razor markup — for this: _ValidationScriptsPartial.cshtml Re-run the application and verify now that the feedback on invalid entries is provided immediately, without a server round trip.

    You now have validation in place for required fields, string lengths and agreement to terms. Sometimes you need more complex validation, and you will implement one such rule now.

    If one of the products proves particularly popular, the inventory could run out. You will simulate this by encoding a custom validation rule in the server-side logic. With this in place, if the Mug is selected, an additional model error is added, making the model state invalid. As the error is associated with the Order.Product field, it will be displayed next to that field, indicating that the user must choose another item.

  5. Challenge

    Provide a Confirmation Page

    The final requirement is to display a confirmation page when the user submits a valid order. Now, you need to update the POST handler to redirect to the confirmation page after a valid submission.

    You will do this using a common pattern known as Post/Redirect/Get. This three-stage process incorporates a form post, a redirect and a fresh page load.

    This pattern provides valuable protection against duplicate submissions. A page refresh of the confirmation screen will simply re-display the page; it will not trigger a second form submission. Run the application again and verify that submitting a valid order displays the confirmation page.

  6. Challenge

    Summing Up

    That completes the code lab. You have built a functional order form using ASP.NET Razor Pages.

    You defined a strongly typed model with data annotation attributes to enforce validation rules, used tag helpers to render form controls that are synchronized with the model, and implemented a page handler to process the form submission.

    You added both client-side and server-side validation to ensure data integrity, including a custom business rule.

    Finally, you applied the post/redirect/get pattern to prevent duplicate submissions and present a clean confirmation page.

    These techniques form the foundation for building page-centric form workflows in ASP.NET.

About the author

Andy is a developer and architect, working at a digital agency with a primary technical focus on using .Net and Azure to deliver solutions for clients. Future interests include, .Net, Azure, Azure Functions, Bot Framework, Alexa, Umbraco, Sitecore, and EPiServer.

Real skill practice before real-world application

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Learn by doing

Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.

Follow your guide

All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.

Turn time into mastery

On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.

Get started with Pluralsight