How do I pass ViewData to a HandleError View?
Asked Answered
D

2

14

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?

Decameter answered 25/11, 2009 at 5:53 Comment(0)
P
21

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;
            }
        }
    }
}
Psia answered 25/11, 2009 at 6:13 Comment(4)
Wow, this was a lot more complex than I thought it would be. :) I've looked through it to understand it and it makes perfect sense what your doing and why you're doing it. Thanks for your answer!Decameter
"doesn't work" isn't particularly useful. I did for me and others. You should check the MVC version you're using as I'm sure a lot has changed.Psia
It gives a run time error in MVC3. Your ViewDataDictionary<HandleErrorInfo> constructor fails due to the incorrect model type in filterContext.Controller.ViewData. I found the answer helpful and upvoted anyway, it just required a small change.Duplessis
Maybe you can tell us what the actual solution was of the error?Unschooled
F
3

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?

Farmhand answered 8/4, 2013 at 23:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.