When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article
Jobs

ASP ASP.NET ASP FAQs Message Board Feedback ASP Jobs
 
Print this Page!
Published: Wednesday, January 27, 2010

URL Routing in ASP.NET 4

By Scott Mitchell


Introduction


In the .NET Framework 3.5 SP1, Microsoft introduced ASP.NET Routing, which decouples the URL of a resource from the physical file on the web server. With ASP.NET Routing you, the developer, define routing rules map route patterns to a class that generates the content. For example, you might indicate that the URL Categories/CategoryName maps to a class that takes the CategoryName and generates HTML that lists that category's products in a grid. With such a mapping, users could view products for the Beverages category by visiting www.yoursite.com/Categories/Beverages.

In .NET 3.5 SP1, ASP.NET Routing was primarily designed for ASP.NET MVC applications, although as discussed in Using ASP.NET Routing Without ASP.NET MVC it is possible to implement ASP.NET Routing in a Web Forms application, as well. However, implementing ASP.NET Routing in a Web Forms application involves a bit of seemingly excessive legwork. In a Web Forms scenario we typically want to map a routing pattern to an actual ASP.NET page. To do so we need to create a route handler class that is invoked when the routing URL is requested and, in a sense, dispatches the request to the appropriate ASP.NET page. For instance, to map a route to a physical file, such as mapping Categories/CategoryName to ShowProductsByCategory.aspx - requires three steps: (1) Define the mapping in Global.asax, which maps a route pattern to a route handler class; (2) Create the route handler class, which is responsible for parsing the URL, storing any route parameters into some location that is accessible to the target page (such as HttpContext.Items), and returning an instance of the target page or HTTP Handler that handles the requested route; and (3) writing code in the target page to grab the route parameters and use them in rendering its content. Given how much effort it took to just read the preceding sentence (let alone write it) you can imagine that implementing ASP.NET Routing in a Web Forms application is not necessarily the most straightforward task.

The good news is that ASP.NET 4 has greatly simplified ASP.NET Routing for Web Form applications by adding a number of classes and helper methods that can be used to encapsulate the aforementioned complexity. With ASP.NET 4 it's easier to define the routing rules and there's no need to create a custom route handling class. This article details these enhancements. Read on to learn more!

- continued -

ASP.NET Routing Overview


ASP.NET Routing cleanly decouples URLs from web page file names and can be used to create clean, terse, SEO-friendly URLs. For a detailed discussion on why one should use ASP.NET Routing in their web application, refer to "The Case for ASP.NET Routing" section in Using ASP.NET Routing Without ASP.NET MVC, where I spend several paragraphs extolling the benefits of ASP.NET Routing.

In a nutshell, ASP.NET Routing allows a developer to define routing rules, which map a route pattern - such as Categories/CategoryName - to a class that handles the request. These routing rules are defined when the application starts, in Global.asax's Application_Start event handler. In a Web Forms application we likely already have ASP.NET pages that generate the content of interest; we just need to have the routing rule map the route pattern to that ASP.NET page, passing along any route parameters (such as CategoryName) to the ASP.NET page. When using ASP.NET Routing in ASP.NET 3.5 SP1 there is no way to directly map the routing pattern to an ASP.NET page. Instead, we must create a route handler class, which is passed information about the incoming request and must return an HTTP Handler to process the request. Typically, a route handler class in a Web Forms application performs the following steps:

  1. Parse the URL as needed, perhaps examining certain routing parameters and making decisions based on those values.
  2. Load any routing parameters from the URL that need to be passed to the ASP.NET page or HTTP Handler that will handle this request. In short, we need to make sure that the ASP.NET page that will actually generate the content for this request knows the values of any routing parameters, such as CategoryName. One way to convey such information is to place it in the HttpContext.Items collection, which serves as a repository for storing data that persists the length of the request. (See HttpContext.Items - a Per-Request Cache Store for more on the Items collection.)
  3. Return an instance of the ASP.NET page or HTTP Handler that does the processing.
Typically these route handler classes are pretty boilerplate. You store the routing parameters into the HttpContext.Items collection and then create and return an instance of the ASP.NET page that is responsible for generating the content for this URL. Given the boilerplate nature of the route handler class, building these classes becomes tedious rather quickly, as each new route needs a new route handler class that does almost exactly the same thing as the ones before it.

Another challenge of using ASP.NET Routing in ASP.NET 3.5 SP1 centers around the ASP.NET page that is responsible for generating the content. This page has to read the routing parameters out of the HttpContext.Items collection (or wherever the route handler class stored them). Also, the syntax for generating routing-friendly URLs (like Categories/CategoryName) for a hyperlink or Response.Redirect call is a bit verbose and confusing.

ASP.NET Routing in ASP.NET 4 has been enhanced to include a variety of new routing-related methods that make defining routing rules that map to an actual ASP.NET page much simpler. No longer do you need to create an intermediary custom route handler class; instead, you can reference the ASP.NET page directly from the routing rule in Global.asax. When specifying an ASP.NET page from the routing rule, the routing parameters are automatically stored in a new RouteData collection, which is accessible from the ASP.NET page via Page.RouteData. What's more, the .NET Framework 4 includes a custom parameter control so that you can declaratively use values from the RouteData collection from ASP.NET's data source controls (SqlDataSource, LinqDataSource, etc.). And there also methods to generate routing-friendly URLs and to redirect to a routing-friendly URL.

This article highlights the improvements to the ASP.NET Routing system in ASP.NET 4. The demo available for download at the end of this article is the same demo presented in Using ASP.NET Routing Without ASP.NET MVC. Namely, it's a Web Forms application that is an online storefront for Northwind Traders. It uses ASP.NET Routing to create terse, SEO-friendly URLs. For example, /Categories/All displays all categories; /Categories/Beverages lists the products in the Beverages category; and /Products/Chai displays information about the product Chai. The demo downloadable from Using ASP.NET Routing Without ASP.NET MVC shows how to use ASP.NET Routing with ASP.NET 3.5 SP1. The demo at the end of this article uses the ASP.NET Routing available with ASP.NET 4.

From the end user's perspective, the two demos are identical in their function and form. The difference lies in the steps needed to configure the website to use ASP.NET Routing and the syntax used in Global.asax and the code-behind pages. For instance, to use ASP.NET Routing in ASP.NET 3.5 SP1 you need to explicitly add the System.Web.Routing assembly to your project and add some markup to Web.config. These steps are no longer necessary with ASP.NET 4. The syntax used to define the routing rules in Global.asax is shorter, simpler, and more readable in ASP.NET 4, as is the code in the code-behind pages.

The remainder of this article walks through the steps to use ASP.NET Routing in ASP.NET 4.

Step 0: Prerequisites


This demo uses features of ASP.NET Routing that were added to ASP.NET 4. If you are using Visual Studio 2010 or Visual Web Developer 2010 (or beyond) you're good to go.

Step 1: Defining the Routing Rules in Global.asax


To use the ASP.NET Routing system you need to define one to many routes when the application starts. Start by adding the Global Application Class file type to your project (Global.asax), where we'll register the routes in the Application_Start event.

The routes defined in Global.asax indicate what route handlers are responsible for what URL patterns. A popular pattern for MVC applications is Controller/Action/ID, meaning that requests of the form /Products/View/Aniseed Syrup or Categories/Edit/Beverages would be handled by the configured route handler. You have total flexibility in defining what routes exist in your application. You can define multiple parts to patterns, define default values for missing parts, and even constraint parts to certain types of inputs.

The demo application available for download at the end of this article is a simple data-driven application that uses the Northwind database and accepts "hackable" URLs of the following patterns:

  • /Categories/All - lists all categories in the database,
  • /Categories/CategoryName - lists the products for the specified category, and
  • /Products/ProductName - displays information about the specified product.
Consequently, I defined three routes in my Global.asax file's Application_Start event handler, as the following code shows. (Note: the RouteTable object and RouteCollection classes are located in the System.Web.Routing namespace, so you'll either need to import that namespace or fully qualify these class names, as in System.Web.Routing.RouteTable.)

void Application_Start(object sender, EventArgs e)
{
   RegisterRoutes(RouteTable.Routes);
}

void RegisterRoutes(RouteCollection routes)
{
   // Register a route for Categories/All
   routes.MapPageRoute(
      "All Categories",      // Route name
      "Categories/All",      // Route URL
      "~/AllCategories.aspx" // Web page to handle route
   );


   // Route to handle Categories/{CategoryName}.
   // The {*CategoryName} instructs the route to match all content after the first slash, which is needed b/c some category names contain a slash, as in the category "Meat/Produce"
   // See http://forums.asp.net/p/1417546/3131024.aspx for more information
   routes.MapPageRoute(
      "View Category",               // Route name
      "Categories/{*CategoryName}",  // Route URL
      "~/CategoryProducts.aspx"      // Web page to handle route
   );


   // Register a route for Products/{ProductName}
   routes.MapPageRoute(
      "View Product",           // Route name
      "Products/{ProductName}", // Route URL
      "~/ViewProduct.aspx"      // Web page to handle route
   );

}

In Application_Start we call the RegisterRoutes method (which is defined below), passing in the RouteTable.Routes RouteCollection. In RegisterRoutes we call the RouteCollection class's MapPageRoute, which defines a route mapping between a routing pattern and an ASP.NET page. For example, in the first call to MapPageRoute we create a route named "All Categories" that maps the route pattern Categories/All to the ASP.NET page ~/AllCategories.aspx.

The following two calls to MapPageRoute create routing patterns with parameters. The "View Product" route maps the pattern Products/{ProductName} to the ASP.NET page ~/ViewProduct.aspx. {ProductName} is a parameter, meaning that any request of the form Products/ProductName will be routed to ~/ViewProduct.aspx. As we'll see shortly, the value of the {ProductName} parameter is accessible to ~/ViewProduct.aspx via the Page.RouteData parameter.

Step 2: Create the ASP.NET Pages That Process the Requests


With ASP.NET 4, there's no need to create a custom route handler class. That's all done for you behind the scenes when using the MapPageRoute method. All that remains is to create the ASP.NET pages that process the requests (AllCategories.aspx, CategoryProducts.aspx, and ViewProduct.aspx). For the demo application these pages are pretty simple - they use a data source control that is programmatically bound to database results from the Categories or Products table based on the routing parameters.

The demo uses the LINQ-to-SQL tool for data access. You'll find a Northwind.dbml file in the App_Code folder, which creates the NorthwindDataContext class. The ViewProduct.aspx page contains a DetailsView control with fields defined to display the product's name, supplier, quantity per unit, price, and other pertinent information. The page's code-behind class has the following (abbreviated) code:

protected void Page_Load(object sender, EventArgs e)
{
   dvProductInfo.DataSource = new Product[] { Product };
   dvProductInfo.DataBind();
}

private Product _Product = null;
protected Product Product
{
   get
   {
      if (_Product == null)
      {
         string productName = Page.RouteData.Values["ProductName"] as string;

         NorthwindDataContext DataContext = new NorthwindDataContext();
         _Product = DataContext.Products.Where(p => p.ProductName == productName).SingleOrDefault();
      }

      return _Product;
   }
}

In the Page_Load event handler the DetailsView control is bound to the Product object returned by the Product property. The Product property reads in the value of the ProductName parameter in the URL from the Page.RouteData collection, using the syntax: Page.RouteData.Values["ProductName"]. This ProductName parameter value is then used in a LINQ query to get back information about the particular product.

The screen shot below shows the ViewProduct.aspx page in action. The URL to the page is /Products/Chai, and detailed information about Chai is displayed on the page. (For more screen shots of the demo, refer to Using ASP.NET Routing Without ASP.NET MVC, which includes screen shots of the Categories/All and Categories/CategoryName pages.

The View Products page, when viewing Chai.

And... that's it! That's all there is to setting up ASP.NET Routing in ASP.NET 4! Much easier than with ASP.NET 3.5 SP1, which required five steps (instead of 2).

Generating Routing-Friendly URLs


When creating hyperlinks or sending a user from one page to another via Response.Redirect, it would be ideal to use the routing patterns defined in Global.asax rather than referencing the ASP.NET page by it's actual name. For instance, the ViewProducts.aspx page has a link back to the page that shows all products for the product's category that links to Categories/CategoryName, where CategoryName is the name of the category whose products to view. You can generate these routing-friendly URLs using the Page.GetRouteUrl method. There are a number of overloads to this method, but the simplest version takes in two parameters: the name of the route and the values of the parameters.

For example, to get the proper URL back to the Categories/CategoryName page, use the following syntax:

Page.GetRouteUrl("View Category", new { CategoryName = CategoryName });

Here, "View Category" is the name of the routing rule defined in Global.asax, and CategoryName is the value of the CategoryName parameter to appear in the URL. More specifically, the code:

Page.GetRouteUrl("View Category", new { CategoryName = "Beverages" });

There's also a new version of Response.Redirect available, named Response.RedirectToRoute. Like the Page.GetRouteUrl method, Response.RedirectToRoute can take in the route name and parameter values and then redirect the user to the appropriate, routing-friendly URL. The following example shows how to redirect the user to view a particular product:

Response.RedirectToRoute("View Product", new { ProductName = ProductName });

Conclusion


ASP.NET Routing is a powerful library in the .NET Framework that enables developers to decouple the URLs handled by their application from underlying physical files. Introduced in ASP.NET 3.5 SP1, ASP.NET Routing has initially positioned to be used in ASP.NET MVC applications. While it could be used in Web Form applications, configuring it required a number of tedious steps and resulted in seemingly unnecessary and duplicated code. ASP.NET 4 beefs up the ASP.NET Routing library and provides more flexible and intuitive usage scenarios for Web Form applications. As we saw in this article, mapping a routing pattern to an ASP.NET page requires just a few lines of code in Global.asax. There is no longer any need to create a custom route handler class. Behind the scenes, the ASP.NET Routing library automatically saves the routing parameters to the RouteData collection, which is accessible from the Page class. Moreover, these RouteData values can be accessed declaratively from data source controls like the SqlDataSource and ObjectDataSource.

Happy Programming!

  • By Scott Mitchell


    Attachments:


  • Download the code used in this article

    Further Readings:


  • Using ASP.NET Routing Without ASP.NET MVC
  • Dissecting ASP.NET Routing
  • URL Routing with ASP.NET 4 Web Forms
  • ASP.NET 4 SEO Improvements
  • Routing with ASP.NET Web Forms


  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article