Routing In MVC with Code Example

Introduction

There exists an assumption that there is a direct relationship between the requested URLs and the files stored on the server. But does this make sense in an MVC application, where requests are processed by action methods and controllers and there is no relation to files on the server (disk). MVC applications use a routing system to track the requests and give responses to the users. The routing system helps create patterns of URLs, that to patterns of our wish. The routing system does the following job, incoming URL examination. It checks the URL requested and it intends the controller and action based on the URL, it redirects the controller based on the client request. This is a very interesting and important subject of discussion. Once we create an MVC application, it is already configured to use the ASP.NET routing system. Four sections are anyway required relevantly for the routing system in the application. They are the following:

  • system.web httpModules: These are required to configure the modules within an application. HTTP Modules are assemblies that are called on every request made to the application.
  • system.web.httpHandlers: This runs in response to the request made to the application.

The above-mentioned are required for the routing system and should be present in the configuration of the application. We need to make sure the above configurations exist in our application.

Let's start with snippets

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

Now, this RouteConfig.cs resides inside the App.Config folder. The RegisterRoutes method inside the RouteConfig.cs gets called from the heart of the MVC application i.e. Global.asax.cs, which sets the core MVC features when the application is started. The RegisterRoutes method gets called inside Global.asax.cs as follows.

RouteConfig.RegisterRoutes(RouteTables.Routes); 

To the method as we can see the parameter is passed i.e. RouteTables.Routes which are collection routes or the instance of the RouteCollection.cs. To register to add a new route, we need to register inside the RegisterRoutes method as below.

Route demoRoute = new Route("{controller}/{action}", new MvcRouteHandler());  
routes.Add("DemoRoute", demoRoute);  

Let's understand the above snippet, here a new route named demoRoute is being created. MvcRouteHandler() is also passed to the constructor. This extends the IHttpHandler and passes the request routes, the object above is an instance of the RouteCollection class which is passed from the Global.asax.cs. There is another easy way of registering routes, i.e. by using the MapRoute method defined by the RouteCollection class like the following code snippet.

routes.MapRoute("RouteName", "{controller}/{action}) 

One thing to note here is, that if the route in case does not match any of the routes in the RouteCollection, then the error is thrown to the user. Generally, the default route is set to Index as action. Then, the URL with http://demosite/home will also get the user to the Index action method. If both the controller and action are provided to the routes, then routes match the URL with no segments (if the default is defined in the RouteConfig). Generally, the controller is only used in the URL, then the Controller's action Index method is called i.e.

http://demosite.com/Admin

This would be called the Admin Index method. If any other action method is called, then similarly based on the segments in the URL requested, the action in the specified controller is called.

Static URL segments

There is this concept of static URL segments. This is rarely used but the concept is quite interesting. Here, the route is registered inside RouteConfig like the following code snippet.

routes.MapRoute(""), "X{controller}/{action}"); 

Here, what happens is whatever is present after X or suffixed to X, that controller and action gets called. For example, http://demosite.com/XAdmin/Index, here what happens is the controller Admin and the Action Index is called. There is a concept of Route Ordering as well, in which in the order the routes are defined in the RouteCollection, the same order the route mapping is done. I.E. when a URL request is made to the application, the route config maps the route defined until a match is found in the same order routes are defined. Another interesting fact to notice here. Suppose we have a controller named "Admin" and sometime in the future, we are required to modify the name of that controller. Now if we do not modify our RouteConfig, we would get a 404 NOT FOUND error. To avoid this we made the following modifications to the Route.Config.cs.

public static void RegisterRoutes (RouteCollection routes) {  
 routes.MapRoute ("Admin","Admin/{action}",  
    new {controller="Home"});  
} 

Thus, when a request as /Admin/Index comes to the server, the controller called is /Home/Index, avoiding the 404 error page. Since there is no segment variable supplied, the default provided is used. The same can be done in case of an Action as well, i.e. if an action is removed from the controller, we can have a default route check for that action as well and redirect to the specified and default route URL if not found. Like as below.

routes.MapRoute("Admin","Admin/department",  
  new {controller = "Admin",action = "Index"});  

Defining custom segment variables

Controller and Action are common parts of any URL request to the MVC application and these would be considered as the in-built segment variables. However, other segment variables can be customized for the service requests. Let's take a look at the following snippet.

public static void RegisterRoutes(RouteCollection routes) {  
    routes.MapRoute("RouteDemo", "{controller}/{action}/{id}")  
        new {controller = "Admin" , action="GetDept", id = "DefaultID"});  
} 

Here we can see we have declared a custom variable "id" with a default value. Thus any URL request with two or three segment variables would work out. If the id is not specified explicitly in the URL, then it takes the default id value. Usually, the third segment variable is used to fetch the parameter to be utilized in the service methods.

public ActionResult GetDepartment(int id) {  
  //Used id to get the particular department.  
}

Here if the ID is not provided in the URL, then it searches using the default ID specified and may return null. Thus, to avoid this, we can also define the third segment variable to be optional, so that we have action to also return the result if no id is specified in the URL.

routes.MapRoute("OptionalRoute", "{controller}/{action}/{id}",  
     new {controller = "Admin", action = "GetDept", id = UrlParameter.Optional }); 

Thus the UrlParameter.Optionally makes the third segment variable optional and rund through without any default variables as well.

Prioritizing controllers with the same name using namespaces

This is a very interesting and strong concept that can be handy when working on a huge MVC application. Let's take a scenario to understand this, suppose we have a controller name "Home "inside the controller's folder and another controller with the same name "Home" inside another folder say "AddonControllers". So when we run the project and hit the URL /Home/Index, then the routing and mapping that searches the URL requested, will be in ambiguity as it fetches two controllers with the same names. Thus this will throw an error with the exception message.

Multiple types were found that match the controller named "Home". This can happen if the route that services this request ('{controller}/{action}/{id}') does not specify the namespace to search for a controller that matches the request.

Thus, from the error message itself, we can see that this error can be resolved easily by specifying the namespace for which you want to prioritize the search when a URL request is made to the server. Let's see how.

routes.MapRoute("DemoRoute", "{controller}/{action}/{id}",  
    new {controller = "Home", action = "Index", id = UrlParameter.Optional },  
      new []{"URLsAndRoutes.AddonControllers"});  

Here, specifying the new array of strings and saying the MVC routing to search for the controller every time in the AddonControllers or the specified namespace first, and if not found then move into the controller's folder to search for the match of the URL request. This really would be of high use when the requirement is as such.

Conclusion & Points of Interest

Thus here we discussed one of the critical concepts of MVC. Routing and pipelining are mandatory concepts to know to start with learning MVC. Using an MVC application and not using Routing is not wise. There are opportunities to manipulate and customize the Routing system, so it is always advisable to use Routing whenever an MVC application is in the picture. The URL segment variables Controllers, Actions, and parameters are required to customize the URLs to the convenience of the end users. The routing can narrow down the search of the routes if the maximum hit URL requests are ordered highly in the Route Collection.

References

Asp.NET MVC Professional by Adam Freeman.


Similar Articles
Invincix Solutions Private limited
Every INVINCIAN will keep their feet grounded, to ensure, your head is in the cloud