ASP.NET MVC Tip #22 — Return a View without Creating a Controller Action

In this tip, I demonstrate how you can eliminate controller methods that simply return views. I show you how to use the HandleUnknownAction method to handle every request against a controller automatically.

I saw Phil Haack use the following tip in a demo that he presented. I thought that it was such a great idea that I had to share it.

There is no good reason to write code unless there is a good reason to write code. I discover that I write a lot of controller actions that do nothing more than return a view. For example, consider the CustomerController in Listing 1.

Listing 1 – CustomerController.vb

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.MvcNamespace Tip22.Controllers
Public Class CustomerController
Inherits Controller

Public Function Index() As ActionResult
Return View()
End Function

Public Function Details() As ActionResult
Return View()
End Function

Public Function Help() As ActionResult
Return View()
End Function

End Class
End Namespace

Listing 1 – CustomerController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Tip22.Controllers
{
    public class CustomerController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Details()
        {
            return View();
        }
        public ActionResult Help()
        {
            return View();
        }

    }
}

This controller includes three actions that return three different views. Each of these actions contains a single line of code. In fact, each of these actions contains exactly the same line of code. This code reeks of needless work. How can we fix it?

The Controller class includes a method, named the HandleUnknownAction() method, that executes whenever you attempt to invoke an action on a controller that does not exist. The controller in Listing 2 takes advantage of the HandleUnknownAction() method to render views even when a corresponding controller method does not exist.

Listing 2 – HomeController.vb

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.Mvc

Namespace Tip22.Controllers
    <HandleError> _
    Public Class HomeController
        Inherits Controller

        Public Function Details() As ActionResult
            ViewData("message") = "Hello from controller action!"
            Return View()
        End Function

        Protected Overrides Sub HandleUnknownAction(ByVal actionName As String)
            Me.View(actionName).ExecuteResult(Me.ControllerContext)
        End Sub

    End Class
End Namespace

Listing 2 – HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Tip22.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public ActionResult Details()
        {
            ViewData["message"] = "Hello from controller action!";
            return View();
        }

        protected override void HandleUnknownAction(string actionName)
        {
            this.View(actionName).ExecuteResult(this.ControllerContext);
        }

    }
}

When you use the controller in Listing 2, you can call any action and the controller will attempt to return a view that corresponds to the action. You don’t need to explicitly code an action method for each view.

Notice that the controller includes a Details() action. When you need to pass ViewData, then you need to explicitly code the action method.

Discussion

  1. Webdiyer says:

    Thank you for your great tips!

  2. haacked says:

    As Eilon pointed out to me, you can accomplish the same thing using routing and a parameterized action method.

    Route: url=”/show/{viewName}”, defaults={controller=”home”, action=showview}

    public ActionResult ShowView(string viewName) {
    return View(viewName);
    }

    And now, you don’t have to write action methods for each view in the views/home directory, you can just go to /show/viewname

  3. http:// says:

    Sorry Stephen, I feel the last tip from Phil/Elion is a lot cleaner. I don’t like the idea of using something error-handling-like to build your functionality on.

    But I suppose the HandleUnkownAction method will still be called when you submit a url like “/show/nonexistingview” to the controller? There is no check for existing views in both your code samples, and that would be ugle too, having some switch or if statement.

  4. joshka says:

    Hi Stephen.
    Would it be possible to provide a full text feed from your site. I generally read your blog in an RSS reader, and this is one of the few that I have to click through to read the actual article.
    A couple of suggestions for your blog theme that hopefully won’t go astray – if you cssify your vb code with a vbwrapper class instead of csharpwrapper css class, you could provide some quick javascript to hide one or the other. An option to expand all code samples for easier reading would be pretty nice also. The scrolling textboxes are nice at first, but are an annoyance when you want to read the code with the article.

  5. http:// says:

    @joshka – thanks for the feedback and for reading my blog. I have to publish excerpts because feedburner places a size limit on blog entries (feedburner stops broadcasting a blog when it gets too long). I’ll look into a method for hiding/displaying different code versions. Thanks!

  6. http:// says:

    What if the client puts in a URL for a view that does not exist? I assume they get an error of some sort? What if we would like to display a “File (view) not found” page instead?

    Thanks.

  7. http:// says:

    I don’t see why this would be good other then for DRY. If you go to a page that doesn’t exist, you will encounter an exception (I would think, never tested it though) that the view doesn’t exist. At this point, does MVC push the user to the error page or does an ugly exception page show up?

  8. Andrei Rinea says:

    Great tip! I’ll try it out. I just love a DRY tip :)

  9. Thor Larholm says:

    This works great if you have actually created a corresponding view and simply want to leave out the glue code of returning a View with the same name as the action.

    However, if you have not created a corresponding view you will receive an InvalidOperationException when you trigger the ExecuteResult method. This is in contrast to the “404 Not Found” message you would otherwise receive on an unknown action.

    If you want to be both DRY and able to handle unknown action/view combinations you can add a custom route as suggested by “haacked” and then explicitly provide a fallback action by overriding the OnException method.

    routes.MapRoute(
    “Home”,
    “Home/{viewName}”,
    new { controller = “Home”, action = “ShowUnknownView” }
    );

    public ActionResult ShowUnknownView(string viewName)
    {
    return View(viewName);
    }

    protected override void OnException(ExceptionContext filterContext)
    {
    filterContext.HttpContext.Response.Redirect(“/Home”);
    }

  10. trendbender says:

    thx, very helpfull :)

  11. i will really appriciate that.

    Many thanks,

  12. yt I tried to mock a call to a Linq to SQL query, but I am struggling.

  13. very nice post thanks! i like it

    very interestng

  14. nvalidOperationException when you trigger the ExecuteResult method. This is in contrast to the “404 Not Found” message you would otherwise receive on an unknown action.

  15. A Good Date says:

    Very useful post, thanks..

  16. mevric says:

    nvalidOperationException when you trigger the ExecuteResult method. This is in contrast to the “404 Not Found” message you would otherwise receive on an unknown action. online ged

  17. mbv It looks like DataContextExtensions.cs line 45 of the Save method should pass the primaryKeyName through to Update.

  18. HD Video Converter says:

    As the users of HD Camcorders like Sony, Canon, Panasonic, this HD Video Converter is necessary to help us convert hd Video easily and quickly. The Converter for HD provides several practical editing functions to help you achieve ideal output effect. Trim function is to cut videos into clips which you can just convert and transfer to your player. Crop function helps you remove black bars around the movie. You could use Effect function to adjust video brightness, contrast, saturation and more parameters. More powerful and considerate functions are waiting for you to explore.Mac Video Converter l Rip Blu Ray l VOB Converter