Skip to content

Aspnet Controllers

DodaTech 4 min read

title: ASP.NET Core Controllers — Complete Guide to Request Handling description: 'Learn ASP.NET Core controllers: action methods, parameters, returning IActionResult, model binding, validation, filters, and dependency injection in controllers.' date: 2026-06-28 lastmod: 2026-06-28 weight: 15 tags: [backend, aspnet]


ASP.NET Core controllers are C# classes that handle incoming HTTP requests, process input data, execute business logic via injected services, and return HTTP responses.

## What You'll Learn

By the end of this tutorial, you'll create controller actions with various return types, bind request data using model binding, validate input, use filters, and inject services.

## Real-World Use

An API controller for a shopping cart handles POST to add items, GET to retrieve cart, PUT to update quantities, DELETE to remove items. Each action uses model binding and validation.

## Controllers Learning Path

```mermaid
flowchart LR
  A[MVC] --> B[Controllers]
  B --> C[Views]
  C --> D[Models]
  D --> E[Routing]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

Basic Controller

using Microsoft.AspNetCore.Mvc;
namespace MyApp.Controllers;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _service;
    public ProductsController(IProductService service)
    {
        _service = service;
    }
    [HttpGet]
    public ActionResult<List<Product>> GetAll()
    {
        return Ok(_service.GetAll());
    }
    [HttpGet("{id}")]
    public ActionResult<Product> GetById(int id)
    {
        var product = _service.GetById(id);
        if (product == null)
            return NotFound();
        return Ok(product);
    }
}

Action Return Types

public class SamplesController : ControllerBase
{
    // Specific type
    [HttpGet("direct")]
    public Product GetDirect() => new Product { Id = 1, Name = "Test" };
    
    // IActionResult (flexible)
    [HttpGet("action-result")]
    public IActionResult GetActionResult()
    {
        return Ok(new Product { Id = 1, Name = "Test" });
    }
    
    // ActionResult<T> (hybrid)
    [HttpGet("typed-result")]
    public ActionResult<Product> GetTypedResult()
    {
        return NotFound();  // Compiles because ActionResult<T> : IActionResult
    }
}

Model Binding

// From route: /api/products/5
[HttpGet("{id}")]
public IActionResult GetById(int id) { ... }

// From query: /api/products?category=electronics
[HttpGet]
public IActionResult Search([FromQuery] string? category) { ... }

// From body (POST/PUT)
[HttpPost]
public IActionResult Create([FromBody] Product product) { ... }

// From form
[HttpPost]
public IActionResult Upload([FromForm] IFormFile file) { ... }

Filters

// Custom action filter
public class LoggingFilterAttribute : ActionFilterAttribute
{
    private readonly ILogger<LoggingFilter> _logger;
    public LoggingFilterAttribute(ILogger<LoggingFilter> logger)
    {
        _logger = logger;
    }
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        _logger.LogInformation("Action {Action} executing",
            context.ActionDescriptor.DisplayName);
    }
}

[ApiController]
[Route("api/products")]
[LoggingFilter]  // Apply to all actions
public class ProductsController : ControllerBase { ... }

Common Mistakes

1. Not Using [ApiController]

The [ApiController] attribute enables automatic model validation, binding source inference, and problem details responses.

2. Ignoring ModelState.IsValid

Without checking ModelState.IsValid, invalid data passes through. Return ValidationProblem() when invalid.

3. Returning Domain Models Directly

Domain models expose internal details. Return DTOs or ViewModels specifically shaped for the API consumer.

4. Using sync I/O in Actions

Database calls and file operations should be async. Use async Task with await.

5. Hardcoding Routes

Use attribute routing with [Route] and [HttpGet("{id}")]. Avoid magic strings for action names.

Practice Questions

1. What is the difference between Controller and ControllerBase?

Controller inherits from ControllerBase and adds view support (View(), ViewBag). ControllerBase is for API-only controllers.

2. How do you bind a route parameter to an action method?

Add a parameter to the action method with the same name as the route parameter. ASP.NET Core binds automatically.

3. What is the purpose of filters?

Filters run code before or after action execution for cross-cutting concerns like logging, authorization, and validation.

4. How do you handle file uploads in a controller?

Use IFormFile parameter with [FromForm] attribute. The form must use multipart/form-data encoding.

5. Challenge: Create a controller with CRUD actions for a Task model with validation and error handling.

[ApiController]
[Route("api/tasks")]
public class TasksController : ControllerBase
{
    [HttpGet]
    public async Task<ActionResult<List<TaskDto>>> GetAll() { ... }
    [HttpGet("{id}")]
    public async Task<ActionResult<TaskDto>> GetById(int id) { ... }
    [HttpPost]
    public async Task<ActionResult<TaskDto>> Create(TaskDto task) { ... }
    [HttpPut("{id}")]
    public async Task<IActionResult> Update(int id, TaskDto task) { ... }
    [HttpDelete("{id}")]
    public async Task<IActionResult> Delete(int id) { ... }
}

FAQ

Can I inject services into controller properties?

Yes, use [FromServices] on a property, but constructor injection is the standard approach.

What does [FromBody] do?

Tells the model binder to read the parameter from the HTTP request body (JSON/XML).

How do I return different status codes?

Return Ok() for 200, Created() for 201, NotFound() for 404, BadRequest() for 400.

What is the purpose of async actions?

Async actions don't block threads during I/O, improving scalability under high load.

Can a controller have multiple routes?

Yes. Use [Route] attribute multiple times or multiple [HttpGet] attributes with different templates.

Mini Project: RESTful API Controller

Build a complete REST controller for managing books.

[ApiController]
[Route("api/books")]
public class BooksController : ControllerBase
{
    private static List<Book> _books = new();
    [HttpGet] public ActionResult<List<Book>> GetAll() => Ok(_books);
    [HttpGet("{id}")] public ActionResult<Book> GetById(int id) {
        var book = _books.Find(b => b.Id == id);
        return book is null ? NotFound() : Ok(book);
    }
    [HttpPost] public ActionResult<Book> Create(Book book) {
        book.Id = _books.Count + 1;
        _books.Add(book);
        return CreatedAtAction(nameof(GetById), new { id = book.Id }, book);
    }
}

What's Next

ASP.NET Core Views ASP.NET Core Models

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro