Controllers, Routing & Action Methods in ASP.NET Core
Controllers are the backbone of any ASP.NET Core API. They receive HTTP requests, process input, communicate with services and databases, and send structured responses back to clients. Routing determines which controller action gets executed, while action methods define how each endpoint behaves.
In this chapter, we take a deep dive into building controllers, configuring routing, creating action methods, handling parameters, returning responses, and following REST best practices.
1. What is a Controller?
A controller is a C# class that handles HTTP requests. In ASP.NET Core, APIs typically use ApiController classes that inherit from ControllerBase. Controllers map incoming HTTP requests to action methods, which then return responses in JSON, XML, or other formats.
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult GetAll()
{
return Ok(new[] { "Laptop", "Mobile", "Tablet" });
}
}
Key Attributes Explained:
- [ApiController] → Enables automatic validation, binding, and routing conventions
- [Route("api/[controller]")] → Maps URL to controller name
- [HttpGet] → Maps GET HTTP method to action
2. How Routing Works in ASP.NET Core
Routing decides which controller method should handle an incoming request. ASP.NET Core uses attribute routing, which means you define your routes directly above controllers and action methods.
2.1 Attribute Routing
Attribute routing means defining routes on controller classes and action methods using attributes like [Route], [HttpGet], etc. It gives developers full control over URL structure.
[Route("api/products")]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult Get() { ... }
[HttpGet("{id}")]
public IActionResult GetById(int id) { ... }
}
2.2 Route Parameters
Route parameters extract values directly from the URL. They allow you to pass dynamic information (like product ID or category name) into action methods.
[HttpGet("{id}")]
public IActionResult GetProduct(int id) { ... }
[HttpGet("category/{categoryName}")]
public IActionResult GetByCategory(string categoryName) { ... }
Optional Parameters:
Optional parameters allow routes to work with or without a parameter.
[HttpGet("{id?}")]
public IActionResult Get(int? id) { ... }
Constraint Example:
Route constraints validate the parameter format directly in the route.
[HttpGet("{id:int:min(1)}")]
public IActionResult Get(int id) { ... }
3. Action Methods
Action methods are the functions inside controllers that execute when a route matches the request. They return results using IActionResult or ActionResult<T>.
[HttpPost]
public IActionResult CreateProduct(ProductDto model)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
return CreatedAtAction(nameof(GetById), new { id = 101 }, model);
}
Common IActionResult Responses:
- Ok(obj) → 200 OK
- CreatedAtAction() → 201 Created
- NoContent() → 204 No Content
- BadRequest() → 400 Bad Request
- NotFound() → 404 Not Found
4. Query Parameters
Query parameters allow clients to filter, sort, or search resources using the URL. They appear after a ? in the request.
/api/products?category=mobile&sort=price_desc
[HttpGet]
public IActionResult Search(string category, string sort = "price_asc")
{
return Ok($"Category: {category}, Sort: {sort}");
}
5. Request Body Parameters
Body parameters contain JSON data provided by the client, usually during POST or PUT operations.
[HttpPost]
public IActionResult AddProduct([FromBody] ProductDto model)
{
return Ok(model);
}
6. [FromRoute], [FromQuery], [FromBody]
These attributes specify where ASP.NET Core should read the value from.
[HttpPut("{id}")]
public IActionResult Update(
[FromRoute] int id,
[FromBody] ProductDto model)
{
return Ok($"Updating product {id}");
}
7. HTTP Method Attributes
HTTP method attributes specify the type of request the action handles, ensuring REST-compliant API design.
[HttpGet] → Fetch data
[HttpPost] → Create resource
[HttpPut] → Replace resource
[HttpPatch] → Partial update
[HttpDelete] → Remove resource
8. Produces and Consumes Attributes
Use [Produces] to define the output format of an action and [Consumes] to define accepted input formats.
[Produces("application/json")]
[Consumes("application/json")]
9. Naming Action Methods
Action names must follow REST naming conventions. Avoid method names that indicate verbs.
Good:
Get(), GetById(), Create(), Update(), Delete()
Avoid:
GetAllProducts(), InsertProduct(), RemoveProduct()
10. Route Prefixing
A route prefix defines a base URL for an entire controller. Useful for versioning.
[Route("api/v1/products")]
public class ProductsController : ControllerBase { }
11. Custom Route Patterns
Custom route patterns allow you to create nested or hierarchical routing structures. This is useful when resources have child entities.
Example:
GET /api/orders/10/items
[HttpGet("{orderId}/items")]
public IActionResult GetItems(int orderId) { ... }
12. Content Negotiation
Content negotiation allows clients to request responses in different formats (JSON, XML, etc.) using the Accept header.
builder.Services.AddControllers()
.AddXmlSerializerFormatters();
13. IActionResult vs ActionResult<T>
IActionResult returns a generic HTTP response, while ActionResult<T> returns a typed object plus status code.
public ActionResult<ProductDto> Get(int id)
{
return new ProductDto { Id = id, Name = "Watch" };
}
14. Standard API Response Structure
Creating a consistent API response format makes debugging and client-side integration easier.
public class ApiResponse<T>
{
public bool Success { get; set; }
public T Data { get; set; }
public string Message { get; set; }
}
15. Best Practices for Controllers
To ensure clean, maintainable controllers, follow these guidelines:
- Keep controllers thin—place business logic in services
- Use DTOs, never expose entity classes directly
- Use async/await for database or network calls
- Always return appropriate HTTP status codes
- Use dependency injection instead of instantiating objects
- Split controllers by responsibility (small controllers)
- Use validation libraries instead of manual validation
Conclusion
Controllers handle the core processing logic of your API. Mastering routing, HTTP methods, action methods, and response formats sets the foundation for scalable API design. In the next chapter, we will explore Models, DTOs, ViewModels, and mapping patterns.