2011-05-14 165 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