Razor Pages
It’s a framework for building multiple-pages based websites generated on the server-side.
The flow is as follows:
- A Razor Page is selected by the routing middleware.
- The
PageModel
associated with the page is executed (e.g.OnGet
). - The view is rendered using the data from the
PageModel
.
Pages are stored in the .cshtml
files within the Pages
directory of a
project (by default). Razor is a templating syntax.
Here’s an example of Pages/Privacy.cshtml
:
The file name matches the URL path. localhost/Privacy
will run the
Pages/Privacy.cshtml
page.
The default Program.cs
file looks as follows:
MVC
The following components take up roles in the MVC paradigm:
- Model - the logic and data provided by our business logic (like some
WeatherProviderService
) - View - the Razor Page template (the
.cshtml
file) - Controller - the
.cshtml.cs
file containing page’s logic. It interacts with the Model and invokes the View (or returns redirect/error)
Razor Pages vs ASP.NET Core MVC
We can build server-side websites with either Razor Pages or ASP.NET Core MVC
directly. In the latter case, we’d have Controller
s and action methods within
it, just like with the ASP.NET Core Web APIs.
The controller would construct a view model and return a view based on it:
In contrast, int the Razor Pages approach, the PageModel
is both the
Controller and the View Model.
The two approaches are quite similar, but some of the advantages of Razor Pages are:
- each page is a separate set of files - in MVC, actions of some domain are all in the same controller (by convention), making it large sometimes.
- Razor Pages has a convention of placing related things close to each other. In
MVC all controllers are in one
Controllers
directory, all view models are in theView Models
directory, and all the views are in thhViews
directory (with Razor syntax). - pages that are static are easier/shorter to code using Razor Pages. In MVC we’d have to add an action that just returns the view (boilerplate code).
Both approaches are pretty similar. Razor Pages should be the preference though.
PageModel
Complex logic for a page should be handled in a the PageModel
(a base class
for page models). We could use it to load data from some DB, etc. It is a “code
behind” file, similar to WPF.
The model to use for a Page is specified with the @model
directive.
The PageModel
is executed first, then the page generated.
An example:
The properties of the model are accessible to the .cshtml
view making it
possible to render dynamic data from the model. That data, exposed by the
PageModel
may be called a View Model.
Binding
Data is bound from:
- form
- path
- query
- headers?
By default, the framework looks through all of these sources for each parameter to bind. We can also specify the source explicitly:
We can also use it on the method parameters.
Class
If our Page has a few properties to bind from a request, we can introduce a separate class that encompasses that data:
Validation
Go to Validation;
URL
By default, the page’s file path in the project defines the URL that would
execute that page. E.g. and URL /products/list
would execute the page at
Pages/Products/List.cshtml
.
Handler
A single PageModel
class may contain multiple methods to handle requests. The
name of the method should contain the HTTP method and, optionally, some
additional suffix. If we have multiple methods for the same HTTP method, but
with different suffixes, the selected suffix needs to be provided as a handler
parameter.
Method name template: On{verb}{handler}[Async]
.
The handler
should be included in the URL template for our Page.
Data in Razor
Razor Page can access “external” data in the following ways:
PageModel
’s public properties - the recommended way (accessed as@Model.{property}
)ViewData
- a dictionary whose key-values pair may be set from aPageModel
. It’s useful for passing data between layouts.HttpContext
object@inject {service}
- we can use services from DI directly in views
ViewData
ViewData
can be set directly in the view itself:
It can also be set in the PageModel
:
Razor Syntax
MSDN is the best place to follow.
Tag Helpers
Tag Helpers are useful mostly with forms. In general, tag helpers reduce the amount of markup that we have to write, making the HTML more readable and less C#-y.
The HTML elements that have tag helpers attached to them are modified by the framework. No other element can be modified.
Tag Helpers, like validation, relies on the DataAnnotations
.
An example:
With this markup, the generated HTML elements have proper content, ids, names, client-side validation.
Some tag helpers are about attributes that mey be added to elements (asp-*
).
Some other tag helpers are completely new HTML elements:
We could have this functionality without Tag Helpers, we could use some @if
.
However, Tag Helper makes it shorter and more readable.
Reusing Code
Layouts
Razor Pages has a concept of Layouts, which allows to define common
structures of web pages. It avoids duplication of common parts of web pages.
Layouts can be found in the Pages/Shared
directory.
A simple layout example:
Every layout needs to call the @RenderBody()
function. That is where the child
view will be rendered. We can also use Sections in a layout to define more
spots for rendering content.
For example, layouts could define:
- a general page structure - navbar, sidebar, footer, and a place to put content
- multiple-column layout with separate sections to put content in these columns
Choosing a Layout
The base layout should be named _Layout.cshtml
. This layout is selected for
every Page by default. A different layout may be selected by setting the
Layout
property in a view (e.g. Layout = "_MyLayout"
).
Partial Views
Another option to use is Partial Views. They act much more similarly like components in SPAs.
- Layouts define the OUTSIDE of the Page.
- Partial Views may be included INSIDE of the page
Partial Views are all about HTML markup and reusing it. When using a Partial
View we can pass some data into it. Partial Views do not have PageModel
s
associated with them.
The file of the Partial View may be located anywhere in teh directory tree of
the Page that uses it, or in the Pages/Shared
or Views/Shared
.
_ViewImports
The _ViewImports.cshtml
file may be used to define namespace imports that are
common in our application. It can be placed in any directory and it will be used
by any Page in that folder and sub-folders. Placing it in the /Pages
directory
makes it applicable to all pages.
We can use @using
and @addTagHelper
in _ViewImports
.
_ViewStart
The _ViewStart.cshtml
allows us to run some common code BEFORE the view itself
executes. It’s often used to set the Layout
. This way we don’t have to do it
repeatedly in every page.
The filesystem placement works the same way as with the _ViewImports
files.