【问题标题】:Returning a view with it's model from an ActionFilterAttribute从 ActionFilterAttribute 返回带有模型的视图
【发布时间】:2009-11-03 19:18:37
【问题描述】:

在强类型视图上使用内置验证助手实现错误处理时,您通常会在控制器中创建一个 try/catch 块,并将其对应模型的视图作为参数返回给 View()方法:


控制器

public class MessageController : Controller
{
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(Models.Entities.Message message)
    {
        try
        {
            // Insert model into database
            var dc = new DataContext();
            dc.Messages.InsertOnSubmit(message);
            dc.SubmitChanges();

            return RedirectToAction("List");
        }
        catch
        {
            /* If insert fails, return a view with it's corresponding model to
               enable validation helpers */
            return View(message);
        }
    }
}



视图

<%@ Page
    Language="C#"
    Inherits="System.Web.Mvc.ViewPage<Models.Entities.Message>" %>

<%= Html.ValidationSummary("Fill out fields marked with *") %>

<% using (Html.BeginForm()) { %>

    <div><%= Html.TextBox("MessageText") %></div>

    <div><%= Html.ValidationMessage("MessageText", "*") %></div>

<% } %>



我以 ActionFilterAttribute 的形式实现了一个简单的错误处理程序,它将能够重定向到通用错误视图,或重定向到引发异常的视图,并让验证助手栩栩如生。

这是我的 ActionFilterAttribute 的外观:

public class ErrorLoggingAttribute : ActionFilterAttribute, IExceptionFilter
{
    private Boolean _onErrorRedirectToGenericErrorView;

    /// <param name="onErrorRedirectToGenericErrorView">
    /// True: redirect to a generic error view.
    /// False: redirect back the view which threw an exception
    /// </param>
    public ErrorLoggingAttribute(Boolean onErrorRedirectToGenericErrorView)
    {
        _onErrorRedirectToGenericErrorView = onErrorRedirectToGenericErrorView;
    }

    public void OnException(ExceptionContext ec)
    {
        if (_onErrorRedirectToGenericErrorView)
        {
            /* Redirect back to the view where the exception was thrown and
               include it's model so the validation helpers will work */
        }
        else
        {
            // Redirect to a generic error view
            ec.Result = new RedirectToRouteResult(new RouteValueDictionary
            {
                {"controller", "Error"},
                {"action", "Index"}
            });

            ec.ExceptionHandled = true;
        }
    }
}

重定向到引发异常的视图相当简单。但关键是:为了让验证助手工作,您需要为视图提供它的模型。

您将如何返回引发异常的视图并为视图提供相应的模型? (在本例中为 Models.Entities.Message)。

【问题讨论】:

    标签: c# asp.net-mvc asp.net-mvc-routing


    【解决方案1】:

    我让它工作了!

    出于某种奇怪的原因,我需要做的就是将ViewData 传递给新的ResultView

    这是完整的代码:

    public class ErrorLoggingAttribute : ActionFilterAttribute, IExceptionFilter
    {
        private String _controllerName, _actionName;
        private Boolean _redirectToGenericView = false;
    
    
        public ErrorLoggingAttribute()
        {
        }
    
    
        public ErrorLoggingAttribute(String actionName, String controllerName)
        {
            _controllerName = controllerName;
            _actionName = actionName;
            _redirectToGenericView = true;
        }
    
    
        void IExceptionFilter.OnException(ExceptionContext ec)
        {
            // log error
    
            if (_redirectToGenericView)
            {
                ec.Result = new RedirectToRouteResult(new RouteValueDictionary
                {
                    {"controller", _controllerName},
                    {"action", _actionName}
                });
            }
            else
            {
                ec.Result = new ViewResult
                {
                    ViewName = ((RouteData) ec.RouteData).Values["action"].ToString(),
                    TempData = ec.Controller.TempData,
                    ViewData = ec.Controller.ViewData
                };
            }
    
            ec.ExceptionHandled = true;
        }
    }
    


    用法


    以下是您如何在控制器操作上使用该属性,以在发生异常时重定向到相同的视图(with 它的关联模型)以启用标准验证助手:

    [ErrorLogging]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(Models.Entities.Message message)
    {
        var dc = new Models.DataContext();
        dc.Messages.InsertOnSubmit(message);
        dc.SubmitChanges();
    
        return RedirectToAction("List", new { id = message.MessageId });
    }
    

    当发生异常时,您将如何使用该属性重定向到通用视图:

    [ErrorLogging("ControllerName", "ViewName")]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(Models.Entities.Message message)
    


    这是逻辑的完全分离。控制器中什么都没有,只有最基本的。

    【讨论】:

    • 您的控制器应该专注于应用程序流,使用这种方法通过将其错误部分转换为属性来模糊逻辑应用程序流。仅仅因为你可以,并不意味着你应该。
    • 太好了。这几天一直在为此苦苦挣扎。不过,不得不稍微修改一下我的代码。我使用通过 jQuery ajax 呈现的部分视图。现在控制器完全没有 try catch final 了。 ((RouteData) ec.RouteData).Values["action"].ToString() 返回视图的名称,但我需要部分视图的名称。
    • 从您的 OnException 方法中借用了一些代码。 Sweet... :D 感谢您提出并回答您自己的问题!
    【解决方案2】:

    由于您从 OnActionExecuting 继承 ActionFilterAttribute,因此您可以获取模型。

      public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var model = filterContext.Controller.ViewData.Model as YourModel;
    
            ...
        }
    

    但是在MVC系统中已经定义了HandleError,你为什么不使用这个而不是自己烘焙呢。

    我建议你在这个问题上阅读blog

    【讨论】:

    • 如何将模型分配给视图并重定向到该视图?
    • ExceptionContext ec 有一个 result 属性,你可以在那里分配你的模型。 ec.Result = new ViewResult { ViewName = View, MasterName = Master, ViewData = yourmodel, TempData = filterContext.Controller.TempData };
    • 只有一个问题:模型在IActionFilter IResultFilter的两个方法中都是空的。这个好像不行。
    • 我发现了如何让它工作。请原谅我暂时投反对票。我总是投票支持那些花时间帮助我的人。赏金计时器到期后,我会立即对您的答案进行投票。 (我无法接受我自己对附带赏金问题的回答)。
    【解决方案3】:

    如果您的操作引发异常,则无法将模型传递给视图,因为模型可能尚未创建 - 或者尚未完全创建。这可能就是结果为空的原因。抛出异常后不能依赖数据。

    但是您可以像这样将“默认”模型传递给您的操作过滤器:

    [ErrorLogging(new EmptyModel())] 
    // or to create using Activator
    [ErrorLogging(typeof(EmptyModel))]
    // or even set view name to be displayed
    [ErrorLogging("modelerror", new EmptyModel())]
    

    这样,您的过滤器将传递您明确设置为在发生错误时显示的“错误模型”。

    【讨论】:

    • 我发现了如何让它工作。请原谅我暂时投反对票。我总是投票支持那些花时间帮助我的人。赏金计时器到期后,我会立即对您的答案进行投票。 (我无法接受我自己对附带赏金问题的回答)。
    • 虽然我一直很欣赏人们彼此公平竞争,但我并不是很喜欢 SO 投票系统 - 这不是我生活的重要组成部分 ;-) 所以我并不介意-投票。不过还是谢谢啦。
    【解决方案4】:
    public class MessageController : Controller
    {
      public ActionResult Create()
      {
        return View();
      }
    
      [AcceptVerbs(HttpVerbs.Post)]
      public ActionResult Create( Message message )
      {
        try
        {
          // Exceptions for flow control are so .NET 1.0 =)
          // ... your save code here
        }
        catch
        {
          // Ugly catch all error handler - do you really know you can fix the problem?  What id the database server is dead!?!
          return View();
        }
      }
    }
    

    模型的详细信息已经存在于模型状态中。任何错误也应该已经存在于模型状态中。您的异常处理程序只需要处理您想要重定向到通用错误页面的情况。更好/更明显的是把属性扔掉,如果你想在 catch 中重定向,返回一个重定向结果。

    【讨论】:

    • 我发现了如何让它发挥作用。请原谅我临时投反对票。我总是投票支持那些花时间帮助我的人。赏金计时器到期后,我会立即对您的答案进行投票。 (我无法接受我自己对附带赏金问题的回答)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-11
    • 1970-01-01
    • 1970-01-01
    • 2019-05-20
    相关资源
    最近更新 更多