URL routing in Microsoft MVC 5 when Areas are present: potential pitfalls

The “convention over configuration” philosophy of Microsoft’s MVC engine is very useful, as it automates a lot of “wiring” among the different components of a web application.

However, when the “going gets tough”, you have be careful how to circumvent certain convention-based automations that may bring undesired results.

One very important aspect to consider is routing, which technically is not part of MVC (it is a separate component of .net, as you can also use it with web forms) but plays an incredibly important role in the functioning of MVC. Routing is the mechanism that calls a certain object (in MVC, it is usually a controller) based on the URL the user typed or clicked on.

In MVC’s views based on Razor 3, we have a very user HTML helper which creates a URL based on:

– the controller we need

– the action we need.

Its syntax is the following:

@Html.ActionLink(“link text”, “action name”, “controller name”)

When you divide the application in Areas, you have to be extra-careful, as the overload that allows you to specify the Area you need has five arguments rather than four as you might wonder (since the Area is the fourth argument.)

So, in case you need a link that should call a controller inside an Area, it should read:

@Html.ActionLink(“link text”, “action name”, “controller name”, new {Area=”Area Name”}, new {})

The last new{} parameter is in lieu of html attributes. Without that specification, the compiler will think we’re calling the four-parameter overload, which has as fourth parameter… exactly the html attributes, not the Routing object and we would be wrong.

Is that all? No. There is another consideration related to the priority that the routing mechanism uses when you register more than one route in an Area route registration class (or any registration class, for that matter.)

When you create an area, in fact, an additional route AreaRegistration subclass is created for you.

For instance: if you create an area called “Particularities”, Visual Studio will create a class with this code (could not reproduce the indentation correctly here):
using System.Web.Mvc;
namespace RMTestSite.Areas.Particularities
{
public class ParticularitiesAreaRegistration : AreaRegistration
{
public override string AreaName
{
  get
{
  return "Particularities";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
  context.MapRoute(
  "Particularities_default",
  "Particularities/{controller}/{action}/{id}",
   new { action = "Index", id = UrlParameter.Optional }
 );
}
}
}

So, if you want to point to the new Area controller from a page called by a controller that is in another Area, you would have to code:

@Html.ActionLink("Particularities","Index", "Home", new{Area="Particularities"}, new {})

The result of this link is an reference to:
http://localhost:53869/Particularities/Home, which is OK.

Now let us imagine we add another route to the Particularities Area.

For instance, let us add this route before our default route (as a rule, specialized routes should precede general routing rules):

context.MapRoute(
"Particularities_AdditionalParticularitiesdefault",
"Particularities/AdditionalParticularities/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);

What happens now if I use again my Html.ActionLink? I mean: if I use exactly again,
@Html.ActionLink("Particularities","Index", "Home", new{Area="Particularities"}, new {})

The result is different. The address becomes
http://localhost:53869/Particularities/AdditionalParticularities/Home

Why is this? Because the routing mechanism found the first (in terms of: how the code is ordered) pattern in the route series which is matched by our controller/action ActionLink request. It does not choose the “simplest” one.

How can we continue to point to the previous controller/page?

We can specify the route itself when we build our link.

We can do this very easily by using RouteLink rather than the ActionLink. Some say this is in general a preferred routing habit: you can have thusly a list of what routes should do regardless where you want to implement the controllers and you are safer (well, in this case we would have been safe.)

Following our example, our code should have been
@Html.RouteLink("Particularities!", "Particularities_default", new { controller = "home", action = "index" }, new { })

We added the route name to the link, rather than the Area name, and we know we won’t get the wrong controller.

All in all, MVC’s routing mechanism (I specify, again, that is not a part of MVC per se) is a great thing, but we never have to give for granted that it will work in the way we guess a convention should work: it is the .net framework developer’s convention, not necessarily the one we think it is.