Welcome to part 12 of my tour through ASP.NET 4.0. In the last episode, we saw how improvements in ASP.NET 4.0 have made it much simpler to get routing for webforms set up and working. In this one, we’ll look in more detail at the new routing-friendly classes, properties and methods you can start to use once routing is set up.

Defining Routes

Let’s start by defining a couple of routes and using them as examples throughout this post.

routes.MapPageRoute("Users", "user-{username}", "~/users.aspx");
routes.MapPageRoute("Forums", "forum-{forumname}", "~/forum.aspx");
routes.MapPageRoute("Threads", "forum-{forumname}/thread-{threadid}", "~/thread.aspx");

We'll also create another one with default values for each of the route parameters, using another of MapPageRoute's overloads.

routes.MapPageRoute("Posts", "post/{forumname}/{threadid}/{postid}", 
  "~/thread.aspx", false,
  new RouteValueDictionary { 
    { "forumname", "Fishing" }, { "threadid", "12" }, { "postid", "1" } 
  });

Should we now visit ~/post/, thread.aspx will load and be passed in "Fishing", "12" and "1" as the values for the forumname, threadid and postid route parameters respectively. Should we visit ~/post/art, the default values for the threadid and postid parameters will passed in along with "art" as the forumname.

MapPageRoute has a couple of other tricks up its sleeve as well. An overload with five parameters allows you to define a basic validation for legal values for each of the route parameters. For example, let's say we wanted forumname to contain only lower-case letters. Then we would define the route like this.

routes.MapPageRoute("Posts", "post/{forumname}/{threadid}/{postid}", 
  "~/thread.aspx", false,
  new RouteValueDictionary { 
    { "forumname", "Fishing" }, { "threadid", "12" }, { "postid", "1" } 
  },
  new RouteValueDictionary { { "forumname", "^[a-z]*$" } }
);

Don't forget as well that MapPageRoute is just a quick route to registering a route that is handled by the built-in PageRouteHandler class. If you want to handle the request using your own handler, you can make the equivalent call to routes.Add.

routes.Add("Posts",
  new Route("post/{forumname}/{threadid}/{postid}",
    new RouteValueDictionary { 
      { "forumname", "Fishing" }, { "threadid", "12" }, { "postid", "1" } },
    new RouteValueDictionary { { "forumname", "^[a-z]*$" } }, null,
    new PageRouteHandler("~/thread.aspx", false)));

Hyperlinks

With our routes defined, we can now set up hyperlinks between routes. All we need are the name of the route and the values of any route parameters. If we need to do it inline in the code (perhaps because it's part of a databinding operation) we can use the new <%$RouteUrl %> syntax. For example.

Go to <a runat="server" 
  href='<%$RouteUrl:RouteName=Users, UserName=Dan  %>'>dan's page</a><br />
Go to <a runat="server" 
  href='<%$RouteUrl:RouteName=Forums, ForumName=Coding  %>'>coding forum</a><br />
Go to <a runat="server" 
  href='<%$RouteUrl:RouteName=Posts %>'>the default post</a><br />

If we need to set the hyperlink in code-behind, it's easiest to use GetRouteUrl(), a new method on System.Web.UI.Control and therefore the Page class. (This actually calls RouteCollection.GetVirtualPath() to get the required URL if you'd prefer to use that instead).

GetRouteUrl() has four overloads, broadly taking the following form

GetRouteUrl([string routeName], routeParameters);

Where specifying the routeName is optional and the routeParameters can be added as either an object of anonymous type or as a RouteValueDictionary. For example,

// Named route, parameters in anonymous type object
hypUsers.NavigateUrl = Page.GetRouteUrl("users", new {username = "Dan" });

// Named route, parameters in RouteValueDictionary
hypForums.NavigateUrl = Page.GetRouteUrl("forums", 
  new RouteValueDictionary { { "forumname", "Fishing" } );

// Named route, no parameters set, so defaults used
hypPosts.NavigateUrl = Page.GetRouteUrl("posts", new {} );

// No route named
hypNoName.NavigateUrl = Page.GetRouteUrl(new { forumname = "Gaming" });

In the last example here, we've not named a route to use a basis for a URL, but we have specified a route parameter called forumname, which appears in three of our declared routes. Which route will it use as a basis to generate the URL? Simply, the first route defined that contains the named parameter. In this case, that's "forums".

Redirection

If you need to redirect a user's browser to a given address, you'll need to call one of the three new redirect methods we mentioned briefly in part 10: either Response.RedirectToRoute() or Response.RedirectToRoutePermanent(). Both have the same five overloads with the only difference that RedirectToRoute() sends an HTTP 302 temporary redirect to the browser and RedirectToRoutePermanent() sends a HTTP 301.

// No Route Named, Parameters In Anonymous Type object
Response.RedirectToRoute( new { username = "Dan" });

// No Route Named, Parameters In RouteValueDictionary
Response.RedirectToRoute( 
  new RouteValueDictionary { { "forumname", "Fishing" } ););

// Only route named, so default parameter values used
Response.RedirectToRoute("posts"); 

// Named route, parameters in anonymous type object
Response.RedirectToRoute("users", new {username = "Dan" });

// Named route, parameters in RouteValueDictionary
Response.RedirectToRoute("forums", 
  new RouteValueDictionary { { "forumname", "Fishing" } );

Note that both these methods are equivalent to calling Response.Redirect(url, stopProcessing) with stopProcessing set to false. Again, more on that here in part 10.

RouteParameters

Last but not least, DataSource users will be interested to know that Microsoft have added a new RouteParameter object to ASP.NET 4.0 which you can use in exactly the same way as, for instance, a QueryStringParameter or a ControlParameter. If you use the Configure DataSource wizard you'll see it added to the bottom of the Source list when you need to add a WHERE clause to the data source.

1_RouteParameter

If you do select Route from the Source list, you'll need to set the name of the route parameter you want to use and also give a default value for the parameter if it isn't available for some reason.

2_RouteParameter

These are the only differences between using a RouteParameter and any other parameter object you use with your DataSources. If you prefer to write the code yourself, the net result of the screesnshots above is the following markup.

<asp:SqlDataSource ID="SqlDataSource1" runat="server" 
  ConnectionString="<%$ ConnectionStrings:UsersDB %>" 
  SelectCommand="SELECT [UserId], [Username] FROM [Users] WHERE ([Username] = @Username)">
  <SelectParameters>
    <asp:RouteParameter DefaultValue="Dan" Name="Username" RouteKey="username" 
         Type="String" />
  </SelectParameters>
</asp:SqlDataSource>

So then, in this article, we've looked at the new methods and objects you'll likely use most often when working with webforms and routing. Until the next time, happy coding.