ASP.NET MVC ActionResults explained

Action results are an important part of the ASP.NET MVC controller system, and definitely worth taking a good look at. Understanding how they work gives you many more choices in MVC and that will certainly help make your code better.

What is an ActionResult?

An ActionResult is a return type of a controller method, also called an action method, and serves as the base class for *Result classes. Action methods return models to views, file streams, redirect to other controllers, or whatever is necessary for the task at hand. The controller takes on this responsibility to connect system components, acting as a traffic cop.

There are many derived ActionResult types you can use to return results that are more specific for a particular view. You can quickly access the derived types of the ActionResult during development by hovering over an ActionResult in an action result’s method signature then expanding the type tool window, to see what *Results the ASP.NET Framework provides.To get a deeper look at what exactly an ActionResult type is and how it works, running the code with a breakpoint set at the end of an action result method will get the information we want. Inspecting the image below of the Watch window shows some of the properties that you can tap into that ActionResults return.

Notice the Model, TempData, ViewBag, and ViewData properties. Drilling into the Watch window even further exposes the Model property’s strongly typed collection of objects.

SNAGHTML4d9b785_thumb3_2

These objects contain everything you need when working in the view. Of course, the Model is the most heavily used object, containing our application’s schema and data. The TempData object holds data between multiple controllers, while ViewBag and ViewData objects holds data between controllers and/or views.

Since the Model property is the actual model the view uses to render the data, its data type should align with the type specified in the @model directive in the target view. As pointed out in the image above, the Model property contains a strongly typed list of Product objects (or whatever you’re returning), matching the view’s model.

Looking in the Watch window shows that the runtime converts the ActionResult to a ViewResult, however you’ll want to be more specific during design time. Swapping ActionResults for more specific/derived classes, for example, a ViewResult, produce the exact same object structure as the ActionResult. With this in mind, using more specific types increases code accuracy, maintainability, and even performance as the compiler has a much better idea of what should happen at compile time, rather than at the last minute at runtime, since no runtime conversion happens.

Action methods, action results & routing

Action methods and the routing system work together in every MVC application. MVC does this by using a set of conventions, defined as routing patterns in the global.asax.cs file. These patterns connect HTTP requests with controllers and action methods. As an HTTP Request enters the routing system, it determines which controller should serve data to which view (if any).

The global.asax.cs defines the default routing pattern, {controller}/{action}/{id}, as described in more detail here:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
    );
}

Picking apart each piece of the route pattern is a great way to digest what the routing system is doing…

  • {controller} Controller maps a part of the URL to the name of the controller class. For example, a class named ProductsController, will map to URLs that start with /Products (after the domain of course). ASP.NET automatically drops the word “Controller” from the URL so is it’s more user friendly, e.g., /Products vs. /ProductsController.
  • {action} Action maps a part of the URL to the individual methods inside controllers. Therefore the ProductsController.Edit(…) and ProductsController.Details(…) methods align with the /Products/Edit and Products/Details URLs, respectively. Action maps without any parameters, since the next route pattern, id, defines them.
  • {id} Id maps to an identifier, such as a product id or a product name. These are controller action method parameters, so the value at the end of the URL is generally the value of the method’s argument. For example, /Products/Edit/2 matches up with the id argument in ProductsController.Edit(int id). The controller methods normally use this id to query for a single record with a matching id to return to the view.

Below is a table detailing the mappings between routes, controllers, action methods and their parameters.

Action Method Route {controller}/{action}/{id} HTTP POST/GET
public ActionResult Index() { } /Products GET
public ActionResult Details(int id) { } /Products/Details/id GET, values in URL
public ActionResult Edit(int id) { } /Products/Edit/id GET, values in URL
[HttpPost]
public ActionResult Edit(int id, FormCollection collection) { }
/Products/Edit/id POST, values in FormCollection
public ActionResult Create() { } /Products/Create GET
[HttpPost]
public ActionResult Create(FormCollection collection) { }
/Products/Create POST, values in FormCollection
public ActionResult Delete(int id) { } /Products/Delete/id GET, values in URL
[HttpPost]
public ActionResult Delete(int d, FormCollection collection) { }
/Products/Delete/id POST, values in FormCollection

You may have noticed that the Edit, Create, and Delete action methods come overloaded as pairs, and an [HttpPost] attribute decorates one method in each pair. Since [HttpGet] is the default, the other method doesn’t need to be marked. Additionally, all methods decorated with the [HttpPost] attribute are meant to accept HTML input fields located between the <form> tags. The collection parameter of type FormCollection[1] contains all the input fields from the <form> of the HTTP Post.

Every method in a controller class returns an ActionResult by default; however, some actions will be more optimized, clear, or accurate in the code, if we are to specify a more specific action results, for instance, a ViewResult, rather than an ActionResult. Action result methods can also designate that its result will return something other than data for a view, e.g., a HttpNotFoundResult or an HttpStatusCodeResult, as shown in the code below:

Returns an HTTP 404 Not found error:

public HttpStatusCodeResult NotFound()
{
    var result = new HttpStatusCodeResult(404);
    return result;
}

Redirects to a separate view altogether:

[HttpPost]
public RedirectResult Create(FormCollection collection)
{
    // code to save form data ...     
    return new RedirectResult("Home/Index", false);
}

There’s many more ActionResult types to explore. Check them out and find something that meets the needs of your controller and/or view.

Summary

ActionResults are a key component to ASP.NET MVC, so understanding them is essential for MVC application development. Whether you need to make AJAX calls, redirect, return a file stream, or just return data to a view, ActionResults are right there in the center of all the……action.

[1] I recommend rather than using FormCollection, you should use a strongly typed parameter or multiple, differently typed parameters that match to data types in the HTML form. Look forward to a blog post soon to demonstrate this.

One thought on “ASP.NET MVC ActionResults explained

  1. Josh May 2, 2017 / 4:04 am

    Hey..
    Thank you very much forr this nice and clear explanation!! I just have one little note: Your picture-link is broken somehow. You may want to fix that šŸ™‚

    Best regards!!

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.