Custom Routes in ASP.NET MVC

Posted by: Suprotim Agarwal , on 1/22/2014, in Category ASP.NET MVC
Views: 74401
Abstract: Learn how to use Custom RouteBase class in ASP.NET MVC to manipulate incoming and outgoing URLs based on specific relation and criteria.

ASP.NET MVC Routing is pretty flexible in itself and as demoed in Friendly URLs in ASP.NET Web Forms , we can mix and match old web forms style URLs with modern friendly URLs, even keeping both side by side. However, there are often quirks of implementation that need us to dig a little deeper for a solution.

Side Note: For those interested, ASP.NET MVC 5 has introduced AttributeRouting. You can read more about it in the 10th edition of the DNC .NET Magazine

Today we’ll pick an easy and superfluous reason to demonstrate how to do a custom RouteBase implementation. We will see how we can re-direct non-standard URLs like html/asp and aspx files to a cleaner ‘Friendly Url’ format. The Requirement is to handle extensions like html/pdf and redirect to Friendly URLs that ‘look like’ MVC generated URLs.

This can be done using a custom route.



The MVC Custom Route Implementation

We have to derive our own Route class from Route base and implement two methods:

1. GetRouteData: This method is called when Inbound URL Matching is required.

2. GetVirtualPath: This method is called when Outbound URL Generation is required

Step 1: To create a custom route base, we start off with a Basic Project template.

Step 2: Next we add a CustomRouteController to channel all the requests to. The controller simply puts the incoming URL in a label in our sample, but in reality, it could use the incoming URL and find out from a mapping database what the new equivalent URL was and do a RedirectPermanent action to a new Controller/ActionMethod too. That’s with the Controller.

public class CustomRouteController : Controller
public ActionResult DirectCustomUrls(string customUrl)
   if (customUrl.EndsWith("html"))
    return new RedirectResult(customUrl);
    ViewBag.ErrorMessage = "Unrecognized URL";
    return View();

Step 3: Now let’s setup the Custom Route. The constructor by design takes an array of valid URLs that we will have to pass when we are creating the route for this.

The GetRouteData method is called when a new request is made. In this method, the Requested path is checked to see if it contains any of the URLs that were passed in the constructor. If anyone matches, we send back the required RouteData, if not, we return Null and MVC takes the Route resolution to the next registered route.

Point to note is - the mechanism to verify the incoming Request Information matches the ‘custom requirements’ has to be in the GetRouteData method. Our oversimplistic scenario has resulted in a rather petty check involving string matching.

public override RouteData GetRouteData(HttpContextBase httpContext)
RouteData result = null;
string requestedURL = string.Empty;
for (int i = 0; i < urls.Length; i++)
   if (httpContext.Request.AppRelativeCurrentExecutionFilePath.Contains(urls[i]))
    requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath;

if (!string.IsNullOrEmpty(requestedURL))
   result = new RouteData(this, new MvcRouteHandler());
   result.Values.Add("controller", "CustomRoute");
   result.Values.Add("action", "DirectCustomUrls");
   result.Values.Add("customUrl", requestedURL);
return result;

Step 4: Registering our Route -  We register our route as follows in the App_Data\RouteConfig.cs

public static void RegisterRoutes(RouteCollection routes)
 routes.Add(new CustomRouteBase.Routing.CustomRouteBase(
   name: "Default",
   url: "{controller}/{action}/{id}",
   defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }

Essentially we are taking over all URLs that contain an html or pdf and redirecting via our Route. We add two default views for the Home Controller and the DynamicController. The DynamicController picks up the message from View Bag and displays it if any.

Let’s Run our app.

We initially are at the home page. Thereafter when we navigate to a random URL that ends with html we get redirected to our custom page via our CustomRouteBase.



Generating Outgoing URLs

Lets say now user needs to generate an HttpActionLink, to support this, we need to implement the GetVirtualPath method in our CustomRouteBase class. Once again, if we are unable to deal with the request, we let the routing system know by returning null. Otherwise, we return an instance of the VirtualPathData class.

public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
     VirtualPathData result = null;
     if (values.ContainsKey("html") || urls.Contains((string)values["html"], StringComparer.OrdinalIgnoreCase))
         result = new VirtualPathData(this,
         new UrlHelper(requestContext)
     return result;

Now we place the following markup in our Index.cshtml

<div>This is a URL:
@Html.ActionLink("Click me", "CustomUrl",
new { html = "~/html/OldFile.html" })

This results in the following ActionLink getting generated


We can manipulate the generated link any way we want based on the final requirement.


With that we conclude this demo of how to Manipulate Route in ASP.NET MVC with a custom implementation of RouteBase.

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of DotNetCurry, DNC Magazine for Developers, SQLServerCurry and DevCurry. He has also authored a couple of books 51 Recipes using jQuery with ASP.NET Controls and The Absolutely Awesome jQuery CookBook.

Suprotim has received the prestigious Microsoft MVP award for Sixteen consecutive years. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that offers Digital Marketing and Branding services to businesses, both in a start-up and enterprise environment.

Get in touch with him on Twitter @suprotimagarwal or at LinkedIn

Page copy protected against web site content infringement 	by Copyscape

Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by Guy on Sunday, April 6, 2014 3:21 AM
This article has given me some good insight into the matter of Routing in MVC.
I may have misunderstood something but it seems to me that in several places in your examples you use an array called 'urls' which seems to have no value in the context within which it is used... This is in both the incoming and outgoing examples.
Comment posted by Suprotim Agarwal on Monday, April 7, 2014 9:44 AM
@Guy The string array represents the individual URLs that this routing class will support. It is passed to the constructor of this class. Why is it needed? Read this para "The GetRouteData method is called when a new request is made. In this method, the Requested path is checked to see if it contains any of the URLs that were passed in the constructor. If anyone matches, we send back the required RouteData, if not, we return Null and MVC takes the Route resolution to the next registered route."

It is not clear as I have not explicitly shown the constructor definition in the example, I just mention it. My bad and sorry about that.
Comment posted by Eric Richards on Friday, July 18, 2014 11:04 AM
Any chance that you could post the full source code for this example?
Comment posted by Suprotim Agarwal on Saturday, July 19, 2014 5:32 AM
@Eric: Try this: