Custom Route Constraint in ASP.NET Core MVC 

How to Create Custom Route Constraints in ASP.NET Core MVC

In this article, I will discuss How to Create Custom Route Constraints in ASP.NET Core MVC Web Applications with Examples. Please read our previous article discussing Custom Routing in the ASP.NET Core MVC Web Application. We will work with the same example we created in our previous article.

Custom Route Constraints in ASP.NET Core MVC Web Application:

Custom Route Constraints in ASP.NET Core MVC allow us to enforce specific rules on the routes at the routing level, which can help to ensure that URLs match expected formats, values, and conditions. These constraints can be used to validate parameters in the URL before the request even reaches the controller action.

Custom route constraints allow us to implement complex route validation logic (such as being an integer, having a specific range of values, or matching a regular expression) for the route parameters that cannot be expressed with standard constraints like int, guid, minlength, etc.

How do you Create a Custom Route Constraint in ASP.NET Core?

To create a Custom Route Constraint in ASP.NET Core MVC, we must create a class that implements the IRouteConstraint interface. This interface has a single method called Match, and we need to implement this Match method to define our custom constraint logic. This method contains the logic to check whether a particular URL parameter meets a specific requirement and returns a boolean indicating whether the constraint is met. If you go to the definition of the IRouteConstraint interface, you will see the following.

How to Custom Route Constraints in ASP.NET Core MVC Web Applications with Examples

This method takes the following five parameters.

  • HttpContext? httpContext: This parameter provides the context of the current HTTP request. It includes all information about the request, such as headers, cookies, and the request path.
  • IRouter? route: The router that this constraint belongs to, i.e., the URL.
  • string routeKey: This is the name of the route parameter to which you apply the constraint. For example, if your route is defined as {controller}/{action}/{id:int}, and you have a constraint on the id parameter, the routeKey would be “id”.
  • RouteValueDictionary values: This dictionary contains all the route values for the current request. This is where you retrieve the value of the parameter that you need to validate. You can access it using the routeKey.
  • RouteDirection routeDirection: This parameter indicates whether the routing is happening for an incoming request or a URL generation. The values can be RouteDirection.IncomingRequest or RouteDirection.UrlGeneration. This is important because you might want to apply the constraint differently depending on whether the URL is being generated or processed in response to an incoming request.
Example to Understand Custom Route Constraint in ASP.NET Core MVC

We will create a Custom Route Constraint to validate the Alpha Numeric String. That means if the incoming Route Data Contains both Alphabet and Numeric, it will be considered a valid value; if it contains only alphabet or only numeric, it will be considered an invalid value. For this, we don’t have any built-in constraint in ASP.NET Core, and to achieve this in ASP.NET Core MVC Application, we need to create a Custom Route Constraint.

So, create a class file named AlphaNumericConstraint.cs within the Models folder and copy and paste the following code. As you can see, the class implements the IRouteConstraint interface and provides the implementation for the Match method. As part of the Match method, we implemented one alphanumeric regex to check the incoming request parameter and determine whether the route parameter contains Alphabets (A-Z, a-z) and numeric (0-9). If it contains both alphabet and numeric, it returns true; otherwise, it returns false.

using System.Text.RegularExpressions;
namespace RoutingInASPDotNetCoreMVC.Models
{
    public class AlphaNumericConstraint : IRouteConstraint
    {
        public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
        {
            //validate input params  
            if (httpContext == null)
                throw new ArgumentNullException(nameof(httpContext));

            if (route == null)
                throw new ArgumentNullException(nameof(route));

            if (routeKey == null)
                throw new ArgumentNullException(nameof(routeKey));

            if (values == null)
                throw new ArgumentNullException(nameof(values));

            if (values.TryGetValue(routeKey, out object? routeValue))
            {
                var parameterValueString = Convert.ToString(routeValue);
                if (Regex.IsMatch(parameterValueString ?? string.Empty, "^(?=.*[a-zA-Z])(?=.*[0-9])[A-Za-z0-9]+$"))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            return false;
        }
    }
}
Register the Custom Route Constraint:

Before using the Custom Route Constraint, we need to register it with the Routing System. This is typically done in the Program.cs file. We need to register the Custom Route Constraint within the ConstraintMap dictionary. The ConstraintMap is a dictionary that contains the list of built-in route constraints. So, we have to add our Custom Route Constraint to this ConstraintMap dictionary with the help of route options. We can register the Custom Route Constraint service to the built-in dependency Injection container in two ways. They are as follows:

Method 1: Configuring the Custom Route Constraint Service using the AddRouting Method
builder.Services.AddRouting(options =>
{
      options.ConstraintMap.Add("alphanumeric", typeof(AlphaNumericConstraint));
});
Method 2: Configuring the Custom Route Constraint Service using the Configure Method
builder.Services.Configure<RouteOptions>(routeOptions =>
{
      routeOptions.ConstraintMap.Add("alphanumeric", typeof(AlphaNumericConstraint));
});
Use the Custom Route Constraint in Route Configuration:

Once you register the Custom Route Constraint, then you can use the Custom Route Constraint in the Route Template. You have to use the “:” separator between the route parameter and constraints. For example, you can use the alphanumeric (whatever name you provided while registering the service) route constraint as follows:

app.MapControllerRoute(
      name: "CustomRoute",
      pattern: "{controller}/{action}/{id:alphanumeric?}",
      defaults: new { controller = "Home", action = "Index" }
);

In the above code, we have specified the id parameter with Custom Route Constraint alphanumeric and made this parameter optional.

using RoutingInASPDotNetCoreMVC.Models;

namespace RoutingInASPDotNetCoreMVC
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddControllersWithViews();

            //Configuring the Custom Route Constraint Service using AddRouting Method
            builder.Services.AddRouting(options =>
            {
                options.ConstraintMap.Add("alphanumeric", typeof(AlphaNumericConstraint));
            });

            //Configuring the Custom Route Constraint Service using Configure Method
            //builder.Services.Configure<RouteOptions>(routeOptions =>
            //{
            //    routeOptions.ConstraintMap.Add("alphanumeric", typeof(AlphaNumericConstraint));
            //});

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.MapControllerRoute(
                name: "CustomRoute",
                pattern: "{controller}/{action}/{id:alphanumeric?}",
                defaults: new { controller = "Home", action = "Index"}
            );

            app.Run();
        }
    }
}

Note: You can use the Custom Route Constraint in both Convention-Based and Attribute-Based Routing in ASP.NET Core Application.

Next, modify the Student Controller as follows. Now, you can access the Index and Details action method without passing any route values. However, if you want to pass route values for the Details action method, the value should be alphanumeric; otherwise, it will give a 404 Resource Not Found Error.

using Microsoft.AspNetCore.Mvc;
namespace RoutingInASPDotNetCoreMVC.Controllers
{
    public class StudentController : Controller
    {
        public string Index()
        {
            return $"Index() Action Method of StudentController";
        }
        public string Details(string id)
        {
            return $"Details({id}) Action Method of StudentController";
        }
    }
}

In the next article, I will discuss Attribute Routing in ASP.NET Core MVC Applications. In this article, I explain How to Create Custom Route Constraints in ASP.NET Core MVC Web Application with Examples. I hope you enjoy this Custom Route Constraint in ASP.NET Core MVC Web Application article.

Leave a Reply

Your email address will not be published. Required fields are marked *