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 Class | Used For | Returns |
|---|---|---|
ControllerBase | Web API controllers | JSON data (no views) |
Controller | MVC controllers | HTML 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
}
| Attribute | What 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 Method | HTTP Status Code | Meaning |
|---|---|---|
Ok(data) | 200 | Success with data |
Created(uri, data) | 201 | Resource created successfully |
NoContent() | 204 | Success with no data to return |
BadRequest(message) | 400 | Invalid request from client |
NotFound() | 404 | Resource not found |
Unauthorized() | 401 | Not authenticated |
Forbid() | 403 | Authenticated 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, notController. [ApiController]enables automatic validation and binding features.[Route("api/[controller]")]sets the base URL for all actions in the controller.- Action methods return
IActionResultwhich wraps the response data and HTTP status code. Ok(),NotFound(),BadRequest(),NoContent(), andCreatedAtAction()are the most used helper methods.
