On a project I’m currently working on the new ASP.NET Routing was used to realize a navigation system for the website but also to present the Urls in a way that is friendly to the search engines’ analysis tools in order to improve indexing a bit.

In this article I’m going to describe what ASP.NET Routing is, there are already a plethora of good resources available on the web, I will show you a flexible way to retrieve parameters from the Url that is associated to a particular request.

We suppose to have the usual Person class that defines an object in our database:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
}

Let’s say we have a page that filters a set of data, and we can apply the filter on each of the field of the class. the first thing to do is to define a route that will be called to visualize the filtered data. We want to minimize the length of our Url and we want to be flexible enough that adding ne parameters to the search do not impact out navigation system.

We would like to use something like:

http://www.mysite.com/PersonListFilter/id/2 - filters only on Id

http://www.mysite.com/PersonListFilter/nm/Alessandro - filters only on the name

http://www.mysite.com/PersonListFilter/sn/Giorgetti - filters only on the surname

http://www.mysite.com/PersonListFilter/nm/Alessandro/id2 - combined filter, the order of parameters is irrelevant, we can also add more parameters to the search.

The route we use will be defined like this: “PersonListFilter/{*params}”

in the Global.asax (or in any other accepted point) we insert the route in the routing table:

System.Web.Routing.RouteTable.Routes.Add("PersonListFilter", new System.Web.Routing.Route("PersonListFilter/{*params}", new SearchModelRouteHandler("~/Pages/PersonListFilter.aspx")));

To parse the parameters from the Url we use a custom route handler object, it will extract the parameters and their values from the Url and it will insert them into the context of the web request. Later on, when you want to use them you can retrieve them directly from the context, without the need to parse again the Url.

here’s the custom route handler:

/// <summary>
/// routing handler for the filter page, it will analyze the parameters passed in and
/// insert them into the httpcontext as name/value pairs
/// </summary>
public class FilterRouteHandler : IRouteHandler
{
   public string VirtualPath { get; set; }
 
   public FilterRouteHandler(string virtualPath) { this.VirtualPath = virtualPath; }
 
   #region IRouteHandler Members
 
   public IHttpHandler GetHttpHandler(RequestContext requestContext)
   {
      foreach (var value in requestContext.RouteData.Values)
      {
         // this string contains a conctat of name/value pairs in the format eye/1/hair/5/etcetc...
         if (value.Value == null)
            continue;
         // store the whole search string into a single string too
         requestContext.HttpContext.Items["FilterParams"] = value.Value;
 
         string[] values = value.Value.ToString().Split('/');
         for (int i = 0; i < values.Length; i+=2 )
            requestContext.HttpContext.Items[values[i]] = values[i+1];
      }
 
      IHttpHandler page = (IHttpHandler)BuildManager.CreateInstanceFromVirtualPath(this.VirtualPath, typeof(Page));
      return page;
   }
 
   #endregion
}

As you can see it is very generic, It accepts a string in the constructor that will be the destination of the redirection, plus it only parses the string that defines the parameters splitting it into a series of (name,value) pairs.

The last step is how to use the values with classic ASP.NET data binding, to accomplish the task we can define a custom Parameter object that any data source (ObjectDataSource, SqlDataSource, etc...) can use:

/// <summary>
/// Retrieve a parameter from the querystring or the httpcontext (querystring has higher priority).
/// Used by datasource objects.
/// </summary>
public class QueryStringOrHttpContextParameter : Parameter
{
   /// <summary>
   /// The key in the query string or in the context.items collection.
   /// </summary>
   public string ItemName { get; set; }
 
   protected override object Evaluate(System.Web.HttpContext context, System.Web.UI.Control control)
   {
      if (String.IsNullOrEmpty(ItemName))
         throw new ArgumentNullException("Fill in 'ItemName' parameter");
      // read data from query string
      if (context.Request.QueryString.AllKeys.Contains(ItemName))
         return context.Request.QueryString[ItemName];
      // read data from context
      if (context.Items.Contains(ItemName))
         return context.Items[ItemName];
 
      return DefaultValue;
   }
}

Again the class is pretty generic, plus it first looks at the same values into the QueryString in case we want to revert from the ASP.NET Routing navigation model to a classical one, so you do not have to worry about rewriting the binding section in the page.

Here’s how you can use it:

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="GetSearchResult"
   TypeName="PersonFilterDataSource">
   <SelectParameters>
      <myextension:QueryStringOrHttpContextParameter ItemName="mlpi" DefaultValue="0" Name="pageindex" Type="Int32" />
      <myextension:QueryStringOrHttpContextParameter ItemName="id" Name="id" />
      <myextension:QueryStringOrHttpContextParameter ItemName="nm" Name="name" />
      <myextension:QueryStringOrHttpContextParameter ItemName="sn" Name="surname" />
      <asp:Parameter DefaultValue="12" Name="pagesize" Type="Int32" />
      <asp:Parameter DefaultValue="0" Direction="Output" Name="numrows" Type="Int32" />
   </SelectParameters>
</asp:ObjectDataSource>

A similar technique can be used in ASP.NET MVC and for calling WebServices too.