Section 4 of 6

Route Constraints

🎯 What You'll Learn

  • What route constraints are
  • Built-in constraints (int, guid, regex, etc.)
  • Multiple constraints
  • Custom constraints
  • When to use constraints
  • Best practices

What are Route Constraints?

Route constraints restrict parameter values to ensure only valid URLs match your routes.

Without Constraint C#
[HttpGet("{id}")]
public IActionResult GetProduct(int id) { }

// ✅ /api/products/123 → works
// ❌ /api/products/abc → 400 Bad Request (binding fails)
With Constraint C#
[HttpGet("{id:int}")]
public IActionResult GetProduct(int id) { }

// ✅ /api/products/123 → works
// ❌ /api/products/abc → 404 Not Found (route doesn't match)
💡 Key Difference

Without constraint: Route matches, binding fails → 400 Bad Request
With constraint: Route doesn't match → 404 Not Found

Built-in Constraints

Constraint Description Example
:int Integer {id:int}
:long Long integer {id:long}
:decimal Decimal number {price:decimal}
:double Double {value:double}
:float Float {value:float}
:bool Boolean {active:bool}
:guid GUID {id:guid}
:datetime DateTime {date:datetime}
:alpha Letters only {name:alpha}
:min(n) Minimum value {age:min(18)}
:max(n) Maximum value {age:max(100)}
:range(min,max) Value range {age:range(18,100)}
:length(n) Exact length {code:length(5)}
:minlength(n) Minimum length {name:minlength(3)}
:maxlength(n) Maximum length {name:maxlength(50)}
:regex(pattern) Regular expression {zip:regex(^\\d{{5}}$)}

Common Constraint Examples

Integer Constraint

Integer ID C#
[HttpGet("{id:int}")]
public IActionResult GetProduct(int id)
{
    return Ok($"Product {id}");
}

// ✅ /api/products/123
// ❌ /api/products/abc

GUID Constraint

GUID ID C#
[HttpGet("{id:guid}")]
public IActionResult GetOrder(Guid id)
{
    return Ok($"Order {id}");
}

// ✅ /api/orders/3fa85f64-5717-4562-b3fc-2c963f66afa6
// ❌ /api/orders/123

Range Constraint

Age Range C#
[HttpGet("age/{age:range(18,100)}")]
public IActionResult GetByAge(int age)
{
    return Ok($"Age: {age}");
}

// ✅ /api/users/age/25
// ❌ /api/users/age/15 (too young)
// ❌ /api/users/age/150 (too old)

Regex Constraint

Zip Code Pattern C#
[HttpGet("zip/{zipCode:regex(^\\d{{5}}$)}")]
public IActionResult GetByZip(string zipCode)
{
    return Ok($"Zip: {zipCode}");
}

// ✅ /api/locations/zip/12345
// ❌ /api/locations/zip/1234 (too short)
// ❌ /api/locations/zip/abcde (not digits)
⚠️ Regex Escaping

In regex constraints, use double curly braces {{}} to escape single braces.

Multiple Constraints

Combine multiple constraints with : separator.

Multiple Constraints C#
// Integer with minimum value
[HttpGet("{id:int:min(1)}")]
public IActionResult GetProduct(int id) { }

// ✅ /api/products/1
// ❌ /api/products/0
// ❌ /api/products/-1

// String with length constraints
[HttpGet("{code:alpha:length(5)}")]
public IActionResult GetByCode(string code) { }

// ✅ /api/products/ABCDE
// ❌ /api/products/ABC (too short)
// ❌ /api/products/ABC12 (contains digits)

Custom Constraints

Create custom constraints by implementing IRouteConstraint.

Custom Constraint C#
public class ProductSkuConstraint : IRouteConstraint
{
    public bool Match(
        HttpContext? httpContext,
        IRouter? route,
        string routeKey,
        RouteValueDictionary values,
        RouteDirection routeDirection)
    {
        if (!values.TryGetValue(routeKey, out var value) || value == null)
            return false;

        var sku = value.ToString();
        
        // SKU format: PRD-XXXXX (e.g., PRD-12345)
        return sku?.StartsWith("PRD-") == true && sku.Length == 9;
    }
}

Register Custom Constraint

Program.cs C#
builder.Services.AddRouting(options =>
{
    options.ConstraintMap.Add("productsku", typeof(ProductSkuConstraint));
});

Use Custom Constraint

Using Custom Constraint C#
[HttpGet("{sku:productsku}")]
public IActionResult GetBySku(string sku)
{
    return Ok($"Product SKU: {sku}");
}

// ✅ /api/products/PRD-12345
// ❌ /api/products/ABC-12345
// ❌ /api/products/PRD-123

Complete InvenTrack Example

Controllers/ProductsController.cs C#
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    // Integer constraint
    // GET /api/products/123
    [HttpGet("{id:int:min(1)}")]
    public IActionResult GetById(int id)
    {
        return Ok($"Product {id}");
    }

    // GUID constraint
    // GET /api/products/guid/3fa85f64-5717-4562-b3fc-2c963f66afa6
    [HttpGet("guid/{id:guid}")]
    public IActionResult GetByGuid(Guid id)
    {
        return Ok($"Product GUID: {id}");
    }

    // Range constraint
    // GET /api/products/price/50
    [HttpGet("price/{maxPrice:decimal:range(1,10000)}")]
    public IActionResult GetByMaxPrice(decimal maxPrice)
    {
        return Ok($"Products under ${maxPrice}");
    }

    // Alpha constraint
    // GET /api/products/category/electronics
    [HttpGet("category/{name:alpha:minlength(3)}")]
    public IActionResult GetByCategory(string name)
    {
        return Ok($"Category: {name}");
    }

    // Regex constraint for SKU
    // GET /api/products/sku/PRD12345
    [HttpGet("sku/{sku:regex(^PRD\\d{{5}}$)}")]
    public IActionResult GetBySku(string sku)
    {
        return Ok($"Product SKU: {sku}");
    }

    // Boolean constraint
    // GET /api/products/active/true
    [HttpGet("active/{isActive:bool}")]
    public IActionResult GetByActiveStatus(bool isActive)
    {
        return Ok($"Active: {isActive}");
    }
}

When to Use Constraints

  • Type safety: Ensure parameters are the correct type
  • Route disambiguation: Distinguish between similar routes
  • Validation: Enforce business rules (min/max values)
  • SEO-friendly URLs: Enforce URL patterns
  • API versioning: Match specific version patterns

Best Practices

  • Use constraints for type safety: {id:int} instead of {id}
  • Combine constraints: {id:int:min(1)} for positive integers
  • Regex sparingly: Complex regex can hurt performance
  • Custom constraints: For complex business rules
  • 404 vs 400: Constraints return 404 (route doesn't match)
  • Document constraints: Comment complex patterns

Key Takeaways

  • Route constraints: Restrict parameter values
  • Built-in constraints: int, guid, regex, range, etc.
  • Multiple constraints: Combine with :
  • Custom constraints: Implement IRouteConstraint
  • 404 vs 400: Constraints → 404, binding fails → 400
  • Type safety: Use :int, :guid, etc.
  • Validation: Use :min, :max, :range
  • Patterns: Use :regex for complex patterns
🎯 Next Steps

You now understand route constraints! In the next section, we'll explore URL Generation—how to generate URLs programmatically for links, redirects, and API responses.