asp.net-mvcviewdatahandleerror

How do I pass ViewData to a HandleError View?


In my Site.Master file, I have 3 simple ViewData parameters (the only 3 in my entire solution). These ViewData values are critical for every single page in my application. Since these values are used in my Site.Master, I created an abstract SiteController class that overrides the OnActionExecuting method to fill these values for every Action method on every controller in my solution.

[HandleError(ExceptionType=typeof(MyException), View="MyErrorView")]
public abstract class SiteController : Controller
{
  protected override void OnActionExecuting(...)
  {
    ViewData["Theme"] = "BlueTheme";
    ViewData["SiteName"] = "Company XYZ Web Portal";
    ViewData["HeaderMessage"] = "Some message...";        

    base.OnActionExecuting(filterContext);

  }
}

The problem that I'm faced with is that these values are not being passed to MyErrorView (and ultimately Site.Master) when the HandleErrorAttribute kicks in from the SiteController class level attribute. Here is a simple scenario to show my problem:

public class TestingController : SiteController
{
  public ActionResult DoSomething()
  {
    throw new MyException("pwnd!");
  }
}

I've tried filling the ViewData parameters by overriding the OnException() method in my SiteController as well, but to no avail. :(

What is the best way to pass the ViewData parameters to my Site.Master in this case?


Solution

  • This is because HandleErrorAttribute changes the ViewData passed to the view when an error occurs. It passes an instance of HandleErrorInfo class with information about Exception, Controller and Action.

    What you can do is replace this attribute with the one implemented below:

    using System;
    using System.Web;
    using System.Web.Mvc;
    
    public class MyHandleErrorAttribute : HandleErrorAttribute {
    
        public override void OnException(ExceptionContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }
            if (!filterContext.ExceptionHandled && filterContext.HttpContext.IsCustomErrorEnabled)
            {
                Exception innerException = filterContext.Exception;
                if ((new HttpException(null, innerException).GetHttpCode() == 500) && this.ExceptionType.IsInstanceOfType(innerException))
                {
                    string controllerName = (string) filterContext.RouteData.Values["controller"];
                    string actionName = (string) filterContext.RouteData.Values["action"];
                    // Preserve old ViewData here
                    var viewData = new ViewDataDictionary<HandleErrorInfo>(filterContext.Controller.ViewData); 
                    // Set the Exception information model here
                    viewData.Model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
                    filterContext.Result = new ViewResult { ViewName = this.View, MasterName = this.Master, ViewData = viewData, TempData = filterContext.Controller.TempData };
                    filterContext.ExceptionHandled = true;
                    filterContext.HttpContext.Response.Clear();
                    filterContext.HttpContext.Response.StatusCode = 500;
                    filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
                }
            }
        }
    }