Matthew Jones

If you've been working in ASP.NET MVC at all, you've seen code like this before:

public ActionResult Index()
{
return View();
}

Any action in an MVC controller, generally speaking, returns an ActionResult. Obviously that class represents an action of some kind, but exactly what does it do? Let's peel back the covers on ActionResult and discover why it and its derived classes are critical to understanding ASP.NET MVC.

What is an ActionResult?

ActionResult is an abstract class that represents the result of an action method. The class itself inherits from System.Object, and only adds one additional abstract method: ExecuteResult, which is an abstract method that the derived classes of ActionResult will implement themselves.

Many of the derived classes we're going to discuss have associated helpers. These helpers provide shortcuts to the constructor methods of their related Results. It's what allows us to return View() rather than return new ViewResult().

Why is ActionResult an abstract class? So that different controller actions can return different types of results and still have MVC handle them properly.

You may have guessed that this implementation is done because ActionResult has a lot of derived classes, and you'd be exactly right. But what exactly are these different kinds of results? I've categorized them into three sections: content-returning, redirection, and status results. First off, let's take a look at the wide variety of content-returning results.

Content-Returning Results

These ActionResults are responsible for returning content to the browser or calling script. In basic scenarios, the most common of these is the ViewResult.

Since MVC follows convention-over-configuration, MVC will look for a View named "Index" in the Views/Home subfolder, and then look in Views/Shared if it doesn't find it (and will throw an InvalidOperationException if it can't find it at all).

What if I wanted to return a view other than the one that matches the action name?

public ActionResult Alternate()
{
return View("Other");
}

Now it will attempt to find a view named "Other" in the Views/Home folder.

If we were to redirect to this action, we would see the content of that partial view, but without the layout page applied. This isn't very useful by itself, so a more useful application might be to call this action in an AJAX scenario and display the returned view.

If we want to return a file, this is the ActionResult we use. Depending on which overload we pick, we can specify what action the browser is to take with the downloaded file. For example, if we just specify a URL and a MIME type, the browser attempts to display the file specified (at least it does in Firefox, Chrome, and IE10, where I tested it):

FileResult is one of the most open-ended ActionResults, and can handle a lot of different scenarios. It can accept byte arrays, FileStreams, and URLs of files, and in all scenarios return or download the file specified.

ContentResult is used when we want to allow the action to specify what should be returned. It's a sort of catchall for scenarios where we need to allow the action full control over the returned content. All we need to do is specify the content and MIME type:

MVC wants you to use EmptyResult when the action is specifically intended to return nothing. Unlike all of the previous ActionResults though, EmptyResult doesn't have a helper. Additionally, if an action returns null, MVC will detect that and make it return an EmptyResult.

In my opinion, this is one of the coolest ActionResults. JsonResult is used to represent JSON-encoded data, which is most commonly used to return structured data to a calling script, especially in AJAX scenarios.

But that's not why I like it so much. In ASP.NET MVC, you can JSONify anything. For example:

Problems with JSON dates aside, the implications of this are staggering. Since that just encoded an anonymous object, it follows that JsonResult can handle any object, including user-defined ones, and encode them into the JSON format.

However, there's one trick you should be aware of. If you attempt to redirect to the action above, MVC will throw an exception saying that "This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request." MVC is trying to protect you here; it doesn't want you to share JSON information over a GET request because it could potentially contain sensitive information. This is attempting to protect you from an exploit known as JSON Hijacking, and Phil Haack has a great blog post on it.

JsonRequestBehavior.AllowGet does exactly what it sounds like; it allows browsers to access this JSON information in a GET request. I'd only recommend turning this on if you know what you are doing, since it could potentially expose you to JSON Hijacking.

This can be really useful for MVVM scenarios and other dynamic script scenarios, but be cautious about using it, as it potentially violates separation of concerns by making the Controller in charge of View functionality.

Redirection Results

So far, we've been dealing with ActionResults that return or display content. Now, let's see what kind of ActionResults we need for redirecting to other URLs or actions.

RedirectToAction follows the same convention-over-configuration idea from ViewResult; namely, that it looks for a corresponding action in the current controller if you don't specify which controller to look in.

RedirectToAction has many overloads that allow you to specify controllers, actions, route values, etc. It's probably the second most common ActionResult derived class, because it's so readable.

Status Results

The final set of ActionResults are the Status Results, which return status codes to the browser for it to use.

HttpStatusCodeResult return an HTTP status code to the browser, along with a custom message to be displayed:

public HttpStatusCodeResult UnauthorizedStatusCode()
{
return new HttpStatusCodeResult(HttpStatusCode.Unauthorized, "You are not authorized to access this controller action.");
}
public HttpStatusCodeResult BadGateway()
{
return new HttpStatusCodeResult(HttpStatusCode.BadGateway, "I have no idea what this error means.");
}

Notice that there is no helper method for this ActionResult.

The HttpStatusCode enumeration contains all HTTP status codes (so that you don't have to remember what 402 or 307 mean). These are useful in exception-driven scenarios where you have custom error pages defined. However, as any experienced web developer knows, some kinds of status are MUCH more common than others, and MVC has ActionResults for the two most common.

Summary

There you have it, all of the ActionResults fit to demo. There's a few more of them, such as FileContentResult, but their functionality is pretty easy to figure out, so I leave that to you, my readers.

If you want some more information, check out Professional ASP.NET MVC 5, particularly Chapter 16 (Advanced Topics), which has an entire section breaking down the ActionResult class and its derivatives.

You can grab a sample project with simple demos of all of these ActionResults from GitHub.