Named Routes in MVC
In
this post you will learn what are ‘Named Routes’ and how to use them to
generate URLs. I will explain this using problem and then solution approach.
Introduction
In
MVC naming route is not required, we just create route without name and hand it
to routing engine and it just works. But there are situations when we need
named routes, it allows more control over route selection when generating URLs.
It also helps to avoids route ambiguity issue.
Why use ‘Named
Routes’? Let’s face the problem.
When
we create MVC Application we get following route defined.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
In
above code, you can see the route name is ‘Default’. Now, let’s create another
route just above the ‘Default’ route.
routes.MapRoute(
name: "FriendsRoute",
url: "Contacts/2013/{action}/{id}",
defaults: new { controller = "Friends", action = "Index", id = UrlParameter.Optional }
);
Keep
in mind, MVC routing system respect ordering route. Your last route must be
generic as possible, and your previous route must be specific as possible and
that’s why above route should go before ‘Default’ route.
Now,
let’s go ahead and create two RouteLink without specifying which route to use
to generate the links.
@Html.RouteLink("Go
to home", new {controller = "Home", action
= "Index"})
@Html.RouteLink("Go
to friends", new {controller = "Friends", action
= "Index"})
In
above code, to create RouteLink I have used following version of overloaded
method, image given below.
Please
note I’m just making use of ‘linkText’ and ‘routeValues’, and not using any route
name (Default or FriendsRoute) in order to generate URLs. So, it is completely
up to MVC to pick the route. Once you have defined both routes (in
RouteConfig.cs page), run the application.
This
is fine for these simple cases, but there are situations where this can bite
you. Let’s add a ‘page route’ as given below (yellow highlighted) just above
the ‘FriendsRoute’, means this route is going to be on the top.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapPageRoute("new", "anypage/url",
"~/aspx/profile.aspx");
routes.MapRoute(
name: "FriendsRoute",
url: "Contacts/2013/{action}/{id}",
defaults: new { controller = "Friends", action
= "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action
= "Index", id = UrlParameter.Optional }
);
}
Now,
see what happened.
Can
you see all the URLs goes into a subtle behavior of routing, which is
admittedly somewhat of an edge case and this is something that we run into from
time to time.
Even
don’t create that page route, just put ‘FriendsRoute’ after ‘Default’ route,
this also goes into subtle behavior of routing.
The
reason is simple, all the routes you have has URL route format
{controller}/{action}/{id}, and you are expected to supply values for
controller, action, and id to generating URLs. In this case, because the new
route doesn't have any URL parameters, it matches every URL generation attempt
because technically, any route value is supplied for each URL parameter, admit
it.
Fix
is very simple, use names for all your routes and always use the route name
when generating URLs. Letting Routing engine sort out which route you want to
use to generate a URL is really leaving it to chance to bite you. If you need
to use non-named routes and leave it all on Routing engine, I recommend to
write unit tests to verify the expected URL generation.
Let’s fix above
problem by using ‘Named Routes’.
I
just updated my RouteLink and included ‘routeName’ as given below.
@Html.RouteLink(linkText: "Go
to home", routeName: "Default", routeValues: new {controller="Home", action="Index"})
@Html.RouteLink(linkText: "Go
to friends", routeName: "FriendsRoute", routeValues: new {controller="Friends", action="Index"})
Now,
run the application, you will find both URLs working but rest all will be still
in subtle routing behavior. And to fix this, just bring that ‘page route’ at
the bottom and also write unit tests to verify the URL generation.
Hope
this helps.
wow... Named Routes are really great. Didn't ever think about such situations.
ReplyDeleteThough it is off-topic, I was wondering how we can implement the dynamic routing that depends on database data instead of hard-coding it in Global file like -
example.com/user1
example.com/user2/profile etc.
Can you please shed some light on this concept of routing. It would be really very helpful.
Thanks.