Controllers and Actions

A controller is a class whose public methods, its actions, answer requests. Learn how the default route picks one from the address, where parameters come from, and what an action can return.

Journeyman · ASP.NET Core · C# · Updated

After this lesson you can

  • Write a controller with actions that answer requests
  • Explain how the default route maps an address to a controller, an action and an id
  • Return a view, text, Not Found or a redirect from an action

Key takeaways

  • A controller's name ends in Controller and it inherits from Controller. Each of its public methods is an action.
  • The default route {controller=Home}/{action=Index}/{id?} maps /Products/Details/7 to ProductsController, its Details action and id 7.
  • Parameters are filled by name: id from the route, other names from the query string. A missing value gets its type's default.
  • View() returns HTML, Content() plain text, NotFound() a 404, and RedirectToAction() a redirect.
  • Look the item up, return NotFound() if it isn't there, and otherwise return its view.

A controller is the part of an MVC app that answers requests. It's an ordinary C# class, with two rules:

  • Its name ends in Controller, and it lives in the Controllers folder.
  • It inherits from ASP.NET Core's Controller class, which gives it methods such as View() and NotFound().

Each public method of a controller is an action: one thing the app can do, reached at its own address.

Your first controller

Add a file Controllers/ProductsController.cs to NorthwindWeb (in Visual Studio: right-click Controllers, Add > Controller..., MVC Controller - Empty):

using Microsoft.AspNetCore.Mvc;

namespace NorthwindWeb.Controllers;

public class ProductsController : Controller
{
    // GET /Products/Hello
    public IActionResult Hello()
    {
        return Content("Hello from the Products controller!");
    }
}

Run the app and go to /Products/Hello (for example http://localhost:5204/Products/Hello). The browser shows:

Hello from the Products controller!

How the address picks the action

Remember the route in Program.cs:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

The pattern reads the address in up to three parts. The first names the controller (without the word Controller), the second the action, and the third, if there is one, is the id. =Home and =Index are defaults for a part that's missing, and the ? makes id optional.

Address Controller Action id
/ HomeController Index
/Products ProductsController Index
/Products/Hello ProductsController Hello
/Products/Details/7 ProductsController Details 7
/Home/Privacy HomeController Privacy

Upper or lower case doesn't matter: /products/hello works too. An address that matches no controller or action gets 404 Not Found.

Parameters

An action's parameters are filled in from the request, by name. id comes from the route; any other name comes from the query string, the part after ?:

// GET /Products/Greet?name=Ada
public IActionResult Greet(string name)
{
    return Content(quot;Hello, {name}!");
}
Address Shows
/Products/Greet?name=Ada Hello, Ada!
/Products/Greet?name=Grace Hello, Grace!
/Products/Greet Hello, !

When the value is missing, the parameter gets its type's default: null for a string (which prints as nothing), 0 for an int. The names must match (upper or lower case doesn't matter): ?nam=Ada fills nothing.

What an action returns

An action returns an IActionResult: a description of the response. The Controller class has a method for each kind:

You write The browser gets
return View(product); HTML, built by the view with the same name as the action
return Content("Hello"); Plain text
return NotFound(); 404 Not Found
return RedirectToAction("Index"); A redirect: the browser goes to that action's address instead

A controller that uses the database

Real actions get their data from a DAO. Copy Product into Models/Product.cs and ProductDao into Data/ProductDao.cs from Example: a Product DAO (with namespace NorthwindWeb.Models; and namespace NorthwindWeb.Data; at the top), and add a GetAll method that returns every product ordered by name.

The DAO needs the connection string. A controller can ask for the app's settings in its constructor, as an IConfiguration, and ASP.NET Core passes them in when it creates the controller for a request:

using Microsoft.AspNetCore.Mvc;
using NorthwindWeb.Data;
using NorthwindWeb.Models;

namespace NorthwindWeb.Controllers;

public class ProductsController : Controller
{
    private readonly ProductDao productDao;

    public ProductsController(IConfiguration configuration)
    {
        string connectionString = configuration.GetConnectionString("Northwind")!;
        productDao = new ProductDao(connectionString);
    }

    // GET /Products
    public IActionResult Index()
    {
        List<Product> products = productDao.GetAll();
        return View(products);
    }

    // GET /Products/Details/7
    public IActionResult Details(int id)
    {
        Product? product = productDao.GetById(id);
        if (product is null)
        {
            return NotFound();
        }
        return View(product);
    }
}

Details shows the pattern you'll use again and again: look the item up, return Not Found if it isn't there, otherwise return the view. The views themselves come in the next lesson; until you add them, these two actions fail with The view 'Index' was not found.

ASP.NET Core makes a new controller for every request, so productDao lives only as long as one request. (Later, in ASP.NET Core MVC in Depth, you'll let ASP.NET Core create the DAO too.)

Common problems

What you see What to do
404 for an action you just wrote Check the class name ends in Controller, the method is public, and you restarted the app.
The view 'Index' was not found The action works; its view is missing. Add Views/Products/Index.cshtml.
A parameter is always empty or 0 Its name doesn't match the query string's name.

Check yourself

3 questions to try on the lesson page.

So what

Controllers are where a request becomes C#: the route picks the action, the request fills its parameters, and what it returns becomes the response. Every page and, later, every API call starts in an action like these.

Key terms

Cheat sheets