ASP.NET MVC Routing

What is routing?

Routing examines an incoming URL and find out for which controller and action is mapped with request. It constructs outgoing URLs that correspond to controller actions.

Why we need Routing?

Web server knows nothing about our code. It knows how to map URLs to files, but not to controllers and action. So, that Microsoft provides a tool name routing that can deeply understand our code.

How Routing Works

MVCBlog4

routing engine finds a match in the route table for the incoming request’s URL, it forwards the request to the appropriate controller and action. If there is no match in the route table for the incoming request’s URL, it returns a 404 HTTP status code.

Understand URL Patterns

Each route contains a URL pattern, which is compared to an incoming URL. If the pattern matches the URL, then it is used by the routing system to process that URL. For example

  • http://talenttuner.com /Admin/Index

URLs can be broken down into segments. These are the parts of the URL, excluding the host name and query string, that are separated by the / character. In the example URL, there are two segments, as shown below

MVCBlog1

 The first segment contains the word Admin, and the second segment contains the word Index. The first segment relates to the controller and the second segment relates to the action. But for understand the routing system we need to provide a pattern. We will use URL pattern like this:

  • {controller}/{action}

 MVC application will usually have several routes and the routing system will compare the incoming URL to the URL pattern of each route until it finds a match. By default, a URL pattern will match any URL that has the correct number of segments. For example, the pattern {controller}/{action} will match any URL that has two segments

MVCBlog2

 URL patterns are conservative, and will match only URLs that have the same number of segments as the pattern. You can see this below.

MVCBlog3

 Configure Route

 In ASP.Net MVC project routes are defined in the RouteConfig.cs file, which is in the App_Start project folder.

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
            });
        }
    }

The static RegisterRoutes method is defined in the RouteConfig.cs file is called from the Global.asax.cs file, which sets up some of the core MVC features when the application is started.

 public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
    }
}

The Application_Start method is executed when application is first started, and calls the RouteConfig.RegisterRoutes method.

Default Route Pattern

We can have a default routing pattern for our application to call a particular action method under a controller with some default parameters when a user doesn’t provide any arguments in the URL.

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 }
            );
        }
    }

If we consider this pattern, controller and action are mandatory parameters whereas id is an optional one. So, let’s have a look at the valid URLs which can be handled by this Pattern.

Valid Patterns:

  • /Home/Index/5 – Valid ( it will call Index(int? id) action method of HomeController and it passes 5 as id)
  • /Home/Index    – Valid ( as Id is an optional parameter, this URL will still work and call Index() action method of HomeController)
  • /Home/            – Valid (in place of controller it will take HomeController but as we din’t mention any action       method, it will take it from defaults we set i.e., Index , Hence it would be equal to /Home/Index/)
  • /                      – Valid (it will take all the parameters from defaults, hence it would be equal to /Home/Index/)

Invalid Patterns:

  • /Home/Index/test – Invalid ( it will search for Index action method inside HomeController which takes string as   an argument, as we have only one Index method which takes int value as argument, this will raise an exception)
  • Please note that if the Index(int? id) method under HomeController has int argument instead of int? (nullable int), It will throw exception for /Home/Index , /Home , / – as it cannot parse null as integer.

Static URL:

We can create patterns that have static segments. Suppose that we want to match a URL like this to support URLs that are prefixed with Public:

  • http://talenttuner.com /Public/Home/Index

We will write route this rule for above url:

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute("MyRoute", "{controller}/{action}",new { controller = "Home", action = "Index" });
            routes.MapRoute("MyStaicRoute", "Public/{controller}/{action}",new { controller = "Home", action = "Index" });

        }

This new pattern will match only URLs that contain three segments, the first of which must be Public. The other two segments can contain any value, and will be used for the controller and action Variables. If the last two segments are omitted, then the default values will be used.

Using custom routes

We can register our own custom routes in the routing collection, for example, that we are building a blog application. You might want to handle incoming requests that look like this:

  • http://talenttuner.com /Archive/12-25-2009
public static void RegisterRoutes(RouteCollection routes)
         {
             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
             routes.MapRoute(
                 "Blog", // Route name
                 "Archive/{entryDate}", // URL with parameters
                 new { controller = "Archive", action = "Entry" } // Parameter defaults
                 );
             routes.MapRoute(
                 "Default", // Route name
                 "{controller}/{action}/{id}", // URL with parameters
                 new { controller = "Home", action ="Index", id = "" } // Parameter defaults
                 );
         }

The order of the routes that you add to the route table is important. Our new custom Blog route is added before the existing Default route. If you reversed the order, then the Default route always will get called instead of the custom route.

The custom Blog route matches any request that starts with /Archive/. So, it matches all of the following URLs:

  • http://talenttuner.com /Archive/12-25-2009
  • http://talenttuner.com /Archive/10-6-2004

The custom route maps the incoming request to a controller named Archive and invokes the Entry() action. When the Entry() method is called, the entry date is passed as a parameter named entryDate.

You can use the Blog custom route with the controller:

 public class ArchiveController : Controller
    {
        public string Entry(DateTime entryDate)
        {
            return "You requested the entry from " + entryDate.ToString();
        }
    }

Route Constraint

We use route constraints to restrict the browser requests that match a particular route. We can use a regular expression to specify a route constraint.

http://talenttuner.com /Product/23

For example in the above we want to do is only match URLs that contain a proper integer productId. We can use a constraint when defining a route to restrict the URLs that match the route. The modified Product route in Listing 3 contains a regular expression constraint that only matches integers.

public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.MapRoute("Product","Product/{productId}", new { controller ="Product", action = "Details"},
                new { productId = @"\d+"  });
            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = "" } // Parameter defaults
                );

        }
    }

The regular expression d+ matches one or more integers. This constraint causes the Product route to match the following URLs:

  • http://talenttuner.com/Product/3
  • http://talenttuner.com/Product/8999

But not the following URLs:

  • http://talenttuner.com/Product/apple
  • http://talenttuner.com/Product