Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Custom Routes in ASP.NET MVC

0.00/5 (No votes)
13 Jul 2015 1  
In this tip, we will try to understand why, when and how we should implement Custom Routes.

Introduction

Microsoft has given many options and extension points in MVC for making our life as a programmer easy and smooth. Custom route handlers are one of them. First of all, we will try to understand how MVC routing works, what route handlers are and why we need custom route handler. After that, we will try to understand how we can create and use custom route handlers and also what caution to take while using it. So let’s jump in.

How MVC Routing Works?

URLs are the most basic but important part of any web applications. Majorly developed as part of MVC framework, the URL routing module is a native part of ASP.NET framework now. The same component provides services to both web forms and MVC applications though through a slightly different API.

Routing HTTP Module decides which handler will be responsible to handle the request and dispatching them to the most appropriate executor. As a developer, we are not likely to deal with this module directly instead on this we need to provide it routes that our application supports.

Route Handlers

This is a class that implements IRouteHandler interface which has only one method GetHttpHandler(), which provides route handler class instance that will process the request. The interface looks like the following:

public interface IRouteHandler 
{     
    IHttpHandler GetHttpHandler(RequestContext requestContext); 
}

ASP.NET MVC provides a default Route Handler called as MvcRouteHandler.

According to MSDN – “MvcRouteHandler class implements IRouteHandler, therefore it can integrate with ASP.NET routing. The MvcRouteHandler class associates the route with an MvcHandler instance. A MvcRouteHandler instance is registered with routing when you use the MapRoute method. When the MvcRouteHandler class is invoked, the class generates an MvcHandler instance using the current RequestContext instance. It then delegates control to the new MvcHandler instance.”

In a nut shell, this class returns an MVCHandler instance based on RequestContext that further handles the request.

Why and Where We Need a Custom Route Handler?

Well, that's a tricky question as creating any custom component can be a good practice or just a matter of architecture choice. Any software designing challenge can be handled ìn multiple ways.

  1. One scenario is when we want to process some data before creating the instance of the controller class. Like if we want to process any authentication header with which we need to decide whether the user has access to the system or not. We can create a Custom Route Handler which will check that header information and create either the controller which we need to invoke in case the user is authorized or the generic error controller instance which will notify user about the authentication issue. Again, this is not the way we usually handle this scenario but this can be an option.
  2. If we want to override the convention used by MVC. For e.g. if we want that in Controller folder, the class names should not contain a Controller suffix. We can create a Custom Route Handler which can accomplish this task though this is not the actual way through which it should get implemented.
  3. And finally the place where I have seen it useful. Let’s suppose we have a normal MVC website and we also use CMS pages, e.g., articles. What we want is when any request comes to server, it should first check our website routes and if none of them matches the request, it should then check our CMS page routes and then at last if nothing matches return 404. Confused? Let’s discuss it in detail.

Creating A Custom Route Handler

First of all, we will create a MVC project “CustomRouteHandler” which will contain HomeController in Controllers folder. We will then add RouteHandler folder which will contain two files CustomRouteHandler.cs and CustomHandler.cs. We will also add one other MVC project in the same solution and we call it CMS. This will contain ContentController in Controllers folder. We have added both the projects in the same solution just for simplicity of this demo. In real life, these should be on separate domains.

CustomRouteHandler Project

HomeController.cs 

This is the default controller file created by MVC framework. I have not made any changes in it.

CustomRouteHandler.cs

This class implements IRouteHandler and has only one method “GetHttpHandler”. The main purpose of this class is to return the instance of CustomHandler class. In the constructor, we are passing the RequestContext to the handler.

    using System.Web;
    using System.Web.Routing;
    
    namespace CustomRouteHandler.RouteHandler
    {
        public class CustomRouteHandler : IRouteHandler
        {
            public IHttpHandler GetHttpHandler(RequestContext requestContext)
            {
                return new CustomHandler(requestContext);
            }
        }
    }

CustomHandler.cs 

This class implements the IHttpHandler interface same as Generic handlers (.ashx). I have added one property LocalRequestContext that will hold the value of RequestContext object. In ProcessRequest method, I have first tried to get the controller based on the passed controller value and in case it doesn’t exist, it will throw an exception so in catch block I have created an instance of WebClient and call the DownloadString method with the URL of the CMS application. In case it fails to load the URL, it will also throw an exception so I have handled it in another catch block and return 404 in that case. Again, I want to mention this is just a basic implementation for the sake of simplicity of this demo. In real life, we should never need to write this logic directly in the handler and also take of cross-cutting measures.

    using System;
    using System.Net;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    
    namespace CustomRouteHandler.RouteHandler
    {
        public class CustomHandler : IHttpHandler
        {
            public RequestContext LocalRequestContext { get; set; }
    
            public CustomHandler(RequestContext requestContext)
            {
                LocalRequestContext = requestContext;
            }
    
            public void ProcessRequest(HttpContext context)
            {
                try
                {
                    var controllerName = LocalRequestContext.RouteData.GetRequiredString("controller");
                    var controller = ControllerBuilder.Current.GetControllerFactory().
                    CreateController(LocalRequestContext, controllerName);
                    if (controller != null)
                    {
                        controller.Execute(LocalRequestContext);
                    }
                }
                catch
                {
                    try
                    {
                        var client = new WebClient();
                        var content = client.DownloadString("http://localhost:24220/" + 
                        LocalRequestContext.HttpContext.Request.FilePath);
                        LocalRequestContext.HttpContext.Response.Write(content);
                    }
                    catch
                    {
                        LocalRequestContext.HttpContext.Response.StatusCode = 404;
                    }
                }
            }
    
            public bool IsReusable
            {
                get
                {
                    return true;
                }
            }
        }
    }

RouteConfig.cs 

In RouteConfig class, we have changed the default route to call the custom route handler. To use the custom route handler, we need to create new Route and add it in the RouteCollection.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    
    namespace CustomRouteHandler
    {
        public class RouteConfig
        {
            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
                RouteValueDictionary defaults = new RouteValueDictionary();
                defaults.Add("controller", "Home");
                defaults.Add("action", "Index");
                defaults.Add("id", "");
                var customRoute = new Route("{controller}/{action}/{id}", 
                defaults, new RouteHandler.CustomRouteHandler());
                routes.Add(customRoute);
            }
        }
    }

CMS Project

ContentController.cs 

I have not done anything fancy here. This is again a basic MVC controller with only one method “Article”.

How It Works?

Now when we load the application, as we have only added the custom route, it will call the Custom Route Handler. The route handler will then call the CustomHandler to handle the incoming request. In case no route of current application matches the request, it will then try to get data from CMS application and if it’s not able to get the data from there also, it will return the 404 page. If you will see the URL, it is acting as the URL is part of the same application.

Conclusion

In most of the cases, the default MVCRouteHandler will do your work but in few cases where we need some extra control over the routes, Custom Route Handlers can do the trick.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here