Introduction
The ASP.NET Routing system is primarily responsible for two operations:
- It maps incoming HTTP requests to a route handler given a collection of routes.
- It generates URLs (links) from these routes.
A typical route definition is a string that contains a URL template to match, such as:
"blog/{year}-{month}-{day}/{slug}"
With the URL template, the route definition can also contain default values and constraints for parts of the URL. This helps define exactly which URLs the route can match, for example:
routes.MapRoute( name: "Product", template: "Product/{productId}", defaults: new { controller = "Product", action = "Details" }, constraints: new { productId = @"\d+" });
The first argument "Product", is the name of the route. The second argument "Product/{productId}", is the URL template for this route. The URL must be of the form “Products/n”, where n is the productID. The 3rd argument contains the default route values. In this example, the default controller is ProductController.
When a route matches a request, route data is generated and passed to the route handler. The route data contains a set of key value pairs typically representing the matched segments and default data. In an MVC application the matched segments are used to select a controller and action and execute it.
The ASP.NET Routing system can use the same templates to generate URLs as well.
History
Originally, ASP.NET Routing was written as integrated feature of ASP.NET MVC (in pre-v1.0-beta days of ASP.NET MVC). Later it was extracted in its own assembly (System.Web.Routing) and it was made part of the .NET Framework. This more general routing system supports not only MVC, but also WebForms with page routes.
When ASP.NET Web API was introduced there was a need for it to be hostable outside of the ASP.NET pipeline in IIS. In that scenario Web API cannot use ASP.NET routing. As a result, Web API was built using a routing façade, which can delegate to its own version of routing. When Web API is hosted on ASP.NET in IIS with the WebHost NuGet package the Web API routing façade delegates to ASP.NET’s built-in routing system.
What’s new in vNext
In ASP.NET vNext the routing systems have been unified to a single implementation that is used when hosting in IIS, self-hosting, and for in-memory local requests for unit testing. Routing in ASP.NET vNext resides in the Microsoft.AspNet.Routing NuGet package. The source repository is located here: https://github.com/aspnet/Routing. Similarly MVC and Web API have been unified into a new MVC framework.
There are several major improvements to routing in ASP.NET vNext.
Rerouting the request
To understand this new feature, it’s important to understand that routing and action selection are two separate systems.
- The routing system’s responsibility is to find a matching route, create route data, and dispatch the request to a handler.
- Action selection is an implementation detail of the MVC’s handler. It uses route data and other information from the incoming request to select the action to execute. In an MVC application, MvcRouteHandler is the handler the request gets dispatched to.
In MVC 5 and Web API 2 there is no way for the handler to return control to the routing system if no action could be matched. In this scenario MVC and Web API will typically return an “HTTP 404 - Not Found” response. That meant that a developer had to be careful about the order of the routes, and constraints had to be crafted carefully to make sure incoming URLs reached the desired action.
In ASP.NET vNext there are two improvements to this behavior:
- The handler can return control to the routing system indicating that it cannot handle the request. In that case the routing system will try and match the next route in the route collection. This allows for a more flexible ordering of routes and reduces the need to add constraints to each route.
- Before dispatching the request to the handler, the routing system can use dependency injected constraints, so it can in effect look into what actions are available down stream at the time of matching.
Routing as middleware
Since ASP.NET MVC and WebAPI’s release, the ASP.NET framework is a family of pluggable components rather than a single framework.
In ASP.NET vNext, Routing is built as a middleware component that can be added to the request pipeline. It can be composed along with any other middleware components, such as the static file handler, error page, or the SignalR server.
Of course just like in MVC 5, a developer can hook up handlers other than MVC’s MvcRouteHandler.
The request flow through the routing pipeline
This section brings together everything that you’ve read so far:
- When a request is processed by ASP.NET vNext, the routing middleware will try to match the request with routes in the route collection.
- If one of the routes matches the request, it looks for the handler for the route.
- The RouteAsync method of the handler is called.
- The RoutingContext has a flag called IsHandled. If that is set to true, it means the request was successfully handled by the handler. If it is set to false, it means the route wasn’t able to handle the request, and that the next route should be tried.
Inline constraints
Route constraints let developers restrict how the parameters in the route template are matched. For example, a regex constraint requires that a regular expression match the value in order for the route to match. In MVC 5, the constraint syntax looks like this:
routes.MapRoute("Product", "Product/{productId}", defaults: new { controller = "Product", action = "Details" }, constraints: new { productId = @"\d+" });
In this code sample the productId parameter has a regex constraint that matches an integer of one or more digits (0-9). This style of defining routes and constraints is called “convention-based routing”. In MVC 5 and Web API 2 we introduced a new style of constraints and defaults for attribute routing scenarios, called inline constraints. In ASP.NET vNext, we are introducing a similar syntax for defining route constraints for convention-based routing. It simplifies specifying the constraints with an inline syntax in the form of:
"{parameter:constraint}"
A developer can now apply a constraint in a more succinct way as part of the URL template, Here are a few approaches. The full list of constraints that can be applied will be identical to the Web API 2 attribute routing constraints.
Constraint | Description | Example template |
alpha | Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z) | "Product/{ProductName:alpha}" |
int | Matches a Signed 32-bit integer value. | "Product/{ProductId:int}" |
long | Matches a Signed 64-bit integer value. | "Product/{ProductId:long}" |
minlength | Matches a string with a minimum length. | "Product/{ProductName:minlength(10)}" |
Regex | Matches a regular expression. | "Product/{productId:regex(^\\d{4}$)}" |
Optional URI Parameters and Default Values
A URI parameter can be made optional by adding a question mark to the route parameter. The example specifies that the productId parameter needs to satisfy the built-in “long” constraint. This example also shows that the parameter is optional, as denoted by the “?” token.
routes.MapRoute("Product", "Product/{productId:long?}", new { controller = "Product", action = "Details" });
You can specify a default value inside the route template as follows. This example shows that the productId is optional and default value is 1.
routes.MapRoute("Product", "Product/{productId:long=1}", new { controller = "Product", action = "Details" });
Looking forward
For the beta release of ASP.NET vNext the following features are planned. These features should be available in the nightly builds as they become available.
- Inline constraints for convention-based routing, as described in the details above.
- Attribute based routing with inline constraints
The Web API 2 already has this feature, and it will soon be available for ASP.NET vNext.
There are a lot of improvements done and planned for Routing for ASP.NET vNext. Your feedback will be very valuable. Please provide your feedback on these features and feel free to let us know what you’d like to see that you don’t see here – we’ll definitely consider it! You can provide feedback in comments on this blog or on GitHub (https://github.com/aspnet/Routing). If you ask a question in Stack Overflow, use the asp.net-vnext tag.