【问题标题】:Can I return an action result from an action filter?我可以从动作过滤器返回动作结果吗?
【发布时间】:2011-05-14 10:49:37
【问题描述】:

通常在将数据提交到数据库之前,我会在操作方法中验证我的模型。

[HttpPost]
public ActionResult MyActionMethod(MyModelType model){
if (ModelState.IsValid){
   //commit changes to database...
   return View("SuccessView",model);
}
return View(model);
}

但在极少数情况下,我需要在提交模型时在业务层执行一些额外的验证。如果发生验证错误,我想在业务层中引发异常并使用该异常返回带有验证错误的视图。

我正在寻找一种方法来实现这一点,而无需更改控制器中的任何代码。所以我正在寻找一种方法来避免这种情况:

[HttpPost]
public ActionResult MyActionMethod(MyModelType model){
if (ModelState.IsValid){
   try {
   //commit changes to database...
   } catch (ValidationException e){
      ModelState.AddModelError(...);
      return View(model);
   }
   return View("SuccessView",model);

}
return View(model);
}

有没有办法做到这一点?

我正在考虑一个动作过滤器,它可以捕获 ValidationExceptions 并在常规 [HandleError] 过滤器启动之前返回带有验证错误的合适视图。这样的事情可能吗?

编辑:我刚刚找到了解决方案(见下文),但在 48 小时过去之前我无法将其标记为正确答案...

【问题讨论】:

    标签: asp.net-mvc validation action-filter actionresult


    【解决方案1】:

    我在 ASP.NET MVC 源代码中搜索了一下才找到解决方案:

    它不能用动作过滤器来完成,因为它是在调用动作方法之前和之后调用的,但它实际上并没有包装动作方法调用。

    但是,可以使用自定义 ActionMethodInvoker 来完成:

    public class CustomActionInvoker : ControllerActionInvoker
    {
        protected override ActionResult InvokeActionMethod(
            ControllerContext controllerContext, 
            ActionDescriptor actionDescriptor, 
            System.Collections.Generic.IDictionary<string, object> parameters)
        {
            try
            {
                //invoke the action method as usual
                return base.InvokeActionMethod(controllerContext, actionDescriptor, parameters);
            }
            catch(ValidationException e)
            {
                //if some validation exception occurred (in my case in the business layer) 
                //mark the modelstate as not valid  and run the same action method again
                //so that it can return the proper view with validation errors. 
                controllerContext.Controller.ViewData.ModelState.AddModelError("",e.Message);
                return base.InvokeActionMethod(controllerContext, actionDescriptor, parameters);
            }
        }
    }
    

    然后,在控制器上:

    protected override IActionInvoker CreateActionInvoker()
    {
        return new CustomActionInvoker();
    }
    

    【讨论】:

      【解决方案2】:

      您显然可以在动作过滤器中设置动作结果。但是如果您使用 ActionExecuting (filterContext.Result) 来设置操作结果,那么您的控制器代码将不会被调用。我认为如果额外的验证逻辑与模型相关联,而不是 ActionFilter,则更好的解决方案是使用自定义模型绑定器。

      希望对您有所帮助。

      【讨论】:

      • 问题在于额外的验证逻辑并没有真正与模型绑定。我尝试实施的业务规则是为一家使用我的 SaaS 网站的公司定制的。他们想要一些不应该适用于其他用户的额外验证规则。由于这些东西很特别,我想尽可能地把它排除在我的常规代码之外。将额外的验证逻辑放入模型或控制器中正是我想要避免的。
      • @Adrian 也许您可以创建不同的模型绑定器并根据您的 SaaS 应用程序的客户端进行注册。事实上,在 Action 过滤器的情况下,您仍然必须找出应用自定义逻辑的客户端。
      【解决方案3】:

      您为什么不定义一个静态 BusinessValidator 帮助器并执行以下操作:

      [HttpPost]
      public ActionResult MyActionMethod(MyModelType model){
      var businessErrors = null;
      if ((ModelState.IsValid) && (BusinessValidator<MyModelType>.IsValid(model, out businesErrors)){
         //commit changes to database...
         return View("SuccessView",model);
      }
      
      if (businessErrors != null)
      {
       // TODO: add errors to the modelstate
      }
      
      return View(model);
      }
      

      【讨论】:

      • 我尝试实施的业务规则是为一家使用我的 SaaS 网站的公司定制的。他们想要一些不应该适用于其他用户的额外验证规则。由于这些东西很特别,我想尽可能地把它排除在我的常规代码之外。将额外的验证逻辑放入模型或控制器中正是我想要避免的。
      猜你喜欢
      • 2011-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-22
      相关资源
      最近更新 更多