Routing Library resides in the System.Web.Routing Namespace of the .NET Framework 3.5, which provides us the flexibility to use URLs that has no mapping to a physical file. This means ASP.NET MVC framework provides flexible URL mapping engine and enables us to write SEO (Search Engine Optimization) friendly URLs with very little effort. No one can deny the importance of SEO, to be successful in search based marketing. What better way to analyze a business than from what customers are looking for on the Internet through keyword research. SEO is the way to go.
SEO friendly URL Format for an e-commerce application may be
/Products/List/ProductCategory
/Products/Detail/ProductName
URL Example
/Products/List/CareCare
/Products/Detail/MiracleCarDuster
In ASP.NET MVC world the URL Routing System maps the incoming URLs to the relevant Controller and Action, in the above example our Contoller is Products and Action is List or Detail. We normally go and define a Route object and add it to the RouteCollection and register the RouteCollection during Application_Start(). Out of the box System.Web.Mvc library ships with the RouteCollectionExtensions which allows us to define routes easily using the the different overloads of MapRoute method.
Example:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
If you look under the hood you will find, a Route object is created and added to the RouteCollection.
Route route = new Route(url, new MvcRouteHandler()){};
.....
.....
routes.Add(name, route);
note that "MvcRouteHandler" have been passed as a parameter by default, when we use the routes.MapRoute() method. The overloads of MapRoute() extension methods are just helper methods to make things easy for us, it is not mandatory that we will have to always use this. We can define a Route object in plain .NET code and assign necessary properties to it, we can also pass our preferred IRouteHandler. This gives superb flexibility with handling URLs. For instance In a practical world of web marketing / search marketing we always need to support Legacy URLs. In the web marketing world ad hoc campaigns are launched, and what may have worked last week may not work this week any more, as a result the URL structure changes frequently and we face the need to start redirecting to the new URLs.
On Top of that during this transformation of URLs we need to implement "301 Moved Permanently" redirections, which means the previous URL has been permanently removed and all future requests should be directed to the given new URI. Handling this scenario has become very easy with the Routing Engine. All we need to do is add a new Route or modify the existing Route to fit our need. Matt Hawley has an excellent post on Legacy Url Routing which describes how to route existing aspx file based URLs to the appropriate MVC Controller and Action. This article also gives directions on how to implement custom Route and custom RouteHandler.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
routes.Add("", new LegacyRoute(
"Users/Login.aspx",
"Login",
new LegacyRouteHandler())); // Defined a Custom Route class and have passed a custom IRouteHandler.
}
As you can see in the above code, how easily we can add a custom route and pass a custom IRouteHandler (in this case LegacyHandler).
Tracking - is another common task performed, we want to track everything, every single clicks a consumer performs on the site. This has also becomes easy in the world of ASP.NET MVC. By design we define separate actions for each functionality and we Route to the Controller - Action to get anything done. So tracking would be a viable option to do centrally just before creating the Controller object or just before delegating to the Action.
You will notice the implementation of IRouteHandler requires to implement only one method "GetHttpHandler" which returns a IHttpHandler
public interface IRouteHandler
{
IHttpHandler GetHttpHandler(RequestContext requestContext);
}
You will find the MvcRouteHandler implements IRouteHandler like this
public class MvcRouteHandler : IRouteHandler {
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
return new MvcHandler(requestContext);
}
}
The real delegation of a Route to a Controller and Action happens in the ProcessRequest(HttpContext context); method of the MVCHandler class which is the implementation of IHttpHandler. The ProcessRequest(HttpContext contxt) method may be a be a good place to centrally control tracking in one of our custom Handlers. The implementaiton of MVCHandler is as follows, where you can see how a controller is created by the IControllerFactory factory, and then Execute() method is called. We can do our tracking somewhere before calling the Execute() method.
protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
AddVersionHeader(httpContext);
// Get the controller type
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
// Instantiate the controller and call Execute
IControllerFactory factory = ControllerBuilder.GetControllerFactory();
IController controller = factory.CreateController(RequestContext, controllerName);
if (controller == null) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.ControllerBuilder_FactoryReturnedNull,
factory.GetType(),
controllerName));
}
try {
controller.Execute(RequestContext);
}
finally {
factory.ReleaseController(controller);
}
}
Did you know, Routes in the ASP.NET Mvc are matched and executed on a first match bases! It may be important to order the routes so that the correct pattern is matched first before a more general pattern matches and executes it. It is sometimes hard to figure out which particular pattern will be caught first when we have a lot of routes, ASP.NET Routing Debugger comes in to rescue, Phil Hack has put together this nice little route tester utility which can save a lot of time. This utility quickly displays in Red and Green color what Route patterns have matched for a particular URL. So we can type in various URLs in the addressbar to see which routes matches.
Lets now looks at a different problem, we normally define all the routes in the global.aspx.cs file, this causes a problem when Routes changes frequently, every single time a new route is added or an existing one is modified we need to recompile web application and upload the new dll to the server, again it is not mandatory to write Routing rules in the global.aspx.cs file, we can easily store the routing rules to a Xml file and use a combination XML related .NET libraries and .NET Reflection APIs to read from the Xml file and create/deserialize Route Objects to add them to the RouteCollection during the Application_Start(). But still we haven't overcome the limitations of restarting the application as the RouteCollection gettting registered during Application_Start. I think we have to live with that, unless we go and implement some kind of FileSystemWatcher to monitor the Xml file and force to refresh the RouteTable.Routes object when the xml file changes. I haven't tried implementing this yet but this would work I think.
We have discussed here, how ASP.NET Routing engine eases writing SEO friendly Url, maitaining Url redirections and tracking centrally. Hope this helps.
Thank you for being with me so far.