2011-05-14 122 views
6

通常我在将数据提交给数据库之前,在动作方法中验证我的模型。我可以从操作过滤器返回操作结果吗?

[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小时过去了,以纪念这是正确的答案...

回答

6

我只是找到解决方案在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(); 
} 
+1

非常好,直到现在还不知道这个扩展点! – Oliver 2013-05-09 11:00:12

3

可以很明显的设定动作导致行动过滤器。但是,如果您使用ActionExecuting(filterContext.Result)来设置操作结果,那么您的控制器代码将不会被调用。我认为不是ActionFilter,如果额外的验证逻辑与模型绑定在一起,更好的解决方案是使用Custom Model binder。

希望有所帮助。

+0

的问题是,额外的验证逻辑是不是真的与模型绑。我试图实施的业务规则是为一家使用我的SaaS网站的公司定制的。他们想要一些额外的验证规则,不应该适用于其他用户。由于这个东西非常特殊,我想尽可能地避免使用常规代码。把额外的验证逻辑放到模型或控制器中正是我想要避免的。 – 2011-05-14 13:38:43

+0

@Adrian也许你可以创建不同的模型绑定器,并根据你的SaaS应用程序的客户端进行注册。事实上,如果使用Action过滤器,您仍然需要找出应用自定义逻辑的客户端。 – kazimanzurrashid 2011-05-14 15:45:40

1

你为什么不定义静态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); 
} 
+0

我试图实施的业务规则是为一家使用我的SaaS网站的公司定制的。他们想要一些额外的验证规则,不应该适用于其他用户。由于这个东西非常特殊,我想尽可能地避免使用常规代码。把额外的验证逻辑放到模型或控制器中正是我想要避免的。 – 2011-05-14 17:58:28