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.
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).
- 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. - 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.