Routing Constraint – By Narendra Shrestha
Introduction
There is no doubt regarding the
default functionality supplied by MVC. It is single handedly capable of
leveraging the whole project. Moreover, we can add our own customizing feature
to enhance the default one, or implement our own. Due to these features, it has
gathered such an enormous fame in such a short span. Even though we are not
bound to implement these customizing features, at least for the sake of
understanding the real deal of MVC, it is worthy to explore these aspects.
The MVC Routing System is
responsible for validating the incoming URL, inferring the name of the
controllers and action based on the registered routes, and sending those
requests down to the Request Pipeline to further perform the action designated
in action methods of controllers. In case for non-existent action and
controller, the unpopular error page is displayed. Remember that routing system
is unaware whether the controller, action or other parameters are existent or
not. Its job is to only provide the first matching URL and throttle request
down the pipe line. When I refer it as the First, it means the first, not the
best!!!
For your total recall, the routes
are registered in the Global.asax.cs file of the MVC web application’s root
folder. One of the common ways to register a Route is specified by the code
listed as below:-
routes.MapRoute(
"",
"{Controller}/{action}",
new { controller = "Home",
action = "Index" }
);
Listing
1:-Registering Routes
Default Constraints
We can add constrictions to our route
in several different ways. For example as in case below by Regular Expression:-
routes.MapRoute(
"",
"{controller}/{action}/{id}",
new { controller = "Home",
action = "Index", id = UrlParameter.Optional },
new { Controller = "^H.*",action="^Index$|^About$" }
);
Listing
2:-Default Constraint by Regular Expression
The above
specified route causes the routing system to only accept those URL whose
controller name begins with H. Also, it limits the possible actions down to “Index”
& “About”. These kinds of constriction come into play when there is any
repair/maintenance work going on. We strictly confide the periphery of the
user. The method prevents users from
accessing other actions in controllers whose name begins with H (including “Home”
controller).
Customizing Constraints
These are
default constrictions. The flexibility of the MVC is only realized when we can
apply our own self defined constrictions. In order to customize the
constrictions, we need to create a customizing class which implements IRouteCosntraint Interface. This interface defines a Method named Match
whose signature is:-
bool Match(HttpContextBase
httpContext, Route route, string parameterName,
RouteValueDictionary
values, RouteDirection routeDirection)
Inside the Match method, we then implement
rest of our own logic.
We get access to various part of the
application through the supplied parameters.
HttpContextBase httpContext
|
Provide
access to the various ASP.NET functionality such as user request, response,
sessions, services etc.
|
Route route
|
Provide
access to the current Route in which we have specified our customized
constrictions
|
string parameterName
|
The
value of the constructor Parameter for the class that Implements IRouteCosntraint
interface.
|
RouteValueDictionary
values
|
Provides
access to the various segment of the URL such as controller, action, area and
others.
|
RouteDirection routeDirection
|
Specify
whether we are dealing with incoming URL or outgoing URL
|
Example
To
demonstrate the use of IRouteCosntraint interface, let us assume that we display
separate views for different languages based on the user browser language settings.
We have added “Nepali” language as one of the language preferences in the
Chrome (or any browser).
Add new
controller to our project called “Home” controller. Change the listing of the
Home controller as shown below
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Web;
using
System.Web.Mvc;
namespace
RouteConstraint.Controllers
{
public class HomeController
: Controller
{
//
// GET:
/Home/
public ActionResult Index()
{
return
View();
}
public ActionResult Nepali()
{
return
View();
}
}
}
Listing
3:-Home Controller
For an
action Index, add a View “Index” and change the listing as following
@{
ViewBag.Title = "Index";
}
<h2>Default</h2>
Listing
4:-Index View Razor file
Similarly,
for an action Nepali, add a View “Nepali” and change the listing as following
@{
ViewBag.Title = "Nepali";
}
<h2>नेपाली</h2>
Listing
5:-Nepali View Razor file
Now, that
we have everything in right place, it’s time to bring out IRouteCosntraint from our
arsenal.
To do so,
right click the MVC project in the solution explorer, and add a new folder
called “infrastructure”. It’s not a hard and fast rule to give the same name to
your folder. Since, all of our interfaces and customizing codes sit in this
folder, it’s an appropriate name. Add a
class MyConstraint.cs inside this folder that implements the
interface IRouteCosntraint. The
listing of this class is as shown below.
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
namespace
RouteConstraint.Infrastructure
{
public class MyConstraint
: IRouteConstraint
{
#region IRouteConstraint Members
public bool Match(HttpContextBase
httpContext, Route route, string parameterName,
RouteValueDictionary
values, RouteDirection routeDirection)
{
if
(httpContext.Request.UserLanguages.Contains("ne;q=0.8"))
{
return
true;
}
return
false;
}
#endregion
}
}
Listing
6:-Custom Constraint
Inside the
“Match” method, we have tried to access the browser languages by “httpContext.Request.UserLanguages” which is
of type “string[]”. Hence, whatever language preferences that are added in the
browser will fill up this array. We check if it contains “Nepali” and act
accordingly.
If we try
to run our project at this point, we will not get the desired outcome. There is
still one glitch left. We have not yet instructed “MVC” that we are going to
make use of our custom constraint MyConstraint.cs.
We have to
make changes in “RegisterRoutes()” method in “Global.asax.cs”. Hence, we
change the listing of this method as follows
public static void
RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("",
"{controller}/{action}", new { controller = "Home",
action = "Nepali"
}, new { contraint = new
MyConstraint() });
routes.MapRoute(
"Default",
// Route name
"{controller}/{action}/{id}",
// URL with parameters
new
{ controller = "Home", action = "Index", id = UrlParameter.Optional
}
// Parameter defaults
);
}
Listing
7:-Registering route
Here in
portion
routes.MapRoute("", "{controller}/{action}",
new { controller = "Home",
action = "Nepali"
}, new { contraint = new
MyConstraint() });
We haven’t
provided the name for the route. Also, we accept the URL of form
“{controller}/{action}”. By the help of anonymous type,
new { controller = "Home", action = "Nepali" }
we set the
default controller to be “Home” and default action to be “Nepali”.
For the
last anonymous type,
new { contraint = new MyConstraint()
}
we have
told the routing engine that we prefer the custom routing constraint of type “IRouteConstraint” i.e. “MyConstraint”. Thus, our
constraint is validated according to the “Match” method inside the “MyConstraint”.
If method “Match”
in custom routing code returns false, routing engine falls down to route named
“default”. Otherwise, the routing engine happily puts this particular unmanned
route in request pipeline.
Run the
project and see its effect in the all possible browsers (especially chrome in
our case).
First we
run the project without adding “Nepali” language in the browser setting. We can
see the effect as in the figure below.
Figure
1:-Without adding Nepali language in browser language setting
Now let us add “Nepali” in the browser
language setting. Thus, we can see the result as below.
Figure
2:-With Nepali language added in browser language setting
We
have demonstrated the use of customizing routing constraint. In our example we
have used customizing routing constraint to display the user an appropriate language
based on their browser language settings. However, you shouldn’t rely on the
routing behavior for this purpose. Instead you must use the globalization and
the localization feature. The routes may vary along with changes in the requirements as the project proceeds. So, relying on this behavior may later result in several hours of
debugging and headache. We have used this example as only mean of explaining
the customizing routing constraint and nothing more.
No comments:
Post a Comment