Build real web applications with ASP.NET Core Razor Pages

Última actualización: 03/15/2026
  • Razor Pages offers a page-centric model on top of ASP.NET Core, sharing the same powerful routing, middleware and Razor view engine as MVC.
  • Real projects center around the Pages folder, wwwroot, appsettings.json and Program.cs, where services, middleware and error handling are configured.
  • Tools like Visual Studio, Rider and VS Code streamline development, debugging, navigation and refactoring of models, views and Razor syntax.
  • ASP.NET Core simplifies publishing Razor apps to IIS, Azure, custom servers or Docker, enabling scalable and repeatable deployments.

ASP.NET Core Razor real world web apps

If you are coming from Angular plus ASP.NET Web API and you are starting to enjoy C# on the backend, Razor Pages is an incredibly natural way to bring that joy to the frontend without abandoning your existing JavaScript knowledge. Instead of jumping head-first into a completely different UI stack, you can stay in familiar ASP.NET Core territory, use the Razor syntax for server-side rendering, and still sprinkle JavaScript wherever it makes sense.

ASP.NET Core Razor Pages is Microsoft’s recommended approach for building modern web apps on .NET, offering a clean page-based model that sits on top of the powerful ASP.NET Core pipeline. It is cross‑platform, works seamlessly with tools like Visual Studio, Visual Studio Code and JetBrains Rider, and scales from tiny prototypes to production‑grade, database‑backed applications. In this guide we will walk through how real‑world Razor Pages apps are structured, how Program.cs wires everything together, how static files and configuration work, and how tooling, debugging and deployment come into play.

What ASP.NET Core Razor Pages actually is (and how it compares to MVC)

Razor Pages is a feature of ASP.NET Core that lets you build web apps around pages instead of controllers, offering a simpler mental model while still using the same underlying framework as MVC. Under the hood it runs on the same routing, middleware and hosting stack as controllers and views, but each page handles its own behavior instead of centralizing everything in controller classes.

Each Razor Page is typically represented by a pair of files: a .cshtml file for markup and a .cshtml.cs file for the page’s C# logic. The .cshtml file contains your HTML mixed with Razor syntax (for example loops, conditions, and HTML helpers), while the code‑behind .cshtml.cs file holds handler methods like OnGet, OnPost, model properties and any logic needed to render or process the page.

Before Razor Pages, the dominant pattern in ASP.NET was MVC, where controllers returned views and routed all requests through action methods. MVC is still fully supported and is a battle‑tested pattern with strong conventions, but for many scenarios Razor Pages is faster to reason about because the code that loads and processes a page is physically next to its markup instead of buried in a separate controller.

Even though Razor Pages shifts the focus away from controllers, it still uses the same Razor view engine and supports both HtmlHelpers and TagHelpers to generate dynamic HTML. TagHelpers are particularly handy: they extend normal HTML tags with attributes like asp-action, asp-controller or asp-route so you can wire up links and forms to backend endpoints without writing a bunch of manual URLs or inline JavaScript.

For developers who already know JavaScript and have lived in SPA frameworks, Razor Pages offers a hybrid approach: server‑rendered HTML for fast first loads and SEO, with JavaScript and front‑end libraries layered on top where needed. You are not locked into or against any particular JS framework, and you can keep the backend and frontend in the same solution, which simplifies deployment and maintenance.

Creating and running a Razor Pages web app

When you create a new ASP.NET Core Razor Pages project using Visual Studio, Visual Studio Code or Rider, the template wires up a minimal but complete application, including Program.cs, the Pages folder, configuration files and the static web root. Out of the box you get a working site that you can run immediately and then evolve into something more sophisticated like a movie catalog or any other data‑driven app.

Before running the app on HTTPS, ASP.NET Core needs to use a development certificate that your operating system trusts, and the first time you run the project you may see a dialog asking you to trust it. When that dialog appears, choosing Yes indicates that you are OK with the local development certificate being used for HTTPS traffic on your machine, which is required for proper testing of secure endpoints without browser warnings.

On Windows, macOS or Linux, Visual Studio Code lets you launch the app by pressing Ctrl+F5 to run without debugging, or using the Run and Debug panel if you want to attach the debugger. The first time, VS Code may prompt you to select a debugger type such as C#, .NET 5+ and .NET Core or a specific launch configuration like C#: RazorPagesMovie [https] RazorPagesMovie depending on the .NET version and your workspace configuration.

After launching, your default browser opens on a URL similar to https://localhost:<port>, where the port is randomly assigned or specified in launchSettings.json, and you are looking at the home page served by the Razor Pages app. In some templates, you will instead see http://localhost:5001 or another port; the key thing is that localhost indicates it is your own machine and not an external host.

Once you are done testing, you can stop the running app from your IDE: in Visual Studio Code use the Run menu and select Stop Debugging or press Shift+F5, while in Visual Studio for Mac you use Debug > Stop Debugging. This kills the Kestrel web server instance that was started for the session and frees the port for other runs.

Understanding the project structure: folders and key files

Real‑world Razor Pages applications are organized around a few important folders and configuration files that you will work with constantly: Pages, wwwroot, appsettings.json and Program.cs (and in older versions, Startup.cs). Getting comfortable navigating these pieces is crucial because virtually every tutorial, sample or production project uses the same conventions.

The Pages folder is the heart of a Razor Pages project, containing all the .cshtml pages and their .cshtml.cs code‑behind files along with shared layout and partial views. Each page pair (for example Index.cshtml and Index.cshtml.cs) represents a callable endpoint in your app, and special files starting with an underscore, such as _Layout.cshtml, define content reused across many pages.

The layout file, usually named _Layout.cshtml, defines the chrome of your site, such as the top navigation bar, footer and copyright notice, and provides a place to render the body of each individual page. When you change the layout, you instantly affect the look and feel of all Razor Pages that use it, so it is the go‑to spot for editing menus, branding and shared scripts or styles.

The wwwroot folder is the designated web root where static assets live, including CSS, JavaScript, images and plain HTML files that can be served directly by the web server. Anything placed under wwwroot can be accessed by the browser (subject to your static file configuration), making it the right home for site stylesheets, client‑side libraries and images referenced in your markup.

Configuration for the app is typically stored in appsettings.json (and environment‑specific variants like appsettings.Development.json), which contain settings such as connection strings and feature flags. ASP.NET Core’s configuration system loads these files and merges them with environment variables and other providers, making it simple to bind sections to strongly typed options classes in your code.

Program.cs and the ASP.NET Core pipeline

The Program.cs file contains the entry point for your Razor Pages app and defines how the web host, services and middleware pipeline are configured before the first request hits your site. In modern ASP.NET Core versions, Program.cs uses a streamlined “minimal hosting” model with a top‑level statement that creates a WebApplicationBuilder and then builds and configures the WebApplication instance.

The typical pattern in Program.cs starts with var builder = WebApplication.CreateBuilder(args); which sets up a host with common defaults, then calls builder.Services.AddRazorPages(); to register Razor Pages with the dependency injection container. After configuring services, var app = builder.Build(); creates the application object that you then wire with middleware and endpoints.

Error handling and security behavior depend heavily on the environment, so you usually see an environment check like if (!app.Environment.IsDevelopment()) to enable production‑grade features. Inside that condition you will normally find app.UseExceptionHandler("/Error"); which sends unhandled errors to a dedicated Error page, and app.UseHsts(); which activates HTTP Strict Transport Security (HSTS) to instruct browsers to always use HTTPS for your domain.

The middleware pipeline is then assembled with calls such as app.UseHttpsRedirection();, app.UseStaticFiles(); or app.MapStaticAssets();, app.UseRouting(); and optionally app.UseAuthorization(); followed by endpoint mappings. HTTPS redirection forces insecure HTTP requests to be upgraded to HTTPS, static file middleware (or the newer static asset mapping in .NET 9) allows direct serving of resources from wwwroot, and routing decides which endpoint handles each incoming URL.

Finally, Razor Pages are wired into routing with app.MapRazorPages(); optionally chained with .WithStaticAssets(); in newer templates to integrate static asset optimization, and the application is started using app.Run();. At that point the app is listening on configured ports and the Kestrel server is ready to handle real requests, whether locally in development or on a production host such as IIS, Azure App Service or Docker.

Razor Pages, models and view models in real applications

Behind every non‑trivial Razor Pages app sits a set of domain models and view models that represent your data and how it is displayed, whether you are managing a movie catalog, a blog, or a business dashboard. Models typically map closely to database entities, whereas view models may be tailored to one specific screen or user flow, combining multiple models or pre‑formatted values for easier rendering.

A common development workflow is to start with simple C# classes that use fields and method signatures as stubs, and gradually evolve them into proper models with encapsulated properties, validation attributes and logic. Tools like JetBrains Rider make that evolution smoother with intention actions that automatically convert fields to properties, create derived types for inheritance hierarchies and apply other refactorings as you refine your object model.

Inheritance and interfaces help enforce a coherent structure for your models, aligning them with real business rules and enabling polymorphism where certain behaviors are shared but implementations differ. For instance you might have a base ContentItem type with derived Movie, Series and Documentary classes, each with subtle differences but a common contract used throughout your app.

Once your models are in place, Razor Pages or MVC views can be created either by hand or via scaffolding tools which generate pages for listing, creating, editing and deleting entities. Scaffolding dramatically speeds up early development and ensures that routing, model binding and validation are wired correctly, which you can then customize with your own markup and styling.

Razor syntax used in .cshtml files combines smoothly with strongly typed models and view models, allowing you to display data, run loops and conditionals, and generate links and forms using HtmlHelpers or TagHelpers without losing compile‑time safety. This mix of C# and markup keeps a lot of logic server‑side but still yields clean HTML in the browser that plays nicely with CSS and JavaScript.

Working with Razor syntax, TagHelpers and navigation in Rider

Razor syntax is a light layer over HTML that kicks in whenever the @ symbol appears, making it easy to embed C# expressions, statements or helper calls directly in your markup. You can loop through lists of items, show or hide elements based on conditions, or display values and formatted dates without writing a separate template language or sprinkling JavaScript everywhere.

TagHelpers feel like a natural extension of HTML where special attributes starting with asp- modify the behavior or output of elements, often replacing older HtmlHelper methods and removing the need for inline script glue. Examples include asp-action and asp-controller to route anchor tags and forms to specific actions, or route attributes like asp-route-id to pass parameters cleanly in URLs.

IDE support matters a lot when you are deep in HTML, and Rider provides helpful features like breadcrumbs at the bottom of the editor to show your current location in the document’s structure. Breadcrumbs can be customized under Preferences or Options in the Editor section, and they make navigating long Razor files with nested tags far less painful.

In MVC projects, Rider also understands the conventions that link controllers, actions and views, so hovering over action results can show you the possible view paths and Ctrl+Click (or Cmd-Click on macOS) jumps straight to the corresponding .cshtml file. Shortcuts like Ctrl+B or Cmd-B give a quick way to navigate through your code base without hunting through solution explorers.

Beyond Razor‑specific tooling, Rider includes a broad set of intentions and quick‑fixes for HTML, CSS and JavaScript that help you write clean, well‑structured client‑side code inside the same IDE as your C# backend. This tight integration can save a lot of context switches when building complex, interactive UI that still relies on server‑rendered Razor views or pages.

Debugging Razor Pages and ASP.NET Core apps

Debugging is a daily activity in web development, and ASP.NET Core apps running Razor Pages are no exception, so having strong debugging support in your IDE is essential. Both Visual Studio and Rider provide interactive debuggers that can attach to your Kestrel process, step through C# code, inspect variables and evaluate expressions while the app is running.

Rider’s debugger on Windows supports Edit and Continue, which lets you tweak code while the app is paused at a breakpoint and apply the changes without restarting the whole debugging session. That ability to fix small mistakes or experiment during a debugging run speeds up troubleshooting significantly, especially in large projects with non‑trivial startup times.

The default developer exception page in ASP.NET Core is automatically enabled when the environment is set to Development, giving you a detailed stack trace, request information and diagnostics whenever unhandled exceptions occur. This view is extremely helpful during local debugging but dangerous in production because it can leak internal details about your app and environment.

To protect sensitive information, production and test environments typically disable the developer exception page and instead use the configured exception handler route, often /Error, to show a user‑friendly error screen while logging the real details server‑side. This behavior is controlled in Program.cs via the environment check and calls to UseExceptionHandler and UseHsts.

When things truly go off the rails and tutorials are not matching your behavior, it is often useful to compare your project with a known good sample provided by Microsoft or other authoritative sources. Many official Razor Pages tutorials publish a completed sample project that you can view or download to diff against your own code and spot missing configuration, typos or mis‑placed files.

Publishing and deploying real ASP.NET Core Razor apps

Shipping your Razor Pages app is where all the earlier structure and configuration pays off, because ASP.NET Core supports several deployment options that fit different hosting environments and workflows. Whether you prefer IIS on Windows, Linux containers in Docker, or a managed platform like Azure App Service, the publish process can be driven by MSBuild and integrated into your CI/CD pipelines.

Visual Studio and Rider both offer publishing profiles that can package your application and deploy it to IIS using Web Deploy (MSDeploy), copy it to a local or network folder, or push it directly to a remote server via FTP, FTPS or SFTP. Creating a publish profile encodes your deployment settings so that future publishes are as simple as choosing the profile and clicking a button or running a command.

For cloud scenarios, Azure App Service is a popular target, and IDEs integrate Azure tooling to create and publish web apps straight from your project, again piggybacking on MSBuild and MSDeploy under the hood. This approach keeps build and deployment consistent between local and cloud environments and can be automated in Azure DevOps, GitHub Actions or other CI systems.

Docker is another first‑class option for ASP.NET Core, letting you containerize your Razor Pages app so it can be run predictably in any environment that supports containers. Rider and Visual Studio can help you generate Dockerfiles and docker‑compose configurations, enabling a workflow where you develop, debug and deploy your app inside containers, whether locally or in orchestrators like Kubernetes.

Regardless of target, the publish step compiles your C# code, bundles Razor views, copies static assets, and, depending on settings, may also generate a self‑contained runtime so the host machine doesn’t need a shared .NET installation. This bundling is what turns your development project into a deployable artifact ready for use by real users.

Putting all these pieces together—from development certificates and Program.cs, through Pages and wwwroot, to debugging and publishing—Razor Pages offers a pragmatic way to build real‑world ASP.NET Core web applications that are maintainable, performant and comfortable for developers who already enjoy working in C# and are not ready to fully bet on a single-page framework for every situation.

Related posts: