如何在同一个视图中显示异常?

时间:2013-05-20 14:11:18

标签: asp.net-mvc

我搜索一种显示抛出异常的通用方法,而不重定向到错误页面,但在同一视图中显示它。我试过以下这些:

1)我首先尝试通过在global.asax中添加自定义过滤器并在我的Attribute类中覆盖public override void OnException(ExceptionContext filterContext)来处理它们,但是这样,我无法以我想要的方式填充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);
}

2 个答案:

答案 0 :(得分:0)

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

您需要某种BaseController操作才能返回新的View

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

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

BaseController:

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继承的特定控制器中,返回ActionResult具体为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);
}

答案 1 :(得分: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));
    }