joydip_kanjilal
Contributor

How to create route constraints in ASP.NET Core

how-to
Oct 05, 20208 mins
C#Microsoft .NETSoftware Development

You can take advantage of custom route constraints in ASP.NET Core to validate route values and avoid passing unwanted or unnecessary requests to action methods.

detour sign
Credit: Thinkstock

Route constraints in ASP.NET Core are used to filter out or restrict unwanted data from reaching your controller actions. For a primer on routing in ASP.NET Core, you can refer to my previous article on attribute-based routing versus convention-based routing in ASP.NET Core. This article goes beyond the basics to explore the advanced operations using route constraints.

To work with the code examples provided in this article, you should have Visual Studio 2019 installed in your system. If you don’t already have a copy, you can download Visual Studio 2019 here

Create an ASP.NET Core MVC project in Visual Studio 2019

First off, let’s create an ASP.Net Core project in Visual Studio 2019. Assuming Visual Studio 2019 is installed in your system, follow the steps outlined below to create a new ASP.Net Core project in Visual Studio.

  1. Launch the Visual Studio IDE.
  2. Click on “Create new project.”
  3. In the “Create new project” window, select “ASP.NET Core Web Application” from the list of templates displayed.
  4. Click Next.
  5. In the “Configure your new project” window, specify the name and location for the new project.
  6. Optionally check the “Place solution and project in the same directory” check box, depending on your preferences.
  7. Click Create.
  8. In the “Create a New ASP.NET Core Web Application” window shown next, select .NET Core as the runtime and ASP.NET Core 3.1 (or later) from the drop-down list at the top.
  9. Select “Web Application (Model-View-Controller)” as the project template to create a new ASP.NET Core MVC application. 
  10. Ensure that the check boxes “Enable Docker Support” and “Configure for HTTPS” are unchecked as we won’t be using those features here.
  11. Ensure that Authentication is set to “No Authentication” as we won’t be using authentication either.
  12. Click Create.

Following these steps will create a new ASP.NET Core MVC project in Visual Studio 2019. We’ll use this project in the sections below to illustrate how we can use route constraints in ASP.NET Core 3.1.

The RouteCollection class in ASP.NET Core

The RouteTable class in ASP.NET Core contains a property named Routes which stores all routes as RouteCollection. The RouteCollection class contains some extension methods that can be used to map routes or ignore them.

MapRoute is an overloaded method that accepts constraints as a parameter. You can use this to pass your constraint to the route. The following is the declaration of the MapRoute method.

public static Route MapRoute(this RouteCollection routes, string name,
    string url, object defaults, object constraints);

The IRouteConstraint interface in ASP.NET Core

The IRouteConstraint interface is a contract that contains the declaration of only one method named Match. This interface must be extended by a class and the Match method implemented in it to check if a particular URL parameter is valid for a constraint. Here is how the IRouteConstraint interface is defined:

namespace Microsoft.AspNetCore.Routing
{
    public interface IRouteConstraint
    {
        bool Match(
            HttpContext httpContext,
            IRouter route,
            string routeKey,
            RouteValueDictionary values,
            RouteDirection routeDirection);
    }
}

The ConstraintMap dictionary in ASP.NET Core

A ConstraintMap is a dictionary that contains a list of route constraints that maps the route constraint keys to the IRouteConstraint implementations. The code snippet given below illustrates how you can add your custom constraints to this dictionary.

public void ConfigureServices(IServiceCollection services) 
{  
  services.Configure<RouteOptions>(routeOptions =>  
  { 
     routeOptions.ConstraintMap.Add("emailconstraint", typeof(EmailRouteContraint)); 
  }); 
} 

Implement the IRouteConstraint Match method in ASP.NET Core

To create a custom route constraint, you should create a class that extends the IRouteConstraint interface and implements its Match method. The constraint can be used to thwart unwanted incoming requests and prevent a route from being matched unless a particular condition is satisfied. For example, you might want to ensure that the parameter passed to an action method is always an integer.

The Match method accepts the following parameters:

  • HttpContext – encapsulates all HTTP specific information about a request
  • IRouter – represents the router that will apply the constraints
  • RouteKey – represents the route parameter that is being validated
  • RouteDirection – an enum that contains two values, namely IncomingRequest and UrlGeneration, and is used to indicate whether the URL is being processed from the HTTP request or generating a URL
  • RouteValues – contains the URL parameters

Structure of a custom route constraint in ASP.NET Core

Here is an example of the structure of a custom route constraint:

public class CustomRouteConstraint : IRouteConstraint
    {
        public bool Match(HttpContext httpContext, IRouter route,
        string routeKey, RouteValueDictionary values, 
        RouteDirection routeDirection)
        {
            throw new NotImplementedException();
        }
    }

Custom route constraint example in ASP.NET Core

Let us now implement a custom route constraint that can check for email Ids. First off, create a class that extends the IRouteConstraint interface and implements the Match method. The following code snippet shows a custom route constraint class named EmailRouteContraint that extends the IRouteConstraint interface.

public class EmailRouteContraint : IRouteConstraint
    {
        public bool Match(HttpContext httpContext, IRouter route,
        string routeKey, RouteValueDictionary values,
        RouteDirection routeDirection)
        {
            return true;
        }
    }

The following code listing shows the EmailRouteConstraint class with the Match method implemented.

public class EmailRouteContraint : IRouteConstraint
    {
        public bool Match(HttpContext httpContext, IRouter route,
        string routeKey, RouteValueDictionary values,
        RouteDirection routeDirection)
        {
            if (values.TryGetValue(routeKey, out var routeValue))
            {
                var parameterValueString = Convert.ToString(routeValue,
                CultureInfo.InvariantCulture);
                return IsEmailAddressValid(parameterValueString);
            }
            return false;
        }
        private bool IsEmailAddressValid(string emailAddress)
        {
            return true;
        }
    }

Note the IsEmailAddressValid method here simply returns “true.” I leave it up to you to write the necessary code for validating the email address.

Register a custom route constraint in ASP.NET Core

You should register your custom route constraint with the routing system in the ConfigureServices method of the Startup class. The following code snippet illustrates this. 

public void ConfigureServices(IServiceCollection services)
      {
          services.AddControllersWithViews();
          services.Configure<RouteOptions>(routeOptions =>
          {
              routeOptions.ConstraintMap.Add("ERC",
              typeof(EmailRouteContraint));
          });
      }

You should also configure your custom route constraint in the Configure method of the Startup class as shown in the code snippet below.

app.UseEndpoints(endpoints =>
{
     endpoints.MapControllerRoute(
         name: "default",
         constraints: new { ERC = new EmailRouteContraint() },
         pattern: "{controller=Home}/{action=Index}/{id?}");
});

And that’s it. You can now specify the constraint in your controller or your action methods and start using the application.

The ASP.NET Core runtime validates if the defined pattern and route constraints match the pattern and values of the incoming request. The validation logic of the constraint is defined inside the Match method of your custom route constraint. You can take advantage of constraints to avoid unnecessary requests as well as to validate route values before the request is passed to an action method.

How to do more in ASP.NET Core:

joydip_kanjilal
Contributor

Joydip Kanjilal is a Microsoft Most Valuable Professional (MVP) in ASP.NET, as well as a speaker and the author of several books and articles. He received the prestigious MVP award for 2007, 2008, 2009, 2010, 2011, and 2012.

He has more than 20 years of experience in IT, with more than 16 years in Microsoft .Net and related technologies. He has been selected as MSDN Featured Developer of the Fortnight (MSDN) and as Community Credit Winner several times.

He is the author of eight books and more than 500 articles. Many of his articles have been featured at Microsoft’s Official Site on ASP.Net.

He was a speaker at the Spark IT 2010 event and at the Dr. Dobb’s Conference 2014 in Bangalore. He has also worked as a judge for the Jolt Awards at Dr. Dobb's Journal. He is a regular speaker at the SSWUG Virtual Conference, which is held twice each year.

More from this author