ASP.NET MVC’s default route is good for most simple case scenarios, allowing us to access actions through these type of URLs:
[blockquote]
http://{server:port}/{controller}/{action}
[/blockquote]
Therefore, given a controller class with actions such as these:
public class CorporateInformationController : Controller
{
public ActionResult AboutUs()
{
return View();
}
public ActionResult OurMission()
{
return View();
}
public ActionResult JoinUs()
{
return View();
}
}
… we could access them using natural addresses such as /CorporateInformation/AboutUs, or /CorporateInformation/OurMission. Without doubt, this is great progress when offering our user friendly URLs, and improving our search engine optimization.
However, wouldn’t it be better if we could access actions and controllers using hyphens to divide the different terms? I mean, for example, uisng the /Corporate-Information/About-Us URL, or /Corporate-Information/Join-Us.
Obviously we can’t change the name of our controllers and actions, since our programming languages don’t allow the use of hyphens in identifier names.
Upon this scenario, a possibility could be assigning through the [ActionName] attribute an alternative name for each one of these actions. Nevertheless, besides the work this implies, there isn’t an easy way to achieve for the names of the controller classes either. Another option would be to register in the route table a specific entry for each one of the actions. Even though it doesn’t have any contraindications and it would cover are needs, it’s way too much work!!!
Fortunately, there are several expansion points in the framework, were we can hook up our process logic to the requests, and use them to obtain the solution to these kinds of problems globally. And one of them is the routing system.
1. Conventions forever!
Next we are going to see how to achieve it globally using the routing system, a basing on a very simple convention that we are going to set: the identifiers of the controllers and the actions that contain an underscore (“_”) will be converted in hyphens in the routes.
Since the underscore is perfectly valid for class and method names, we only have to introduce this character in the place we want the hyphens to appear, and leave the routing system in charge of the converting them automatically. Of course, we will perform this transformation bidirectionally: both when the URL is analyzed to determine the controller and the action to be executed, as well as when the address is generated from route parameters.
2. The FriendlyRoute class
Next we are going to see a new class, that we’ll call FriendlyRoute, and which we will provide with the hyphen to underscore conversion logic, and viceversa. Nothing we can’t solve in 20 code lines:
public class FriendlyRoute : Route
{
public FriendlyRoute(string url, object defaults) :
base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
{ }
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var routeData = base.GetRouteData(httpContext);
RouteValueDictionary values = routeData.Values;
values["controller"] = (values["controller"] as string)
.Replace("-", "_");
values["action"] = (values["action"] as string)
.Replace("-", "_");
return routeData;
}
public override VirtualPathData GetVirtualPath(
RequestContext ctx, RouteValueDictionary values)
{
values["controller"] = (values["controller"] as string)
.Replace("_", "-").ToLower();
values["action"] = (values["action"] as string)
.Replace("_", "-").ToLower();
return base.GetVirtualPath(ctx, values);
}
}
From the code above, let’s point out some things:
- I have only created a constructor, sincerely, out of laziness. Actually, if we wanted to cover more cases, like restrictions or namespaces, that are allowed by the overloads of the Route class constructors, we should create all of them for our class. But in any case, it is very easy.
- the GetRouteData() method is used by the framework to get the route data of an incoming request. As you can see iin the code above, we simply run the logic off the base class, and apply the corresponding transformations (from hyphens to underscores).
- the GetVirtualPath() method is used for inverse transformation, this is, to generate a URL starting from route paraneter values. In this case, we first retouch the controller and action name values slightly, converting the underscores into hyphens and putting them in small case, and next we invoke the base class logic so it generates the URL for us.
From this moment on we could add this class to the routing table with a code as follows:
routes.Add("default",
new FriendlyRoute(
"{controller}/{action}/{id}", // URL with parameters
new { controller = "home",
action = "start_here",
id = UrlParameter.Optional
}
)
);
Notice that one of the advantages of using this technique is that at code level we will always use the real name of controllers and actions (with underscore), and the routing system performs these conversions. This enable us to keep using T4MVC to avoid “magic strings” in our code, among other advantages.
3. Hey… but I’m used to using routes.MapRoute()… O:-)
It’s ok, it is also very easy. The MapRoute() methods that we use in the RegisterRoutes methods of the global.asax are simply RouteCollection class extensions: Therefore, we can base on this same idea and create custom extensions that give better access to our friendly route mapping:
public static class RouteCollectionExtensions
{
public static Route MapFriendlyRoute(this RouteCollection routes,
string name, string url,
object defaults)
{
var route = new FriendlyRoute(url, defaults);
routes.Add(name, route);
return route;
}
}
This way now we just have to reference from the global.asax the space of names where we have defined the previous class and we can already fill the route table using sentences such as:
routes.MapFriendlyRoute(
"Default", // route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "home", action = "start_here", id = UrlParameter.Optional }
);
This way once we enter the previous route in the place of the one that comes by default in ASP.NET MVC projects, our system is already ready to receive a request such as GET /Name-Controller/Name-Action and map it towards the Name_Action action of the Controller_NameController class.








SQL Server 2012 fundamentals for developers
Data Access with Entity Framework 5: Up & Running
70-511 TS: Windows Applications Development with Microsoft .NET Framework 4 exam preparation course
Learn Web Development with ASP.NET 4.0 Web Forms from scratch
Professional ASP.NET AJAX 

Very helpful, thanks.
NB: I had to put a check for routeData != null in GetRouteData().
Thanks for contributing!
Great article.
All that stuff to make from:
http://www.mydomain.com/signUp
the URL:
http://www.mydomain.com/sign-Up
??
(note the sign-up is the controller name, default action is “index”)
Nope – there must be a simplie way?
Hi, Lelala!
The simplest way to do this is, for sure, to use the standard routing system to route the requests made to the url “/sign-up” to the controller class “SignUpController”.
And of course, you could use more sophisticated approaches, such as replacing the ControllerFactory and other mechanisms to make it possible.
Regards,
Jose.