Different types of routing in ASP.NET MVC

Sheonarayan
Posted by in ASP.NET MVC category on for Intermediate level | Points: 250 | Views : 13234 red flag
Rating: 5 out of 5  
 4 vote(s)

In this article, we are going to learn about different types of Routing in ASP.NET MVC. We will learn convention based routing. In the next article, we shall learn attribute based routing.
Recommendation
Read FileSystem API in HTML5 - Working with Files in the browser before this article.

Introduction


Routing is a mechanism to process the incoming url that is more descriptive and give desired response. In this case, URL is not mapped to specific files or folder as was the case of earlier days web sites.

There are two types of routing (after the introduction of ASP.NET MVC 5).

  1. Convention based routing - to define this type of routing, we call MapRoute method and set its unique name, url pattern and specify some default values.
  2. Attribute based routing - to define this type of routing, we specify the Route attribute in the action method of the controller.

Different types of Convention based Routing in ASP.NET MVC


Those who are already familiar with ASP.NET MVC, the routing is defined in App_Start/RouteConfig.cs page like this (It is convention based routing)

 routes.MapRoute(
     name: "Default",
     url: "{controller}/{action}/{id}",
     defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
     );
Where the unique name is defined for this type of url pattern and if no value is specified in the requested url for controller, action and id, the default value comes as Home, Index (id being an optional, it is not mandatory to specify in the url).

The sample url for the above route looks like this

  • http://localhost:63087/ - here controller and action is not passed in the url so it is set to default ie. Home and Index.
or
  • http://localhost:63087/home/outgoingurl - here controller is passed as "home" and action is "outgoingurl".

Using Static URL segments in Routing


In routing, we can also specify a static segments and remaining as dynamically passed variable.

routes.MapRoute(
   name: "MyCustomRoute",
   url: "CustomController/{action}/{id}",
   defaults: new { action = "Index", id = UrlParameter.Optional }
 );
In the above code snippet, the first segment of the url is hard coded, so a sample url can be

  • http://localhost:63087/CustomController/Index - where CustomController is the static segment and Index is the action method variable value passed in the url.
We can also amend the url parameter like "Custom/{controller}/{action}/{id}". In this case, the incoming url should look like 

  • http://localhost:63087/Custom/Home/Index - where Custom is static segment, Home is the controller and Index is the action method.
It can also be mixed like this "C/{controller}/{action}/{id}". In this case, the url should look like below
  • http://localhost:63087/CHome/Index - where C comes as static segment, Home comes from the controller variable and Index is the action variable value.

Defining variable length routing


This type of routing is used to accept any number of url arguments and popularly known as CatchAll scenario where any data after specific segments are caught.

 routes.MapRoute("CatchAll", "Route/{controller}/{action}/{id}/{*catchall}",
     new { controller = "RoutingStuffs", action = "CatchAll", id = UrlParameter.Optional });
In the above code, after id segments we have {*catchall} that catches all segments of data after id like below

  • http://localhost:63087/Route/RoutingStuffs/CatchAll/50/Delete/And/Other/Parameter - 
In this case Route is the static segment (not necessarily, catchall scenario can be used only in static segments url, it can be used in all convention based routing), controller is RoutingStuffs, action method is CatchAll, id is 50 and remaining url segments comes under catchall.

The CatchAll action method of the RoutingStuffs controller should be defined like this
public ActionResult CatchAll(string id = null, string catchall = null)
        {
            return View();
        }

Here, we get the catchall parameter value = "Delete/And/Other/Parameter".


Prioritizing controller by Namespaces in Routing


In case we have more than one controller with the same name (lets say "RoutingStuffsController" in different namespace) in our MVC Project, MVC Framework looks for all controller with the same name and doesn't know which one to execute, as a result it throws below error.

Multiple types were found that match the controller name RoutingStuffs. This can happen if the route that services this request ...... does not specify namespaces to search for a controler that match the request. If this is the ase, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.

To overcome this issue, as suggested in the above error, we can use the overload method of MapRoute like below.

// prioritizing controller by namespace
 routes.MapRoute("CatchAllPriorityHome", "Home/Route/{controller}/{action}/{id}/{*catchall}",
      new { controller = "RoutingStuffs", action = "CatchAll", id = UrlParameter.Optional },
      new[] { "MVCTraining5.Controllers" });

 routes.MapRoute("CatchAllPriority", "Route/{controller}/{action}/{id}/{*catchall}",
      new { controller = "RoutingStuffs", action = "CatchAll", id = UrlParameter.Optional },
      new[] { "MVCTraining5.Controllers.OtherFolder" });

In above code snippet, the first MapRoute method specify if the url starts with "Home/Route/....." then go for RoutingStuffs controller that is in the MVCTraining5.Controllers namespace, if not found then look for other namespaces.

The second MapRoute method specify that if the url starts with "/Route/........" then go for the RoutingStuffs controller that is in the MVCTraining5.Controllers.OtherFolder namespace.

Notice that the last parameter, ie. namespaces are given in array of strings.

Constraining Routes using Regular Expression or Hard coded values


We can also constrain the routes by specifying the Regular Expression for controller, action method etc.

 routes.MapRoute("ConstrainRouteR", "{controller}/{action}/{id}/{*catchall}",
    new { controller = "RoutingStuffs", action = "Index", id = UrlParameter.Optional },
    new { controller = "^R.*" });

Above route will be applicable to only those requests whose controller value is starting with "R".

routes.MapRoute("ConstrainRouteRA", "{controller}/{action}/{id}/{*catchall}",
   new { controller = "RoutingStuffs", action = "Index", id = UrlParameter.Optional },
   new { controller = "^R.*", action = "^Index$|^About$" });
Above route will be applicable to only those request whose controller starts with "R" or action method is either Index or About.

routes.MapRoute("ConstraintRouteHttp", "{controller}/{action}/{id}/{*catchall}",
  new { controller = "RoutingStuffs", action = "Index", id = UrlParameter.Optional },
  new { controller = "RoutingStuffs", action = "Index|About", 
  httpMethod = new HttpMethodConstraint("GET", "POST") });
Above route will be applicable to only those request whose controller name is RoutingStuffs, action is either Index or Above and request type is either "GET" or "POST".

routes.MapRoute("ConstraintRouteRange", "{controller}/{action}/{id}/{*catchall}",
  new { controller = "RoutingStuffs", action = "Index", id = UrlParameter.Optional },
  new
      {
        controller = "^H*",
        action = "Index|About",
        httpMethod = new HttpMethodConstraint("GET"),
        id = new RangeRouteConstraint(10, 20)
     });

We can even constrain the route using its value constraint. Like in above example, above route will be applicable to only those request whose controller name starts with "H", action name is either Index or About, request type is GET and value of id is in between 10 and 20.

Note, that RangeRouteContraint and other value constraints are supported only in ASP.NET MVC 5+.

Similar to RangeRouteContraint, there are few more value constraints 
  • BoolRouteConstraint - checks for value that can be parsed as boolean
  • DateTimeRouteConstraint - checks for value that can be parsed as date and time
  • AlphaRouteConstraint - checks for alphabets character either upper or lower case
  • IntRouteConstraint - checks for value that can be parsed as integer
  • MaxLengthRouteConstraint & MinLengthRouteConstraint - checks for maximum and minimum length of the characters

Custom constraint route in ASP.NET MVC


Apart from above constraint, we can also define custom constraint to suit our specific need. Think about a scenario, where a particular feature or style of your app doesn't work in Google chrome but works in all other browser like Internet Explorer and FireFox. In this case we can use custom constraint routing.

To define a custom constraint, we need to create a class file that looks something like 
using System.Web;
using System.Web.Routing;

namespace MVCTraining5.Utility
{
    public class BrowserDetectConstraint : IRouteConstraint
    {
        string _userAgent = string.Empty;

        public BrowserDetectConstraint(string userAgent)
        {
            _userAgent = userAgent;
        }

        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
        {
            return httpContext.Request.UserAgent != null
                && httpContext.Request.UserAgent.Contains(_userAgent);
        }
    }
}
When we inherit this class file with IRouteConstraint, we have to implement Match method. Here at the instantiation of the class we are passing the user agent to check, the same is being matched and returns true/false in the Match method.

The above custom constraint can be used like below

routes.MapRoute("ConstraintCustomChrome", "{controller}/{action}/{id}/{*catchall}",
  new { controller = "RoutingStuffs", action = "Index", id = UrlParameter.Optional },
  new
     {
       customConstraint = new Utility.BrowserDetectConstraint("Chrome")
     }, new[] { "MVCTraining5.Controllers.OtherFolder" });

routes.MapRoute("ConstraintCustom", "{controller}/{action}/{id}/{*catchall}",
  new { controller = "RoutingStuffs", action = "Index", id = UrlParameter.Optional }
  , new[] { "MVCTraining5.Controllers" });

The first MapRouote method of the above code use BrowserDetectConstraint by passing Chrome as user agent parameter. If the request comes with this user agent (ie from Google Chrome browser), RoutingStuffs controller from MVCTraining5.Controllers.OtherFolder is called otherwise second MapRoute executes and call the RoutingStuffs controller from MVCTraining5.Controllers namespace.

Conclusion


Routing - a nice feature of ASP.NET MVC Framework, if implemented correctly can avoid many mess in the application that could have otherwise be checked in the application logic. 

Hope this article was useful. Do write your feedback or comments about this article.

Thanks for reading. If you are looking for ASP.NET MVC training from me, enroll here.
Page copy protected against web site content infringement by Copyscape

About the Author

Sheonarayan
Full Name: Sheo Narayan
Member Level: HonoraryPlatinum
Member Status: Administrator
Member Since: 7/8/2008 6:32:14 PM
Country: India
Regards, Sheo Narayan http://www.dotnetfunda.com
http://www.snarayan.com
Ex-Microsoft MVP, Author, Writer, Mentor & architecting applications since year 2001. Connect me on http://www.facebook.com/sheo.narayan | https://twitter.com/sheonarayan | http://www.linkedin.com/in/sheonarayan

Login to vote for this post.

Comments or Responses

Posted by: Allemahesh on: 3/30/2016 | Points: 25
Excellent article. I was looking for this one. Thanks for posting this one.

Login to post response

Comment using Facebook(Author doesn't get notification)