- Home
- .NET tutorials
- ASP.NET Core route constraints: A quick guide for developers
ASP.NET Core route constraints: A quick guide for developers
Published: Monday 22 December 2025
When building APIs in ASP.NET Core, routing plays a crucial role in how HTTP requests are mapped to endpoints. Route constraints give you the ability to validate and shape route parameters directly within the URL pattern. This ensures that only values matching the expected format are passed into your endpoint.
In this tutorial, we'll walk through the route constraints available in ASP.NET Core Web APIs. We'll use minimal APIs for examples, but the same concepts also apply when working with controllers.
Integer, boolean, and GUID constraints
Route constraints can enforce parameter types such as int, bool, or Guid. For instance:
// Program.cs
app.MapGet("product/{id:int:required}/active/is-{active:bool}", (int id, bool active)
=> new { id, active });
app.MapGet("product/identifier/{id:guid}", (Guid id)
=> new { id });
Notice how multiple constraints can be added to a parameter by chaining them with colons (:). For example, {id:int:required} enforces that id must be a valid integer and is mandatory.
Other numeric and date types
ASP.NET Core also supports constraints for other common types such as DateTime, decimal, double, float, and long.
// Program.cs
app.MapGet("products/released/{dateReleased:datetime}", (DateTime dateReleased)
=> new { dateReleased });
app.MapGet("products/under-{price:decimal}", (decimal price)
=> new { price });
app.MapGet("products/over-{price:double}", (double price)
=> new { price });
app.MapGet("products/exactly-{price:float}", (float price)
=> new { price });
app.MapGet("products/from-{price:long}", (long price)
=> new { price });
This makes it easy to enforce correct data types at the routing level before they even hit your logic.
Alpha, minimum and maximum length
Constraints can also validate string parameters, ensuring they contain only alphabetic characters or fall within certain length boundaries.
// Program.cs
app.MapGet("product/{slug:alpha:minlength(4):maxlength(8)}", (string slug)
=> new { slug });
app.MapGet("product/{slug:alpha:length(8)}/features", (string slug)
=> new { slug });
app.MapGet("product/{slug:alpha:length(4,8)}/spec", (string slug)
=> new { slug });
The alpha constraint ensures that only alphabetic characters are used. The minlength constraint ensures that the parameter has a minimum number of characters with the maxlength constraint doing the opposite.
With the length constraint:
- if you provide one parameter in the constraint, it means that the route parameter must be the exact number of characters specified. E.g.
length(8)means that the route parameter must have 8 characters. - If you provide two parameters, it combines the
minlengthandmaxlengthtogether. E.g.length(8,16)means that the route parameter must have between 8 and 16 characters.
Range constraints
The range constraint allows you to enforce minimum and maximum values for integers.
// Program.cs
app.MapGet("product/code/{code:int:range(1,9999)}",
(int code, int page) =>
new { code, page });
app.MapGet("product/code/{code:int:min(1):max(9999)}/features",
(int code, int page) =>
new { code, page });
If you want to specify the minimum or maximum value of an integer in a route parameter, you can use either the min or max route constraint.
Regex constraints
For ultimate flexibility, you can use regular expressions to define route constraints.
// Program.cs
app.MapGet("product/sku/{sku:regex(^[A-Za-z]{{0,3}}\\d{{1}}$)}", (string sku) =>
new { sku });
In this example, the sku parameter must begin with up to three letters followed by a single digit.
File and non-file constraints
If your endpoint deals with files, you can use file and nonfile constraints to distinguish between directory-like paths and file names.
// Program.cs
app.MapGet("product/files/{directory:nonfile}/{filename:file}",
(string directory, string filename) =>
new { directory, filename });
With these route constraints:
filecan contain path segments, but its last segment must have a dot (.) and be followed by one or more non-dot characters.nonfilecan not have a dot in its last path segment.
Matching routes
When working with Minimal APIs, it's possible for multiple route templates to match the same incoming request. Consider the following two routes:
// Program.cs
app.MapGet("product/{id:regex(\\d{{5}})}", (int id) =>
new { response = "First route" });
app.MapGet("product/{id:int}", (int id) =>
new { response = "Second route" });
Both routes accept numeric values. The first uses a regex constraint to match exactly five digits, while the second uses the built-in int constraint, which also accepts five-digit numbers. Because both routes can handle product/12345, ASP.NET Core cannot determine which route to use.
When this happens, you’ll receive the following error:
AmbiguousMatchException: The request matched multiple endpoints
To resolve this, you can assign explicit execution order using .WithOrder(). Routes with a lower number have higher priority.
// Program.cs
app.MapGet("product/{id:regex(\\d{{5}})}", (int id) =>
new { response = "First route" })
.WithOrder(1);
app.MapGet("product/{id:int}", (int id) =>
new { response = "Second route" })
.WithOrder(2);
With ordering applied:
- A 5-digit ID will match the first route that uses the
regexconstraint - Any other integer ID will match the second route that uses the
intconstraint
Creating a custom route constraint
ASP.NET Core provides many built-in route constraints, but sometimes you need something more specific. For example, suppose you want a dedicated constraint for validating U.S. ZIP codes (exactly 5 digits).
You can create your own constraint by implementing IRouteConstraint.
// ZipCodeConstraint.cs
public class ZipCodeConstraint : IRouteConstraint
{
public bool Match(
HttpContext? httpContext,
IRouter? route,
string routeKey,
RouteValueDictionary values,
RouteDirection routeDirection)
{
if (!values.TryGetValue(routeKey, out var routeValue))
{
return false;
}
return new Regex("^[0-9]{5}$", RegexOptions.CultureInvariant)
.IsMatch(routeValue?.ToString() ?? string.Empty);
}
}
Registering the constraint
Add the custom constraint to the route system in Program.cs:
// Program.cs
builder.Services.Configure<RouteOptions>(routeOptions =>
{
routeOptions.ConstraintMap.Add("zipcode", typeof(ZipCodeConstraint));
});
Adding it using the zipcode key means we can use zipcode as the constraint.
Using the constraint in a Minimal API route
Once registered, use it just like any built-in constraint:
// Program.cs
app.MapGet("/zip-code/{zip:zipcode}", (string zip) =>
new { zip });
Now the route will only match if the {zip} parameter is exactly five digits.
Watch the video
When you watch the video, you can see each of these route constraints in action.
If a route constraint isn't matched
Route constraints in ASP.NET Core Web APIs provide a powerful way to validate inputs right in the URL pattern.
If a URL pattern can not be matched against a route, it will return a 404 Not Found status code to the response.
Using the correct route constraints helps reduce boilerplate validation code meaning a cleaner solution.
Related tutorials
Add API key authentication to an Minimal API endpoint
Learn how to add API key authentication to minimal API endpoints using a saved key in the config and apply a fallback policy to secure all routes.