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 minlength and maxlength together. 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:

  • file can contain path segments, but its last segment must have a dot (.) and be followed by one or more non-dot characters.
  • nonfile can 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 regex constraint
  • Any other integer ID will match the second route that uses the int constraint

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.