ASP.NET Controllers Fundamentals
On this page
What are Controllers?
In ASP.NET Core, controllers are central to handling incoming HTTP requests. They are typically represented by classes that contain public methods called action methods. These action methods are responsible for processing requests, interacting with models, and ultimately returning responses, often by selecting a view to render or returning data directly (like JSON).
Controllers act as the intermediary between the client's request and the application's logic and data. They follow the Model-View-Controller (MVC) architectural pattern, which helps organize code into logical components, making applications more maintainable, testable, and scalable.
Controller Actions
Action methods are public methods within a controller class. When a request matches a route that points to a specific controller and action, that action method is executed. Action methods can accept parameters, which are typically bound from the incoming request (e.g., query string parameters, route data, or request body).
The return type of an action method determines the type of response sent back to the client. Common return types include:
IActionResult
orActionResult<T>
: A flexible interface that allows returning various types of HTTP responses, including views, redirects, JSON, or plain text.string
: Returns plain text.JsonResult
: Returns data serialized as JSON.ContentResult
: Returns plain text with control over the Content-Type.StatusCodeResult
: Returns an HTTP status code without a response body.
Routing to Controllers
ASP.NET Core uses a powerful routing system to map incoming HTTP requests to the appropriate controller action. By default, it uses a convention-based router that looks for routes in the pattern /{controller}/{action}/{id}
. However, you can customize routing extensively using attribute routing or by configuring routes in the Startup.cs
or Program.cs
file.
Attribute Routing: You can decorate your controller classes and action methods with attributes like [Route("api/[controller]")]
or [HttpGet("details/{id}")]
to define specific URL patterns that map directly to them.
Attribute routing provides more explicit control over URL structures and is generally preferred for its clarity and flexibility, especially in API development.
The Controller Base Class
ASP.NET Core provides a base class, Controller
, from which most controllers inherit. This base class offers a wealth of functionality, including:
- Access to request and response objects (
Request
,Response
). - Access to user identity and authentication information (
User
). - Methods for returning different types of results (e.g.,
View()
,Json()
,RedirectToAction()
). - Access to
TempData
andViewBag
for passing data between requests and to views. - Error handling mechanisms.
For API controllers, you'd typically inherit from ControllerBase
or ApiController
, which provides a lighter-weight set of features optimized for API scenarios.
Handling Requests
Controllers are designed to handle HTTP methods like GET, POST, PUT, DELETE, etc. You can specify which HTTP method an action method should handle using attributes like [HttpGet]
, [HttpPost]
, [HttpPut]
, [HttpDelete]
, or combined attributes like [AcceptVerbs("GET", "POST")]
.
Example: Handling different HTTP methods
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
// Handles GET requests to /api/products
[HttpGet]
public IActionResult GetAllProducts()
{
// Logic to fetch all products
return Ok(new[] { "Product A", "Product B" });
}
// Handles GET requests to /api/products/{id}
[HttpGet("{id:int}")]
public IActionResult GetProductById(int id)
{
// Logic to fetch product by ID
if (id <= 0)
{
return BadRequest("Product ID must be a positive integer.");
}
return Ok($"Details for Product ID: {id}");
}
// Handles POST requests to /api/products
[HttpPost]
public IActionResult CreateProduct([FromBody] Product newProduct)
{
// Logic to create a new product
// ...
return CreatedAtAction(nameof(GetProductById), new { id = 123 }, newProduct);
}
// Handles PUT requests to /api/products/{id}
[HttpPut("{id:int}")]
public IActionResult UpdateProduct(int id, [FromBody] Product updatedProduct)
{
// Logic to update product with id
return NoContent(); // Indicates success, no content to return
}
// Handles DELETE requests to /api/products/{id}
[HttpDelete("{id:int}")]
public IActionResult DeleteProduct(int id)
{
// Logic to delete product with id
return Ok($"Product with ID {id} deleted.");
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
Passing Data to Views
When a controller action returns a ViewResult
, it typically needs to pass data to the associated view. This can be achieved in several ways:
- Strongly-Typed Models: Pass an instance of a model class to the
View()
method. This is the recommended approach for type safety. ViewBag
: A dynamic property bag that allows you to pass data to the view without a strongly-typed model. It's less type-safe and can lead to runtime errors if properties are misspelled.ViewData
: A dictionary that also allows passing data to the view. Similar toViewBag
, but requires explicit casting.TempData
: A dictionary that is used to pass data between requests, typically for short-term messaging (e.g., confirmation messages after a form submission). Data inTempData
is available for the next request only.
Example: Passing data to a view
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
public class HomeController : Controller
{
public IActionResult Index()
{
var items = new List<string> { "Item 1", "Item 2", "Item 3" };
ViewBag.PageTitle = "Welcome to Our Site";
ViewData["Message"] = "This is a message from ViewData.";
// Passing a strongly-typed model
var viewModel = new HomePageViewModel
{
FeaturedProducts = new List<Product>
{
new Product { Id = 1, Name = "Gadget Pro" },
new Product { Id = 2, Name = "Tech Master" }
}
};
return View(viewModel); // Passes viewModel to the view
}
}
public class HomePageViewModel
{
public List<Product> FeaturedProducts { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
In your view (e.g., Views/Home/Index.cshtml
), you would access these:
<h1>@ViewBag.PageTitle</h1>
<p>@ViewData["Message"]</p>
<h2>Featured Products:</h2>
<ul>
@foreach (var product in Model.FeaturedProducts)
{
<li><a href="/products/@product.Id">@product.Name</a></li>
}
</ul>
Examples
Here are some common controller scenarios:
- API Controller: Handles requests for data and returns JSON or XML. Uses
ApiController
attribute and returnsIActionResult
or specific result types likeOkObjectResult
. - MVC Controller: Handles requests that involve rendering HTML views. Inherits from
Controller
and typically returnsViewResult
. - Resource Controller: Manages CRUD operations for a specific resource (e.g.,
UsersController
,OrdersController
).
Organize your controllers into logical folders (e.g., Controllers/Api
, Controllers/Web
) to maintain a clean project structure.