Controllers and Action Results
Level: Intermediate
- Action method:
public async Task<IActionResult> GetStudent(int id)handles request, returns response IActionResulttypes:View()(HTML),Json()(API response),RedirectToAction()(navigate to another action),BadRequest(),NotFound()View(): Return HTML generated from .cshtml template (return View(student)passes data)View("ViewName"): Return specific view template (default: action name)RedirectToAction(): Navigate to another action (return RedirectToAction("Index")after saving)BadRequest(): Return HTTP 400 (validation failed)NotFound(): Return HTTP 404 (student not found)[HttpGet]: Handle GET request (display form)[HttpPost]: Handle POST request (form submission, process data)- Route parameters:
Details(int id)captures URL parameter (/students/details/5→ id=5) - Model binding:
Create(Student student)automatically populates Student from form data ModelState.IsValid: Check if form validation passed before saving- ViewBag/ViewData: Weakly-typed data (
ViewBag.Title = "Students",ViewData["Count"]) - TempData: Temporary data for redirect (user feedback message after save)
- School Management CRUD: Index (list students), Details (show one), Create (form), POST Create (save), Edit (edit form), POST Edit (update), Delete (confirm), POST Delete (remove)
- Return View with errors:
return View(student)shows form again with validation messages if invalid
Action Method Basics
Action = public method in Controller. Returns IActionResult (View, JSON, Redirect, etc).
public class StudentsController : Controller
{
// GET: /students
public async Task<IActionResult> Index()
{
var students = await _service.GetStudentsAsync();
return View(students); // Returns Index.cshtml with students
}
// GET: /students/details/5
public async Task<IActionResult> Details(int id)
{
var student = await _service.GetStudentAsync(id);
return View(student);
}
// GET: /students/create
public IActionResult Create()
{
return View(); // Empty form
}
// POST: /students/create
[HttpPost]
public async Task<IActionResult> Create(Student student)
{
if (ModelState.IsValid)
{
await _service.CreateStudentAsync(student);
return RedirectToAction(nameof(Details), new { id = student.Id });
}
return View(student);
}
}
IActionResult Types
| Method | Purpose | Sends |
|---|---|---|
View() | Render Razor template | HTML |
View(model) | Render with data | HTML with data |
PartialView() | Render partial template | HTML fragment |
Redirect(url) | Redirect to URL | 302 status |
RedirectToAction(action) | Redirect to action | 302 status |
RedirectToRoute(route) | Redirect to route | 302 status |
Json(data) | Return JSON | JSON data |
Ok(data) | HTTP 200 OK | JSON/Content |
BadRequest() | HTTP 400 error | Error response |
NotFound() | HTTP 404 error | Not found response |
Unauthorized() | HTTP 401 error | Auth required |
Forbid() | HTTP 403 error | Access denied |
StatusCode(code) | Custom HTTP status | Custom status |
Content(text) | Plain text | Text response |
File(bytes) | Download file | File download |
Returning Views
public async Task<IActionResult> ListStudents()
{
var students = await _service.GetStudentsAsync();
// Explicitly named view
return View("StudentList", students);
// Default: look for StudentList.cshtml or Index.cshtml
}
Default view names: {ActionName}.cshtml or Shared/{ActionName}.cshtml
Route Parameters
Parameters auto-bound from route.
// Route: {controller}/{action}/{id?}
public async Task<IActionResult> Details(int id)
{
// id bound from URL
var student = await _service.GetStudentAsync(id);
return View(student);
}
// Route: /students/details/10 → id = 10
Query String Parameters
// GET: /students?className=10-A&status=Active
public async Task<IActionResult> Filter(string className, string status)
{
var students = await _service.FilterAsync(className, status);
return View(students);
}
POST and Forms
[HttpPost] attribute for POST requests.
Prevent CSRF: [ValidateAntiForgeryToken]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Student student)
{
if (!ModelState.IsValid)
return View(student); // Re-display form with errors
await _service.CreateStudentAsync(student);
return RedirectToAction(nameof(Index));
}
Form must include anti-forgery token:
<form asp-action="Create" method="post">
@Html.AntiForgeryToken()
<!-- form fields -->
</form>
JSON Responses
Return JSON for AJAX or mobile clients.
[HttpPost]
public async Task<IActionResult> CreateJson(Student student)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var created = await _service.CreateStudentAsync(student);
return Json(new { success = true, id = created.Id });
}
JavaScript:
fetch('/students/createjson', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Ravi', rollNumber: 'SMS-2024-001' })
})
.then(r => r.json())
.then(data => console.log(data));
Redirects
// Redirect to action
return RedirectToAction(nameof(Details), new { id = student.Id });
// Redirect to route
return RedirectToRoute("default", new { controller = "Home", action = "Index" });
// Redirect to URL
return Redirect("https://nexcoding.in");
Redirect = HTTP 302 (temporary). URL changes in browser.
Status Code Responses
public async Task<IActionResult> GetStudent(int id)
{
var student = await _service.GetStudentAsync(id);
if (student == null)
return NotFound(); // 404
if (student.Status == StudentStatus.Inactive)
return BadRequest("Student is inactive"); // 400
return Ok(student); // 200 with data
}
Response Headers
public IActionResult Download()
{
var fileBytes = System.IO.File.ReadAllBytes("student.pdf");
return File(
fileBytes,
"application/pdf",
"students.pdf"
); // Triggers download
}
ActionResult Filters
Execute before/after action.
[Authorize] // Only logged-in users
public IActionResult AdminPanel()
{
return View();
}
[Authorize(Roles = "Admin")] // Only admins
public IActionResult DeleteAll()
{
return View();
}
Key Takeaways
- Action = public method handling request
- IActionResult = flexible response type
- View(model) = render HTML with data
- RedirectToAction = go to another action
- ModelState.IsValid = form validation passed
- StatusCode responses = 200, 400, 404, 500
- POST + CSRF token = secure form submission
Keep actions simple. Delegate logic to services.
Use ChatGPT, Claude, or Copilot to go deeper on Controllers and Action Results. Try these prompts:
"What's the difference between View and PartialView?""When should I use RedirectToAction vs Redirect?""How does model binding work from route parameters?""When should I return JSON vs View?""Quiz me on controllers"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.