Friday 4 October 2013

In this tutorial, Stephen Walther demonstrates how you can control how browser requests match routes by creating route constraints with regular expressions. You use route constraints to restrict the browser requests that match a particular route. You can use a regular expression to specify a route constraint. For example, imagine that you have defined the route in Listing 1 in your Global.asax file. Listing 1 - Global.asax.vb routes.MapRoute( _ "Product", _ "Product/{productId}", _ New With {.controller = "Product", .action = "Details"} _ ) Listing 1 contains a route named Product. You can use the Product route to map browser requests to the ProductController contained in Listing 2. Listing 2 - Controllers\ProductController.vb Public Class ProductController Inherits System.Web.Mvc.Controller Function Details(ByVal productId As Integer) As ActionResult Return View() End Function End Class Notice that the Details() action exposed by the Product controller accepts a single parameter named productId. This parameter is an integer parameter. The route defined in Listing 1 will match any of the following URLs: /Product/23 /Product/7 Unfortunately, the route will also match the following URLs: /Product/blah /Product/apple Because the Details() action expects an integer parameter, making a request that contains something other than an integer value will cause an error. For example, if you type the URL /Product/apple into your browser then you will get the error page in Figure 1. The New Project dialog box Figure 01: Seeing a page explode (Click to view full-size image) What you really want to do is only match URLs that contain a proper integer productId. You can use a constraint when defining a route to restrict the URLs that match the route. The modified Product route in Listing 3 contains a regular expression constraint that only matches integers. Listing 3 - Global.asax.vb routes.MapRoute( _ "Product", _ "Product/{productId}", _ New With {.controller = "Product", .action = "Details"}, _ New With {.productId = "\d+"} _ ) The regular expression \d+ matches one or more integers. This constraint causes the Product route to match the following URLs: /Product/3 /Product/8999 But not the following URLs: /Product/apple /Product These browser requests will be handled by another route or, if there are no matching routes, a The resource could not be found error will be returned.


Stephen Walther demonstrates how you can create a custom route constraint. We implement a simple custom constraint that prevents a route from being matched when a browser request is made from a remote computer.
The goal of this tutorial is to demonstrate how you can create a custom route constraint. A custom route constraint enables you to prevent a route from being matched unless some custom condition is matched.
In this tutorial, we create a Localhost route constraint. The Localhost route constraint only matches requests made from the local computer. Remote requests from across the Internet are not matched.
You implement a custom route constraint by implementing the IRouteConstraint interface. This is an extremely simple interface which describes a single method:
Function Match ( _ httpContext As HttpContextBase, _ route As Route, _ parameterName As String, _ values As RouteValueDictionary, _ routeDirection As RouteDirection _ ) As Boolean
The method returns a Boolean value. If you return False, the route associated with the constraint won’t match the browser request.
The Localhost constraint is contained in Listing 1.
Listing 1 - LocalhostConstraint.vb
Public Class LocalhostConstraint Implements IRouteConstraint Public Function Match( _ ByVal httpContext As HttpContextBase, _ ByVal route As Route, _ ByVal parameterName As String, _ ByVal values As RouteValueDictionary, _ ByVal routeDirection As RouteDirection _ ) As Boolean Implements IRouteConstraint.Match Return httpContext.Request.IsLocal End Function End Class
The constraint in Listing 1 takes advantage of the IsLocal property exposed by the HttpRequest class. This property returns true when the IP address of the request is either 127.0.0.1 or when the IP of the request is the same as the server’s IP address.
You use a custom constraint within a route defined in the Global.asax file. The Global.asax file in Listing 2 uses the Localhost constraint to prevent anyone from requesting an Admin page unless they make the request from the local server. For example, a request for /Admin/DeleteAll will fail when made from a remote server.
Listing 2 - Global.asax
Public Class MvcApplication Inherits System.Web.HttpApplication Shared Sub RegisterRoutes(ByVal routes As RouteCollection) routes.IgnoreRoute("{resource}.axd/{*pathInfo}") routes.MapRoute( _ "Admin", _ "Admin/{action}", _ New With {.controller = "Admin"}, _ New With {.isLocal = New LocalhostConstraint()} _ ) 'routes.MapRoute( _ ' "Default", _ ' "{controller}/{action}/{id}", _ ' New With {.controller = "Home", .action = "Index", .id = ""} _ ') End Sub Sub Application_Start() RegisterRoutes(RouteTable.Routes) End Sub End Class
The Localhost constraint is used in the definition of the Admin route. This route won’t be matched by a remote browser request. Realize, however, that other routes defined in Global.asax might match the same request. It is important to understand that a constraint prevents a particular route from matching a request and not all routes defined in the Global.asax file.
Notice that the Default route has been commented out from the Global.asax file in Listing 2. If you include the Default route, then the Default route would match requests for the Admin controller. In that case, remote users could still invoke actions of the Admin controller even though their requests wouldn’t match the Admin route.

0 comments:

Post a Comment