Every action method returns an IActionResult — an abstraction over what should be sent as the HTTP response. ASP.NET Core provides a rich set of built-in result types for every common scenario: rendering views, redirecting browsers, returning JSON, sending files, and returning error status codes. Understanding which result type to use in each situation makes your controllers expressive and your HTTP responses semantically correct. Using the wrong result type (returning 200 OK for a not-found resource, or rendering a view instead of redirecting after a form submission) causes confusing user experiences and broken browser behaviour.
Complete Action Result Reference
public class DemoController : Controller
{
// ── View results ──────────────────────────────────────────────────────
public IActionResult ShowView()
=> View(); // → Views/Demo/ShowView.cshtml
public IActionResult ShowViewWithModel(MyViewModel vm)
=> View(vm); // same + passes model
public IActionResult ShowNamedView(MyViewModel vm)
=> View("CustomViewName", vm); // → Views/Demo/CustomViewName.cshtml
public IActionResult ShowPartial()
=> PartialView("_MyPartial", model); // no layout, for AJAX inclusion
// ── Redirect results ──────────────────────────────────────────────────
public IActionResult RedirectSame()
=> RedirectToAction(nameof(Index)); // same controller
public IActionResult RedirectOther()
=> RedirectToAction("Details", "Posts", new { id = 42 }); // other controller
public IActionResult RedirectNamed()
=> RedirectToRoute("blog", new { year = 2025, slug = "hello" }); // named route
public IActionResult RedirectPerm()
=> RedirectToActionPermanent(nameof(Index)); // 301 Permanent
public IActionResult RedirectUrl()
=> Redirect("https://example.com"); // external URL redirect
// ── Error results ─────────────────────────────────────────────────────
public IActionResult Return404() => NotFound();
public IActionResult Return404M() => NotFound("Post not found"); // with message
public IActionResult Return400() => BadRequest();
public IActionResult Return400M() => BadRequest(ModelState); // with errors
public IActionResult Return403() => Forbid();
public IActionResult Return401() => Unauthorized();
public IActionResult Return409() => Conflict("Already exists");
public IActionResult Return500() => StatusCode(500, "Internal error");
// ── Data results ──────────────────────────────────────────────────────
public IActionResult ReturnJson()
=> Json(new { id = 1, title = "Hello" }); // application/json
public IActionResult ReturnContent()
=> Content("<h1>Hello</h1>", "text/html"); // raw HTML or plain text
// ── File results ──────────────────────────────────────────────────────
public IActionResult DownloadPdf(byte[] pdfBytes)
=> File(pdfBytes, "application/pdf", "report.pdf");
public IActionResult StreamFile(Stream stream)
=> File(stream, "image/jpeg", "photo.jpg");
public IActionResult PhysicalFile(string path)
=> PhysicalFile(path, "application/pdf", "doc.pdf");
public IActionResult VirtualFile()
=> File("~/files/guide.pdf", "application/pdf"); // from wwwroot
}
RedirectToAction() returns HTTP 302 (Found — temporary redirect) by default. Use RedirectToActionPermanent() for HTTP 301 (Moved Permanently) when a URL has permanently moved and you want search engines to update their index. 302 is correct for Post-Redirect-Get (PRG) after form submission. 301 is correct for URL restructuring and SEO redirects. Using 301 for PRG causes browsers to cache the redirect and reuse it indefinitely, potentially breaking form re-submission behaviour.return View(model) (no quotes, no explicit view name) whenever possible — it follows the view location convention and does not need to be updated if the action is renamed. Only use an explicit view name (return View("CustomName", model)) when you need to render a different view from the one named after the action. Similarly, prefer RedirectToAction(nameof(Index)) over RedirectToAction("Index") for compile-time safety.Redirect(userProvidedUrl) with URLs that come from user input — this enables open redirect attacks where an attacker crafts a link to your site that redirects to a malicious URL (e.g., /login?returnUrl=https://evil.com). Always validate redirect URLs are local using Url.IsLocalUrl(returnUrl) before redirecting to them. The safe redirect-after-login pattern: if (Url.IsLocalUrl(returnUrl)) return Redirect(returnUrl); else return RedirectToAction(nameof(Index));Post-Redirect-Get with TempData
// ── Classic PRG pattern — prevents duplicate form submissions ─────────────
// Problem: user submits form → POST action creates record → returns View()
// User refreshes browser → browser re-submits the POST → duplicate record!
//
// Solution: POST → do work → Redirect (302) → GET → return View()
// Browser refresh after redirect just re-runs the GET, not the POST
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(CreatePostViewModel model)
{
if (!ModelState.IsValid) return View(model); // validation failed: stay on form
await _service.CreateAsync(model.ToRequest());
TempData["SuccessMessage"] = "Post created successfully!"; // survives the redirect
return RedirectToAction(nameof(Index)); // POST → Redirect
}
// GET /posts/index — after redirect
public async Task<IActionResult> Index()
{
var posts = await _service.GetAllAsync();
// TempData["SuccessMessage"] available in the view once, then cleared automatically
return View(new PostIndexViewModel(posts));
}
// In Index.cshtml:
// @if (TempData["SuccessMessage"] is string msg) {
// <div class="alert alert-success">@msg</div>
// }
Common Mistakes
Mistake 1 — Using Redirect() with user-provided URLs (open redirect vulnerability)
❌ Wrong — attacker redirects login users to phishing sites:
return Redirect(returnUrl); // returnUrl could be "https://phishing.com"!
✅ Correct — validate with Url.IsLocalUrl(returnUrl) before redirecting.
Mistake 2 — Returning View() after a successful POST (double-submit bug)
❌ Wrong — user refreshes browser, browser re-sends POST, duplicate record created.
✅ Correct — always RedirectToAction after a successful POST (Post-Redirect-Get pattern).