1

我寻找一种通用方式来显示抛出的异常,而不重定向到错误页面,而是在同一个视图中显示它。我在下面尝试了这些:

1)我首先尝试通过在 global.asax 中添加自定义过滤器并public override void OnException(ExceptionContext filterContext)在我的 Attribute 类中覆盖来处理它们,但这样,我无法以我想要的方式填充 filterContext.Result,因为视图的旧模型不是可达,所以我只能重定向到错误页面,但这不是我想要的。

2)然后我试图在我的BaseController(我的所有控制器都继承自它)上捕获异常。我再次public override void OnException(ExceptionContext filterContext)在我的控制器中覆盖并将异常详细信息等放入 ViewBag 并将页面重定向到相同的视图,filterContext.HttpContext.Response.Redirect(filterContext.RequestContext.HttpContext.Request.Path );但 ViewBag 内容在重定向页面中丢失了,所以我想不出任何其他方式?

我怎样才能做到这一点?我写的代码示例BaseController如下:

protected override void OnException(ExceptionContext filterContext) {
    var controllerName = (string)filterContext.RouteData.Values["controller"];
    var actionName = (string)filterContext.RouteData.Values["action"];

    //filterContext.Result = new ViewResult
    //{
    //    ViewName = actionName,
    //    ViewData = new ViewDataDictionary<??>(??),
    //    TempData = filterContext.Controller.TempData,
    //};

    filterContext.ExceptionHandled = true;
    filterContext.HttpContext.Response.Clear();

    filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
    ModelState.AddModelError("Error", filterContext.Exception.Message);
    ViewBag.das = "dasd";
    filterContext.HttpContext.Response.Redirect(filterContext.RequestContext.HttpContext.Request.Path);
}
4

2 回答 2

0

也许你可以在你的BaseController类中设置一个属性来获得你想要使用的视图的名称,在任何控制器操作处理请求时设置它。然后OnException()你可以有一个方法,重定向到一个控制器操作,它只返回一个View对应于视图名称的方法?每个控制器操作都必须在执行其他任何操作之前设置默认视图名称,因为只有它知道它会调用哪个视图(如果有的话),以及它可能被哪个视图调用。

您需要某种BaseController返回 new 的操作View

路由可能需要或许多不需要配置以具有某种可选参数,您可以将其设置为要发送到视图的错误信息。例如,在默认路由中:

routes.MapRoute(RouteNames.Default,
                "{controller}/{action}/{id}",
                new {controller = "Home", action = "Index", id = "", errorInfo = UrlParameter.Optional}

基础控制器:

protected ActionResult ErrorHandler()
{
    ViewBag.das = (string)filterContext.RouteData.Values["errorInfo"];
    return View(ViewName); 
}

protected string ViewName { get; set; }

protected void GoToErrorView(ExceptionContext context, string exceptionData)
{
    var actionName = "ErrorHandler";
    var newVals = new RouteValueDictionary();
    newVals.Add("errorInfo", exceptionData);
    this.RedirectToAction(actionName, newVals);
}

BaseController.OnException()

    // ...
    filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
    ModelState.AddModelError("Error", filterContext.Exception.Message);
    // anything else you need to do to prepare what you want to display
    string exceptionData = SomeSortOfDataYouWantToPassIntoTheView;
    this.GoToErrorView(filterContext, exceptionData);
}

在从其继承的特定控制器中,具体BaseController返回一个ActionResulta ViewResult

[HttpGet]
public ActionResult Index()
{
    ViewName = <set whatever view name you want to here>
    // code here, including preparing the Model
    // ...
    var model = new MyViewModel();
    model.SomethingIWantToGiveTheView = someDataThatItNeeds;
    // ...

    return View(<model name>, model);
}
于 2013-05-20T20:20:55.107 回答
0

我不久前找到了解决方案并添加了解决方案,以便它可以帮助其他人。我使用 TempData 和 _Layout 来显示错误:

public class ErrorHandlerAttribute : HandleErrorAttribute
{
    private ILog _logger;

    public ErrorHandlerAttribute()
    {
        _logger = Log4NetManager.GetLogger("MyLogger");
    }

    public override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.ExceptionHandled)
        {
            return;
        }

        if (!ExceptionType.IsInstanceOfType(filterContext.Exception))
        {
            return;
        }

        // if the request is AJAX return JSON else view.
        if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
        {
            filterContext.Result = new JsonResult
            {
                JsonRequestBehavior = JsonRequestBehavior.AllowGet,
                Data = new
                {
                    error = true,
                    message = filterContext.Exception.Message
                }
            };
            filterContext.HttpContext.Response.StatusCode = 500;
        }

        // log the error using log4net.
        _logger.Error(filterContext.Exception.Message, filterContext.Exception);

        filterContext.ExceptionHandled = true;
        filterContext.HttpContext.Response.Clear();

        filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;

        if (filterContext.HttpContext.Request.Headers["X-Requested-With"] != "XMLHttpRequest")
        {
            if (filterContext.Controller.TempData["AppError"] != null)
            {
                //If there is a loop it will break here.
                filterContext.Controller.TempData["AppError"] = filterContext.Exception.Message;
                filterContext.HttpContext.Response.Redirect("/");
            }
            else
            {
                int httpCode = new HttpException(null, filterContext.Exception).GetHttpCode();

                switch (httpCode)
                {
                    case 401:
                        filterContext.Controller.TempData["AppError"] = "Not Authorized";
                        filterContext.HttpContext.Response.Redirect("/");
                        break;
                    case 404:
                        filterContext.Controller.TempData["AppError"] = "Not Found";
                        filterContext.HttpContext.Response.Redirect("/");
                        break;
                    default:
                        filterContext.Controller.TempData["AppError"] = filterContext.Exception.Message;
                        //Redirect to the same page again(If error occurs again, it will break above)
                        filterContext.HttpContext.Response.Redirect(filterContext.RequestContext.HttpContext.Request.RawUrl);
                        break;
                }
            }
        }
    }
}

在 Global.asax 中:

    protected void Application_Error(object sender, EventArgs e)
    {
        var httpContext = ((MvcApplication)sender).Context;
        var ex = Server.GetLastError();

        httpContext.ClearError();
        httpContext.Response.Clear();
        httpContext.Response.StatusCode = ex is HttpException ? ((HttpException)ex).GetHttpCode() : 500;
        httpContext.Response.TrySkipIisCustomErrors = true;

        var routeData = new RouteData();
        routeData.Values["controller"] = "ControllerName";
        routeData.Values["action"] = "ActionName";
        routeData.Values["error"] = "404"; //Handle this url paramater in your action
        ((IController)new AccountController()).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));
    }
于 2013-06-11T05:46:16.170 回答