Keyvan Nayyeri is a Ph.D. student in Computer Science and previously held a B.Sc. degree in Applied Mathematics. He was born in Kermanshah, Kurdistan, Iran in 1984, and is currently living in San Antonio, TX. His primary research interests are Programming Languages & Compilers and Software Engineering.  He’s also a software architect and developer with focus on Microsoft development technologies as well as Open Source platforms. Keyvan is an avid community leader and contributor who has written four books for Wiley/Wrox and several articles for prominent community websites. Also he has contributed to many Open Source projects. As a result of his long-time contributions to the community, he has received several recognitions and awards from Microsoft, its partners, and community websites. Keyvan is a continues learner who loves to study, learn, and discover new technologies everyday, and is enthusiast for serving to the humankind through his research and contributions. For a long time he has been blogging about various topics on his blog that has become a rich resource for software developers. His blog is available at www.nayyeri.net. Keyvan has posted 36 posts at DZone. View Full User Profile

IRouteHandler in ASP.NET MVC

04.29.2009
| 8087 views |
  • submit to reddit

In my last post I covered one of the nice extensibility points in ASP.NET MVC to customize route constraints in your MVC applications. I also said that I’m going follow the rich set of extensibility options listed by Simone to write more posts about the topic of ASP.NET MVC extensibility.

Now it’s the second post that covers IRouteHandler interface as another extensibility point in the area of routing that provides a lower level of customizability for developers. This interface is actually an option that allows you handle a request without routing engine, and replace it with any handler that you want.

You may know that ASP.NET MVC has a built-in routing engine that is easy to customize as is. Additionally, you can use custom route constraint to modify it in a higher level. However, there are still some cases when you need to do something beyond the behavior of default routing engine, and need to handle the incoming requests to ASP.NET MVC with your own implementation. Of course, this should be as common as the first two scenarios but there are still some circumstances that may need such a level of customizability.

Here IRouteHandler interface comes to play and assist you to replace the default behavior of your routing system. As one of the common categories of the applications for this interface, you can use it to replace your controller factories. As a more practical example, you may have read a nice post by Phil Haack about using routing with ASP.NET WebForms.

This interface has a single GetHttpHandler function that gets a RequestContext object and returns an IHttpHandler instance. This can be a regular handler or an MvcHandler based on your requirements.

As you see, this is a very general interface that opens much space for your development and you can do many things with that. But to be honest, I couldn’t find very appropriate ideas to implement with it as an example for this post. So here I use a very simple example in which I write a handler that acts for URLs made to commercial orders section of the website, and checks for the country of origin of the visitors and blocks their access if they’re coming from a specific country. In real world recently this has become an entertainment for some jerks to prohibit others from free access to information thinking that world is responsible to give them all the knowledge and information, and they own it!

The first step is implementing IRouteHAndler interface. My implementation is very simple.

using System.Collections.Generic;
using System.Web;
using System.Web.Routing;

namespace IRouteHandlerSample.Routing
{
public class CountryProhibitionRouteHandler : IRouteHandler
{
#region Fields

private List<string> _countries;

#endregion

#region Public Constructor

public CountryProhibitionRouteHandler(List<string> countries)
{
this._countries = countries;
}

#endregion

#region IRouteHandler Members

public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
IpBlockHandler handler = new IpBlockHandler(this._countries, requestContext);
return handler;
}

#endregion
}
}

It gets a list of string values of country name abbreviations that should be blocked via its public constructor. In the GetHttpHandler method it creates an instance of an MvcHandler called IpBlockHandler and returns it. This handler gets the list of countries along with RequestContext object as its constructor parameters. The rest of the work is done by this MvcHandler to check incoming requests and block them if necessary.

But the IpBlockHandler is also a simple handler that checks the incoming requests based on their IP and blocks access to those who come from specified countries.

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Xml.Linq;

namespace IRouteHandlerSample.Routing
{
public class IpBlockHandler : MvcHandler
{
#region Fields

private List<string> _countries;

#endregion

#region Public Constructor

public IpBlockHandler(List<string> countries, RequestContext requestContext)
: base(requestContext)
{
this._countries = countries;
}

#endregion

#region MvcHandler

protected override void ProcessRequest(HttpContext httpContext)
{
string Query = "http://api.hostip.info/?ip=" + httpContext.Request.UserHostAddress;

XDocument doc = XDocument.Load(Query);

XNamespace defaultNamespace = doc.Root.GetDefaultNamespace();
XNamespace xNamespace = doc.Root.GetNamespaceOfPrefix("gml");

string country = doc.Root.Element(xNamespace + "featureMember").Element
(defaultNamespace + "Hostip").Element(defaultNamespace + "countryAbbrev").Value;

if (this._countries.Contains(country))
httpContext.AddError(new Exception
("Sorry! You can't order anything from your country. We recommend you to relocate!"));
}

#endregion
}
}

The logic behind this handler is mainly located inside ProcessRequest method where some LINQ to XML operations are used to call a remote server by passing the IP address of the client in order to receive the geographical information about the IP. Then this XML file is traversed to find the country abbreviation and check for its existence in the blocked countries list. If it’s there, then an exception is thrown to notify the user. If you wonder why this error is presented in this pure and ugly manner, I’d say that I have inspired this from the same page on a big internet service company!

The last step is putting these things together and using them in routing system.

using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Routing;
using IRouteHandlerSample.Routing;

namespace IRouteHandlerSample
{
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

RouteTable.Routes.Add
(new Route("order/{0}",
new CountryProhibitionRouteHandler(new List<string>() { "XX" })));

routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}

protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
}
}

As you see, I’ve added a new route to the RouteTable which works for the order section and applies an instance of CountryProhibitionRouteHandler by passing the list of countries.

Now if I visit the order section with my local IP (that is presented as XX country), I’ll get the exception!

Output

As always, the source code sample for this post is available for download.

 

References
AttachmentSize
IRouteHandlerSample.zip252.72 KB
Published at DZone with permission of its author, Keyvan Nayyeri. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)