Extending Controller Class to Handle Unknown Action at One Place in MVC

Introduction
 
The HandleUnknownAction method is invoked when a controller is unable to find an appropriate action method to execute when a browser requests it. 
 
This method is a virtual method that comes with the MVC's controller class that our user-defined controller inherits. For example HomeController, DemoContorller and so on.
 
Syntax : protected virtual void HandleUnknownAction(string actionName); 
 
The following is the syntax of HomeController:
  1. public class HomeController : Controller  // Base controller   
  2. {    
  3.     //    
  4.     // GET: /Home/    
  5.   
  6.     public ActionResult Index()    
  7.     {    
  8.         return View();    
  9.     }   
  10. } 
So we can override this method in our controllers  to handle the "not found" error when an action method is not found.  But I think it might be good if we place this in one place so that all our controllers in our app can use it from there.

Let's check this with a demo app.
 
Demo Application
 
Step 1

Create a new MVC project: 
 
 asp dot net mvc
 
Now choose Basic MVC.
 
  
 
Step 2

Add a controller to our application. Right-click on the Controllers folder in the Solution Explorer then select "Add" -> "Controller...".
 
 
 
Provide the name "HomeContoller" to that controller.
 
 
 
Repeat Step 2 to create another controller "DemoController".

Step 3

Add Index views for both controllers.
 
Open HomeController.cs and right-click on the HomeController's Index Action method. 
 
 
 
 Name the view as "Index".
 
 
 
Do the same to create an Index view for DemoController. Now we build our app try to run it using the following URLs.
  •  http://localhost:64220/Home/Index
  •  http://localhost:64220/Demo/Index  
The preceding URLs work fine because the Home and Demo controllers have an "Index" method. But it will get the following error if we give something else instead of "Index" in the URL because both are a controller.
 
 
We can handle this error by overriding the abstract  HandleUnknownAction in our controllers in the following way.  
  1. public class HomeController : Controller    
  2. {    
  3.     //    
  4.     // GET: /Home/    
  5.   
  6.     public ActionResult Index()    
  7.     {    
  8.         return View();    
  9.     }    
  10.   
  11.     protected override void HandleUnknownAction(string actionName)    
  12.     {    
  13.         //Code to handle the error.  
  14.     }    
  15. } 
Then the error will not exist if the browser tries to execute any action method that is not available in the HomeController.  Okay, great . But what about the DemoController? Do we need to override HandleUnknownAction there too? Do we need to do the same for every controller that we add in the future? Can't we put that handling in one place so that all controllers can access it without modifying them and without duplicating the same code? Good question, right? This question came to my mind when I was reading an article.  Actually that is a good blog post . 
 
Step 4

To avoid that we need to create another controller that overrides HandleUnknownAction. Right-click on the "Controller" folder in Solution Explorer yhen select Add then click on Controller.  Name it "MyBaseController". Inside that class remove the index action method and add the following method. 
  1. protected override void HandleUnknownAction(string actionName)    
  2. {    
  3.     string errorMsg = "The Action: \"" + actionName + "\" is not found in controller \"" +     
  4.                       this.ControllerContext.RouteData.Values["controller"] + "\"";    
  5.     ViewBag.ErrMsg = errorMsg;    
  6.     this.View("UnHandledActionError").ExecuteResult(this.ControllerContext);                  
  7. } 
Now create a View to serve as a response whenever the HandleUnknownAction method fires. Go to Solution Explorer and select Views then right-click on the Shared folder then select Add then click on View. Name it "UnHandledActionError".  Open that view and add the following code to it. 
  1. @{  
  2.     ViewBag.Title = "UnHandledActionError";  
  3. }  
  4.   
  5. <h2>Unhandled Action Error</h2>  
  6. <h2 style="color:Red">Error : @ViewBag.ErrMsg</h2>  
Step 5

Remove the HandleUnknownAction method from our HomeController.cs. Open the HomeController.cs and DemoController.cs files and replace "Controller" from the code with 'MyBaseController" as in the following:
 
HomeController.cs:
  1. public class HomeController : MyBaseController // Our Custom base controller    
  2. {    
  3.     //    
  4.     // GET: /Home/    
  5.   
  6.     public ActionResult Index()    
  7.     {    
  8.         return View();    
  9.     }  
  10. } 
DemoController.cs
  1. public class DemoController : MyBaseController //Our custom base controller    
  2. {    
  3.     //    
  4.     // GET: /Demo/    
  5.   
  6.     public ActionResult Index()    
  7.     {    
  8.         return View();    
  9.     }  
  10. } 
Step 6

Now we execute the URLs that gave an error the last time. They got the given default error response now. They gave the custom response like the following.

For http://localhost:64220/Home/Test:
 
  
For http://localhost:64220/Demo/test:
 
 
 
See, our error handling code is now in one place, in the MyBaseController class and both of our controllers use that function by inheriting  that class. If we need to add any other controller to our app then we simply create that controller and inherit the MyBaseController class in it. Then the HandleUknownAction method will be inherited to it. No need to add that to our new controller explicitly. 
  1. public class SomeOtherController : MyBaseController  
  2. {  
  3.     //  
  4.     // GET: /Demo/  
  5.   
  6.     public ActionResult Index()  
  7.     {  
  8.         return View();  
  9.     }  
  10.   
  11. }  
I have added the code base. You can find that at the top of the article. You can download and test it.
 
Thanks for reading.


Similar Articles