Core API Controllers and Action Methods

Controllers are the front door of an ASP.NET Core Web API. Every HTTP request that reaches the API is handled by a controller. This topic explains what controllers are, how action methods work, and how they fit into the BookStore API.

What Is a Controller?

A controller is a C# class that groups related API endpoints together. For the BookStore API, all endpoints that deal with books (get all books, get one book, add a book, delete a book) live inside BooksController.

Think of a controller as a receptionist desk at a company. The receptionist (controller) receives all incoming requests and directs them to the right person (action method) who can handle the specific task.

Incoming Request: GET /api/books/1
         |
         v
[ BooksController ]
         |
         v
[ GetById(int id) ]  ← action method that handles this specific request
         |
         v
Returns: Book with Id = 1

Controller vs ControllerBase

ASP.NET Core provides two base classes for controllers:

Base ClassUsed ForReturns
ControllerBaseWeb API controllersJSON data (no views)
ControllerMVC controllersHTML views + JSON

Web API controllers always inherit from ControllerBase because they return data, not HTML pages.

The [ApiController] and [Route] Attributes

Every Web API controller has two important attributes at the top:

[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
    // action methods go here
}
AttributeWhat It Does
[ApiController]Enables automatic model validation, binding, and error responses
[Route("api/[controller]")]Sets the URL prefix — [controller] is replaced with the class name minus "Controller"

So BooksController with [Route("api/[controller]")] maps to the URL: /api/books

What Is an Action Method?

An action method is a public method inside a controller that handles a specific HTTP request. Each action method is mapped to one HTTP verb (GET, POST, PUT, DELETE) and one URL.

HTTP Verb + URL              → Action Method
─────────────────────────────────────────────
GET    /api/books            → GetAll()
GET    /api/books/1          → GetById(1)
POST   /api/books            → Create(book)
PUT    /api/books/1          → Update(1, book)
DELETE /api/books/1          → Delete(1)

Return Types – IActionResult

IActionResult is the standard return type for action methods. It allows the method to return different HTTP status codes along with data.

Helper MethodHTTP Status CodeMeaning
Ok(data)200Success with data
Created(uri, data)201Resource created successfully
NoContent()204Success with no data to return
BadRequest(message)400Invalid request from client
NotFound()404Resource not found
Unauthorized()401Not authenticated
Forbid()403Authenticated but not allowed

Building BooksController with All Action Methods

Below is the full BooksController with all action methods using an in-memory list (the database connection comes in Topic 12):

// Controllers/BooksController.cs
using Microsoft.AspNetCore.Mvc;
using BookStoreAPI.Models;

namespace BookStoreAPI.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class BooksController : ControllerBase
    {
        // Simulating a database with a static list
        private static List<Book> _books = new List<Book>
        {
            new Book { Id = 1, Title = "Clean Code", Author = "Robert C. Martin",
                       Price = 29.99m, Category = "Technology",
                       IsAvailable = true, CreatedDate = DateTime.Now },

            new Book { Id = 2, Title = "The Pragmatic Programmer", Author = "David Thomas",
                       Price = 34.99m, Category = "Technology",
                       IsAvailable = true, CreatedDate = DateTime.Now }
        };

        // GET /api/books
        [HttpGet]
        public IActionResult GetAll()
        {
            return Ok(_books);
        }

        // GET /api/books/1
        [HttpGet("{id}")]
        public IActionResult GetById(int id)
        {
            var book = _books.FirstOrDefault(b => b.Id == id);
            if (book == null)
                return NotFound();

            return Ok(book);
        }

        // POST /api/books
        [HttpPost]
        public IActionResult Create([FromBody] Book book)
        {
            book.Id = _books.Max(b => b.Id) + 1;
            book.CreatedDate = DateTime.Now;
            _books.Add(book);

            return CreatedAtAction(nameof(GetById), new { id = book.Id }, book);
        }

        // PUT /api/books/1
        [HttpPut("{id}")]
        public IActionResult Update(int id, [FromBody] Book updatedBook)
        {
            var book = _books.FirstOrDefault(b => b.Id == id);
            if (book == null)
                return NotFound();

            book.Title = updatedBook.Title;
            book.Author = updatedBook.Author;
            book.Price = updatedBook.Price;
            book.Category = updatedBook.Category;
            book.IsAvailable = updatedBook.IsAvailable;

            return NoContent();
        }

        // DELETE /api/books/1
        [HttpDelete("{id}")]
        public IActionResult Delete(int id)
        {
            var book = _books.FirstOrDefault(b => b.Id == id);
            if (book == null)
                return NotFound();

            _books.Remove(book);
            return NoContent();
        }
    }
}

Understanding CreatedAtAction

CreatedAtAction returns an HTTP 201 response. It also sets the Location header in the response to the URL where the new resource can be found.

return CreatedAtAction(nameof(GetById), new { id = book.Id }, book);

// This sends back:
// HTTP 201 Created
// Location: https://localhost:7001/api/books/3
// Body: { "id": 3, "title": "...", ... }

Action Method Flow Diagram

POST /api/books
  Body: { "title": "Design Patterns", "author": "Gang of Four", "price": 45.00 }
       |
       v
[ BooksController.Create() ]
       |
       ├── Assigns new Id
       ├── Sets CreatedDate
       └── Adds to list
       |
       v
HTTP 201 Created
Location: /api/books/3
Body: { "id": 3, "title": "Design Patterns", ... }

Multiple Controllers in One Project

As the BookStore API grows, more controllers can be added. Each entity (Books, Authors, Orders) gets its own controller.

Controllers/
├── BooksController.cs     → handles /api/books
├── AuthorsController.cs   → handles /api/authors
└── OrdersController.cs    → handles /api/orders

Each controller is completely independent and handles only its own set of requests. This separation makes the code clean and focused.

Key Points

  • A controller is a C# class that groups related API endpoints.
  • Web API controllers inherit from ControllerBase, not Controller.
  • [ApiController] enables automatic validation and binding features.
  • [Route("api/[controller]")] sets the base URL for all actions in the controller.
  • Action methods return IActionResult which wraps the response data and HTTP status code.
  • Ok(), NotFound(), BadRequest(), NoContent(), and CreatedAtAction() are the most used helper methods.

Leave a Comment