Routing with Razor Pages in ASP.NET Core
Routing is the process of mapping an incoming request to a handler (Razor Page, or an action on some MVC controller).
Routing uses URL’s path to decide on the handler. It doesn’t use query parameters. These are bound in the handler to parameters/properties.
RoutingMiddleware
is added to the app with:
This middleware is responsible for selecting the endpoint for a requests.
Endpoints can be either MVC or Razor Pages (or some middleware). All of them
need to be registered with the EndpointMiddleware
. That middleware has a
dictionary of all registered endpoints, and it is shared with the
RoutingMiddleware
. Routing middleware looks through the dictionary for every
requests and makes a “note” about the selection in the HttpContext
object.
EndpointMiddleware
looks at that “note” and executes the selected endpoint
handler.
Configuration
The routing middleware comes with a set of conventions, which may be changed. It can be done as follows:
Route Templates
When defining routes, the template syntax is used making it possible to have some parts of the URLs dynamic.
Here are the options:
- static URL, e.g.
/users
- placeholders, e.g.
/products/{productId}
- placeholder with defualt value, e.g.
/products/{productId=123}
- when navigating to “/products”, a “/products/123” route will be taken - optional placegolder, e.g.
/products/{productId}/{color?}
- optional parameters only make sense at the end of the route template. - catch-all parameter, e.g.
/products/{*restOfUrl}
-restOfUrl
will contain the rest of the URL, even if there are slashes. (there can be either 1 or 2 asterisks (*
) in the template).
Constraints
To avoid weird binding issues we can constraint dynamic parts of the template:
/products/{id:int}
-id
has to be convertible to an integer/products/{id:min(5)}
-id
has to be integer and min 5./products/{id:length(3)}
-id
has to have 3 characters./products/{id:int?}
-id
is optional, but if provided it has to be convertible to an integer/products/int:max(10)?
Convention vs Attributes
Routes can be defined either in a signle place making all endpoints follow some convention, or we can use attributes on every action to define a route for that endpoint specifically. The latter apporach gives more freedom and makes it much easier to introduce changes for a specific endpoint.
Razor Pages
Razor Pages uses a combination of convention and attribute-based routing. The
routes are created by the MapRazorPages()
extension method. All files in the
Pages
direcotry are analyzed and routes are created for them based on their
placement.
To customize the route of a given Razor Page, we need to modify the @page
directive of a given page. Some examples for a page at Pages/Items.cshtml
:
@page "Something"
- the URL will be “/items/something” (appending)@page "{category}/{productId}"
- the URL will be “items/{category}
/{productId}
” (appending)@page "/Something"
- the URL will be “/something” (replacing)@page "/{category}/{productId}"
- the URL will be “/{category}
/{productId}
” (replacing)
With such modifications, the default file location-based route is no longer valid.
Generating URLs
Razor Pages
The framework has a helper for generating URLs to other parts of our app. An example:
The Url
object is a property on PageModel
base class. It has various methods
for building URLs. We can provide some parameters, and the helper will fit this
into the template of the taget page (either as path, or as query).
MVC
It’s very similar to Razor Pages. We also have the Url
object. However, we’d
use the Action
method on it, together with the inputs: action name, controller
name, and optional parameters.
LinkGenerator
There is also a LinkGenerator
class, which may be used in a code outside of
Razor Pages or MVC controllers (such as middleware, or any other code).