Introduction
Routing is the first and foremost phenomenon in the ASP.NET MVC pipeline. Here you will learn about routing, its working and its variants.
Background
Last week one of my friends asked this question: "Can we make our custom route with some constraints and what is attribute routing?" I am dedicating this article to him. I hope he will like this.
The topics to be covered are:
- Routing and its working
- Parts of a Route
- Default Route and Custom Route
- What is the purpose of
IgnoreRoute()
method? - How to apply constraints to Custom Route?
- What is Attribute Routing?
- Default and optional parameter
- Default action and RoutePrefix
- Name and Order property
- Attribute Routing including Constraints
Routing:
Routing is the 1st step in MVC pipeline and this is the replacement of the concrete, physical files used in the URLs. In other words, routing is the phenomenon in which controller and actions execute rather than the concrete physical files.
Why we need routing?
Traditionally, the URL of the browser represents the physical file. File can be HTML file, ASPX page etc. Let’s understand this traditional way:
www.abcd.com/images/john.aspx?id=2
This URL shows that first go to “images” folder then access the “john.aspx” file having id is 2. Hence it is not the good way to expose your precious information to the URLs of the browser, so with these URLs site can be hacked. As a result, Routing comes into action and solves this problem of traditional file based system. Implementation of routing starts with route table. Route table is a collection of all possible, correct routes that can be used to map the HTTP request URLs. Let’s understand RouteTable
and the working of Routing in detail.
Routing is a system in which URL patterns are matched with the URL patterns defined in the RouteTable
by using MapRoute
method. The process of Routing comes into action when the application starts, firstly it registered the routes in the RouteTable
. This registration of routes in the RouteTable
is essential because it is the only thing that tells the Routing Engine how to treat the requested URL requests. The routes are registered in the RouteConfig
class App_Start
file of the application. Let's have a look at it:
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 Global.asax
file looks like:
protected void Application_Start()
{
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
The Figure is illustrating the working of routes and depicts that how the routing engine processes URL request and give response to the browser.
Routing Engine is the module whose responsibility is to treat the HTTP request. When HTTP request comes, the UrlRoutingModule
starts matching the perfect URL pattern from the RouteTable
, when find successfully, then Routing Engine forwards the request to RouteHandler
. In RouteHandler
its interface IRouteHandler
comes into action and called its GetHttpHandler
method. This method look like as below:
public interface IRouteHandler
{
IHttpHandler GetHttpHandler(RequestContext requestContext);
}
When all the above process completed successfully then the ProcessRequest()
method is invoked, as shown in figure above otherwise if the requested URL doesn’t match with any pattern in the RoutTable
then the user will redirect to HTTP 404 error page.
Parts of a Route:
When you are going to register the routes you have to use overloaded version of MapRoute()
method in the RouteConfig
class. There are 6 overloaded versions of MapRoute()
method, the last method having all parameters is explained below:
public static class RouteCollectionExtensions
{
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces);
}
Above MapRoute()
method have following parameters:
- Name of the Route
- URL pattern of the Route
- Defaults of the Route
- Constraints on the Route
- Namespaces to the Route
Let’s understand each!
Name of Route:
First of all I want to say, there are very ambiguous ideas in the community about Name of the Route. So I highly recommend to read this section carefully and please leave your comment about this because I have learnt it myself. So please correct me with reason if I wrong.
The route that is registered in the RouteTable
must have a unique name to differentiate from other routes. This name refers to a specific URL pattern in the RouteTable
. Most important thing is that this name is only use for URL generation. Hence I concluded that Routing does not happen on the basis of Name, it happens on the basis of its URL pattern. In fact, URL pattern tells the UrlRoutingModule
what to do with the request, not the name of route. Then the question comes why it should be unique, it should because it is the thing that create uniqueness for the URL generation. Let’s understand it practically.
- Make a controller having two action methods.
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Student()
{
return View();
}
}
- Create routes for understanding Route Name.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "SecondRoute",
url: "Home/Student",
defaults: new { controller = "Home", action = "Student" }
);
routes.MapRoute(
name: "FirstRoute",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
- Now we have to create the Views.
Student.cshtml
@{
ViewBag.Title = "Second Route";
}
<h2>Second Route</h2>
This is our 2nd Route.
Index.cshtml
@{
ViewBag.Title = "Second Route";
}
<h2>First Route</h2>
<a href="@Url.RouteUrl("SecondRoute")">This URL will direct you to Second Route.</a>
- Now when we execute the application then default generic route will come into action as shown in figure:
- When we click on URL then it will redirect us to View of Second Route, as shown in figure:
As you can see, we pass the name of route “Second Route” in the anchor tag and when we click it, it will redirect us to that route whose name is given in anchor tag.
So conclusion is, route names are used for URL generation. Now question comes Why Route Name should be unique? So, If the route name is not unique then how UrlRoutingModule
will know about the Second Route and it will throw a runtime error as shown below:
URL Pattern of the Route: URL pattern of the route consists of literal values and variable placeholders. These variables are also called URL parameters. All these literals and placeholders are separated by forward slash (/), and called Segments.
Literal means a “fixed” value and variable placeholder means “replacement of some value”. In a URL pattern you can define a placeholder with the help of curly braces { }. You can define more than one placeholders in a segment with the help of literal. For examples, see figure!
Hence, if you want to add more than one placeholders in a segment then you have to use a literal between those placeholders.
Defaults of Route:
When you define a route, you can assign defaults values for each segment of the URL, these default values come into action when no parameter is provided in the URL. You can set defaults values for the route by making the anonymous object of the RouteValueDictionary
class.
Constraints to Route:
Simply constraints are limitations on Route parameters. When you set any constraint to a route then if the URL parameter does not fulfill the constraint requirements then that route will not work. And then request goes to route that has defaults parameters. You add constraints to route to ensure that it will work according to your application requirements. You will see its more detail in this topic.
Namespaces in the Route:
You can set your own namespaces for the web application. Namespaces can be added into routes by making object of RouteValueDictionary
class. This parameter used when you want to redirect your URL to a specific controller having specific namespace.
Default Route and Custom Route:
Default Route:
The default route in MVC is a generic route which saves in RouteTable
with the name “defaults”. Point of interest is the default route has only three segments which are delimited by forward slash:
{controller}/{action}/{id}
Figure illustrates that a route is going to be registered in the RouteCollection
(routes.RouteTable
) having a unique name "Defaults", a URL pattern in which controller, action, id all are placeholders, then there is a defaults property whose responsibility is to initialize the controller and action automatically if it doesn’t include in the requested URL.
Custom Route:
Let’s create a custom route to understand Routing. As we have HomeContoller
and the Index
action method. Let’s create another action method named as Student
:
public class HomeController : Controller
{
public string Index()
{
return "This is Index action method";
}
public string Student(int rollno)
{
return $"Roll Number is {rollno}";
}
}
Now we have to create a custom route in RoutConfig
class:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "FirstRoute",
url: "Home/{action}/{rollno}",
defaults: new { controller = "Home", action = "Student", rollno = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Before going to execute the application I want to tell you very crucial thing about Routing, that is, “Always define your routes from Specific to General”. In above code default route is most general so it should be in the end of the RouteTable. And our most specific route is 1st route that we define ago. Routing flow is as follows:
Now when you execute your application and write the URLs as given below then the output is as follows:
Table illustrates clear big picture of URL patterns and its output. Error in 2nd URL is because of absence of parameter value in URL. On the other hand when you pass Parameter value in 3rd URL then that route comes into action and show the output. This type of data passing in the URL is called Query String method.
Query String has question mark sign (?) in its URL. But this way of passing data is not a good way because through this method your data can be exposed to the hackers or penetrators. Better than query string, way of passing data is such as:
https://Server/Home/Student/12345
What is the purpose IgnoreRoute()
method?
In the above code of RouteConfig
class you can see the IgnoreRoute()
method as shown below:
As you know, the URL pattern {controller}/{action}/{id} is handled by UrlRoutingModule
by default. But point of interest is, if you really want that RoutingModule
handles the above mentioned pattern then you have to set the RouteExistingFiles
property of the RouteCollection
to true.
public class RouteCollection : Collection<RouteBase>
{
public bool RouteExistingFiles { get; set; }
}
You can see in above code, the datatype of RouteExistingFiles
property is bool
so it can be true or false. When it is true, then the RoutingModule
will handle all the requests that matches with the defined URL pattern in RouteConfig
class. Otherwise when this property is set to false, UrlRoutingModule
will never handle any request.
Now, the problem is, when you have set RouteExistingFiles
property to true, routing module handle all the requests, including your web resource files. So when you want to stop the access of some routes to the browser, then how can you do that?
This can be done by using IgnoreRoute()
method. IgnoreRoute
method will stop accessing the routes defined in IgnoreRoute
parameters. Method looks like:
public static void IgnoreRoute(string url);
This will ignores the specified URL route from the list of available routes in RouteTable
.
Now in RouteConfig
class, what dose the following code means?
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
The above code of IgnoreRoute()
shows that resource.axd
files are ban for the access. Last point is, these .axd
files are not present in our project, and these are reserved for HTTP handling.
How to apply constraints to Custom Route?
Constraints are limitations on Route parameters. When you set any constraint to a route then if the URL consists of parameter values that doen't fulfill the constraint requirements then that route will not work.
Let’s understand it in a practical way, suppose you want to limit the roll number of the student to only 5 digits, then how can you do that?
This can be done with the help of constraints. Let’s play with it.
routes.MapRoute(
name: "FirstRoute",
url: "Home/{action}/{rollno}",
defaults: new { controller = "Home", action = "Student", rollno = UrlParameter.Optional },
constraints: new { rollno = @"\d{5}" }
);
As shown above, roll number digits must be 5. When we execute the following URL:
https://Home/Student/12345
Output: Roll Number is 12345
@”\d{5}” has @ sign as prefix which is called verbatim literal. Its opposite and bad way is using Escape sequences. Escape sequences and their use is defined in figure below:
Let’s know about the difference between both:
With Escape Sequence: “C:\\Computer\\Lectures\\ASP.NET”
With Verbatim Literal: @“C:\Computer\Lectures\ASP.NET”
As we can see verbatim literal gives us great readability. Hence in this way you can make your own constraints on your custom routes.
What is Attribute Routing?
Attribute routing is the new type of routing in ASP.NET MVC 5. According to the name Attribute Routing, one can suppose that this type of methodology will use Attribute to define routes. Yes! Let’s understand it in details.
Why we need Attribute Routing?
First of all, look our previous custom route:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "FirstRoute",
url: "Home/{action}/{rollno}",
defaults: new { controller = "Home", action = "Student", rollno = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
We need attrubute routing because:
- In the above code there is only two routes defined with the help of
MapRoute
. But when our application is very large enough to have more than two routes, then it will become very complex to make each route separately. For each route, you have to write 5 lines so it will consume your time and disturb your development time. - You have to take care of the URL pattern in
RouteConfig
class as well as in Controller
where your request will be handled. So this to and fro movement in between these folders create complexity for you. - It will reduces the chances of errors, because in
RouteConfig
class there can be any mistake while creating a new custom route. - It is easy to use and help in mapping the same action method with two routes.
- You don’t have to take care about the routing flow i.e, from most specific to most general. All here is the attribute you use above the action method.
Now let’s move to use Attribute Routing.
First of all, you have to enable the access of attribute routing. So RouteConfig
class becomes:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
}
}
Now let’s go into controller and take a look at its working. A [Route]
attribute is used at the top of the action method, as shown below:
public class HomeController : Controller
{
[Route("Home/Student/{rollno}")]
public string Student(int rollno)
{
return $"Name is {rollno}";
}
}
Output is:
How to make optional parameter and set the default value of a parameter in Attribute Routing?
If you want to make a parameter optional in attribute routing then simply place a question mark at the end or parameter such like below:
public class HomeController : Controller
{
[Route("Home/Student/{rollno ?}")]
public string Student(int rollno)
{
return $"Name is {rollno}";
}
}
And if you want to set the default value of any parameter then you have to use this pattern:
ParameterName = Value, as shown below:
public class HomeController : Controller
{
[Route("Home/Student/{rollno = 23}")]
public string Student(int rollno)
{
return $"Name is {rollno}";
}
}
What is RoutePrefix?
You may see that many routes have same portion from its start, means their prefixes are same. For example:
Home/Student
Home/Teacher
Both above URLs have the same prefix which is Home.
So rather than repeatedly typing the same prefix again and again, we use RoutePrefix
attribute. This attribute will set at controller level. As shown below:
[RoutePrefix("Home")]
public class HomeController : Controller
{
[Route("Student/{rollno= 23}")]
public string Student(int rollno)
{
return $"Roll Number {rollno}";
}
[Route("Teacher")]
public string Teacher()
{
return "Teacher’s method";
}
}
In the above code, RoutePrefix
is set to controller level and on action methods we don’t have to use Home prefix again and again. Output is as follows:
RoutePrefix
has hold on whole controller and all the action methods are bound to use that. But when you want to execute an action that shouldn’t use the prefix as prefix of controller, then what will you do that?
So when you want to override the route prefix then use ~ sign, as shown below:
[RoutePrefix("Home")]
public class HomeController : Controller
{
[Route("Student/{rollno= 23}")]
public string Student(int rollno)
{
return $"Roll Number is {rollno}";
}
[Route("~/Home2/Teacher")]
public string Teacher()
{
return "Teacher’s method";
}
}
As you had seen in Convention-based Routing, we can set the default action method, now the question comes,
Can we set the default action method in attribute routing?
Yes! You can do it by using [Route]
attribute at the top of controller. Look at the code below:
[RoutePrefix("Home")
[Route("{action = Teacher}")]
public class HomeController : Controller
{
public string Teacher()
{
return "Teacher’s method";
}
}
The 2nd Route attribute at the top of controller sets the default value for an action method.
As you had seen in the routing section, we can set Name for the route. So,
Can we set the Name in attribute routing for specific URL?
Yes! Here also the name is only for URL generation. We can set name as:
Syntax:
[Route("URLPath", Name = "RouteName")]
[Route("Home/Student", Name = "StudentRoute")]
public ActionResult Student()
{
return View();
}
Above code shows that how can we set name of route in attribute routing. After reading the section of Route Names in convention-based routing, you can repeat the procedure of URL generation in this Route attribute.
As you had seen in routing section, we should create our routes from more specific to more general flow. Then how can we do that in attribute routing?
Can we set preference of routes in attribute routing?
Yes! You have to use Order
parameter in route attribute. Suppose we have a controller having two action methods named as First and Second. And they have Order parameter set to 2 and 1 respectively.
After giving order to action methods, the action method will execute according to FCFS (First Come First Serve) and its value can be from -2,147,483,648 to 2,147,483,647 (size of int).
public class HomeController : Controller
{
[Route(Order = 2)]
public string First()
{
return "This is First action method having order 2";
}
[Route(Order = 1)]
public string Second()
{
return "This is Second action method having order 1";
}
}
When we run this code, our Second action method will execute because it has higher order.
But if no order is specified in the Route attribute then action methods will execute according to the order in which the routes are registered in the RouteTable
.
As you had seen in the routing section, we can set constraints to the convention-based routing.
Can we set the constraints for attribute routing? If yes then how?
Yes! You can set the constraints in attribute routing. The syntax is as follows:
[Route(URLPath/{parameterName: constraint})]
First of all, take a look at different constraints that can be used in attribute routing.
Let’s look at its practical example.
public class HomeController : Controller
{
[Route("Home/Student/{rollno:maxlength(2)}")]
public string Student(int rollno)
{
return $"Roll Number's length is {rollno}";
}
}
When we pass URL as:
http://localhost:50656/Home/Student/12
Output looks like:
When we pass roll number’s length 3 as follows:
http://localhost:50656/Home/Student/123
Output looks like:
We can also use multiple constraints together:
[Route("Home/Student/{rollno:maxlength(2):range(9,10)}")]
public string Student(int rollno)
{
return $"Roll Number's length is {rollno}";
}
Hence these are limitations to our routes using attribute routing.
Conclusion
These are foremost things about Routing and Attribute Routing and I hope this article has helped you in understanding all the concepts. If you have any query then feel free to contact me in comments. Also give feedback either positive or negative, it will help me to make my articles best and increase my enthusiasm to share my knowledge.