Chapter 25. Using the ASP.NET URL Routing Engine

One of the many new features introduced in ASP.NET 4 is the concept of URL routing. This chapter provides you with an introduction to URL routing and why it should concern you as an ASP.NET Web Forms developer, then continues with examples of various scenarios in which URL routing truly shines, and finishes with coverage of a few advanced usages of URL routing.

When you finish with this chapter, you should have a firm grasp on the technology that makes URL routing work as well as when you might (or might not) want to utilize it in your own applications.

Introduction to URL Routing

URL routing, in general terms, is a system that enables the developer to build in dynamism, flexibility, and (hopefully) readability to the URLs that access the various parts of a web application.

We’ve all seen URLs like the following:

image

And we all know how ugly they are. Not only are they difficult to cut and paste, but they’re also not very memorable or not clean, and users never know whether they can feel free to copy them, Digg them, share them on Facebook, or whatever. The reason for this is that, to an end user, a complicated-looking URL is an unusable or unsharable URL. They assume that there’s so much cruft jammed into the URL that it couldn’t possibly be portable. URL routing enables us to simplify those URLs, up to and including giving us the ability to build RESTful style URLs and even hide the .aspx extension entirely. We no longer even need a 1:1 mapping between the URL and physical page on disk.

The first and foremost reason why URL routing should matter to you is that simple URLs make for calm users. The simpler your URL, the simpler people are going to assume your site is to use. Sure, this is often a bad assumption on the user’s part, but if you’re given an opportunity to make your users feel more welcome, shouldn’t you take it?

Secondly, simple and easy URLs make for better Search Engine Optimization (SEO). An ASPx page that serves up a different product page when the query string varies by a productid parameter might look something like this:

http://my.store.com/product_detail.aspx?productid=12

This is certainly more user-friendly than the previous URL, but is it search-engine friendly? Can a search engine crawler tell that more products are available on this same filename? There are all kinds of tricks you can do, including publishing a page that produces a master list of links to all products, but even those links aren’t the best links.

What if each product detail page URL could include the short name of the product and the category to which it belonged? In this case, the URL carries information that might help make that URL more discoverable via search engines:

http://my.store.com/products/dvdplayers/toshiba/sd1600

This URL, although simple, has packed a truckload of information into it that can not only be used by end users (they immediately know they’re looking at the URL for a Toshiba DVD player, model number SD-1600) but also provides search engine crawlers with a lot of context. Both human and web crawler alike can assume that if they use the /products/dvdplayers URL they will get all DVD players, and the /products/dvdplayers/toshiba URL should provide all Toshiba DVD players.

Finally, dynamically routing between URL and physical page gives you the flexibility to use URL parameters to do things that might otherwise be cumbersome, difficult, or even impossible without routing. For example, take the following two URLs:

http://my.app.com/blog/2010/08/12

and

http://my.app.com/products/tags/discount/new/black

The first one enables you to supply a date directly on the URL, but you don’t need to worry about figuring out what the query string parameter names are for the components—just put the date in the URL with slashes. This reduces a lot of complexity for the underlying blog page and for end users.

The second URL enables you to supply an infinite list of tags directly on the URL. This enables power users to start adding tag after tag to the end of a URL to further limit their list of products—functionality available to users without the developer even having to provide a GUI for it.

Basic URL Routing Scenarios

Now that you’ve had an introduction to what URL routing is and why you might want to use it, let’s take a look at some of the basics of how to use URL routing in your Web Forms application. The ASP.NET MVC Framework also makes use of the URL routing engine, but it does so in a slightly different way. The MVC Framework uses the routing engine to map routes to controllers and actions with parameters whereas ASP.NET Web Forms applications use the routing engine to map routes to physical files on disk and supply parameters to those pages.

Mapping Basic URLs

The first thing that needs to be done to use the URL routing engine is to register your route maps. A route map is just a mapping from a route expression to a physical file on disk (or a controller/action combination in the case of ASP.NET MVC Framework).

These routes are registered at application startup time and the registration is typically wrapped in its own method, as shown in the following code snippet taken from a Global.asax.cs file:

image

The simplest and most basic type of route is one in which you map a static route directly to an ASPx page without any parameters. You typically see this when the target page takes no parameters and has fairly simple functionality such as a login, logout, or about page. The following code shows how to use the MapPageRoute method of the RouteCollection class to add basic route mappings:

image

The effect of these simple route mappings is that users can now use the URL http://server/about, and they will be given the content from the About.aspx page. The URL in the browser appears as “/about”. If that page were to post back to itself as many ASP.NET Web Forms pages do, the post back would be sent to the "/about" URL, but the About.aspx page would still be invoked.

Note

No matter what you have in a route rule, if the URL requested by the user corresponds to a physical file on disk, the routing rule will be ignored. This means any constraints and defaults defined by that rule will also be ignored. As a matter of practice (and good style), you should avoid writing route rules and contain the “.aspx” file extension to avoid accidental conflicts between physical files and routes.

Mapping URLs with Parameters

Static route mappings come in handy, but any application that has any kind of dynamic nature or is driven by an underlying data source is, at some point, going to need parameters passed on the query string. Mapping parameters within a route expression and making those parameters available to the target page is actually quite simple. Thankfully we don’t (yet) need to worry about regular expressions!

Let’s assume that you’re creating a web application that has blogging functionality. Rather than force users to create (or look at) some horribly complicated URL syntax that might even include the GUID of the blog post, you can use URL routing to simplify that URL syntax.

Note

You may be thinking that no one would ever throw the GUID of a blog post entry in the blog URL, but we have actually seen it multiple times. Never underestimate the capability of the Internet to breed bad user experience, and take every opportunity you can to rid the Internet of such!

A common convention used by many blogging platforms is to embed a date directly in the URL, enabling end users, GUIs, and crawlers to easily select the content they want. To do this, we need to direct traffic to URLs like this:

http://my.app.com/blog/2010/05/12

to our blog rendering page (Blog.aspx). We’ve seen how to send static (never changing) URLs to individual ASPx pages, but how do we send dynamic URLs to those pages?

To do this without the routing engine, we’d have to create a low-level HttpHandler to inspect the URLs, parse them, convert them into query string parameters, and then finally load up the appropriate page. Thankfully it’s much easier to just use the following code in our InitializeRoutes method:

image

The words inside the curly braces are now named parameters within the routing engine and, when captured, will be made available to the Blog.aspx page. At this point you might be tempted to try and access these parameters as part of the Request.QueryString object. Don’t! Parameters passed through the URL routing engine are made available inside the Page.RouteData property and not as part of the query string, as shown in the following code:

image

As you see as you progress through the chapter, this is just the beginning of what can be done with route expressions.

Note

Parameters captured by the routing engine that show up in the RouteData dictionary are always stored as strings when used with ASP.NET Web Forms, even if you have constrained their data types via regular expressions (shown later in the chapter). As a result, it is up to you to convert them to their final destination types after getting them from the RouteData dictionary. ASP.NET MVC does a little extra work on your behalf to convert parameters into the data types the controllers expect.

Mapping URLs with Multiple Segments

The technique in the preceding section is great if you know ahead of time exactly which parameters are going to be in the URL and how many of them there are going to be. But what do you do if you don’t know how many parameters you’re going to have, or you want people to pass a list of data on the URL without using cumbersome query string syntax?

For example, let’s say that you have an application that exposes data that can have a deeply nested hierarchy and you want users to drill into that hierarchy directly from the URL. Thankfully, the URL routing engine gives us the ability to map a list of parameters. An example of such a URL might enable the user to drill down to a location geographically, such as

http://my.app.com/location/Earth/USA/NY/NYC/TimesSquare

or as we’ll see in the next code sample, we can supply a list of tags to filter data in a flat hierarchy:

http://my.app.com/tags/free/ammo/guns

(should return everything tagged with free, ammo, and guns, though we might be a little scared to see those results).

Multiple parameters can be supplied to a mapping by putting an asterisk in front of the named parameter:

image

And then in the ProductsByTag.aspx.cs we can obtain the list of values passed on the URL as a slash-delimited list:

image

Linking to Other Pages with Routes

So far we’ve looked at a bunch of ways we can get a user from a URL to a page, but what about going the other way? When the user is on an ASP.NET Web Form, how do we create links to other pages in a way that respects the URL routing scheme without giving each page “magic” knowledge of how the URLs are formatted?

Basically what we want is to keep all the URL routing logic inside the single initialization method at application startup. We never want individual Web Forms to be creating links with hard-coded strings or string concatenation because those pages would cease to work properly if the URL route maps changed.

Ideally, we want to create URLs dynamically by supplying parameters and, optionally, the name of the route map. We can do this either declaratively in the ASPx markup or we can build the URL programmatically in the code behind.

First, let’s take a look at how we can link to another page using the asp:HyperLink server control tag:

image

This markup relies on the RouteUrlExpressionBuilder class to perform the appropriate route map lookup, apply the supplied parameters to the expression, and return a virtual path that respects the route. In the preceding code, the link returned will be for the /products/tags/new/discounted relative path. We supplied the tagnames parameter directly; we did not manually construct the URL fragments. This insulates the Web Form from changes to the URL mapping. If one day we get tired of using “products/tags” and decide to change the mapping to “products/list/bytags” as the prefix, we won’t have to change any of this code—it will just work.

If we want to generate a route-aware URL programmatically, we can do so using the following code (the codeGeneratedLink object is just a hyperlink control):

image

Note

Although these techniques make working with the routing engine seamless, they also have an added benefit: the removal of hard-coded URLs. Hard-coded URLs are the bane of many a website, and by the very act of adopting the use of URL routing in your website, your team will be forced to work without hard-coded URLs. A great functional test would be to add a random word to all the route paths to see if your site continues to function. As we all know, every time you remove a magic string from your code, an angel gets its wings.

Advanced URL Routing

So far you’ve seen examples of how to use URL routing to deal with static routes, routes with known parameters, and even routes with lists of parameters of unknown sizes. In this next section we examine several other advanced scenarios that truly show off the power and flexibility of the ASP.NET URL routing system.

Using Routes with Default Parameters

Another powerful feature of the URL routing system is the capability to supply some meaningful defaults for route parameters. For example, you might have a product category page that displays products within that category. If the user doesn’t specify a category, you might want to pick a default one.

In the following route configuration, we have a category browser page that we can reach with the “/category/{name}” route pattern. If the user doesn’t supply a category name, we supply a default:

image

In this case, the default supplied is the “explosives” category. The target page doesn’t need to know that it was invoked with a default value; it can simply grab the category name from the RouteData dictionary.

Supplying defaults is useful both for creating default landing options for hitting pages without parameters, but also so that the target page doesn’t need to be cluttered up with conditional statements checking to see whether particular values have been supplied in route data.

Using Constrained Routes

For a lot of situations, the type of route patterns that we have discussed thus far are sufficient. If you want static routes, or routes with simple parameters (with or without defaults), you are all set.

However, if you want to further constrain your route patterns so that only after certain conditions are met should your route be invoked, you’re also in luck. One of the overloads of the MapPageRoute method that we haven’t yet discussed actually takes a dictionary of constraints to route parameters. These constraints come in the form of regular expressions. (We warned you earlier that we’d have to talk about them eventually.)

At their simplest level you can use these regular expressions to limit the size of parameters so that when passing a state code on the URL, the code must be only two characters and must not contain numbers. Anyone familiar with regular expressions also knows that you can create incredibly powerful expressions that do far more than just simple validation. Regular expressions are outside the scope of this book, so we’re going to use some simple expressions to illustrate their use in constraining route patterns.

The code below adds a new route to our blog system. This pattern restricts the year to no more than 4 digits, but the year can also be missing. The day and month parameters receive similar treatment, both enabling between 0 (missing) and 2 digits each. Because we’re forcing them to be digits through the regular expression constraints means that the route will not be used if any non-numeric characters are passed on the URL.

Note

Remember that just because we use regular expressions to enforce a rule on our parameters limiting the data to only digits, that doesn’t mean the parameters will be converted to integers for us. On the target page, we still need to perform the appropriate data conversion from strings.

image

In a small amount of code, we accomplish quite a bit. The first thing you see is that we supplied some default values for this route. The default values are set to the year, month, and day when the application started. Keep in mind that these won’t change, so if your application has a long uptime, these values could lose their usefulness. However, they do the trick for this particular demo.

The next thing to look at is the RouteValueDictionary containing the constraints. The year parameter is mapped to a regular expression indicating it can be a digit between 0 and 4 digits. The month and day parameters are constrained to digits between 0 and 2 digits. In a real-world scenario you might choose better regular expressions but, as we said, regular expressions are outside the scope of this book, and plenty of great resources are on the Internet including a great site at http://www.regular-expressions.info.

Another thing you might have noticed is that all the method arguments have names. This is a great new feature of .NET 4 that makes method overloads with large numbers of arguments vastly more readable. Without the named arguments, you would have difficulty deciphering what the two dictionaries were and why they contained those values. If you find yourself dropping to a multiline method invocation because of a large number of arguments, also consider using named arguments to make your code that much easier to read.

Security Concerns with Routes

At this point you might be wondering how the URL routing system integrates with ASP.NET’s declarative, location-based security system. It actually integrates quite well. You might have noticed that in several of the samples in this chapter we have been passing a parameter called checkPhysicalUrlAccess when creating route patterns.

This parameter, when true, tells ASP.NET that it should enforce location-based security after determining which ASPx page to call in response to a given pattern. This means that if you have a pattern that looks like this:

http://my.app.com/blog/2010/01/02

and maps to the following location:

/contentsystem/blogapp/posts.aspx

you can define a <location> element in your web.config to secure the physical location the same way you would normally secure that location, and permissions will be checked before the user gets to that page.

If the web.config-based security system doesn’t work for you, you can always enforce individual permission checks at the page level either by hooking into the page life cycle or by placing code in the code-behind—all tactics that you would use with a traditional ASP.NET application.

Summary

This chapter has provided you with an introduction and a thorough overview of the ASP.NET URL routing engine and how to use it. It provides flexibility and power for developers, user-friendly URLs, and even a URL syntax that can provide added value and additional information to search engine crawlers. All this adds up to a powerful system that can make your website more powerful and easier to use by humans and computers alike.

If you’re like us, at this point after having discovered the new routing engine, you’re probably wondering where this tool has been all your life. Our exercise for you now is to go forth and create route maps and websites with friendly, easy-to-use URL syntax.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset