Monday, 27 January 2014

Routing in ASP.NET MVC 3 Tutorial

Q) What is ASP.NET MVC Routing?

Answer: When request is received from the browser, it is routed to URL routing module object which transfers request to the MVCHttpHandler. The MVCHttpHandler is responsible to Map incoming request with controller [by adding suffix controller to the controller value], action and Parameter to action if any. Parameters to action are optional. ASP.NET URL pattern consists of literal and placeholders where placeholders are separated by '/' and defined by {placeholder name}. This process works like dictionary where keys are the placeholder names defined in the URL pattern, and the values are in the URL.
E.g. Let URL be www.myshop.com\products\show\cosmatics
Keys are {controller}/{action}/{id} and their values are products/show/cosmatics
To understand the MVC Application Execution Process, please refer to the following MSDN article.
Route table: Route table gets created in the global.asax. The Global.asax file, also known as the ASP.NET application file. Global.asax contains code for responding to application-level events raised by ASP.NET or byHttpModules. The route table is created during the Application Start event.
Consider the following URL:
We are targeting home controller and index action.This is defined by adding an entry in to the "routes" collection using the "maproute" function.
Let's have a look at the register route function in the Global.asax.


public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", 
                id = UrlParameter.Optional } // Parameter defaults
    );
} 

routes.MapRoute(string name,string url,object default) function takes 3 arguments:
  1. Route name
  2. URL with parameter
  3. Object of class containing default parameter values

Q) How to create Custom Route?

All the mapped routes are stored as RouteCollection in the Routes property of the RouteTable class. You can add multiple entries of the route in the route collection by defining unique route name for each key.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", 
                id = UrlParameter.Optional } // Parameter defaults
    );
    routes.MapRoute(
                "Home", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index1", 
                id = UrlParameter.Optional } // Parameter defaults
    );
} 

The sequence of the MapRoute is important. In our case, route name "Default" gets executed by default as it is before the route name "Home". If you look at the controller class, it contains the suffix name "Controller" to it, but you do not need to specify suffix controller to the URL as MVC routing engine will add suffix "Controller" to controller name by default.

Q) How to Add Constraint to the Route?

Constraints are the rule enforced on the parameter value of the URL. If the parameter values are not inline with the constraint for a route is not handled by the URL routing engine. You can add the constraint to the URL route based on your application requirement. Similar to the custom routes constraint can be added in the MapRoute function as argument. Constraints are defined by using regular expressions or by using objects that implement theIRouteConstraint interface.
Using regular expression: Consider URL http://localhost/home/index/id, constraint to id variable that it should accept number only. We can apply regular expression as argument to the MapRoute function overload that accepts constraint as the object. MapRoute(string name,string url,object default,object constraint), the application requirement is to add:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", 
id = "id" } // Parameter defaults
, new { id = @"\d+" } //Constraint
);

id = @"\d+" regular expression enforces rule to accept number value of id variable. Route that contains id value other than the number is result into HTTP 404 page not found error.
You can get more information about regular expression in the following MSDN article.
Using object that implements the IRouteConstraint, consider URL http://localhost/home/index/idconstraint to id variable that it should accept Date only. Let's create a class that implements the IRouteConstraint.IRouteConstraint in force to use method Match having the following signature, the application requirement is to add:

public class CustomContraint : IRouteConstraint
{ 
    #region IRouteConstraint Members
    public bool Match(HttpContextBase httpContext, Route route, 
    string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        throw new NotImplementedException();
    }
    #endregion
}
Let's add logic that validates parameterName id with date value only.
public class CustomContraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, 
    string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        DateTime dt;
        bool result = DateTime.TryParseExact(values["id"].ToString(),
        "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out dt);
        return result;
    }
} 

Let's create an object of Class that implements IRouteConstraint and pass to the MapRoute function overload that accepts the constraint object.

routes.MapRoute(
    "Home", // Route name
    "Home/Index1/{id}", // URL with parameters
    new { id = DateTime.Now }, // Parameter defaults
    new { isLocal = new CustomContraint() } //Constraint
);  

New CustomContraint() objects implement the IRouteConstraint interface.