Back to CourseBeginner5 min read
DotNet MVC /

Action Results

Learn every kind of result an MVC action can return, from views and redirects to JSON, files and status codes, and how to pick the right one for each situation.

Bishal Biswas

Bishal Biswas

Self Learner

Published on: August 3, 2026
Last updated: September 13, 2026
Beginner Topic

What an action result is

An action does not write HTML itself. It returns an object that describes the response, and the framework turns that description into real bytes on the wire. That object is an action result.

public IActionResult Index()
{
    return View();   // "render a view and send it as HTML"
}

View() returns a ViewResult. RedirectToAction() returns a RedirectToActionResult. Both implement IActionResult, which is why the action can return either one.

Why the return type is IActionResult

IActionResult is an interface, and every result type implements it. Using it as the return type lets one action pick different outcomes:

public IActionResult Details(int id)
{
    var product = _repo.Find(id);

    if (product is null)
    {
        return NotFound();        // NotFoundResult
    }

    return View(product);         // ViewResult
}

If an action can only ever return a view, ViewResult works as the return type too. Most developers use IActionResult everywhere so they never have to change it later.

Results that render HTML

View() renders a Razor view. It has a few forms:

return View();                       // Views/{Controller}/{Action}.cshtml
return View(product);                // same view, with a model
return View("Summary");              // Views/{Controller}/Summary.cshtml
return View("Summary", product);     // that view, with a model
return View("~/Views/Other/Page.cshtml");   // an exact path

PartialView() renders a view without the layout. It is handy when JavaScript asks for a fragment of HTML to insert into a page:

return PartialView("_ProductCard", product);

Results that redirect

A redirect tells the browser “go to this other address instead”. The browser then makes a second request.

return RedirectToAction("Index");                              // same controller
return RedirectToAction("Index", "Home");                      // another controller
return RedirectToAction("Details", new { id = product.Id });   // with route values
return Redirect("https://example.com/help");                   // any URL
return LocalRedirect("/Products");                             // only URLs on this site

Redirects are the standard move after a form is saved, so that refreshing the page does not save it twice. That pattern is called Post Redirect Get and it has its own topic.

RedirectToAction sends a 302 status. Use RedirectToActionPermanent for a 301 when a page has moved for good, which is what search engines expect.

LocalRedirect throws if the URL points to another site. Use it whenever the target came from user input, such as a returnUrl after login, so nobody can bounce your users to a fake site.

Results that send data

Content() sends plain text:

return Content("Hello");
return Content("<b>Hi</b>", "text/html");

Json() sends an object as JSON. This is how an MVC controller answers JavaScript running on your own pages:

return Json(new { id = 5, name = "Mouse", inStock = true });
{ "id": 5, "name": "Mouse", "inStock": true }

Property names come out in camelCase, the JavaScript convention.

Results that send files

File() sends bytes or a stream as a download or an inline file:

byte[] pdf = _invoices.Generate(orderId);
return File(pdf, "application/pdf", "invoice.pdf");   // download with a file name
return File(pdf, "application/pdf");                   // shown in the browser

The second argument is the content type, which tells the browser what kind of file it is.

For a file on disk, use PhysicalFile with a full path, or File with a path relative to wwwroot:

return PhysicalFile(@"C:\reports\summary.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
return File("/downloads/guide.pdf", "application/pdf");

Results that are just a status code

Sometimes the answer is only a status:

return NotFound();                    // 404
return BadRequest();                  // 400
return Unauthorized();                // 401, not logged in
return Forbid();                      // 403, logged in but not allowed
return StatusCode(503);               // any code you like
return NoContent();                   // 204, success with an empty body
return Ok();                          // 200 with an empty body

In an MVC website, a NotFound() result is later turned into a friendly 404 page by the error handling middleware, which is covered in its own topic.

A quick reference

Helper Result type What the browser gets
View() ViewResult An HTML page
PartialView() PartialViewResult An HTML fragment without the layout
RedirectToAction() RedirectToActionResult A 302 to another action
Redirect() RedirectResult A 302 to any URL
LocalRedirect() LocalRedirectResult A 302, only to this site
Content() ContentResult Plain text
Json() JsonResult JSON
File() FileContentResult A file download
NotFound() NotFoundResult 404
BadRequest() BadRequestResult 400
StatusCode(n) StatusCodeResult Any status code

Returning a plain object

If an action returns something that is not an IActionResult, the framework wraps it for you:

public string Ping() => "pong";                    // becomes ContentResult
public Product Latest() => _repo.Latest();         // becomes JSON

This is common in Web API projects. In MVC websites, be explicit and return IActionResult, because it is clearer to the next person reading the code.

Choosing the right result

Ask what the visitor should see next:

  • A page? View().
  • A different page, after saving something? RedirectToAction().
  • Data for JavaScript on the page? Json().
  • A download? File().
  • Nothing, because the thing does not exist? NotFound().

Quick recap

  • An action returns an object that describes the response. The framework does the actual writing.
  • Use IActionResult as the return type so one action can return a view, a redirect or a status code.
  • View() renders HTML and PartialView() renders a fragment without the layout.
  • RedirectToAction() sends the browser to another action, which is the normal step after a form is saved.
  • Json(), Content() and File() send data instead of pages.
  • NotFound(), BadRequest() and StatusCode() send a status with no page.
Program.cs

// Controllers/DemoController.cs
// One controller that shows every common result type.
// Run the app and try each URL in your browser.
using Microsoft.AspNetCore.Mvc;
using ShopApp.Models;
namespace ShopApp.Controllers;
public class DemoController : Controller
{
    private static readonly Product Sample = new() { Id = 1, Name = "Mouse", Price = 19.99m };
    // /Demo            -> an HTML page (Views/Demo/Index.cshtml)
    public IActionResult Index() => View();
    // /Demo/Product    -> the Details view with a model
    public IActionResult Product() => View("Details", Sample);
    // /Demo/Card       -> an HTML fragment with no layout
    public IActionResult Card() => PartialView("_ProductCard", Sample);
    // /Demo/GoHome     -> 302 to /Home/Index
    public IActionResult GoHome() => RedirectToAction("Index", "Home");
    // /Demo/GoProduct  -> 302 to /Products/Details/1
    public IActionResult GoProduct() => RedirectToAction("Details", "Products", new { id = 1 });
    // /Demo/OldPage    -> 301, the page moved for good
    public IActionResult OldPage() => RedirectToActionPermanent("Index");
    // /Demo/Back?returnUrl=/Products  -> safe redirect, only to this site
    public IActionResult Back(string? returnUrl)
    {
        if (string.IsNullOrEmpty(returnUrl) || !Url.IsLocalUrl(returnUrl))
        {
            return RedirectToAction("Index", "Home");
        }
        return LocalRedirect(returnUrl);
    }
    // /Demo/Text       -> plain text
    public IActionResult Text() => Content("Hello from ShopApp");
    // /Demo/Data       -> JSON: {"id":1,"name":"Mouse","price":19.99}
    public IActionResult Data() => Json(Sample);
    // /Demo/Download   -> a file download called hello.txt
    public IActionResult Download()
    {
        byte[] bytes = System.Text.Encoding.UTF8.GetBytes("Hello from ShopApp");
        return File(bytes, "text/plain", "hello.txt");
    }
    // /Demo/Missing    -> 404
    public IActionResult Missing() => NotFound();
    // /Demo/Broken     -> 400
    public IActionResult Broken() => BadRequest();
    // /Demo/Later      -> 503 Service Unavailable
    public IActionResult Later() => StatusCode(503);
    // /Demo/Find/1 or /Demo/Find/2 -> different results from one action
    public IActionResult Find(int id)
    {
        if (id != Sample.Id)
        {
            return NotFound();
        }
        return View("Details", Sample);
    }
}

Was this explanation helpful?

Your feedback helps us improve the content for everyone.