Creating Custom Routing Constraint In ASP.NET Core MVC

Introduction

In this article, we'll learn how to create custom routing constraint in ASP.NET Core MVC. The routing plays a vital role in MVC framework. It takes care of how a request is handled and mapped to the route handler. This is configured in the start up class of configure method. Routing constraint is responsible for matching the constraint rules with incoming request. It returns yes/no (true /false ) response depending on the incoming request. If the request is matched, then it serves with the appropriate response/content; else it returns 404 error.

Constraint Usages 

  1. It helps to avoid the unnecessary requests on url where we are restricted to route value pattern. Let's say - in action parameter, only integer values are allowed; so except integers, all the requests are rejected by the route constraint and a 404 (Page not found ) response is returned to users.
  2. To validate the route values : - routing constraint validates the incoming requests before passing them to Controller action.

IRouteConstraint interface

It defines the contract that a class must implement in order to check whether a URL parameter value is valid for a constraint or not.

This interface contains only single API in order to match the constrains.  

  1. //https://github.com/aspnet/Routing/blob/dev/src/Microsoft.AspNetCore.Routing.Abstractions/IRouteConstraint.cs
  2. namespace Microsoft.AspNetCore.Routing  
  3. {  
  4.     /// <summary>  
  5.     /// Defines the contract that a class must implement in order to check whether a URL parameter  
  6.     /// value is valid for a constraint.  
  7.     /// </summary>  
  8.     public interface IRouteConstraint  
  9.     {  
  10.         /// <summary>  
  11.         /// Determines whether the URL parameter contains a valid value for this constraint.  
  12.         /// </summary>  
  13.         /// <param name="httpContext">An object that encapsulates information about the HTTP request.</param>  
  14.         /// <param name="route">The router that this constraint belongs to.</param>  
  15.         /// <param name="routeKey">The name of the parameter that is being checked.</param>  
  16.         /// <param name="values">A dictionary that contains the parameters for the URL.</param>  
  17.         /// <param name="routeDirection">  
  18.         /// An object that indicates whether the constraint check is being performed  
  19.         /// when an incoming request is being handled or when a URL is being generated.  
  20.         /// </param>  
  21.         /// <returns><c>true</c> if the URL parameter contains a valid value; otherwise, <c>false</c>.</returns>  
  22.         bool Match(  
  23.             HttpContext httpContext,  
  24.             IRouter route,  
  25.             string routeKey,  
  26.             RouteValueDictionary values,  
  27.             RouteDirection routeDirection);  
  28.     }  
  29. }  

The Match method has 5 parameters . Let's discuss each, in details.

  1. HttpContext encapsulates all http specific information about an http request like request, response, sessions, and more. You can check the httpcontext as well if the request is authenticated or not, and take the appropriate action accordingly.

  2. IRouter is the router which belongs to constraints.

  3. RouteKey is the name of the parameter that is being checked. The same name is defined in route template.

  4. Routevalues contain the parameters of the URL.

  5. RouteDirection indicates whether ASP.NET routing is processing a URL from an HTTP request or generating a URL. This is an enum class that has two directions.

    1. IncomingRequest
      A URL from a client is being processed.

    2. UrlGeneration
      A URL is being created based on the route definition.

ConstraintMap

It is a key value pair dictionary which contains the list of route constraints. The routing framework adds the list of default constraints with their types. You can learn more about default constraints here on github.

https://github.com/aspnet/Routing/blob/dev/src/Microsoft.AspNetCore.Routing/RouteOptions.cs#L41-L71

You have to add custom route constraint in this dictionary with the help of route options.

Let's take an example of creating alphanumeric route constraints. This example is just for demonstration purposes; there are many ideas to use the constraints. You can use the regex constraints in case of any pattern matching.

Here, I'm going to create an alphanumeric constraint which checks if the incoming request parameter contains alphanumeric value or not.

Alphanumeric route constraint is implemented from the IRouteConstraint interface.

  1. public class AlphaNumericConstraint : IRouteConstraint  
  2.     {  
  3.         private static readonly TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(10);  
  4.   
  5.         public bool Match(HttpContext httpContext,   
  6.             IRouter route,   
  7.             string routeKey,   
  8.             RouteValueDictionary values,   
  9.             RouteDirection routeDirection)  
  10.         {  
  11.             //validate input params  
  12.             if (httpContext == null)  
  13.                 throw new ArgumentNullException(nameof(httpContext));  
  14.   
  15.             if (route == null)  
  16.                 throw new ArgumentNullException(nameof(route));  
  17.   
  18.             if (routeKey == null)  
  19.                 throw new ArgumentNullException(nameof(routeKey));  
  20.   
  21.             if (values == null)  
  22.                 throw new ArgumentNullException(nameof(values));  
  23.   
  24.             object routeValue;  
  25.   
  26.             if(values.TryGetValue(routeKey, out routeValue))  
  27.             {  
  28.                 var parameterValueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture);  
  29.                 return new Regex(@"^[a-zA-Z0-9]*$",   
  30.                                 RegexOptions.CultureInvariant   
  31.                                 | RegexOptions.IgnoreCase, RegexMatchTimeout).IsMatch(parameterValueString);  
  32.             }  
  33.   
  34.             return false;  
  35.         }  
  36.     }  

I have alphanumeric regex to check the incoming request parameter that contains the a-z or 0-9.  

Add your constraint into ConstraintMap using route option in configure services method in the startup class. Here, you have to add route key with the type which is created above.

  1. public void ConfigureServices(IServiceCollection services)  
  2.         {  
  3.             // Add framework services.  
  4.             services.AddMvc();  
  5.   
  6.             // add here your route constraint   
  7.             services.Configure<RouteOptions>(routeOptions =>   
  8.             {  
  9.                 routeOptions.ConstraintMap.Add("alphanumeric"typeof(AlphaNumericConstraint));  
  10.             });  
  11.         }   

Add your constraint in route template here. You have to use ":" seperator in between route parameter and constraints. The format looks like  {parameter name: route constraint name }.

  1. app.UseMvc(routes =>  
  2.             {  
  3.                 routes.MapRoute(  
  4.                     name: "default",  
  5.                     template: "{controller=Home}/{action=Index}/{id:alphanumeric}");  
  6.             });   

Summary 

In this article, you have learned how to create custom route constraints. I've demonstrated an example for the same to help you understand it more clearly.