How do I pass ViewData to a HandleError View?

8.5k views Asked by At

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?

2

There are 2 answers

4
Dmytrii Nagirniak On BEST ANSWER

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;
            }
        }
    }
}
0
Aaron Hoffman On

If you just need to pass something via ViewBag it can be done like this: (this happens to be in a BaseController all my controllers inherit from)

    protected override void OnException(ExceptionContext filterContext)
    {
        try
        {
            var v = filterContext.Result as ViewResult;
            v.ViewBag.DataToPassToError = "Hello World!";
        }
        catch { /* no-op */ }

        base.OnException(filterContext);
    }

See Here: How can I use data placed into a ViewBag by a filter in my Error.cshtml view?