IRouteHandler in ASP.NET MVC
Join the DZone community and get the full member experience.
Join For FreeIn 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!
As always, the source code sample for this post is available for download.
Published at DZone with permission of Keyvan Nayyeri. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments