在對FluentValidation有了初步的了解之後,也撰寫了InsertProductModel的驗證程式,並且透過單元測試,我們可以確認我們撰寫的驗證邏輯是沒有錯誤的,那麼我們在今天的分享之中,就要和大家一起來將FluentValidation整合到Api之中,讓Post到Controller的資料可以直接使用FluentValidation來進行驗證。
※與Api整合
-
在WebSite專案中使用Nuget加入FluentValidation.MVC4
-
在Utlity建立Extensions專案,新增ModelValidatorFactorycs,這支程式是用來整合DI Framework與FluentValidation,透過DI Framework提供FluentValidation所需要的Validator,如此一來如果我們需要動態更換Validator就不是一件難事了。
public class ModelValidatorFactory : ValidatorFactoryBase { public override IValidator CreateInstance(Type validatorType) { IValidator validator = DependencyResolver.Current.GetService(validatorType) as IValidator; return validator; } }
-
在WebSite的App_Start新增FluentValidationConfig.cs,註冊FluentValidation到MVC的ModelValidateProviders中
public class FluentValidationConfig { public static void Initialize() { var container = AutofacDependencyResolver.Current.ApplicationContainer as IContainer; var fluentValidationModelValidatorProvider = new FluentValidationModelValidatorProvider(new ModelValidatorFactory()); DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false; fluentValidationModelValidatorProvider.AddImplicitRequiredValidator = false; ModelValidatorProviders.Providers.Add(fluentValidationModelValidatorProvider); } }
-
記得在Global.asax中啟用它
FluentValidationConfig.Initialize();
-
這麼一來我們就完成了FluentValidation的整合,可以直接在Controller當中使用和原本一樣的方法來檢查Model是否正確,並且吐回錯誤訊息
[HttpPost] public ActionResult Create(InsertProductModel product) { if (this.ModelState.IsValid) { this.ProductService.InsertProduct(product); return Json(ApiStatusEnum.Success.ToString()); } else { string messages = string.Join("; ", this.ModelState.Values .SelectMany(x => x.Errors) .Select(x => x.ErrorMessage)); return Json(messages); } }
-
我們故意將CategoryId設為0,重新Post一次資料,可以看到API已經可以吐回FluentValidation的錯誤訊息囉!
※使用ActionFilter統一驗證處理方法
整合了FluentValidation之後,你可以看到使用起來就跟Asp.Net MVC原本提供的機制一模一樣,但如果我們要在所有Api提供的方法中都對輸入資料進行驗證的話,是不是就會產生大量重複的程式碼,又如果萬一某天需要修改驗證回應資訊的格式,或是有某些人回傳的訊息格式定義不一樣,是不是有可能有更多問題呢? 所以像這種幾乎大家的使用方法都一樣的流程,我們可以透過擴充Asp.Net MVC的ActionFilter來處理這樣的邏輯,讓有需要進行驗證的方法,只要在開頭加上一個Attribute,系統就會幫它處理掉其他的工作囉!
-
在Extensions新增ValidateRequestEntityAttribute,在Asp.Net MVC執行Action之前,先檢查輸入資料能否通過驗證,若不行就吐回錯誤訊息
public class ValidateRequestEntityAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { var modelState = filterContext.Controller.ViewData.ModelState; if (!filterContext.Controller.ViewData.ModelState.IsValid) { string errorMessages = string.Join("; ", modelState.Values .SelectMany(x => x.Errors) .Select(x => x.ErrorMessage)); filterContext.Result = new JsonResult() { Data=errorMessages, ContentEncoding = Encoding.UTF8, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } } }
-
改寫Controller,將原本驗證的邏輯移除,改為增加ValidateRequestEntity到函式上方
[HttpPost] [ValidateRequestEntity] public ActionResult Create(InsertProductModel product) { this.ProductService.InsertProduct(product); return Json(ApiStatusEnum.Success.ToString()); }
-
重新Post資料,發現驗證一樣有效,而且我們統一了回傳訊息格式
※什麼是ActionFilter?
Asp.Net MVC所提供的ActionFilter其實是一種Aop的實作模式,取代以往直接在程式碼中呼叫,它透過DataAnnotation的方式標記在Class或Function上頭,而Asp.Net MVC會在初始化或執行時期,根據DataAnnotation所標註的內容,執行對應的指令(例如上面的例子就是在執行Action之前,先檢查輸入資料是否符合驗證)
Action Filter可以將一些常用、通用的邏輯獨立出來並封裝(例如: Log、權限和Cache等),不但可以快速套用到需要的程式碼上,也可以讓每一個程式碼只包含它所需要的邏輯,降低閱讀時的雜訊
延伸閱讀:
* Understanding Action Filters
* AOP 觀念與術語
※撰寫ActionFilter的單元測試
因為ActionFilter可以讓所有需要的Controller都能夠套用,因此確保執行正確無誤也是很重要,所以接下來將對ActionFilter進行單元測試,有了單元測試我們也可以放心的隨時改寫ActionFilter來符合需求的變更。
-
在Utility建立Extentions.Text專案,並使用nuget加入需要的package
-
新增驗證輸入資料功能.feature,描述測試的功能
#language: zh-TW 功能: 驗證輸入資料功能 提供給 UI層 當系統傳入資料時,若驗證失敗傳回錯誤訊息,驗證成功則繼續進行Action
-
撰寫測試案例
場景: 驗證失敗時,回傳驗證失敗訊息 假設 使用者輸入資料驗證失敗 當 觸發驗證使用者傳入資料時 那麼 回傳驗證失敗訊息 場景: 驗證成功時,繼續執行Action 假設 使用者輸入資料驗證成功 當 觸發驗證使用者傳入資料時 那麼 繼續執行Action
-
完成測試
private ActionExecutingContext context; [Given(@"使用者輸入資料驗證失敗")] public void 假設使用者輸入資料驗證失敗() { HttpContextBase httpContext = MockRepository.GenerateStub<HttpContextBase>(); ControllerBase controller = MockRepository.GenerateStub<ControllerBase>(); controller.ViewData = new ViewDataDictionary(); controller.ViewData.ModelState.AddModelError("Error", "Error"); ControllerContext controllerContext = new ControllerContext(httpContext, new RouteData(), controller); this.context = new ActionExecutingContext(controllerContext, MockRepository.GenerateStub<ActionDescriptor>(), new Dictionary<string, object>()); } [Given(@"使用者輸入資料驗證成功")] public void 假設使用者輸入資料驗證成功() { HttpContextBase httpContext = MockRepository.GenerateStub<HttpContextBase>(); ControllerBase controller = MockRepository.GenerateStub<ControllerBase>(); controller.ViewData = new ViewDataDictionary(); ControllerContext controllerContext = new ControllerContext(httpContext, new RouteData(), controller); this.context = new ActionExecutingContext(controllerContext, MockRepository.GenerateStub<ActionDescriptor>(), new Dictionary<string, object>()); } [When(@"觸發驗證使用者傳入資料時")] public void 當觸發驗證使用者傳入資料時() { ValidateRequestEntityAttribute attribute = new ValidateRequestEntityAttribute(); attribute.OnActionExecuting(this.context); } [Then(@"回傳驗證失敗訊息")] public void 那麼回傳驗證失敗訊息() { Assert.IsFalse(this.context.Controller.ViewData.ModelState.IsValid); Assert.IsNotNull(this.context.Result); } [Then(@"繼續執行Action")] public void 那麼繼續執行Action() { Assert.IsTrue(this.context.Controller.ViewData.ModelState.IsValid); Assert.IsNull(this.context.Result); }
-
執行測試
※本日小結
將FluentValidation整合到Asp.Net MVC之後,不但可以使用原有熟悉的方式進行資料驗證,還透過撰寫自訂的ActionFilter來讓驗證可以更輕鬆的套用,除此之外,由於我們是透過Autofac來綁定Model和Validator之間的關聯,因此隨時都可以輕鬆的替換驗證邏輯,讓我們的程式碼更加具有彈性!關於今天的內容,歡迎大家一起討論喔^_^
转 https://ithelp.ithome.com.tw/articles/10136729
标签:我們,Web,Asp,FluentValidation,資料,Api,驗證,new,public From: https://www.cnblogs.com/wl-blog/p/17221409.html