{"id":1254,"date":"2011-12-09T00:00:00","date_gmt":"2011-12-09T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/asp-net-mvc-routing-extensibility\/"},"modified":"2021-05-17T18:36:20","modified_gmt":"2021-05-17T18:36:20","slug":"asp-net-mvc-routing-extensibility","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/asp-net-mvc-routing-extensibility\/","title":{"rendered":"ASP.NET MVC Routing Extensibility"},"content":{"rendered":"<div id=\"pretty\">\n<h2>Introduction<\/h2>\n<p class=\"Start\">In the previous article I introduced <a href=\"http:\/\/www.simple-talk.com\/dotnet\/.net-framework\/an-introduction-to-asp.net-mvc-extensibility\/\">the concept of extensibility, and described where ASP.NET MVC can be extended<\/a>: With this article I&#8217;ll be starting a guided tour around these extensibility points, beginning with the first ones that are encountered when a request hits the application: the routing module.<\/p>\n<p>Before I demonstrate how to extend the routing module, let&#8217;s quickly review the way that incoming requests are evaluated and matched with the correct route.<\/p>\n<div class=\"note\">\n<p class=\"note\"><strong>Note<\/strong>: Strictly speaking, the routing engine is part of ASP.NET, and not something specific to ASP.NET MVC, but I&#8217;m covering it because it is a fundamental part of the processing pipeline.<\/p>\n<\/div>\n<h2>How the routing module works<\/h2>\n<p>Looking at the routing module from a very high level, what it does is to take in the URL of the incoming request and gives out an instance of the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.iroutehandler(v=vs.98).aspx\"><code>IRouteHandler<\/code><\/a> that will create, via its <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.iroutehandler.gethttphandler.aspx\"><code>GetHandler<\/code><\/a> method, the <a><code>IHttpHandler<\/code><\/a> to process the request.<\/p>\n<p class=\"illustration\"><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1407-img92.jpg\" alt=\"1407-img92.jpg\" \/><\/p>\n<p class=\"caption\">Fig 1. Workflow inside the UrlRoutingModule<\/p>\n<p>What happens, in more detail, is that the request is checked against all the routes that are defined in the routing table of the application:<\/p>\n<ul>\n<li>First the matching is done based on the URL pattern and the default values for the URL parameters<\/li>\n<li>Then, if the URL pattern matches, the route parameters are populated with the values extracted from the URL segments<\/li>\n<li>After that, the route parameters are validated against the route constraints: these can either be regular expressions or (and here it comes our first extensibility point) any classes that implement <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.irouteconstraint.aspx\"><code>IRouteConstraint<\/code><\/a>.<\/li>\n<\/ul>\n<p>As soon as a match is found, the process of scanning of the global routing table stops, the route handler (our second extensibility point for the article) specified for the route is instantiated, the Http Handler is created and finally executed (via its <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.ihttphandler.processrequest.aspx\"><code>ProcessRequest<\/code><\/a> method). If no match is found, a &#8216;Page Not Found&#8217; error (Http error 404) is returned to the client.<\/p>\n<p>Hopefully I haven&#8217;t lost you yet. In case I did, and if some of the terms I used are new to you, I recommend you first go reading an introductory explanation on what routing in ASP.NET is and how it works, such as the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/cc668201.aspx\">ASP.NET Routing page<\/a> on MSDN or one of the many books on ASP.NET MVC.<\/p>\n<h3>How to specify routes<\/h3>\n<p>Let&#8217;s now see how this workflow works with a code sample, which also shows how to define routes and basic constraints.<\/p>\n<pre>routes.MapRoute(\"BlogArchive\",\r\n\u00a0\u00a0\u00a0 \"{year}\/{month}\/{day}\",\r\n\u00a0\u00a0\u00a0 new { controller = \"Blog\", action = \"List\", month = \"1\", day = \"1\" },\r\n\u00a0\u00a0\u00a0 new { year = @\"\\d{2}|\\d{4}\", month = @\"\\d{1,2}\", day = @\"\\d{1,2}\" }\r\n\u00a0\u00a0\u00a0 );\r\n\u00a0\r\nroutes.MapRoute(\"Post\",\r\n\u00a0\u00a0\u00a0 \"{title}\",\r\n\u00a0\u00a0\u00a0 new { controller = \"Blog\", action = \"Post\"}\r\n\u00a0\u00a0\u00a0 );\r\n\u00a0\r\nroutes.MapRoute(\"Tags\",\r\n\u00a0\u00a0\u00a0 \"tags\/{tag}\",\r\n\u00a0\u00a0\u00a0 new { controller = \"Blog\", action = \"Tags\" }\r\n);\r\n\u00a0\r\nroutes.MapRoute(\r\n\u00a0\u00a0\u00a0 \"Default\", \r\n\u00a0\u00a0\u00a0 \"{controller}\/{action}\/{id}\", \r\n\u00a0\u00a0\u00a0 new { controller = \"Home\", action = \"Index\", id = UrlParameter.Optional }\r\n);\r\n<\/pre>\n<p class=\"caption\">Code List 1: routing table<\/p>\n<p>This is the routing table for a simple blog engine, with three types of routes defined: the single post, the archive by date, and the archive by tag, and the default route at the end.<\/p>\n<p>The first route is matched by any URL with one to three segments, all formed by digits: it will match \/2011 but also \/2011\/11 and \/2011\/11\/25. The second route will match only URLs with just one segment that is not a 2 or 4 digits number. If the URL is instead made by two non-numeric segments, the first of which is &#8220;tags&#8221;, the third route will be selected. And finally, if none of the specific routes is selected, the default route will be selected.<\/p>\n<p>For example, if the browser sends a request for the URL <code>http:\/\/example.com\/Authors\/List<\/code>, the routing module will first try to match it with the &#8220;BlogArchive&#8221; route: the pattern matches, but not the constraints on the format of the parameters. The rest of the route table is evaluated, but without finding a matching pattern, so the default route definition will be used, and, due to how the default route handler is designed, the processing will move to inside the <code>AuthorsController<\/code> class and its <code>List<\/code> method will be executed.<\/p>\n<p>You might have noticed that nowhere in the code has the route handler to use been specified: the reason is that the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.mvc.routecollectionextensions.maproute.aspx\"><code>MapRoute<\/code><\/a> method creates a <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.route.aspx\"><code>Route<\/code><\/a> specifying always <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.mvc.mvcroutehandler.aspx\"><code>MvcRouteHandler<\/code><\/a>, the default route handler for ASP.NET MVC, as route handler.<\/p>\n<h3>Easier debugging of routes<\/h3>\n<p>When the route table grows big and complex, then debugging, and understanding why it doesn&#8217;t work as expected, can be pretty difficult, especially if the result is a 404 error (which means no route matched). To help you with that, <a href=\"http:\/\/haacked.com\">Phil Haack<\/a>, PM of the ASP.NET MVC team, wrote a simple yet powerful RouteDebugger. To install it into you application all you have to do is get it via Nuget:<\/p>\n<pre>Install-Package RouteDebugger<\/pre>\n<p>When installed it shows all the routes configured in your application, with their parameters, default values, constraints, all the routes that match the current request and the route parameters that will be passed along to the route handler. With the previous example the output is as in Fig 2.<\/p>\n<p class=\"illustration\"><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1407-RouteDebugger.jpg\" alt=\"1407-RouteDebugger.jpg\" \/><\/p>\n<p class=\"caption\">Fig 2. Debugging information in RouteDebugger for the URL http:\/\/example.com\/Authors\/List<\/p>\n<p>Another interesting debugging tool, not just for routes but for all the pipeline of ASP.NET MVC is Glimpse, also installable via Nuget, with the command:<\/p>\n<pre>Install-Package Glimpse.mvc3<\/pre>\n<p>Compared to Haack&#8217;s RouteDebugger, Glimpse has the disadvantage of not giving any information when there is no match. In the example, its output would have been as in Fig 3.<\/p>\n<p class=\"illustration\"><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1407-Glimpse.jpg\" alt=\"1407-Glimpse.jpg\" \/><\/p>\n<p class=\"caption\">Fig 3: Route information from Glimpse.<\/p>\n<p>Let&#8217;s now start exploring the extensibility points, starting with custom route constraints.<\/p>\n<h2>Custom Route Constraint<\/h2>\n<p>A route constraint, as we have seen before, is used when you need more control over the route matching. In the code listing #1 I&#8217;ve specified that I wanted to select the BlogArchive route only if the URL represented a date (so, 3 numbers). But something it was not possible with a regular expression was to make sure the date was valid. For that I need something that can access all the URL parameters: a custom route constraint.<\/p>\n<h3>The interface to implement<\/h3>\n<p>To create a custom Route Constraint, you need to implement the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.irouteconstraint.aspx\"><code>IRouteConstraint<\/code><\/a> interface, which only has one method: <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.irouteconstraint.match.aspx\"><code>Match<\/code><\/a>.<\/p>\n<pre>public interface IRouteConstraint {\r\n\u00a0\u00a0\u00a0 bool Match(HttpContextBase httpContext, Route route, string parameterName,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 RouteValueDictionary values, RouteDirection routeDirection)\r\n}\r\n<\/pre>\n<p>The parameters supplied to the method are:<\/p>\n<ul>\n<li><code>httpContext<\/code>: the http context, which gives access to the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.httpcontextbase.aspx\"><code>\"HttpContextBase<\/code><\/a> class with the complete request, server, application objects<\/li>\n<li><code>route<\/code>: the route object this constraint belongs to<\/li>\n<li><code>parameterName<\/code>: the name of the route parameter being checked<\/li>\n<li><code>values<\/code>: all the URL parameters for the route<\/li>\n<li><code>routeDirection<\/code>: whether the check is being performed when looking for the match for an incoming request or URL is being generated.<\/li>\n<\/ul>\n<p>The method must return <code>true<\/code> if the parameter is valid, or <code>false<\/code> otherwise.<\/p>\n<h3>Sample Implementation<\/h3>\n<p>Let&#8217;s see how to implement the date validation constraint:<\/p>\n<pre>public class IsDateValidConstraint : IRouteConstraint\r\n{\r\n\u00a0\u00a0\u00a0 public bool Match(HttpContextBase httpContext, Route route,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 string parameterName, RouteValueDictionary values,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 RouteDirection routeDirection)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (routeDirection == RouteDirection.IncomingRequest)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 DateTime date;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return DateTime.TryParse(String.Format(\"{0}\/{1}\/{2}\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 values[\"year\"], values[\"month\"], values[\"day\"]),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 CultureInfo.InvariantCulture, DateTimeStyles.None, out date);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return true;\r\n\u00a0\u00a0\u00a0 }\r\n} \r\n<\/pre>\n<p>The code itself is pretty easy: it just tries to create a date, and if it fails (for example the browser was asking for the blog post for the 31st of February) it returns false, and the routing module passes to the next route to try and find the right one.<\/p>\n<p>The new constraint is then specified in the route definition just by adding it to the dictionary of constraints:<\/p>\n<pre>routes.MapRoute(\"BlogArchive\",\r\n\u00a0\u00a0\u00a0 \"{year}\/{month}\/{day}\",\r\n\u00a0\u00a0\u00a0 new { controller = \"Blog\", action = \"List\", month = \"1\", day = \"1\" },\r\n\u00a0\u00a0\u00a0 new { year = @\"\\d{2}|\\d{4}\", month = @\"\\d{1,2}\", day = @\"\\d{1,2}\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 date = new IsValidDateConstraint() }\r\n\u00a0\u00a0\u00a0 );\r\n\u00a0\r\n<\/pre>\n<p class=\"STTipsText\">As just shown in this example, a custom route constraint doesn&#8217;t have to correspond to an actual route parameter (in the example it was specified for a non-existent parameter called &#8220;date&#8221;). It can also be specified for a fictitious parameter, thus performing the validation also based on information coming from other sources, like, the request object or, as in the case, the other parameters.<\/p>\n<h3>Other possible use-cases for custom Route Constraint<\/h3>\n<p>An example of a Route Constraint is the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.HttpMethodConstraint.aspx\"><code>HttpMethodConstraint<\/code><\/a> that comes with ASP.NET 4: it limits a route to be selected to only those that have been requested with a specific HTTP verb, ex GET.<\/p>\n<p>Another possible use-case is that of allowing a certain route to be selected only if the request comes from the local machine, or also if you want to avoid deep linking of resources, such as images or files.<\/p>\n<p>One thing to keep in mind, and this is valid for all the extensibility points of ASP.NET MVC, is that most of the time, you could obtain the same result by putting your custom logic in almost any other extensibility point: for example the last two scenarios could have been handled also as Action Filters (I&#8217;ll cover that in a future article) or as RouteHandler.<\/p>\n<p>As a rule of thumb, try to use an extension point for what it has been originally designed: Route Constraints are for validating an URL parameter for the scope of matching a URL with a route. So, if in your application, a different resource should be selected based on the fact that a request comes from the local system or that was not initiated by clicking on a link on your site, then this logic is a good candidate for a constraint, otherwise it&#8217;s not. Also consider how far in the processing pipeline you want to go. In the case of a DoS attack prevention system, you want to stop the request as soon as possible: this might be another candidate for a Route Constraint.<\/p>\n<table>\n<tbody>\n<tr>\n<td class=\"style1\">Name<\/td>\n<td class=\"style2\">Route Constraint<\/td>\n<\/tr>\n<tr>\n<td class=\"style1\">Area<\/td>\n<td class=\"style2\">Routing<\/td>\n<\/tr>\n<tr>\n<td class=\"style1\">Type<\/td>\n<td class=\"style2\">Case-Specific<\/td>\n<\/tr>\n<tr>\n<td class=\"style1\">Why<\/td>\n<td class=\"style2\">When the validation logic is based on more than just the format of the parameter you want to validate<\/td>\n<\/tr>\n<tr>\n<td class=\"style1\">Interface<\/td>\n<td class=\"style2\"><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.IRouteConstraint.aspx\"><code>IRouteConstraint<\/code><\/a><\/td>\n<\/tr>\n<tr>\n<td class=\"style1\">Standard Implementation<\/td>\n<td class=\"style2\"><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.HttpMethodConstraint.aspx\"><code>HttpMethodConstraint<\/code><\/a><\/td>\n<\/tr>\n<tr>\n<td class=\"style1\">Notable implementations<\/td>\n<td class=\"style2\">None<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>\u00a0Custom Route Handler<\/h2>\n<p>The second extensibility point of this article is the Route Handler, which is the component that is responsible for creating the HttpHandler that will process the request.<\/p>\n<p>There are two reasons why you might want to create your own Route Handler: the first is if you want to manipulate the route data before passing them to <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.mvc.mvchandler.aspx\"><code>MvcHandler<\/code><\/a>, the default handler for ASP.NET MVC. The second is if you want to exit the ASP.NET MVC pipeline and handle the request with a custom Http Handler.<\/p>\n<p>In both situations you also probably need to create a custom <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.route.aspx\"><code>Route<\/code><\/a> that extends <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.routebase.aspx\"><code>RouteBase<\/code><\/a> and adds more information, and in the second case you will also have to create your own Http Handler.<\/p>\n<h3>The interface to implement<\/h3>\n<p>The interface that needs to be implement is <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.iroutehandler.aspx\"><code>IRouteHandler<\/code><\/a> and its only method <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.iroutehandler.gethttphandler.aspx\"><code>GetHttpHandler<\/code><\/a>:<\/p>\n<pre>public interface IRouteHandler {\r\n\u00a0\u00a0\u00a0 IHttpHandler GetHttpHandler(RequestContext requestContext)\r\n}\r\n<\/pre>\n<p>The <code>requestContext<\/code> parameter contains all the information available on the current request: the http context (thus the request, server, application objects) and the route data, which contains the values of parameters and the Route object itself.<\/p>\n<p>The return value is the Http Handler that will process the request.<\/p>\n<h3>Sample Implementation<\/h3>\n<p>The code that follows is a very simple watermarking system, that superimposes a copyright notice on a requested image.<\/p>\n<p>First we need to write the Http Handler that will process the request; It will look for the image, load it and write the copyright notice on top of it.<\/p>\n<pre>public class WaterMarkHandler: IHttpHandler\r\n{\r\n\u00a0\u00a0\u00a0 private string _watermark;\r\n\u00a0\u00a0\u00a0 public WaterMarkHandler(string watermark)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 _watermark = watermark;\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\r\n\u00a0\u00a0\u00a0 public void ProcessRequest(HttpContext context)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 var routeValues = context.Request.RequestContext.RouteData.Values;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/Look for the image, load as graphic element, write the watermark\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 context.Response.Write(\"This is image named \" + routeValues[\"name\"] + \" (C) \"+ _watermark);\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\r\n\u00a0\u00a0\u00a0 public bool IsReusable\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 get { return false; }\r\n\u00a0\u00a0\u00a0 }\r\n}\r\n<\/pre>\n<p>This sample doesn&#8217;t really add a watermark on top of the image. Instead it just returns a text string that contains the name of the image and the copyright notice. But the real sample is in the code you can <a href=\"http:\/\/www.simple-talk.com\/blogbits\/philf\/RoutingSamples.zip\">download from here<\/a>, or from the bottom of the article.<\/p>\n<p>The only problem we&#8217;ll face is that this http handler cannot be used with the standard RouteHandler, so a custom route handler is needed. Fortunately, the task of implementing it is pretty trivial: the only thing it has to do is to instantiate the http handler and return it to the caller.<\/p>\n<pre>public class WaterMarkRouteHandler: IRouteHandler\r\n{\r\n\u00a0\u00a0\u00a0 private string _watermark;\r\n\u00a0\u00a0\u00a0 public WaterMarkRouteHandler(string watermark)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 _watermark = watermark;\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\r\n\u00a0\u00a0\u00a0 public IHttpHandler GetHttpHandler(RequestContext requestContext)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return new WaterMarkHandler(_watermark);\r\n\u00a0\u00a0\u00a0 }\r\n} \r\n<\/pre>\n<p>The last step is that of binding all together. The <code>MapRoute<\/code> method automatically specifies the <code>MvcRouteHandler<\/code>, so we need to create directly with the <code>Route<\/code> object, and add it to the route table.<\/p>\n<pre>Route watermarkRoute = new Route(\"images\/{name}\",\r\n\u00a0\u00a0\u00a0 new WaterMarkRouteHandler(\"CodeClimber - 2011\"));\r\nroutes.Add(\"image\", watermarkRoute);\r\n<\/pre>\n<h3>Other cases for Custom Route Handler<\/h3>\n<p>This was merely an example that showed how to write your own handler, but in combination with writing http handlers and custom routes, a lot more useful stuff can be done: For example you could implement something that <a href=\"https:\/\/blog.maartenballiauw.be\/post\/2010\/01\/26\/translating-routes-(aspnet-mvc-and-webforms).html\">understands multi-lingual routes<\/a>, or you could implement <a href=\"http:\/\/www.eworldui.net\/blog\/post\/2008\/04\/ASPNET-MVC---Legacy-Url-Routing.aspx\">permanent redirection when you move from a webform site to an ASP.NET MVC one<\/a>. You could use it to <a href=\"http:\/\/haacked.com\/archive\/2011\/02\/02\/redirecting-routes-to-maintain-persistent-urls.aspx\">maintain persistent URLs<\/a> when you change your URL structure even when using ASP.NET MVC.<\/p>\n<p>Yet another resource from Phil Haack is <a href=\"http:\/\/haacked.com\/archive\/2011\/01\/30\/introducing-routemagic.aspx\">RouteMagic<\/a>, a collection of utilities for making the routing system a bit more powerful. It also includes a generic <a href=\"http:\/\/haacked.com\/archive\/2009\/11\/04\/routehandler-for-http-handlers.aspx\">HttpHandlerRouteHandler<\/a> that works with all http handlers that don&#8217;t have a constructor with parameters.<\/p>\n<table>\n<tbody>\n<tr>\n<td class=\"style1\">Name<\/td>\n<td class=\"style2\">Route Handler<\/td>\n<\/tr>\n<tr>\n<td class=\"style1\">Area<\/td>\n<td class=\"style2\">Routing<\/td>\n<\/tr>\n<tr>\n<td class=\"style1\">Type<\/td>\n<td class=\"style2\">Case-Specific, Core<\/td>\n<\/tr>\n<tr>\n<td class=\"style1\">Why<\/td>\n<td class=\"style2\">When you need instantiate a different HttpHandler to process the request (thus not entering the standard ASP.NET MVC pipeline) or you want to manipulate the route parameters before passing them to the MvcRouteHandler<\/td>\n<\/tr>\n<tr>\n<td class=\"style1\">Interface<\/td>\n<td class=\"style2\"><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.iroutehandler.aspx\">IRouteHandler<\/a><\/td>\n<\/tr>\n<tr>\n<td class=\"style1\">Standard Implementation<\/td>\n<td class=\"style2\"><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.mvc.mvcroutehandler.aspx\">MvcRouteHandler<\/a>, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.routing.stoproutinghandler.aspx\">StopRoutingHandler<\/a><\/td>\n<\/tr>\n<tr>\n<td class=\"style1\">Notable implementations<\/td>\n<td class=\"style2\">RouteDebugger, <a href=\"http:\/\/haacked.com\/archive\/2011\/01\/30\/introducing-routemagic.aspx\">RouteMagic library<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Conclusions<\/h2>\n<p>In this article we have seen how to customize the first part of the pipeline, the one before the moment in which the controller appears: these extension points allow you to branch out from the standard ASP.NET MVC pipeline and fork your own custom processing pipeline.<\/p>\n<p>In the next article I&#8217;m going to show how you can tap into the components that create the controllers and execute actions.<\/p>\n<p class=\"note\"><a href=\"http:\/\/www.simple-talk.com\/blogbits\/philf\/RoutingSamples.zip\">The source code for the examples<\/a>, including Standard Routing and debugging with RouteDebugger, Standard Routing and debugging with Glimpse, Custom Route Constraint and a Custom route handler (watermarking) is available from the speechbubble at the head of the article (To try the samples you have to download the two files from the speechbubble, unzip them in the same folder) or in one package from <a href=\"http:\/\/www.simple-talk.com\/blogbits\/philf\/RoutingSamples.zip\">here as a single package.<\/a>\u00a0The free wallchart, &#8216;ASP.NET MVC Pipeline&#8217;, that goes with this article is available as a PDF file, also from the speechbubble <a href=\"http:\/\/www.simple-talk.com\/content\/file.ashx?file=6068\">or from here<\/a>. It is best printed on an A3 printer.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>You develop an ASP.NET MVC application by extending it; customising any default logic that you wish to change with your own implementation. Simone starts a tour of the extensibility points of ASP.NET MVC, by looking at the beginning of the pipeline, the Routing Module, and gives a practical example of writing an extension, with source code: a way of &#8216;watermaking&#8217; images &#8216;on the fly&#8217;.&hellip;<\/p>\n","protected":false},"author":185026,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538],"tags":[4143,4229,4156,4178,5166,4179],"coauthors":[11294],"class_list":["post-1254","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","tag-net","tag-net-framework","tag-asp","tag-bi","tag-mvc","tag-source-control"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1254","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/185026"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=1254"}],"version-history":[{"count":9,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1254\/revisions"}],"predecessor-version":[{"id":79410,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1254\/revisions\/79410"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=1254"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=1254"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=1254"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=1254"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}