The Route Constraints helps us to filter out or restrict the unwanted values from reaching the controller action. It does so by checking the Constraint against the value of the URL Parameter. For Example, you would want to Route engine to match a particular route only if int value is supplied for an URL parameter.
Table of Contents
How Route Constraints Work
There are two ways, by which you can add Constraint to a URL Parameter.
- Inline with the URL Parameter
- Using the Constraint argument of the MapRoute method.
The Inline Constraints are added to the URL Parameter after the colon : sign.
1 2 3 | routes.MapRoute("default", "{controller=Home}/{action=Index}/{id:int}"); |
Once the Routing Engine Finds a Match for the Incoming URL, It invokes the Route Constraint for each segment of the URL to see if it passes the check. The constraints make a simple yes/no decision about whether or not the value is acceptable.
Route Constraints
Open the Project, which we created in Routing tutorial. Go to the Configure method copy the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(routes => { routes.MapRoute("default", "{controller}/{action}/{id?)}", new { controller = "Home", action = "Index" }); }); } |
Now, Copy the following code to the Index method of the HomeController
1 2 3 4 5 6 7 8 9 | public class HomeController : Controller { public string Index(int id) { return "I got " + id.ToString(); } } |
A Request for the /Home/Index/10 will match the route. The 10 will be passed as a parameter to the Index Action and you will see “i got 10” in the browser.
A Request for the /Home/Index/Test will also match the route. The Router parses the “test” to value 0 and injects it as the parameter to index action and you will see “I got 0” in the browser.
You can stop this from happening by using a Route Constraint on the id URL parameter.
Inline Constraint
Inline Constraints are added after the URL Parameter and separated by a colon : sign. For example, you can ensure only integer values are allowed by using the int constraint.
1 2 3 | routes.MapRoute("default", "{controller=Home}/{action=Index}/{id:int?}"); |
The int constraint checks to see the value of the Id can be parsed to an integer value. The id segment is optional. Hence, the route will match if the Id is not present, but if the id is present, then it must be an integer value.
with int constraint in place, the Request /Home/Index/Test will not match the route.
Using Constraint method of the MapRoute
The Constraints can also be specified using the constraints argument to the MapRoute method.
To do that you need to import Microsoft.AspNetCore.Routing.Constraints namespace.
1 2 3 4 5 6 7 8 9 10 11 | using Microsoft.AspNetCore.Routing.Constraints; app.UseMvc(routes => { routes.MapRoute("default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index" }, new { id = new IntRouteConstraint() }); }); |
We create an instance of an Anonymous type, which contains properties, whose property name are same as the URL Parameters, on which constraints are applied. These Properties are assigned to the instance of the Constraint class.
In the above example, we have created an Anonymous class. It has id property, which matches to the id URL Parameter. The Instance of IntRouteConstraint is assigned to the id Property
Constraints in Attribute Routing
You can achieve the same using Attribute routing as follows
1 2 3 4 5 6 7 | [Route("Home/Index/{id:int}")] public string Index(int id) { return "I got " + id.ToString(); } |
Where to Use Constraints
The Constraints can be used for input validation, but that is not the reason they exist.
The Input validations must not be handled by the Route Constraints. Instead, the Controller should validate the Input and send the appropriate error message to the end user. If you use Route Constraints for Input Validations, the client will get a 404 (Not Found) error message.
The Route constraints should be used to help the Routing Engine to distinguish between two similar looking routes. For Example, consider the following Route
1 2 3 4 5 6 7 8 9 10 11 12 13 | app.UseMvc(routes => { routes.MapRoute("default", "post/{id:int}", new { controller = "Post", action = "PostsByID" }); routes.MapRoute("anotherRoute", "post/{id:alpha}", new { controller = "Post", action = "PostsByPostName" }); }); |
We have two similar looking Routes. post/{id:int} & post/{id:alpha}. With the int & alpha (Accepts only alphabets) constraints in place, we are able to tell the Routing Engine to choose PostsByID Action method for integers and PostByPostName for string values.
List of Route Constraints
The Microsoft.AspNetCore.Routing.Constraints namespace defines a set of classes that can be used to define individual constraints.
Constraints for Checking Data Type
The following Constrains checks the data type.
constraint | Inline | Class | Notes |
---|---|---|---|
int | {id:int} | IntRouteConstraint | Constrains a route parameter to represent only 32-bit integer values |
alpha | {id:alpha} | AlphaRouteConstraint | Constrains a route parameter to contain only lowercase or uppercase letters A through Z in the English alphabet. |
bool | {id:bool} | BoolRouteConstraint | Constrains a route parameter to represent only Boolean values. |
datetime | {id:datetime} | DateTimeRouteConstraint | Constrains a route parameter to represent only DateTime values. |
decimal | {id:decimal} | DecimalRouteConstraint | Constrains a route parameter to represent only decimal values. |
double | {id:double} | DoubleRouteConstraint | Constrains a route parameter to represent only 64-bit floating-point values |
float | {id:float} | FloatRouteConstraint | Matches a valid float value (in the invariant culture - see warning) |
guid | {id:guid} | GuidRouteConstraint | Matches a valid Guid value |
Constraints for Checking Data Value/Range/Length
The following Constrains checks the Value/Range/Length etc.
constraint | Inline | Class | Notes |
---|---|---|---|
length(length) | {id:length(12)} | LengthRouteConstraint | Constrains a route parameter to be a string of a given length or within a given range of lengths. |
maxlength(value) | {id:maxlength(8)} | MaxLengthRouteConstraint | Constrains a route parameter to be a string with a maximum length. |
minlength(value) | {id:minlength(4)} | MinLengthRouteConstraint | Constrains a route parameter to be a string with a maximum length. |
range(min,max) | {id:range(18,120)} | RangeRouteConstraint | Constraints a route parameter to be an integer within a given range of values. |
min(value) | {id:min(18)} | MinRouteConstraint | Constrains a route parameter to be a long with a minimum value. |
max(value) | {id:max(120)} | MaxRouteConstraint | Constrains a route parameter to be an integer with a maximum value. |
Constraints using a Regular Expression
Using Regular expression or regex as constraint offers more flexibility to limit any input.
constraint | Inline | Class | Notes |
---|---|---|---|
regex(expression) | {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} | RegexRouteConstraint | Constrains a route parameter to match a regular expression. |
Example of Regular Express Route Constraint
In the following example, we using regular expression to restrict the Year value to 4 digits
1 2 3 4 5 6 7 8 | app.UseMvc(routes => { routes.MapRoute("default", "{controller}/{action}/{year:regex(^\\d{{4}}$)}", new { controller = "Home", action = "Index" }); }); |
Update the Index method of the HomeController.cs
1 2 3 4 5 6 7 8 9 | public class HomeController : Controller { public string Index(int year) { return "Year = " + year.ToString(); } } |
A request for /Home/Index/2017 will match the above route, but request for /Home/Index/20 will fail
Note that Regular expression tokens must be escaped. For example, \,{,},[,] characters need to be escaped by doubling them to escape the Routing parameter delimiter characters.
Hence, ^\d{4}$ becomes ^\\d{{4}}$ in the above example.
Combing Constraints
Multiple Constraints can be combined using a colon : separator as shown below
1 2 3 | "/{id:alpha:minlength(6)?}" |
or using the Constraints method of the MapRoute.
1 2 3 4 5 6 7 8 9 10 | Using Microsoft.AspNetCore.Routing.CompositeRouteConstraint; constraints: new { id = new CompositeRouteConstraint( new IRouteConstraint[] { new AlphaRouteConstraint(), new MinLengthRouteConstraint(6) }) |
Summary
The Route Constraints are pretty useful options to disambiguate between similar routes. They help us to restrict the unwanted values from reaching the controller action.
Can we create multiple MapRoutes with same route name?? When using multiple MapRoutes with same route name means throws ‘An item with the same key has already been added. Key: default’ exception. Kindly check.
The Route names can not be the same. Update the Code. Thanks for noticing
Thank you guys!!
Hi, i am learning ASP.NETCORE. In this article, under Inline Constraint – “routes.MapRoute(“default”, “{controller=Home}/{action=Index}/{id:int ?}”);”. Here optional parameter contains space. So it throws an exception. Kindly remove that space.
Thanks. Updated the code