Overview
An incoming request comes in. Routing matches the incoming request to an endpoint:
- It sets an
Endpoint
and routes values to a request feature on theHttpContext
from the current request:HttpContext.GetEndpoint
returns the endpointHttpRequest.RouteValues
returns the collection of route values
URL matching is part of routing and operates in a configurable set of phases:
- Process the URL path against a set of endpoints and their route templates; return a list of ALL matches
2. Remove matches that fail with route constraints applied; return the remaining matches
3. Remove matches that fail the set ofMatcherPolicy
instances; return the remaining matches
4. Use EndpointSelector to make a final decision from the preceding list
The EndpointSelector
choses the highest priority endpoint (based on RouteEndpoint.Order and route template precedence) as the best match.
Use RouteEndpoint.Order
only when necessary to avoid ambiguity. Prefer to trust EndpointSelector
logic.
Route Template Precedence [ Documentation]
Assigns each route template a value based on how specific the route is. Logic:
- Templates with more segments are more specific
- A segment with literal text is more specific than a parameter segment
- A parameter segment with a constraint is more specific than one without
- A complex segment is as specific as a parameter segment with a constraint
- Catch-all parameters are least specific
URL Generation [ Documentation]
The process by which routing creates a URL path based on a set of route values. This enables separation of endpoints and the URLs that access them.
Route Template [ Documentation]
Tokens within {}
are route parameters that are bound if the route is matched. Route parameters MUST be separate by a literal value:
{controller=Home}{action=Index}
is NOT a valid route.
Escape {
with {{
.
Catch-all parameters
Given the route { path = "my/path" }
:
- The route
foo/{*path}
generatesfoo/my%2Fpath
. - The route
foo{/**path}
generatesfoo/my/path
.
Given the template files/{filename}.{ext?}
- Both
/files/myFile.txt
and/files/myFile
will match even though/files/myFile
does not have a dot at the end.
Complex Segments
Complex segments are processed by matching up literal delimiters from right to left. The match is non-greedy.
Assume the route template /a{b}c{d}
and the URL path /abcd
:
- Searching right to left, the first literal found is
c
- Everything to the right of that (
d
) is now matched to the route parameter{d}
- Searching continues. The next literal,
a
, is found. - The value to the right (
b
) is now matched to the route parameter{b}
If the above URL path was /aabcd
instead, it would not match because after b
is matched to route parameter {b}
, no route template remains to match the remaining a
.
Routing with Special Characters [ Documentation]
Route parameters are not always URL-encoded. See this GitHub issue.
Route Constraints [ Documentation]
Route constraints are used to disambiguate similar routes. Do not use route constraints for input validation. Invalid input would result in an HTTP/404 Not Found and instead should result in HTTP/400 Bad Request.
Route constraints are of the format {parameter:constraint(contraint-argument1, …}
Example: blog/{article:minlength(10)}
Multiple constraints can be applied to a single parameter:
Route constraints include int
, bool
, datetime
, decimal
, double
, float
, guid
, long
, minlength(string)
, maxlength(string)
, length(string)
, min(int)
, max(int)
, range(int min, int max)
, alpha
, regex(expression)
and required
.
Note: when using the regex constraint, ASP.NET Core automatically passes a timeout and adds RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant
to the call.
Custom Route Constraints
These can be created by implementing IRouteConstraint
.
See also:
https://github.com/.net/aspnetcore/tree/main/src/Http/Routing/src/Constraints
Route Parameter Transformers
Transformers transform a parameter’s value when generating links and matching actions and pages to URLs. They execute when generating a link.
Example
A custom slugify parameter transformer:
Route pattern blog\{article:slugify}
with Url.Action(new { article = "MyTestArticle" })
generates blog\my-test-article
.
Implementation
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
public string? TransformOutbound(object? value)
{
if (value is null)
{
return null;
}
return Regex.Replace(
value.ToString()!,
"([a-z])([A-Z])",
"$1-$2",
RegexOptions.CultureInvariant,
TimeSpan.FromMilliseconds(100))
.ToLowerInvariant();
}
}
Parameter transformers are configured using ConstraintMap
in Program.cs
:
builder.Services.AddRouting(options =>
options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer));