Controller-Based APIs in ASP.NET Core
A controller-based Web API centers on classes deriving from ControllerBase, decorated with the [ApiController] attribute. ControllerBase supplies shared plumbing, Request, Response, User, ModelState, Url, and the family of result helper methods, while [ApiController] activates a bundle of conventions in one line: automatic HTTP 400 responses when ModelState is invalid, inference of binding sources, and a requirement that the controller use attribute routing rather than conventional routing.
Cricket analogy: A controller class annotated with [ApiController] is like a national cricket board such as the BCCI setting standard playing conditions for every match: once applied, ASP.NET Core automatically enforces conventions like model validation and 400 responses, the way the BCCI enforces DRS rules across every IPL fixture without each ground negotiating separately.
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductService _products;
public ProductsController(IProductService products) => _products = products;
[HttpGet("{id:int}")]
public async Task<ActionResult<Product>> GetById(int id)
{
var product = await _products.GetByIdAsync(id);
if (product is null) return NotFound();
return Ok(product);
}
[HttpPost]
public async Task<ActionResult<Product>> Create(CreateProductDto dto)
{
var product = await _products.CreateAsync(dto);
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
}Attribute Routing
Attribute routing places the route template directly on the controller and action via attributes like [Route("api/[controller]")], [HttpGet], and [HttpPost("{id:int}")], rather than centralizing route patterns in Program.cs. Route tokens such as [controller] and [action] are automatically substituted with the controller's name (minus the 'Controller' suffix) and the action method's name, keeping URL templates consistent as controllers are added without hardcoded strings.
Cricket analogy: Route templates like [Route("api/[controller]/{id:int}")] are like a fielding chart that assigns Virat Kohli to cover point for every over: the [controller] token auto-substitutes 'Products' just as the chart auto-assigns the fielder without re-specifying it each over.
Attribute routes are matched using a specificity-based algorithm, not simply top-to-bottom declaration order; a route with more literal segments and constraints generally wins over a more general one. You can still influence order explicitly with the Order property on [Route] when two templates could otherwise match the same request.
Route Constraints
Route constraints narrow which values a segment will accept before an action is ever invoked. {id:int} only matches an integer segment, {slug:alpha} only matches letters, and custom constraints can be built by implementing IRouteConstraint for domain-specific rules, such as validating a checksum. A request whose segment fails a constraint simply doesn't match that route, typically resulting in a 404 if no other route matches instead.
Cricket analogy: A route constraint like {id:int} is like a boundary rope umpire who only signals a six if the ball actually clears the rope: the framework only matches the route if the segment parses as an integer, rejecting 'abc' the way an umpire rejects a decision outside the rules.
Route Grouping and MapGroup
MapGroup lets you register a set of related routes under a shared prefix and apply shared configuration, such as an authorization policy or endpoint filters, to all of them at once, rather than repeating that configuration on every individual endpoint. This works for both minimal API routes and controller actions mapped through MapControllers, and is particularly useful for grouping a resource's CRUD endpoints together.
Cricket analogy: Grouping routes under a common prefix with MapGroup("api/players") is like organizing all of a team's players under one squad list for a series: every route inherits the group's prefix and filters, the way every player inherits the squad's kit sponsor and training schedule.
Mixing conventional routing (app.MapControllerRoute in Program.cs) with attribute routing on the same controller can produce ambiguous route matches that throw an AmbiguousMatchException at request time rather than at startup. Pick one strategy per controller and stay consistent to avoid runtime surprises.
- [ApiController] combined with ControllerBase enables automatic model validation, binding source inference, and consistent 400 responses.
- Attribute routing uses [Route], [HttpGet], [HttpPost], and similar attributes, with [controller] and [action] tokens auto-filled from class and method names.
- Route constraints like {id:int} and {slug:alpha} reject non-matching URL segments before the action method is invoked.
- MapGroup lets you apply a shared prefix, filters, and authorization policies to a set of related routes at once.
- Mixing conventional and attribute routing on the same controller risks ambiguous route matches at runtime.
- Route matching is based on specificity, not just declaration order, though an explicit Order property can override that.
Practice what you learned
1. What does the [ApiController] attribute enable when applied to a controller class?
2. In [Route("api/[controller]")] applied to ProductsController, what does [controller] resolve to?
3. What happens if a request to /api/Products/abc hits a route defined as [HttpGet("{id:int}")]?
4. What is the risk of mixing conventional routing and attribute routing on the same controller?
5. What does MapGroup primarily let you do with a set of related routes?
Was this page helpful?
You May Also Like
Minimal APIs
A lightweight, low-ceremony way to build HTTP APIs in ASP.NET Core using top-level route handlers instead of controller classes.
Model Binding and Validation
How ASP.NET Core maps incoming request data onto action parameters and validates it before your handler code runs.
Action Results and Status Codes
How ASP.NET Core action results map to HTTP status codes, and how to choose the right response for every outcome.
API Versioning
Strategies and tooling for evolving an ASP.NET Core API's contract without breaking existing consumers.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesMFC (Microsoft Foundation Classes) Study Notes
C++ · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics