We recently experienced an unexpected problem when attempting to re-route obsolete URLs using ASP.Net’s MVC routing mechanism. The scenario was that we had been running a competition, and had a set of URLs associated with it in the format
http://server/Competition/etc. We now needed to re-route all incoming requests to these pages back to the site’s home page. In order to achieve this, we added the following to the routing table:
routes.MapRoute(
null,
"Competition/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
In the majority of cases this worked fine. However, a handful of links unrelated to the competition now exhibited strange behaviour. For example, the menu item on the home page that was a link to itself (i.e. the home page) now had the URL http://server/Competition rather than http://server/. The link itself was generated in the view Home/Index.aspx using an ActionLink:
<%= Html.ActionLink("Home", "Index", "Home")%>
It turned out that the problem was with self-referencing ActionLinks where the link matched the controller and action for the given view. The outgoing URLs for these self-referencing ActionLinks now matched the new competition route. The problem was resolved by adding a constraint to the route, ensuring that this route was only applied to incoming requests:
routes.MapRoute(
null,
"Competition/{action}/{id}",
new { controller = "Home", action = "Index", id = "" },
new { routeDirection = new IncomingRequestConstraint() }
);
The new constraint, IncomingRequestConstraint, checks the route direction:
public class IncomingRequestConstraint : IRouteConstraint
{
public bool Match(System.Web.HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection)
{
return routeDirection == RouteDirection.IncomingRequest;
}
}
Post Script: Legacy URLs
The above example is a slightly simplified version of how we dealt with the obsolete URLs. In order to establish that the re-routing was permanent and use an HTTP 301 redirect, we made use of the legacy URL routing described by Matt Hawley at eXcentrics World. Our actual route looked like this:
routes.Add(
null,
new LegacyRoute
(
"Competition/{action}/{id}",
"RedirectHome",
new LegacyRouteHandler()
)
);
As a result, the same outgoing URL now took the form http://server/Competition/Index/?controller=Home. Adding the constraint resulted in:
routes.Add(
null,
new LegacyRoute
(
"Competition/{action}/{id}",
"RedirectHome",
new LegacyRouteHandler()
)
{
Constraints = new RouteValueDictionary(
new { routeDirection = new IncomingRequestConstraint() }
)
}
);