Course outline

ASP.NET Core / Basic: HTTP and Application Composition / Lesson 3

Routing and Endpoint Selection

Keep route matching, HTTP method selection, binding, domain validation, and authorization as separate decisions.

About 5 min readContent reviewed
On this page

Lesson goals

  • Read route templates and constraint roles.
  • Distinguish 404,405, and validation rejection.
  • Treat route metadata as input to later middleware.

Matching a path does not validate a business entity

Endpoint routing selects candidates using route patterns, HTTP methods, and other metadata. Route values describe the matched path, not trusted database entities. A route such as /orders/{id:int} can constrain a syntactic candidate, but an integer might still be negative, absent from storage, or forbidden to the caller. Keep business and authorization checks explicit.

Templates, parameter names, defaults, optional parts, catch-alls, and constraints affect selection. Avoid ambiguous competing endpoints whose precedence is hard to explain. Do not use route constraints as a substitute for ordinary input validation when the API should return a clear client error for a matched operation.

Inspect the declared route structure

The first complete program parses a route pattern and inspects its parameter metadata. This invokes a framework parser but not a routing middleware pipeline. It confirms the parameter name and inline policy text, not that a real request is bound, authorized, or executed. The distinction prevents a parser test from becoming an exaggerated API test.

Use named route parameters consistently with handler/controller contracts. A changed parameter name can affect binding or link generation even when the visible URL shape looks similar. Keep route versions and generated links in compatibility tests rather than relying only on a browser’s happy-path request.

csharp Inspect a framework route pattern
using System;
using Microsoft.AspNetCore.Routing.Patterns;

var pattern = RoutePatternFactory.Parse("/orders/{id:int}");
var parameter = pattern.Parameters[0];
Console.WriteLine($"parameter={parameter.Name} policy={parameter.ParameterPolicies[0].Content}");
Expected output
parameter=id policy=int

The parser reports id with int policy text; it does not select or execute an endpoint.

No path, wrong method, and bad input differ

If no endpoint matches the path/policies, the pipeline can produce 404. When a path has method-constrained candidates but the method is unsupported, routing can produce 405 with the appropriate contract. A matched handler may instead return 400 for invalid domain input or 404 for a missing entity. Do not collapse these outcomes merely because all are unsuccessful requests.

The second example exercises an integer route constraint directly with synthetic values. It checks a component’s match result only. A full route-selection test needs the actual endpoint set and method metadata, including ambiguous and unsupported cases. Never claim the direct constraint call tests every routing outcome.

csharp Exercise one route constraint in memory
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Constraints;

var constraint = new IntRouteConstraint();
var context = new DefaultHttpContext();
var values = new RouteValueDictionary { ["id"] = "42" };
bool valid = constraint.Match(context, null, "id", values, RouteDirection.IncomingRequest);
values["id"] = "forty-two";
bool invalid = constraint.Match(context, null, "id", values, RouteDirection.IncomingRequest);
Console.WriteLine($"integer={valid} text={invalid}");
Expected output
integer=True text=False

The integer constraint accepts 42 and rejects the text token; no request reaches a host.

Routing can attach a selected Endpoint to HttpContext so later middleware can inspect authorization, CORS, rate, or other metadata. Such middleware must run at the appropriate point. Metadata declarations are contracts interpreted by registered components; a decorative attribute alone does not enforce every policy without its supporting setup.

Link generation has its own inputs, route names, and address scheme. Prefer framework-supported generation over hand-built URLs when that preserves escaping and route changes. Be careful with user-controlled host/scheme and forwarded headers; a generated absolute URL can reflect an untrusted boundary if deployment trust is wrong.

Review the complete endpoint surface

Document method, path, input source, validation, result/status types, authentication/authorization, and versioning for each operation. Keep broad catch-all or fallback endpoints from hiding missing mappings or unsupported methods. A single successful request does not show that unintended paths are rejected or that generated links remain correct.

Tests should include a valid path, wrong method, malformed route token, missing entity, unauthorized caller, encoded characters, and overlapping patterns where relevant. Component examples here are safe and deterministic, while host-level selection remains an explicit integration boundary requiring an isolated authorized setup.

Common errors and fixes

An int route constraint is treated as business validation.

Why it happens
Syntactic selection was confused with domain meaning.
How to fix it
Validate the matched value and entity separately.
Prevent it next time
Test negative and absent entity cases.

A fallback returns success for a missing mapping.

Why it happens
A broad endpoint masks the intended surface.
How to fix it
Define fallback/status behavior deliberately.
Prevent it next time
Test unmapped and wrong-method paths.

A route rename breaks handler input silently.

Why it happens
Parameter naming is part of binding/link contracts.
How to fix it
Keep names aligned and test generated links and inputs.
Prevent it next time
Review route changes as API changes.

Check your understanding

1. Why can /orders/-1 match an integer constraint yet still be invalid for an API?

Show worked solution

A signed integer is syntactically valid, while the domain may require a positive order identifier. A constraint used for candidate selection cannot prove existence or permission. Let the matched operation apply a documented validation rule and return the intended client error, then separately handle missing entities and resource authorization. Keep those categories visible in tests.

2. What is missing from a test that directly calls IntRouteConstraint.Match twice?

Show worked solution

It does not include the application’s endpoint set, HTTP method selection, routing middleware order, parameter binding, action execution, authentication/authorization, fallback behavior, or link generation. It is still a useful component test of integer matching. Report that narrow result and add integration cases for the public method/path/status contract rather than treating the small test as complete API validation.

Key takeaways

  • Route selection, binding, domain validation, and authorization are distinct.
  • Endpoint metadata requires supporting components and correct order.
  • Component match tests do not establish the entire HTTP surface.

Official references