ASP.NET Core Complete Tutorial (ShopNest)
Lesson 5 of 75 7% of course

Controllers and Actions in ASP.NET Core

1 · 7 min · 5/24/2026

Learn Controllers and Actions in ASP.NET Core in our free ASP.NET Core Complete Tutorial (ShopNest) series. Step-by-step explanations, examples, and interview tips on Toolliyo Academy.

Sign in to track progress and bookmarks.

Controllers and Actions in ASP.NET Core — ShopNest Students
Article 5 of 75 · Module 1: Foundations · Student Management on ShopNest
Target keyword: asp.net core controllers · Read time: ~28 min · .NET: 8 / 9 · Project: ShopNest Student Management System

Introduction

In ASP.NET Core MVC, controllers are the traffic cops of your application — they receive HTTP requests, call services or databases, and decide what comes back: an HTML page, JSON, a redirect, or an error. Actions are the individual methods inside a controller (like Index, Create, Delete).

If MVC is the bookstore layout from Article 4, the controller is the assistant who handles each customer request. This lesson teaches every important IActionResult type, action parameters (route, query, form), GET vs POST, a full Student CRUD for ShopNest Academy, and a gentle intro to action filters — exactly what TCS and Infosys interviewers expect from 0–2 year candidates.

After this article you will

  • Define controllers and actions with correct naming conventions
  • Return View, Json, Redirect, NotFound, and other result types confidently
  • Bind route, query, and form parameters to action methods
  • Implement List, Details, Create, Edit, Delete for students
  • Explain GET vs POST and when to use action filters

Prerequisites

What is a Controller?

Level 1 — Analogy

A controller is like a bank teller window. Customers (browsers) don't enter the vault (database) themselves. They hand a slip (HTTP request) to the teller (controller action). The teller validates the slip, talks to the vault clerk (service layer), and gives back cash or a receipt (HTML/JSON/redirect).

Level 2 — Technical

Controllers inherit from Controller (MVC with views) or ControllerBase (API-only, no view support). They are discovered by convention: class name ends with Controller, lives in Controllers/ folder.

// File: Controllers/StudentsController.cs
public class StudentsController : Controller
{
    // Each public method is an "action"
    public IActionResult Index() { ... }
}

Controller base class features

  • View(), Json(), RedirectToAction() helper methods
  • ModelState — validation errors from forms
  • User, HttpContext, Request, Response
  • TempData, ViewData, ViewBag for passing data to views

IActionResult return types (with examples)

Return typeWhen to useExample
ViewResult / View()Render Razor HTMLreturn View(model);
JsonResult / Json()Return JSON (AJAX/API-style from MVC)return Json(students);
RedirectToActionResultPRG pattern after POSTreturn RedirectToAction(nameof(Index));
NotFoundResult404 — resource missingreturn NotFound();
BadRequestResult400 — invalid inputreturn BadRequest(ModelState);
ContentResultPlain text / custom contentreturn Content("OK", "text/plain");
FileResultDownload filereturn File(bytes, "application/pdf", "report.pdf");
StatusCodeResultCustom HTTP statusreturn StatusCode(429);

ActionResult vs IActionResult: Use ActionResult<T> in APIs when you want typed response bodies with automatic 400 on invalid model. IActionResult is the non-generic interface — flexible for MVC returning different result types from one action signature pattern.

Common misconceptions

❌ MYTH: Every action must return View().
✅ TRUTH: POST actions typically RedirectToAction after save (Post-Redirect-Get) to prevent duplicate form submissions.

❌ MYTH: Controllers should contain all business logic.
✅ TRUTH: Keep controllers thin — delegate to services; controllers orchestrate HTTP only.

Action parameters — route, query, form

// Route: /Students/Details/42  → id = 42 (conventional routing)
public IActionResult Details(int id) { ... }

// Query: /Students/Search?term=rahul&page=2
public IActionResult Search(string term, int page = 1) { ... }

// Form POST: [FromForm] on complex model
[HttpPost]
public IActionResult Create([FromForm] Student student) { ... }

// Body JSON (more common in API controllers):
[HttpPost]
public IActionResult Create([FromBody] StudentDto dto) { ... }

Model binding maps HTTP data to C# parameters automatically. Use [FromRoute], [FromQuery], [FromForm], [FromBody] when you need to be explicit (Article 10 covers binding in depth).

GET vs POST in controllers

MethodSafe?Typical useAttribute
GETYes (read-only)List, Details, Edit form display[HttpGet] (default)
POSTNo (changes state)Create, Edit save, Delete confirm[HttpPost]

Never use GET for delete or payment — browsers and crawlers can trigger GET accidentally. Use POST with anti-forgery token ([ValidateAntiForgeryToken]).

Hands-on: Student CRUD (ShopNest Academy)

Student model

// File: Models/Student.cs
using System.ComponentModel.DataAnnotations;

public class Student
{
    public int Id { get; set; }

    [Required, StringLength(100)]
    public string FullName { get; set; } = string.Empty;

    [Required, EmailAddress]
    public string Email { get; set; } = string.Empty;

    [Range(18, 60)]
    public int Age { get; set; }

    public string Course { get; set; } = "ASP.NET Core";
}

In-memory service (EF Core in Article 13)

public interface IStudentService
{
    IReadOnlyList<Student> GetAll();
    Student? GetById(int id);
    void Add(Student student);
    void Update(Student student);
    bool Delete(int id);
}

public class StudentService : IStudentService
{
    private static readonly List<Student> _students = new()
    {
        new() { Id = 1, FullName = "Rahul Sharma", Email = "rahul@example.com", Age = 22, Course = "Full Stack .NET" },
        new() { Id = 2, FullName = "Priya Patel", Email = "priya@example.com", Age = 21, Course = "Cloud Azure" }
    };
    private static int _nextId = 3;

    public IReadOnlyList<Student> GetAll() => _students;
    public Student? GetById(int id) => _students.FirstOrDefault(s => s.Id == id);
    public void Add(Student s) { s.Id = _nextId++; _students.Add(s); }
    public void Update(Student s)
    {
        var i = _students.FindIndex(x => x.Id == s.Id);
        if (i >= 0) _students[i] = s;
    }
    public bool Delete(int id)
    {
        var s = GetById(id);
        if (s is null) return false;
        _students.Remove(s);
        return true;
    }
}

StudentsController — full CRUD

// File: Controllers/StudentsController.cs
public class StudentsController : Controller
{
    private readonly IStudentService _students;
    public StudentsController(IStudentService students) => _students = students;

    // LIST — GET /Students
    public IActionResult Index() => View(_students.GetAll());

    // DETAILS — GET /Students/Details/5
    public IActionResult Details(int id)
    {
        var student = _students.GetById(id);
        return student is null ? NotFound() : View(student);
    }

    // CREATE (form) — GET /Students/Create
    [HttpGet]
    public IActionResult Create() => View(new Student());

    // CREATE (save) — POST /Students/Create
    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Create(Student student)
    {
        if (!ModelState.IsValid) return View(student);
        _students.Add(student);
        return RedirectToAction(nameof(Index));
    }

    // EDIT — GET /Students/Edit/5
    [HttpGet]
    public IActionResult Edit(int id)
    {
        var student = _students.GetById(id);
        return student is null ? NotFound() : View(student);
    }

    // EDIT — POST /Students/Edit/5
    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Edit(int id, Student student)
    {
        if (id != student.Id) return BadRequest();
        if (!ModelState.IsValid) return View(student);
        _students.Update(student);
        return RedirectToAction(nameof(Details), new { id = student.Id });
    }

    // DELETE — GET confirmation
    [HttpGet]
    public IActionResult Delete(int id)
    {
        var student = _students.GetById(id);
        return student is null ? NotFound() : View(student);
    }

    // DELETE — POST confirm
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public IActionResult DeleteConfirmed(int id)
    {
        if (!_students.Delete(id)) return NotFound();
        return RedirectToAction(nameof(Index));
    }
}

Register in Program.cs: builder.Services.AddSingleton<IStudentService, StudentService>();

Create view snippet (form with Tag Helpers)

@model Student
<form asp-action="Create" method="post">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <div class="mb-3">
        <label asp-for="FullName" class="form-label"></label>
        <input asp-for="FullName" class="form-control" />
        <span asp-validation-for="FullName" class="text-danger"></span>
    </div>
    <button type="submit" class="btn btn-primary">Save student</button>
</form>

Action filters — intro

Filters run before or after actions — logging, caching, authorization. Example: log every student admin action.

public class LogActionFilter : IActionFilter
{
  public void OnActionExecuting(ActionExecutingContext context)
  {
    var name = context.ActionDescriptor.DisplayName;
    Console.WriteLine($"Executing: {name}");
  }
  public void OnActionExecuted(ActionExecutedContext context) { }
}

// Register globally in Program.cs:
builder.Services.AddControllersWithViews(options =>
    options.Filters.Add<LogActionFilter>());

Full filter pipeline (authorization, resource, exception) is covered in Article 26.

Interview questions

Q1: What is the difference between Controller and ControllerBase?
A: Controller adds view-related helpers; ControllerBase is lighter for APIs.

Q2: What is IActionResult?
A: Interface representing an HTTP response — View, Json, Redirect, etc.

Q3: Why RedirectToAction after POST?
A: Post-Redirect-Get prevents duplicate submissions on browser refresh.

Q4: What is ModelState?
A: Dictionary of validation errors from Data Annotations and model binding.

Q5: GET vs POST?
A: GET reads; POST creates/updates — POST is not idempotent or cacheable.

Q6: Purpose of [ValidateAntiForgeryToken]?
A: CSRF protection — validates hidden token matches cookie.

Summary

  • Controllers handle HTTP; actions are public methods returning IActionResult
  • Use View, Json, Redirect, NotFound, BadRequest appropriately
  • Bind route/query/form parameters; validate with ModelState
  • Student CRUD demonstrates full MVC lifecycle on ShopNest
  • Filters cross-cut logging and auth around actions

Previous: MVC Architecture
Next: Routing in ASP.NET Core

FAQ

How many actions should one controller have?

Group by feature — StudentsController with Index, Details, Create, Edit, Delete is standard. Split if a controller exceeds ~10 actions or mixed concerns.

Can one action return both View and Json?

Same method can return different IActionResult types based on conditions — e.g. Json for AJAX requests and View for full page loads — but separate actions are cleaner when possible.

Test your knowledge

Quizzes linked to this course—pass to earn certificates.

Browse all quizzes
ASP.NET Core Complete Tutorial (ShopNest)

On this page

Introduction After this article you will Prerequisites What is a Controller? Level 1 — Analogy Level 2 — Technical Controller base class features IActionResult return types (with examples) Action parameters — route, query, form GET vs POST in controllers Hands-on: Student CRUD (ShopNest Academy) Student model In-memory service (EF Core in Article 13) StudentsController — full CRUD Create view snippet (form with Tag Helpers) Action filters — intro Interview questions Summary FAQ How many actions should one controller have? Can one action return both View and Json?
Module 1: Foundations
What is ASP.NET Core? Complete Guide Setting Up ASP.NET Core Development Environment ASP.NET Core Project Structure Explained MVC Architecture in ASP.NET Core — Complete Guide Controllers and Actions in ASP.NET Core Routing in ASP.NET Core — Conventional and Attribute Routing Views and Razor Syntax in ASP.NET Core Layouts, Partial Views and View Components Models and ViewModels in ASP.NET Core Forms, Model Binding and Validation in ASP.NET Core Tag Helpers in ASP.NET Core — Complete Guide Static Files, Bundling and Minification in ASP.NET Core
Module 2: Entity Framework Core
Entity Framework Core — Introduction and Setup EF Core Code First — Models, Migrations, Database EF Core CRUD Operations — Create, Read, Update, Delete EF Core LINQ Queries — Beginner to Advanced EF Core Relationships — One-to-One, One-to-Many, Many-to-Many EF Core Fluent API — Advanced Configuration EF Core Repository Pattern and Unit of Work EF Core Performance Optimization Database First Approach with EF Core (Scaffold) EF Core with SQL Server — Advanced Features
Module 3: Dependency Injection & Middleware
Dependency Injection in ASP.NET Core — Complete Guide Middleware in ASP.NET Core — Complete Guide Configuration in ASP.NET Core — appsettings, Environment Variables, Secrets Filters in ASP.NET Core — Action, Authorization, Exception, Resource, Result Logging in ASP.NET Core — ILogger, Serilog, NLog Error Handling and Exception Management in ASP.NET Core
Module 4: Authentication & Security
ASP.NET Core Identity — Complete Setup Guide Authentication in ASP.NET Core — Cookie and JWT Authorization in ASP.NET Core — Roles, Policies, Claims JWT Authentication with Refresh Tokens — Complete Implementation OAuth2 and External Login (Google, Facebook, Microsoft) Data Protection and Encryption in ASP.NET Core HTTPS, SSL Certificates and Security Best Practices
Module 5: Web API
Building REST APIs with ASP.NET Core — Complete Guide API Versioning in ASP.NET Core Swagger / OpenAPI Documentation in ASP.NET Core Input Validation in Web APIs — FluentValidation and Data Annotations Pagination, Filtering and Sorting in ASP.NET Core APIs HTTP Client and Consuming External APIs in ASP.NET Core Minimal APIs in ASP.NET Core .NET 8 SignalR — Real-Time Web Applications
Module 6: Advanced Architecture
Clean Architecture in ASP.NET Core CQRS Pattern with MediatR in ASP.NET Core Repository Pattern — Deep Dive with Generic Repository Background Services and Hosted Services in ASP.NET Core Caching in ASP.NET Core — In-Memory, Distributed, Redis Health Checks in ASP.NET Core AutoMapper in ASP.NET Core Microservices with ASP.NET Core — Introduction Message Queues with RabbitMQ / Azure Service Bus in ASP.NET Core gRPC with ASP.NET Core
Module 7: Testing
Unit Testing ASP.NET Core with xUnit and Moq Integration Testing in ASP.NET Core Testing EF Core — In-Memory vs SQLite Performance Testing and Load Testing ASP.NET Core APIs Test-Driven Development (TDD) in ASP.NET Core
Module 8: Deployment & DevOps
Deploying ASP.NET Core to IIS on Windows Server Docker and Containerization for ASP.NET Core Deploying ASP.NET Core to Azure App Service CI/CD with GitHub Actions for ASP.NET Core Azure SQL Database with ASP.NET Core Environment Configuration and Secrets Management
Module 9: Real-World Projects
Build a Complete Blog Website with ASP.NET Core MVC Build an E-Commerce Product Catalog API (ASP.NET Core Web API) Build a Student Management System (Complete CRUD App) Build a Job Portal (Full Stack ASP.NET Core) Build a REST API with Clean Architecture — Complete Guide Build a Real-Time Chat App with SignalR and ASP.NET Core
Module 10: Advanced Topics
Blazor WebAssembly and Blazor Server — Complete Guide gRPC, GraphQL and Alternative API Styles in ASP.NET Core Rate Limiting and API Throttling in ASP.NET Core .NET 8 Output Caching in ASP.NET Core .NET 8 ASP.NET Core .NET 9 New Features — Complete Guide