如何将ViewData传递给HandleError视图?

14
在我的Site.Master文件中,我有3个简单的ViewData参数(在整个解决方案中仅有的3个)。这些ViewData值对于我应用程序中的每个页面都非常重要。由于这些值在我的Site.Master中被使用,我创建了一个抽象的SiteController类来覆盖OnActionExecuting方法,并为我的解决方案中每个控制器上的每个Action方法填充这些值。
[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);

  }
}

我面临的问题是,当SiteController类级别的HandleErrorAttribute被触发时,这些值没有传递给MyErrorView(最终传递到Site.Master)。下面是一个简单的场景来展示我的问题:

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

我已经尝试通过在SiteController中重写OnException()方法来填充ViewData参数,但没有任何效果。 :(

在这种情况下,传递ViewData参数到Site.Master的最佳方法是什么?

2个回答

21
这是因为HandleErrorAttribute在出现错误时会更改传递给视图的ViewData。它会传递一个HandleErrorInfo类的实例,其中包含有关异常、控制器和操作的信息。
您可以使用下面实现的属性替换此属性:
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;
            }
        }
    }
}

哇,这比我想象的要复杂得多。 :) 我仔细研究了它,现在明白你在做什么以及为什么要这样做了。感谢你的答案! - Luc
“doesn't work”并不是特别有用的信息。我针对我自己和其他人进行了测试。你应该检查你正在使用的MVC版本,因为我相信已经有很多变化了。 - Dmytrii Nagirniak
在MVC3中出现了运行时错误。您的ViewDataDictionary<HandleErrorInfo>构造函数由于filterContext.Controller.ViewData中的不正确模型类型而失败。我仍然认为这个答案很有帮助并且点赞了,只需要进行一些小改动即可。 - fearofawhackplanet
1
也许您可以告诉我们实际错误的解决方案是什么? - Roger Far

3
如果你只需要通过ViewBag传递某些内容,可以像这样操作:(以下代码位于我所有控制器继承的BaseController中)
    protected override void OnException(ExceptionContext filterContext)
    {
        try
        {
            var v = filterContext.Result as ViewResult;
            v.ViewBag.DataToPassToError = "Hello World!";
        }
        catch { /* no-op */ }

        base.OnException(filterContext);
    }

请参见这里:如何在Error.cshtml视图中使用由过滤器放置在ViewBag中的数据?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接