More Flexible Routing For ASP.NET Web Pages

4.65 (20 votes)

The built-in Web Pages routing system offers a fair degree of freedom in terms of how friendly URLs can be constructed and managed. However, the major limitation with the standard routing mechanism is that it relies on matching segments of the URL to files on disk. ASP.NET MVC and Web Forms enjoy a much more flexible routing system. This article looks at a Package that brings full ASP.NET routing control to Web Pages and examines how to use it.

By default, ASP.NET will always try to match the URL in a request to a file on disk. If no match is found, ASP.NET attempts to see if a match for the URL's pattern can be found in a RouteCollection object. The RouteCollection is a collection of Route objects (sounds obvious when you say it like that...), and each Route object holds the definition of a URL pattern, and instructions on how ASP.NET should handle requests that match each pattern. However, a RouteCollection needs to be populated and a means of handling the request defined. ASP.NET Web Forms and ASP.NET MVC include mechanisms to manage this as part of their standard framework. If you want to add full routing control to your Web Pages application, you need to acquire the Routing For Web Pages package.

The package will install a file called WebPagesRouteHandler.cs into your App_Code folder. It will create an App_Code folder if one doesn't already exist. The file contains three classes: WebPagesRouteHandler, whose job is to locate the correct end point (page) to process the request for a given URL match; RouteCollectionExtension; and ContextExtensions (more of which a bit later). The RouteCollectionExtension class contains one method: MapWebPageRoute() which is used to define a Route and then add it to the RouteCollection.

Defining a Route

A Route has five elements to it:

  • A URL
  • A physical file
  • Default values for route parameters
  • Constraints that must be met for this Route to be a match for a particular URL
  • A name for the Route

Of these, only the first two elements are required. The URL is the one that you want to use to point to resources in your site. They are most likely to be SEO-friendly, for example List/Beverages/ to display all products in the Beverages category rather than, say, ListProducts.cshtml?category=beverages. URL definitions can also be composed of parameters which specify a pattern to match. The List/Beverages URL may be represented as List/{category}/, where {category} is a parameter, or placeholder for a variable. That means that List/Beverages and List/Condiments or indeed List/Anything will all result in matches for this pattern.

The second part of a route is the name of the file that will actually process the request. Note that if the file name without the extension is used as the first segment in any URL, default Web Pages routing will kick in. For that reason, you should avoid naming files the same as a vital part of your URL schema. Continuing with the previous example, if a file called List.cshtml was added to the site, none of the route examples: List/Beverages/, List/{category}/, List/Anything/ would be invoked because a file-on-disk match will be made with List.cshtml.

You can provide default values for parameters. These are stored in a special kind of Dictionary - a RouteValueDictionary. In addition, you can constrain the acceptable values for a parameter through an expression. Route constraints are also stored in a RouteValueDictionary. Finally, you can provide a name to identify each Route by. If you don't provide one, the URL is used instead.

Adding Routes

Routes need to be set up for the lifetime of the application, so the best place to do this is in the _AppStart.cshtml file. A simple Route definition looks like this:

RouteTable.Routes.MapWebPageRoute("List/", "~/ListProducts.cshtml");

This will result in any request to List/ being mapped to the ListProduct.cshtml file. The following example adds a parameter:

RouteTable.Routes.MapWebPageRoute("List/{category}/", "~/ListProducts.cshtml");

No default value for the category parameter has been provided, so a URL must include a value for this segment for it to be considered a match to this Route. List/Beverages/ is a match, as is List/Anything/ or List/1/. If a default value for the category parameter is specified, that segment does not need to be included in the URL for a match to be achieved. In that case, List/ will match. Default parameter values are added to a RouteValueDictionary, with the name of the parameter acting as an entry's key. An anonymous object is used in the MapWebPageRoute method to populate the RouteValueDictionary:

RouteTable.Routes.MapWebPageRoute("List/{category}", "~/ListProducts.cshtml", new { category = "Beverages" });

You can have multiple parameters. In fact, a URL pattern can be composed entirely of parameters. Take a look at the following:

RouteTable.Routes.MapWebPageRoute("{product}/{action}", "~/Catchall.cshtml", new { product = "Beer", action = "Drink" }); 

This will match any one or two part URL because there are default values for both parameters. For that reason, you should be careful when designing URL patterns. Furthermore, when the RouteCollection object is inspected for matches, the first match is returned. If this was added as the first route, all requests consisting of one or two URL segments will result in Catchall.cshtml being requested. A URL with no segments specified (eg www.domain.com) will result in the default document being returned through simple file-on-disk matching, and three ore more segments exceeds the number specified in the pattern. Having said that, a match for three or more segments can be achieved through using a wildcard in the pattern specification. The wildcard character is * and it is applied as follows:

RouteTable.Routes.MapWebPageRoute("{product}/{*action}", "~/Catchall.cshtml", new { product = "Beer", action = "Drink" }); 

Any value can be provided to the parameters in the examples so far. No restrictions or "constraints" have been placed on what is acceptable for a match to be made. You constrain parameter values by providing regular expression patterns that valid values must match. If the category parameter in the first route example expects a word, the regular expression pattern "[a-zA-Z]" will restrict valid values to just letters:

RouteTable.Routes.MapWebPageRoute("List/{category}", "~/ListProducts.cshtml", constraints: new { category = "[a-zA-Z]" });

RouteValues

In the previous example, you need to know what value was passed to the category parameter if you are going to do anything meaningful in ListProducts.cshtml. You acquire the value via the GetRouteValue method, which is the only member of the ContextExtensions class mentioned earlier. The GetRouteValue method requires a valid key:

var category = Context.GetRouteValue("category");

From that point, you can use the value as a parameter in a database call, or in any way that suits your application.

Summary

The built-in Web Pages routing system is pretty flexible, but it is based on matching files on disk. You can achieve some pretty good URLs with this mechanism although you may need to employ a pretty extensive directory structure to give you the right file path to map to a URL. Or you will probably have to contend with some complexity in your pages as you unpick several layers of UrlData. The Web Pages Routing package helps you do away with all that by entirely divorcing the URL from your files. Additionally, the ability to set default values for parameters and constrain valid values offers a very fine level of control.

 

You might also like...

Date Posted:
Last Updated:
Posted by:
Total Views to date: 35601

21 Comments

- Zoltan Szabo

You might want to indicate the download by its unique NuGet ID (WebPageRouteHandler), as the search by name will not produce an unequivocal link.

- Mike

@Zoltan

The Web Pages Package Manager Search is rubbish, so I take your point. If installing from WebMatrix, search for "Route". At the moment, that will produce one result. If you are installing from the Package Manager Console in Visual Web Developer/Visual Studio, use the following command:
PM> Install-Package WebPageRouteHandler

- Chris Bertelli

I tried adding the package you mention, and adding a route as indicated, and when I try to build the website, I get an error "The name 'RouteTable' does not exist in the current context". Something else must be missing. I created the project as an "ASP.NET Web Site (Razor v2)" in VWD 2010 Express, and added a reference to "System.Web.Routing" also. What else is needed?

- Mike

@Chris,

You alos need to add @using System.Web.Routing; to the top of the _AppStart file.

- Terry

There is no such thing as RouteTable.Routes.MapWebPageRoute() so this entire article is bogus

- Mike

Hi Terry,

MapWebPageRoute is a method that comes with the package that the article features.

I don't know whether you are aware, but peeps can make up their own methods in C# and call them pretty much what they want.

But thanks for stopping by.

- Kurt

Does this work on IIS 7.0? It's working fine in Webmatrix but wneh copying the code to W2K8 server on IIS 7.0 the error Chris mentioned appears (despite @using System.Wed.Routing being present in _appstart.cshtml). Are there are other configurations required?

Thanks

- Kurt

Found a solution to the IIS 7.0 WebPageRouteHandler issue

Just add the following to web.config:

<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>

Hope this saves a few people pullig their hair out!
Kurt

- Nuri

Mike, does this article and related WebPageRouteHandler.cs still apply to Web Pages 3.0?

- Mike

@Nuri,

Yes, it works with Web Pages 3.

- Shekhar

I am using VS2008 & framework 3.5. The solution you given to route the links give error:
The name 'RouteTable' does not exist in the current context"

- Mike

@Shekhar,

This article is about using Routing in an ASP.NET Web Pages site. That framework requires at least VS2010 SP1 and .NET 4.0. Nothing in the article above is relevant to your scenario.

- Gaurav

I get "'MapWebPageRoute' is not a member of 'System.Web.Routing.RouteCollection'." error when running the code from IIS7. I know I can use "modules runAllManagedModulesForAllRequests = true" but I won't want to make this true. Is there any other solution to solve this problem.

- Mike

@Gaurav,

Are you sure you included the package?

- Dave

Is there a VB version of this package?

- Mike

@Dave,

No there isn't. You can use something like converter.telerik.com to generate a VB version. That way you can include it in an App_Code folder with existing VB code.

- Sergey

Hi.

How I can set up my site to get urldata from link for default page?

site.com/default/1- this work, but

site.com/1 return 404 error

- Mike

@Sergey,

UrlData doesn't work with the default document. You need to use Query String instead.

- Jan

Hello,
is it possible to specify the range of a parameter to numbers not characters? i tried parameter="[1-999]" but this works only if the parameter is in 1-9 range.

- Mike

@Jan,

You need a regular expression that matches any number from 1-999. This will do that: ^(\d?[1-9]|[1-9]0|[1-9][0-9][0-9])$

- czk

_AppStart.cshtml file should be placed in root directory!

Recent Comments

Pam 30/08/2017 11:30
In response to Sending Email in Razor Pages
Mike, RazorPages sound like a nice choice for somebody still working in ASP classic who wants to to...

Robby Robson 15/08/2017 00:43
In response to Routing in Razor Pages
Mike: great stuff. Now that .Core Standard 2.0 is formally out, how soon will you rewrite your book...

Satyabrata Mohapatra 28/07/2017 08:59
In response to Sending Email in Razor Pages
Bit off topic, but congratulation sir for your MVP award. You deserve it !!!...

Satyabrata Mohapatra 23/07/2017 16:43
In response to Razor Pages - The Elevator Pitch
@Dale Severin You can continue to build apps using asp.net web pages....

Satyabrata Mohapatra 23/07/2017 16:40
In response to Sending Email in Razor Pages
Thanks for sharing...learned a lot...

Gfw 22/07/2017 11:53
In response to Sending Email in Razor Pages
Question... Does System.Net.Mail support SSL?...

Dale Severin 20/07/2017 03:38
In response to Razor Pages - The Elevator Pitch
I work with razor web pages extensively. I appreciate the rapid development it permits me to I am as...

Obinna Okafor 14/07/2017 01:19
In response to Routing in Razor Pages
Thank you, Mike. Good post....

Satyabrata Mohapatra 11/07/2017 16:02
In response to Routing in Razor Pages
Very powerful routing system!!...

Cyrus 05/07/2017 03:41
In response to Razor Pages - Getting Started With The Preview
How can I trim packages and services as much as possible to use just razor pages? I don’t want to to...