Introduction
Before writing this article I have seen many articles written on this topic,even in c-sharpcorner itself.
Let's have look at the following image which specify different routes organised in manner:
Courtesy: lapresse
Image :1
In this article I will explore some interesting facts and the process of how it works.
Whenever I add a new route in an application I scratch my head and think 3 times, because whenever I add new a route I get weird results. I am sure many of you have encountered a similar problem.
I hope this article will help you to understand, how routing works in MVC?
So before going in depth, let's first understand the what and why of routing.
What is a Route and Why to use one
As the name implies, a route is a path or way that takes you to a destination. Like a bus route, train route and so on.
In ASP.NET MVC, routes are responsible for redirecting your request to the specific action (which belongs to the controller). It specifies how the incoming URLs will be mapped to the controller action.
The same exists here, in MVC we define routes to invoke a specific action in the controller. And we always want to keep it clean and readable, for example
http://www.c-sharpcorner.com/Authors/56fb14/ instead of
http://c-sharpcorner.com/ Authors?Id=56fb14. It not only improves readability but also it is Search Engine friendly (nice description here:
ASP.NET MVC and Search Engine Optimization.
Routing is a great feature of MVC since we don't need to specify all URLs individually for each request. Instead we specify a pattern for routes called an URL Pattern. If the incoming URL matches the pattern then the Routing system processes it.
How Routing in MVCWe implement routing in MVC using a RoutTable. And there are four sections that must be configured in the web.config that are required for Routing in MVC; they are:
- system.web.httpModules section
- system.web.httpHandlers section
- system.webserver.modules section
- system.webserver.handlers section.
After getting this configuration done we need to create a RoutTable with our application routes.
Generally we register routes in the Global.ashx file and we invoke a RegisterRoutes method in the Application_Start() method.
Configuring Routes
Here is how we define and configure routes for a MVC application.
Let's have a look at the RegisterRoutes method:
- public static void RegisterRoutes(RouteCollection routes)
- {
- routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
- routes.MapRoute(
- "Admin" ,
- "Admin/{Controller}/{action}" ,
- new { controller = "Administrator", action = "Index" }
- );
- routes.MapRoute(
- "Default" ,
- "{controller}/{action}/{id} /* URL with parameters */",
- new { controller = "Home", action = "Index", id = "" }
- );
-
- }
And we can register routes using:
- protected void Application_Start()
- {
- …
- RouteConfig.RegisterRoutes(RouteTable.Routes);
- }
Understanding URL Pattern
When we request URLs in a MVC application RoutHandler extracts values for the request.
For example if we use http://localhost/Admin /Manage/AddUser
Then it will invoke the AddUser action in the Manage controller inside the Admin Area (read more here: http://msdn.microsoft.com/en-us/library/ee671793(VS.100).aspx) as per the route pattern defined in the RegisterRoute method which is:
- routes.MapRoute(
- "Admin" ,
- "Admin/{controller}/{action}" ,
- new { controller = "Administrator", action = "Index" } );
-
-
- Your action should be
- public ViewResult Index()
- {
- return View();
- }
The MapRout method has the following 3 parameters:
- Route Name: We usually give a unique name to a route for differentiating it from the others. We can specify null if we want. Actually it is not important in routing. We generally use a RedirectToAction or just Action and hardly use RedirectToRoute which is the purpose of giving route a name.
- URL Pattern: In URL pattern we declare variables and when an URL is requested the pattern extracts values from the URL segments, as shown in Image 2.
- Default Values: When an incoming URL matches a route, the MVC Framework takes the value of the controller variable and looks for the appropriate name.
Note: the MVC routing system doesn't find an exact match in the RoutTable (Route Pattern) but it uses the first match always and even the Routing engine doesn't have any specific knowledge of actions, controllers or action parameters, it just extracts values from the variables and in our case the variables are {controller} {action}.
Some invalid and valid URL patterns:
Requested URL |
Match Route |
Error |
http://localhost/Admin/HR/CreateStructure |
Admin/{Controller}/{Action} |
No Error |
http://localhost/Admin/HR |
Admin/{Controller}/{Action} |
No Error |
http://localhost/Admin |
None |
Too few segments |
http://localhost/Admin/HR/EditStructure/1 |
None |
Too many segments |
http://localhost |
{controller}/{action}/{id} |
No Error |
http://localhost/ Home |
{controller}/{action}/{id} |
No Error |
http://localhost/ Home/Index/2 |
{controller}/{action}/{id} |
No Error |
The preceding table will help to understand valid URLs for a specified pattern.
Here is how the Routing Engine extracts segments from requested URLs.
Image 2
In the first example the URL is being mapped to the Admin Area. So we need to hard-code the folder path using the pattern Admin/{controller}/{action}.
Here we have only 2 segments, the controller and action. If we request ~/Admin/HR then it determines the view Index that is specified in the default value.
Prioritizing Controllers
When we request a URL and the routing engine recognizes it then the framework extracts the value of the controller from the segment and searches for the specific class, for example if I request URL http://localhost/ Home then it will search the HomeController class and it is not a fully qualified name, in other words if we have 2 HomeController classes in different assemblies then it will throw an exception as in the following:
We can overcome these using prioritizing controllers. We can specify a namespace as:
- routes.MapRoute(
- "Default" ,
- "{controller}/{action}/{id} /* URL with parameters */",
- new { controller = "Home", action = "Index", id = "" }
- ,new []{"assembly_1_name.Controllers"}
- );
Routing Request for Physical Files on Disk
Apart from mapping the request directly to the controller action we can redirect the request to the physical file on disk.
We can do this using :
- public static void RegisterRoutes(RouteCollection routes)
- {
- routes.RouteExistingFiles = true;
- …
- }
Note: We must specify it before registering routes.
Handle Dynamic URLs
For handling dynamic requests we use {*catchall} in the URL pattern, for example:
- routes.MapRoute("CatchALL", "{controller}/{action}/{id}/{*catchall}",
- new
- {
- controller = "Home",
- action = "Index",
- id = UrlParameter.Optional
- });
Now we have already seen valid and invalid URLs for our mapped routes. But {*catchall} has the ability to handle all of them. Here is how {*catchall} maps URLs to actions:
Requested URL |
Match Route |
Catch |
Error |
http://localhost/Home |
{controller}/{action}/{id}/{*catchall} |
catchall =null |
No Error |
http://localhost/ Home/Index/2 |
{controller}/{action}/{id}/{*catchall} |
catchall =null |
No Error |
http://localhost/ Home/Index/2/3 |
{controller}/{action}/{id}/{*catchall} |
Now catchall=2/3 |
No Error |
http://localhost/ Home/Index/2/3/4 |
{controller}/{action}/{id}/{*catchall} |
Now catchall=2/3/4 |
No Error |
Creating a Route Constraint
Using a route constraint, we can force browser requests to use a specific route. We also define constraints if we want to specify an action for only POST, GET and so on requests called HttpMethod constraints.
Let's have a look at a route:
- routes.MapRoute(
- "" ,
- "Administrator/{action}/{id} /* URL with parameters */",
- new {action = "Index", id = UrlParameter.Optional }
- );
- In this route we always expect its id because it has been mapped with action like:
- public ActionResult EditEmployee(int id=1)
- {
- return View();
- }
But what if we request a URL as in the following:
URL |
Mapping |
http://localhost/Administrator/EditEmployee/FALSE |
Controller =Administrator Action=EditEmployee Id=FALSE |
http://localhost/Administrator/EditEmployee |
Controller =Administrator Action=EditEmployee Id=null /*fine will use default id=1*/ |
So we never want the user to pass FALSE or any string in the id segment.
For this we use a constraint and validate it for integers only using regular expressions.
- routes.MapRoute(
- "" ,
- "Administrator/{action}/{id} /* URL with parameters */",
- new {action = "Index", id = @"\d+" }
- );
HttpMethod Constraint
We use these constraints if we want to specify a route for a specific HttpMethod like GET, POST and so on.
Let's have a look at the same route. If we want to invoke this route only for POST requests then I can use :
- routes.MapRoute(
- "" ,
- "Administrator/{action}/{id} /* URL with parameters */",
- new {action = "Index", id = @"\d+" }
- , new { httpMethod = new HttpMethodConstraint("POST") });
-
- );
We just need to specify another parameter for that.
It must be noted that we need to specify this route first when registering routes.
Important Points:
- When we request a URL it always searches for the first match, not the best match, because it doesn't have any idea of the action and controller, it just extracts values from segments.
- You need to be concerned with the order of the routing whenever you specify a new route for your application because it may cause unexpected behaviour when you do so.
- Don't define unnecessary routes in your application.
- Specify default values for routes (or use *catchall for unexpected behaviour ).
Summary
In this article, we have seen how routing works in MVC . We have also discovered some hidden parts of routing too. Although I have tried to cover at-most I could do and I tried to explain the most useful features. But routing has many, many more useful features. I hope you will love discovering those on your own.
Your feedback and comments are most welcome.