首页 > 编程语言 >使用Asp.Net MVC打造Web Api (12) - 整合FluentValidation到Api中

使用Asp.Net MVC打造Web Api (12) - 整合FluentValidation到Api中

时间:2023-03-16 10:45:36浏览次数:54  
标签:我們 Web Asp FluentValidation 資料 Api 驗證 new public

在對FluentValidation有了初步的了解之後,也撰寫了InsertProductModel的驗證程式,並且透過單元測試,我們可以確認我們撰寫的驗證邏輯是沒有錯誤的,那麼我們在今天的分享之中,就要和大家一起來將FluentValidation整合到Api之中,讓Post到Controller的資料可以直接使用FluentValidation來進行驗證。
※與Api整合

  1. 在WebSite專案中使用Nuget加入FluentValidation.MVC4

  2. 在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;
            }
        }
    
  3. 在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);
            }
        }
    
  4. 記得在Global.asax中啟用它

        FluentValidationConfig.Initialize();
    
  5. 這麼一來我們就完成了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);
            }
        }
    
  6. 我們故意將CategoryId設為0,重新Post一次資料,可以看到API已經可以吐回FluentValidation的錯誤訊息囉!

※使用ActionFilter統一驗證處理方法
整合了FluentValidation之後,你可以看到使用起來就跟Asp.Net MVC原本提供的機制一模一樣,但如果我們要在所有Api提供的方法中都對輸入資料進行驗證的話,是不是就會產生大量重複的程式碼,又如果萬一某天需要修改驗證回應資訊的格式,或是有某些人回傳的訊息格式定義不一樣,是不是有可能有更多問題呢? 所以像這種幾乎大家的使用方法都一樣的流程,我們可以透過擴充Asp.Net MVC的ActionFilter來處理這樣的邏輯,讓有需要進行驗證的方法,只要在開頭加上一個Attribute,系統就會幫它處理掉其他的工作囉!

  1. 在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
                    };
                }
            }
        }
    
  2. 改寫Controller,將原本驗證的邏輯移除,改為增加ValidateRequestEntity到函式上方

        [HttpPost]
        [ValidateRequestEntity]
        public ActionResult Create(InsertProductModel product)
        {
            this.ProductService.InsertProduct(product);
    
            return Json(ApiStatusEnum.Success.ToString());
        }
    
  3. 重新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來符合需求的變更。

  1. 在Utility建立Extentions.Text專案,並使用nuget加入需要的package

  2. 新增驗證輸入資料功能.feature,描述測試的功能

    	#language: zh-TW
    	功能: 驗證輸入資料功能
    		提供給 UI層
    		當系統傳入資料時,若驗證失敗傳回錯誤訊息,驗證成功則繼續進行Action
    
  3. 撰寫測試案例

    	場景: 驗證失敗時,回傳驗證失敗訊息
    		假設 使用者輸入資料驗證失敗
    		當 觸發驗證使用者傳入資料時
    		那麼 回傳驗證失敗訊息
    
    	場景: 驗證成功時,繼續執行Action
    		假設 使用者輸入資料驗證成功
    		當 觸發驗證使用者傳入資料時
    		那麼 繼續執行Action
    
  4. 完成測試

        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);
        }
    
  5. 執行測試

※本日小結
將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

相关文章

  • WEB 项目文件夹上传下载解决方案
    ​ 最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现。在某些业务中,大文件上传是一个比较重要的......
  • 我们为什么要阅读webpack源码
    相信很多人都有这个疑问,为什么要阅读源码,仅仅只是一个打包工具,会用不就行了,一些配置项在官网,或者谷歌查一查不就好了吗,诚然在大部分的时候是这样的,但这样在深入时也会遇到......
  • Web.Config 配置重定向到首页
    Web.Config重定向规则,IIS会根据里面的规则自动重定向;#webredirecttoHomepage<rulename="RootHitForceHTTPSRedirection"enabled="true"stopProcessing="tr......
  • 新手必看的jQuery参考手册主要API
    本文整理了一些主要的jQueryAPI,其中包括jQuery核心函数和方法、jQuery属性参考手册、jQueryCSS操作、jQuery选择器、jQuery文档操作、jQuery筛选操作、jQuery事件方法、j......
  • 记websocket调用feign失败
    在http中正常使用的feign接口,在websocket直接失败了,本来以为是没拿到对象,发现其实拿到了。后来发现feign接口如果已经被aop处理过,就会报jdkProxy的错误。修改一下AOP类的......
  • HTML5智慧仓库Web3D可视化管理平台
    随着5G技术的不断普及,万物互联已经不再遥远。近年来,随着电商业务的飞速发展,仓储物流的压力也越来越大,电子商务的订单不同于一般的B2B订单,其中一个最大的特点就是碎片化严重......
  • maui BlazorWebView Android 中混合使用https和http
    <BlazorWebViewHostPage="wwwroot/index.html"BlazorWebViewInitialized="blazorWebView_BlazorWebViewInitialized"> privatevoidblazorWebView_BlazorWebVie......
  • eWebEditor粘贴word图片且图片文件自动上传功能
    ​图片的复制无非有两种方法,一种是图片直接上传到服务器,另外一种转换成二进制流的base64码目前限chrome浏览器使用首先以um-editor的二进制流保存为例:打开umeditor.js,......
  • VC MFC 使用 soap方式与web服务器交互
    首先安装MicrosoftSOAPToolkit3.0  soapsdk.exe链接:https://pan.baidu.com/s/15NeiuXQm4Wnvjvd8ngPwsA提取码:l8yh  包含头,引用库,命名空间#import"msxml6.d......
  • aspose.cell 把一个Excel的Sheet拷贝到另一个Excel中,并插入在第一个位置
    1、aspose.cell把一个Excel的Sheet拷贝到另一个Excel中,并插入在第一个位置。什么?难道放在最后不行嘛,还要这么变态要求。先说一下,Copy之后放在最后的位置怎么操作。......