Introduction
I got a lot of people questioning me about how routing works in MVC so here is the article where I will be giving you all a brief about it.
The function of a routing system can be divided to two parts:
- RouteData: This mechanism is used to examine an incoming URL and then decide which controller and action the request is need to be send.
- VirtualPath: This mechanism is responsible for generating Outbound url. These are the URLs that appear in the HTML rendered from our views
so that a specific action will be invoked when the user clicks the link.
In the current Article we will be looking into RouteData only. In order to learn basic of MVC3 please read following article click here.
Let’s start by creating a Routing Project
Create a new MVC application using Internet Template naming Routing. As we have selected this template we will get some pre written routing.
As Routing is defined in Global.asax, open the file, and you can see the following lines of code:
namespace Routing
{
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
}
As we all can see that the Application_Start
method is
responsible for calling the RegisterRoutes
method which has down the routing criteria.
This Application_Start
method is called by ASP.NET platform when the application is started.
Note: Fortunately MVC provide us to define URL pattern which each route need to follow instead of manually typing out all of the individual URLs.
If the pattern of the route matches the URL, then it is taken care by it.
Let’s examine a simple URL if you run your application without deleting the current routing and open http://localhost:xxxx/Home/About, you can see the following screen
Lest have a quick discussion on this. URLs can be broken down into segments. These are the parts of the URL, excluding the hostname
and query string, that are separated by the / character. In the example URL, there are two segments:
- Home – It’s the first segment of the URL which is referring to the Home Controller.
- About - It’s the second segment of the URL which is referring to the About Action.
The URL pattern is {controller}/{action}
When processing an incoming URL, the job of the routing system is to match the URL to a pattern,
and then find out the values from the URL for the segment variables. The segment variables are expressed using curly braces (the { and } characters).
Our example has two segment variables having the names controller and action.
We say match to a pattern, because an MVC application.
Now let’s have a quick look at what all cases this rote can handle.
Request URL | Segment Variables |
http://testsite.com/Home/Index | controller = Home, action = Index |
http://testsite.com/Index/Home | controller = Index, action = Home |
http://testsite.com/Home | No match (too few segments) |
http://testsite.com/Home/Index/Soccer | No match (too many segments) |
As we can see that in case first and second the URL pattern matches the correct no of segment and we can reach to respective controller and action while in case third and fourth the URL pattern doesn't matches.
These are the default behaviors, and soon we will be learning how to change the default.
Creating and Registering a Simple Route
As we are going to start from basics for the time being lets delete the content of
RegisterRoutes
method as we are going to learn how to write routers Step by Step.
Once you have a URL pattern in mind, you can use it to define a route.
Now lest create a custom rote to handle the above example
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("CustomRoute", "{controller}/{action}");
}
Here we are creating a new route called CustomRoute
which will process the URL segment to its specified controller and action respectively.
For example:
The URL http://testsite.com/Home/Index will route to controller = Home and action = Index
Defining Default Values
The above defined route will work fine for case first and second but what if need to make sure that the cases third also works. In order to do that we need to define Default value for the View as follows:-
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("CustomRoute ", "{controller}/{action}", new { action = "Index" });
}
This route tell us that if we don’t provide any value for Action please invoke the Index action by default which means The URL http://testsite.com/Home/ will route to controller = Home and action = Index as no other action is specified, mean while URL http://testsite.com/Home/Xyz will route to controller = Home and action = Xyz as it is explicitly mentioned.
Next thing which comes to mind is if we want even a default controller to be fired in case it is missing, yes we can handle that as follows:-
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("CustomRoute ", "{controller}/{action}", new { controller = "Home", action ="Index" });
}
By providing default values for both the controller and action variables, we have created a route that will match URLs that have zero, one,
or two segments, the below mentioned table gives an overview of the behavior
Request URL | Segment Variables |
http://testsite.com/ | controller = Home, action = Demo |
http://testsite.com/Home | controller = Home, action = Demo |
http://testsite.com/Demo/Test | controller = Demo, action = Test |
http://testsite.com/Test/Demo | controller = Test, action = Demo |
http://testsite.com/Test/Demo/List | No match (too many segments) |
Static URL Segments
One can also create patterns that have static segments. Suppose you have a requirement where you want to prefix URL with a word “Student”.
http://testsite.com/Student/Home/Index
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("CustomRoute ", "{controller}/{action}",
new { controller = "Home", action = "Index" });
routes.MapRoute("StaticRoute", " Student /{controller}/{action}",
new { controller = "Home", action = "Index" });
}
This URL pattern will match only URLs that contain three segments, the first of which must be Student. The other two segments can contain any value, and will be used for the controller and action variables.
We can also create URL patterns that have segments containing both static and variable elements.
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("StaticRoute ", "XYZ{controller}/{action}");
routes.MapRoute("CustomRoute ", "{controller}/{action}",
new { controller = "Home", action = "Index" });
routes.MapRoute("AnotherStaticRoute ", "Public/{controller}/{action}",
new { controller = "Home", action = "Index" });
}
The pattern in this route matches any two-segment URL where the first segment starts with the letter XYZ The value for controller is taken from the first segment, excluding the XYZ. The action value is taken from the second segment.
As an example, the following URL would be matched by the route:
http://testsite.com/XYZ/Home/Index
This URL would be directed to the Index action method on the Home controller.
Custom Segment Variables
MVC doesn’t restrict us to route to URL to just the controller and action variables. We are also free to define our own variables as
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("CustomRoute ", "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "DefaultId" });
}
This route will match any zero-to-three-segment URL. The contents of the third segment will be assigned to the id variable, and if there is no third segment, the default value will be used.
Now the first thing which comes to our mind is how we can get this value and use it.
So first let’s talk about fetching this id value, which can be done in following two ways:
- We can access any of the segment variables in an action method by using the
RouteData.Values
property, hence id can be accessed as:
public ViewResult CustomVariable() {
ViewBag.CustomVariable = RouteData.Values["id"];
return View();
}
We can define parameters to our action method with names that match the URL pattern variables, the MVC Framework will pass the values obtained from the URL
as parameters to the action method.
public ViewResult CustomVariable(string id) {
ViewBag.CustomVariable = index;
return View();
}
Now once we have fetch the id value we can display it in view as follows:-
@{
ViewBag.Title = "CustomVariable";
}
<h2>Variable: @ViewBag.CustomVariable</h2>
Defining Optional URL Segments
MVC also gives us leverage to have optional URL segment which means a URL segment that the user does not need to specify, but for which even no default value is also specified.
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("CustomRoute", "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
This route will match URLs whether or not the id segment has been supplied.
No of Segment | URL | Mapping |
0 | http://testsite.com/ | controller = Home, action = Demo |
1 | http://testsite.com/Test | controller = Home, action = Demo |
2 | http://testsite.com/Test/Demo | controller = Test, action = Demo |
3 | http://testsite.com/Test/Demo/List | controller = Test, action = Demo, id = list |
4 | http://testsite.com/Test/Demo/List/NameId | No Match (too many segments) |
Defining Variable-Length Routes
Till now we have seen that we manage routing for any specific no of segments but what if we want to handle cases for ‘n’ no segments.
MVC provide us to handle this case with ease too. One can define support for variable segments by designating one of the segment variables as a catchall, done by prefixing it with an asterisk (*).
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("CustomRoute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
If you observe here we have extended the route from the previous example to add a catchall segment variable, which is we imaginatively called catchall. This route will now match any URL. The first three segments are used to set values for the controller, action, and id variables, respectively. If the URL contains additional segments, they are all assigned to the catchall variable
No Of Segment | URL | Mapping |
0 | http://testsite.com/ | controller = Home, action = Demo |
1 | http://testsite.com/Test | controller = Home, action = Demo |
2 | http://testsite.com/Test/Demo | controller = Test, action = Demo |
3 | http://testsite.com/Test/Demo/List | controller = Test, action = Demo, id = list |
4 | http://testsite.com/Test/Demo/List/NameId/ProfId | controller = Test, action = Demo, id = list, catch = NameId/ ProfId |
There is no upper limit to the number of segments that the URL pattern in this route will match.