OdeToCode IC Logo

.NET Core Opinion 14 - Razor Pages for HTML, Controllers for APIs

Thursday, September 12, 2019

.NET Core opinions are returning from a summer holiday in time to brave the melancholia of autumn.

ASP.NET Core 2.0 introduced Razor Pages, which I believe are superior to the MVC framework when building HTML applications with .NET Core.

Razor Pages encourage a separation of concerns in presentation layer logic, just as the MVC framework does. Razor pages also follow patterns like dependency injection that promote flexibility and testability. But, Razor Pages also offer clear advantages over MVC when generating HTML and processing forms on the server.

Pages Bring Focus

Razor Pages encourage developers to remain constrained in the number of features they add to a class. With pages, you typically pick a specific feature to implement using only GET and POST methods, while controllers can grow to implement dozens of different actions.

Pages versus Controllers

It was easy to see the overstuffed controller phenomenon when using the ASP.NET Identity framework. The Razor Pages version of ASP.NET Identity (shown below on the left) makes it easy to find specific features because the pages are granular. The controller version uses only two controllers with dozens of assorted actions inside. It’s worth noting that the ASP.NET Identity project now supports only Razor Pages when scaffolding a UI.

Imagine trying to find the functionality to change a password in the application. With Razor Pages it is easy to spot the page, while the controller approach hides the feature inside a mega-controller with views and models scattered across other directories.

Pages Organize Features

Speaking of feature organization, I’ve always been a fan of feature folders with the MVC framework. The MVC design pattern wants us to separate concerns using controllers, views, and models. But, nothing in the MVC framework says the models, views, and controllers need to be physically separated into distinct files and spread across separate directories. One of the more frustrating aspects of working with MVC, other than HTML helpers, was trying to track down all the files I need to open when working on a specific feature.

Razor pages maintain a separation of concerns without spraying code across the file system. The page code functions like a controller while the page view contains the presentation logic. You’ll find the code file and the view file nested together. Models and view models can live anywhere but are typically exposed as direct properties of the page, so all the pieces are easy to find.

public class DetailModel : PageModel
    private readonly LeagueData leagueData;

    public int SwanCount = 59;
    public Season Season { get; set; } = new Season();

    public DetailModel(LeagueData leagueData)
        this.leagueData = leagueData;

    public void OnGet(int id)
        var query = new SeasonDetailQuery(id);
        Season = leagueData.Execute(query);

Pages Build Better URLs

Anecdotal evidence tells me that applications built with Razor pages have better URL structures than apps with controllers. Controller apps tend to overuse the default routing template, which leads to a flat URL structure and lots of controller actions taking an ID parameter. Razor pages, on the other hand, tend to promote the organization of related pages into subfolders. Given that ASP.NET Core derives a page URL from the page’s physical location, the URLs are hierarchical and better describe the different features and regions of an application. It’s also easy to look at the UI, and find the Razor page responsible for a particular screen.

What Are Razor Pages Missing?

Razor pages have a lot to offer, including features I haven’t detailed yet, like automatic anti-forgery token validation.

There are also features that do not exist in Razor pages, despite what the ill-informed say.

There is no viewstate in Razor pages. There are also no synthetic initialize or click events. A Razor page is no more stateful than a controller with it’s ModelState data structure. In short, Razor Pages aren’t related to Web Forms or Web Matrix Web Pages. Razor pages are an authentic abstraction for programming the web.


Razor Pages are a welcomed refinement to the MVC framework and include many optimizations for sever-side HTML generation and form processing. Try them out before rushing to judgment!

The need to use the MVC framework is fading into the background with ASP.NET Core. Instead of MVC, we have Razor Pages for HTML apps, Controllers for 'REST' APIs, gRPC services for coupled APIs, and SignalR for real time communications.