URL Routing

Introduction

URL routing in ASP.NET enables you to use URLs that do not have to map to specific files in a Web site. Because the URL does not have to map to a file, you can use URLs in a Web application that are descriptive of the user's action and therefore more easily understood by users.

In URL routing, you define URL patterns that contain placeholders for values that are used when you handle URL requests. You can also use the URL patterns to programmatically create URLs that correspond to the routes, which enables you to centralize the logic for creating hyperlinks in your ASP.NET application.

In an ASP.NET application that does not use URL routing, an incoming request for a URL typically maps to a physical file on disk such as an .aspx file. For example, a request for http://server/application/Products.aspx?id=4 maps to a file named Products.aspx that contains code and markup for rendering a response that is sent to the browser. The Web page uses the query string value of id=4 to determine what type of content to display, but the value is likely to have little meaning to the user.

With URL routing, the pieces of the URL that follow the application name are parsed into discrete values, based on a pattern that you have defined for the URL. For example, in the request for http://server/application/Products/show/beverages, the URL routing parser can pass the values Products, show, and beverages to a handler for the request. In contrast, in a request that is not managed by URL routing, the /Products/show/beverages fragment would be interpreted as the path of a file in the application.

URL routing in ASP.NET differs from other URL rewriting schemes. URL rewriting typically does not have an API for creating URLs based on your patterns. URL rewriting processes incoming requests by actually changing the URL before it sends the request to the Web page. For example, an application that uses URL rewriting might change a URL from /Products/Widgets/ to /Products.aspx?id=4. In URL rewriting, if you change a URL pattern, you must manually update all hyperlinks that contain the original URL.

With URL routing, the URL is not changed when an incoming request is handled, because URL routing can extract values from the URL. When you have to create a URL, you pass parameter values into a method that generates the URL for you. To change the URL pattern, you change it in one location, and all the links that you create in the application that are based on that pattern will automatically use the new pattern.

Defining URL Routes

The URL patterns that you define are known as routes. In a route, you specify placeholders that are mapped to values that are parsed from the URL request. You can also specify constant values that are used for matching URL requests.

In a route, you define placeholders (referred to as URL parameters) by enclosing them in braces ( { and } ). The characters / and . are interpreted as delimiters when the URL is parsed, and the values extracted from between the delimiters are assigned to placeholders. Information in the route definition that is not in braces or square brackets is treated as a constant value.

The following table shows valid route patterns and examples of URL requests that match the pattern.

Valid route definitions

Examples of matching URL

{controller}/{action}/{id}

/Products/show/beverages

{table}/Details.aspx

/Products/Details.aspx

blog/{action}/{entry}

/blog/show/123

{reporttype}/{year}/{month}/{day}

/sales/2008/1/5

Typically, you add routes in the handler for the Application_Start event in the Global.asax file. This approach makes sure that the routes are available when the application starts, and also enables you to call the method directly when you unit-test the application. If you want to call it directly when you are unit-testing the application, the method that registers the routes must be static (Shared in Visual Basic) and have a RouteCollection parameter.

You add routes by adding them to the static Routes property of the RouteTable class. The Routes property is a RouteCollection object that stores all the routes for the ASP.NET application. The following example shows code from a Global.asax file that adds a Route object that defines two URL parameters named action and categoryName.

Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
    RegisterRoutes(RouteTable.Routes)
End Sub

Shared Sub RegisterRoutes(routes As RouteCollection)
    Dim urlPattern As String
    Dim categoryRoute As Route

    urlPattern = "Category/{action}/{categoryName}"
    categoryRoute = New Route(urlPattern, New CategoryRouteHandler)

    routes.Add(categoryRoute)
End Sub
protected void Application_Start(object sender, EventArgs e)
{
    RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
    routes.Add(new Route
    (
         "Category/{action}/{categoryName}"
         , new CategoryRouteHandler()
    ));
}

When URL routing handles URL requests, it tries to match the URL of the request to the pattern that is defined by a route. For example, the route in the previous example matches the URL http://server/application/Category/Show/Tools. In that case, the action parameter is assigned the value Show, and the categoryName parameter is assigned Tools.

However, the route does not match the URL http://server/application/Category/Add, because that URL does not contain values for all three URL parameters in the route definition, and because default values are not defined for the parameters. It also does not match the URL http://server/application/Products/Show/Coffee, because that URL does not start with "Category", which is a constant value and not a parameter.

If there are no routing matches between the URL and the Route objects defined in the RouteTable collection, URL routing does not process the request. Instead, processing is passed to an ASP.NET page, Web service, or other ASP.NET endpoint.

The order in which Route objects appears in the Routes collection is significant. Route matching is tried from the first route to the last route in the collection. When a match occurs, no more routes are evaluated. Typically the default route will be the last route.

Setting Default Values for Route Parameters

When you define a route, you can assign a default value for a parameter. The default value is used if a value for that parameter is not included in the URL. You set default values for a route by assigning a dictionary to the Defaults property of the Route class. The following example shows a route with default values.

Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
    RegisterRoutes(RouteTable.Routes)
End Sub

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    Dim urlPattern As String
    Dim categoryRoute As Route

    urlPattern = "Category/{action}/{categoryName}"
    categoryRoute = New Route(urlPattern, New CategoryRouteHandler)
    categoryRoute.Defaults = New RouteValueDictionary(New With _
    {.categoryName = "food", .action = "show"})

    routes.Add(categoryRoute)
End Sub
void Application_Start(object sender, EventArgs e) 
{
    RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
  routes.Add(new Route
  (
     "Category/{action}/{categoryName}"
          new CategoryRouteHandler()
  )
    {
       Defaults = new RouteValueDictionary 
           {{"categoryName", "food"}, {"action", "show"}}
     }
  );
}

When URL routing handles a URL request, the route definition shown in the example produces the results shown in the following table.

URL

Parameter values

/Category

action = "show"

categoryName = "food"

/Category/add

action = "add"

categoryName = "food"

/Category/add/beverages

action = "add"

categoryName= "beverages"

Handling an Unknown Number of Segments

Sometimes, you need to handle URL requests that contain an unknown number of URL segments. When you define a route, you can specify that the last parameter match the rest of the URL by marking the parameter with an asterisk (*). This is then referred to as a catch-all parameter. The following example shows a route pattern that matches an unknown number of segments.

query/{queryname}/{*queryvalues}

When URL routing handles a URL request, the route definition shown in the example produces the results shown in the following table.

URL

queryvalues parameter

/query/select/bikes/onsale

"bikes/onsale"

/query/select/bikes

"bikes"

/query/select

Empty string

A route with a catch-all parameter will match URLs that do not contain any values for the last parameter.

Adding Constraints When Matching URLs

In addition to matching a URL request to a route definition by the number of parameters in the URL, you can specify that values in the parameters meet certain constraints. You add constraints to make sure that the URL parameters contain values that will work in your application.

You add constraints when you add the route definition to the route collection. The following example shows constraints that limit what values can be included in the locale and year parameters.

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    Dim urlPattern As String
    Dim categoryRoute As Route

    urlPattern = "{locale}/{year}"
    categoryRoute = New Route(urlPattern, New ReportRouteHandler)
    categoryRoute.Constraints = New RouteValueDictionary(New With _
        {.locale = "{a-z}{2}-{A-Z}{2}", .year = "\d{4}"})

    routes.Add(categoryRoute) 
End Sub
void Application_Start(object sender, EventArgs e) 
{
    RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
    routes.Add(new Route
    (
      "{locale}/{year}"
         , new ReportRouteHandler()
    )
       {
          Constraints = new RouteValueDictionary 
          {{"locale", "{a-z}{2}-{A-Z}{2}"},{year, @"\d{4}"}}
       });
}

When URL routing handles a URL request, the route definition shown in the example produces the results shown in the following table.

URL

Result

/en-US

No match. Both locale and year are required.

/en-us/2008

No match. The constraint on locale requires the fourth and fifth characters to be uppercase.

/en-US/08

No match. The constraint on year requires 4 digits.

/en-US/2008

locale = "en-US"

year = "2008"

Creating URLs from Routes

You can use routes to generate URLs when you want to centralize the logic for constructing URLs. You create a URL by passing parameter values as a dictionary to the GetVirtualPath method of the RouteCollection object. The GetVirtualPath method looks for the first route in the RouteCollection object that matches the parameters in the dictionary. The matching route is used to generate the URL. The following example shows a route definition.

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    routes.Add(New Route( _
      "Category/{action}/{categoryName}", _
      New RouteValueDictionary(New With _
          {.categoryName = "food", .action = "show"}), _
           New CategoryRouteHandler()) )
End Sub
public static void RegisterRoutes(RouteCollection routes)
{
  routes.Add(new Route
  (
     "Category/{action}/{categoryName}"
          new CategoryRouteHandler()
  )
    {
       Defaults = new RouteValueDictionary {{"categoryName", "food"}, 
           {"action", "show"}}
     }
  );
}

The following example shows a control that creates a URL based on the route.

HyperLink1.NavigateUrl = RouteTable.Routes.GetVirtualPath _
    (context, _
     New RouteValueDictionary(New With {.categoryName = "beverages", _
        .action = "summarize"})).VirtualPath

HyperLink1.NavigateUrl = RouteTable.Routes.GetVirtualPath
  (context,
  new RouteValueDictionary { 
    { "categoryName", "beverages" }, 
    {"action", "summarize" }}
  ).VirtualPath;

When this code runs, the HyperLink1 control will contain the value "Category/summarize/beverages" in the NavigateUrl property.

This topic is ASP.NET 3.5 Extensions pre-release documentation and is unsupported by Microsoft. Blank topics are included as placeholders and existing content is subject to change in future releases. To provide feedback or ask questions about the release, please go to the ASP.NET 3.5 Extensions forums.