首頁  >  文章  >  後端開發  >  ASP.NET全端開發之在MVC中使用服務端驗證(二)

ASP.NET全端開發之在MVC中使用服務端驗證(二)

无忌哥哥
无忌哥哥原創
2018-07-20 09:20:121754瀏覽

 首先聲明,這篇部落格文章是完善.ASP.NET全端開發之在MVC中使用服務端驗證的,所以重複內容,我就不過多的闡述,很多問題都是在實踐中去發現,然後再去完善,這篇文章也一樣,建立在已閱「.ASP.NET全端開發之在MVC中使用服務端驗證」 的基礎上。

在上一篇文章中,雖然我們完成了服務端驗證,但我們還是需要在Action裡呼叫驗證器來進行驗證,像這樣。

   [HttpPost]        
       public ActionResult ValidatorTest(Person model)
        {var result = this.ValidatorHub.PersonValidator.Validate(model);            
            if (result.IsValid)
            { 
                return Redirect("https://www.baidu.com");
            }else
            { 
                this.ValidatorErrorHandler(result);
            }            
            return View();
        }

很可惡,如果我需要驗證,我需要在每一個Action 裡像這樣寫,一次實驗也就罷了,如果真要在每個Action裡像這樣乾,我想到時候你一定會很討厭這些程式碼的。至少我是這樣認為。所以我很討厭我之前的寫法。

現在我想幹嘛?我們知道其實MVC內建了一個資料校驗。這裡不過多介紹它,(偶爾適當的照照輪子,也有許多好處的)。這裡簡單描述下它的用法。

  [HttpPost]        
  public ActionResult ValidatorTest(Person model)
        {
            if (ModelState.IsValid)
            { /// ok }            
            return View();
        }

和咱們之前那樣寫比起來是精簡了許多,但我還是覺得吧,他還是要在每個Action 裡調用ModelState.IsValid,雖然只有一個if,但這不是我想要的,我希望它能像這樣

  [HttpPost]        
      public ActionResult ValidatorTest(Person model)
        {            
        // 
            //  一大堆代码            
            //            
                return Redirect("https://www.baidu.com");
        }

不要影響我正常的編程,而我也不去做哪些重複的事。

換句話說,其實就是在執行我Action之前就去把資料給校驗了。

於是我們想到了MVC給我們的Filter,OnActionExecuting,打開我們的ControllerEx,在裡面重寫OnActionExecuting,他有一個參數ActionExecutingContext,透過名字我們大致了解了,這個參數是個Action相關的上下文,那他一定裝了Action相關的資料

我就不墨跡了,先直接上程式碼,其實這些程式碼也只是我剛剛寫出來的而已,我對這個參數也不是很了解,透過一個一個去嘗試,慢慢得就試出來了。

 protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {            
            var existError = false;            
            foreach (var value in filterContext.ActionParameters.Values)
            {                
                var modelValidatorPropertyInfo = this.ValidatorHub.GetType().GetProperty(value.GetType().Name + "Validator");                if (modelValidatorPropertyInfo != null)
                {                    
                    var modelValidator = modelValidatorPropertyInfo.GetValue(this.ValidatorHub) as IValidator;                    var validateResult = modelValidator.Validate(value);                    if (!validateResult.IsValid)
                    {                        
                          this.ValidatorErrorHandler(validateResult);
                        existError = true;
                    }

                }
            }            
            if (existError)
            {
                ViewData["Error"] = DicError;
                filterContext.Result = View();
            }            
            base.OnActionExecuting(filterContext);
        }

在 OnActionExecuting 裡,我們先定義了一個existError,用來判斷是否有驗證失敗的,然後我們遍歷了filterContext.ActionParameters.Values

在filterContext 裡,我們看到ActionParameters 是關於Action的參數的,透過調試我發現,他是一個集合,其鍵是參數名,例如拿我們這個Person來講。

 [HttpPost]        
     public ActionResult ValidatorTest(Person model)
        {            
        // 
            //  一大堆代码            
            //            
                return Redirect("https://www.baidu.com");
        }

filterContext.ActionParameters 集合裡就有一個數據,其鍵是"model" 值呢model

所以呢我們透過遍歷filterContext.ActionParameters.Value 就能取出每一個Action的所有參數了,而每一個參數通過.getType().Name 則能取出他的類型名,比如這裡model類型是Person 所以filterContext.ActionParameters["model"].GetType().Name 就是「Person」了。

知道了實體是什麼型,如何取得具體驗證器呢?想想我們的驗證器配置 Person = PersonValidator 那太簡單了,這不是一對一的關係嘛,但總不可能通過一個switch 去工廠返回吧,那這樣還需要維護一個工廠方法。當然不是咯,這就要用到咱.NET 提供的強大反射技術

有時候我們有一個匿名對象,是一個object的時候,又不知道它具體是什麼類型,如何取它的屬性呢?

我這有一個方法,他能解決這個問題。

 public static class ReflectHelper
    {        
        public static object GetPropertyByAnonymousObject(string propertyName, object obj)
        {            
            var property = obj.GetType().GetProperties().Where(p => p.Name == propertyName).FirstOrDefault();            if (property == null)
            {                
                throw new Exception(string.Format("{0}对象未定义{1}属性", nameof(obj), nameof(propertyName)));
            }            
            return property.GetValue(obj);
        }
    }

從使用上,傳遞一個屬性名稱和物件進來,傳回一個object的屬性。

我們看看內部都做了些什麼。

先取得類型,然後取得執行的屬性,誒,這個屬性可不是真的屬性哦,這個是PropertyInfo類型,是反射裡的資料類型,它不是真正的屬性值,但我們如果想要取得真正的屬性值怎麼辦呢?其實只需要呼叫他的GetValue就行了,他有一個參數,這個參數是指取得那個物件上的屬性,於是把object傳進去就行。

有了這個基礎,反觀我們的目的,知道了Person,有一個物件叫ValidatotHub 裡面有個屬性PersonValidator ,所以我們只需要取得一個叫ValidatorHub物件裡的PersonValidator屬性就行了。 (Person是可替換的,是根據參數類型來的,前面已經解釋過了,這裡以Person舉例)

現在有個問題了,我們取到的PersonValidator 是一個object類型的,object類型我可不好使用啊,我們又不能顯示的轉換為具體類型,因為誰知道具體類型是啥呢。如果寫死了就涼了。那肯定也不能用個switch來維護啊,那樣不又增加工作量了嗎。

我们慢慢发现PersonValidator继承自AbstractValidator8abf60ac54173a2785e603c7a1f95b4e 很显然它的基类也需要一个具体类型,不行,继续往上走,诶,发现了AbstractValidator8742468051c85b06f0a0af9e3e506b5c继承自IValidator,并且IValidator定义了Validate方法。这不就好了吗,我as 为IValidator类型,就可以用了。这里使用了(里氏转换原则)。我尽量写得通俗易懂,也将许多基础东西提一下,但不肯能面面俱到,所以还是建立在一部分基础之上的。(当然更重要的一点是,通过这次遇到的问题让我以后在设计泛型类结构的时候,都要去继承一个非泛型的接口,如果FluentValidator没有继承自IValidator 而只是继承自IValidator8742468051c85b06f0a0af9e3e506b5c其实从简单使用上来讲,并没有什么影响啊,但到了我们刚刚这里,问题就出来了,所以这也是给我们狠狠地上了一课啊)

现在我就可以在这里进行验证了,我们知道value 就是那个model 所以直接对他进行验证,验证会返回一个ValidationResult类型接下来的事我就不解释了,相信上一章已经讲得很清楚了。最后根据是否存在错误在进行提前处理,如果有错误的话就直接返回视图呈现错误了,连咱们的Action都不执行了。好了,到这里咱们昨天讲得OnActionExecuted 可以直接Delete拉 。

我们现在把ValidatorTest里的验证代码都去掉来测试一下。

        [HttpPost]        
        public ActionResult ValidatorTest(Person model)
        {            
            // 
                //  一大堆代码            
                //            
                return Redirect("https://www.baidu.com");
        }

在 ValidatorTest 里打上断点,然后什么都不填,直接提交。

断点没触发,但错误消息已呈现。多试几次~.

同样没触发。

那我们来一次正确的验证。

 

断点触发了。并且值都通过了校验

F5放行,最终我们的页面跳转到了 www.baidu.com。

好了,小伙伴们还不快去改改代码造就幸福生活。

以上是ASP.NET全端開發之在MVC中使用服務端驗證(二)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn