ASP.NET MVC Model驗證(四)

ASP.NET MVC Model驗證(四)

前言

本篇主要講解ModelValidatorProvider 和ModelValidator兩種類型的自定義實現,前者是Model驗證提供程序,而ModelValidator類型則是Model驗證執行類型,在下面的示例中會使用Model驗證提供程序結合Model驗證執行類型來執行Model驗證,就是使用上個篇幅中所講的實現個Model綁定器繼承自DefaultModelBinder類型,在自定義Model綁定器中使用ModelValidator類型。

 

Model驗證

  • Model驗證簡單運用示例
  • ModelValidator使用生成過程
  • 自定義實現DefaultModelBinder進行驗證
  • 自定義ModelValidatorProvider 和ModelValidator 
  • ValidationAttribute特性類使用
  • 自定義ValidationAttribute特性類的示例實現

 

自定義ModelValidatorProvider 和ModelValidator

ModelValidator

深呼吸一下,直接進入主題。

在我們定義自定義的ModelValidator類型之前,先看一下ModelValidator類型的定義,代碼1-1.

代碼1-1

    public abstract class ModelValidator
    {
        protected ModelValidator(ModelMetadata metadata, ControllerContext controllerContext);
        protected internal ControllerContext ControllerContext { get; }
        public virtual bool IsRequired { get; }
        //
        // 摘要:
        //     獲取模型驗證程序的元數據。
        //
        // 返回結果:
        //     模型驗證程序的元數據。
        protected internal ModelMetadata Metadata { get; }
        public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules();
        public static ModelValidator GetModelValidator(ModelMetadata metadata, ControllerContext context);
        //
        // 摘要:
        //     在派生類中實現時驗證對象。
        //
        // 參數:
        //   container:
        //     容器。
        //
        // 返回結果:
        //     驗證結果的列表。
        public abstract IEnumerable<ModelValidationResult> Validate(object container);
    }

代碼1-1中,我們可以看到ModelValidator類型的構造是可以重寫的,這樣便於我們自定義一些所需的數據,不過下面的示例中並沒有這麼做,目的是在於簡單的演示,然後我們再看一下Metadata屬性,這個屬性非常重要了,它表示封裝着所要進行驗證的Model或者是Model屬性的信息在此提醒大家關注一下,最後要看的就是Validate()抽象方法了,我們自定義類型必須實現的這麼一個方法,在它的返回類型中有個ModelValidationResult類型,我們就來看一下ModelValidationResult類型,代碼1-2.

代碼1-2

    public class ModelValidationResult
    {
        public ModelValidationResult();

        // 摘要:
        //     獲取或設置成員名。
        //
        // 返回結果:
        //     成員名。
        public string MemberName { get; set; }
        //
        // 摘要:
        //     獲取或設置驗證結果消息。
        //
        // 返回結果:
        //     驗證結果消息。
        public string Message { get; set; }
    }

 

正如大家所看到的,代碼1-2沒啥好說的,看完本篇自然明白。

ASP.NET MVC Model驗證(三)和ASP.NET MVC Model驗證(一)中的示例想必有的朋友是看過了,沒看過的朋友建議去快速的看一下篇幅一和篇幅三的示例部分(本篇示例的基礎依賴上篇的基礎部分,篇幅長度原因這裏就不再次說明了),在這裏我想說的是Model驗證也是分級別的,就是上篇中也有提到過的Model級驗證和Model屬性級驗證,在下面的示例中,我把上篇篇幅中的驗證過程拆成三個部分(可以寫在一起,便於理解)。

我把第一個部分定義爲Model屬性級驗證,我們來看一下示例代碼1-3。

代碼1-3

public class MyCustomModelPropertyValidator : ModelValidator
    {
        public MyCustomModelPropertyValidator(ModelMetadata modelMetadata, ControllerContext controllContext)
            : base(modelMetadata, controllContext)
        { }

        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            switch (Metadata.PropertyName)
            {
                case "ID":
                    if (string.IsNullOrEmpty((string)container) || (string)container == "")
                    {
                        return new ModelValidationResult[]
                            { 
                               new ModelValidationResult()
                               {
                                   MemberName="ID",
                                   Message= "請輸入ID,ID不能爲空!"
                               }
                            };
                    }
                    break;
                case "UserID":
                    if (string.IsNullOrEmpty((string)container) || (string)container == "")
                    {
                        return new ModelValidationResult[]
                            { 
                               new ModelValidationResult()
                               {
                                   MemberName="UserID",
                                   Message= "請輸入用戶賬戶,用戶賬戶不能爲空!"
                               }
                            };
                    }
                    break;
                case "Password1":
                    if (string.IsNullOrEmpty((string)container) || (string)container == "")
                    {
                        ControllerContext.Controller.TempData.Add("Password1", false);
                        return new ModelValidationResult[]
                            { 
                               new ModelValidationResult()
                               {
                                   MemberName="Password1",
                                   Message= "請輸入登錄密碼,登錄密碼不能爲空!"
                               }
                            };
                    }
                    break;
                case "Password2":
                    if (string.IsNullOrEmpty((string)container) || (string)container == "")
                    {
                        ControllerContext.Controller.TempData.Add("Password2", false);
                        return new ModelValidationResult[]
                            { 
                               new ModelValidationResult()
                               {
                                   MemberName="Pssword2",
                                   Message= "請再次輸入密碼,密碼不能爲空!"
                               }
                            };
                    }
                    break;
                case "Name":
                    return Enumerable.Empty<ModelValidationResult>();

            }
            return Enumerable.Empty<ModelValidationResult>();
        }
    }

 

首先我們在代碼1-3中定義了個MyCustomModelPropertyValidator類型,並且定義了構造函數這點非常重要(看到Model驗證提供程序部分就明白了),在Validate()方法中對一些屬性進行了基本的的驗證,並且依據方法所約束的返回類型去返回,對於代碼的實現部分,看過上篇的朋友就知道了在默認的Model綁定器執行Model屬性綁定的時候會進行Model屬性驗證,會在遍歷中循環的調用這個MyCustomModelPropertyValidator類型的驗證方法,這裏有不明白的朋友可以去看一下上篇。

我們再來看第二部分,第二部分的主要驗證是驗證兩次輸入的密碼是否是相同的,來看示例代碼1-4。

代碼1-4

public class MyCustomModelPassWordValidator : ModelValidator
    {
        public MyCustomModelPassWordValidator(ModelMetadata modelMetadata, ControllerContext controllerContext)
            : base(modelMetadata, controllerContext)
        { }

        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            Models.RegistrationInformation regInfo = Metadata.Model as Models.RegistrationInformation;
            if (regInfo != null)
            {
                if (!ControllerContext.Controller.TempData.ContainsKey("Password1")
                    && !ControllerContext.Controller.TempData.ContainsKey("Password2"))
                {
                    if (regInfo.Password1 != regInfo.Password2)
                    {
                        return new ModelValidationResult[]
                        {
                            new ModelValidationResult()
                            {
                                MemberName="Password2",
                                Message="請重新輸入密碼,與上次輸入密碼不同!"
                            }
                        };
                    }
                }
            }
            return Enumerable.Empty<ModelValidationResult>();
        }
    }

 

在代碼1-4中,有細心的朋友發現了跟代碼1-3的實現方式有很大的區別,對的因爲我把這個MyCustomModelPassWordValidator類型定義爲Model級驗證了,MyCustomModelPassWordValidator類型所被調用的位置是在默認Model綁定器對Model屬性驗證完畢並且進行綁定了過後,再在默認Model綁定器的OnModelUpdated()方法中調用的,所以上述可以直接通過Metadata的Model屬性直接獲取Model的值,由此來進行驗證,而這句ControllerContext.Controller.TempData.ContainsKey("Password1")代碼的含義是判斷在Model的Password1屬性在屬性級驗證的時候是否通過了,因爲每個Model驗證器之間無法傳遞消息,只有ControllerContext是指向一個引用的,所有用它來做數據信息的承載。在代碼1-3中已經定義了。(對於第二部分的理解不太明白的朋友可以結合上篇和本篇來看)

現在我們再看第三部分,意思和第二部分一樣,示例代碼1-5。

代碼1-5

public class MyCustomModelNameValidator : ModelValidator
    {
        public MyCustomModelNameValidator(ModelMetadata modelMetadata, ControllerContext controllerConttext)
            : base(modelMetadata, controllerConttext)
        { }

        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            Models.RegistrationInformation regInfo = Metadata.Model as Models.RegistrationInformation;
            if (regInfo != null)
            {
                if (string.Compare(regInfo.Name, "jinyuan", true) == 0)
                {
                    return new ModelValidationResult[] 
                    {
                        new ModelValidationResult()
                        {
                            MemberName="",
                            Message="您輸入的名稱違法了,立即更改不然查水錶"
                        }
                    };
                }
            }
            return Enumerable.Empty<ModelValidationResult>();
        }
    }

 

代碼1-5的功能就是在Model級驗證時對Model的Name屬性進行驗證。

 

ModelValidatorProvider

上個小節定義了三個部分,第一部分爲Model屬性級驗證,二三部分皆爲Model級驗證,然而在默認的Model綁定器(DefaultModelBinder類型)執行的時候,它纔不管呢,它只負責從ModelValidatorProviders.Providers中獲取我們自定義的Model驗證器提供程序(ModelValidatorProvider類型實例),再由Model驗證器提供程序給它提供Model驗證器,這個時候就需要我們的自定義的Model驗證器提供程序具有判斷我們自身是在哪裏被調用的,並且返回對應的Model驗證器,來看示例代碼1-6。

代碼1-6

namespace MvcApplication.ModelValidators
{
    public class MyCustomModelValidatorProvider:ModelValidatorProvider
    {
        public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
        {
            if (metadata.ContainerType == typeof(Models.RegistrationInformation))
            {
                return new ModelValidator[]
                {
                    new MyCustomModelPropertyValidator(metadata,context)
                };
            }
            else if (metadata.ModelType == typeof(Models.RegistrationInformation))
            {
                return new ModelValidator[] 
                {
                    new MyCustomModelPassWordValidator(metadata,context),
                    new MyCustomModelNameValidator(metadata,context)
                };
            }
            return Enumerable.Empty<ModelValidator>();
        }
    }
}

 

在代碼1-6中,我們定義了個自定義的Model驗證器提供程序MyCustomModelValidatorProvider類型,在MyCustomModelValidatorProvider類型中是根據GetValidators()方法的參數metadata所包含的信息來判斷自己被默認的Model綁定器(DefaultModelBinder類型)調用的位置。(對於Model元數據部分大家可查看我前面跟大家分享的Model元數據系列)

到了這裏幾乎就已經全部完成了,現在註冊到系統中是不能使用的,有的朋友會罵了,不能用瞎扯蛋扯半天,大家莫急,聽我解釋,是因爲我們自定義的Model驗證器和Model驗證器提供程序都要受到MVC框架的默認的Model綁定器(DefaultModelBinder類型)調用的約束,是必須按照它方法內部定義的來往下寫,在這裏我們不圖省事,再定義一個自定義實現DefaultModelBinder類型的Model綁定器,來看示例代碼1-7。

代碼1-7

    public class MyCustomModelValidatorDefaultModelBinder : DefaultModelBinder
    {
        protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
        {
            base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
            foreach (ModelValidator modelValidator in ModelValidatorProviders.Providers.GetValidators(bindingContext.PropertyMetadata[propertyDescriptor.Name], controllerContext))
            {
                IEnumerable<ModelValidationResult> modelValidationResults = modelValidator.Validate(value);
                foreach (ModelValidationResult modelValidationResult in modelValidationResults)
                {
                    bindingContext.ModelState.AddModelError(modelValidationResult.MemberName, modelValidationResult.Message);
                }
            }

        }
        protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            //base.OnModelUpdated(controllerContext, bindingContext);
            foreach (ModelValidator modelValidator in ModelValidatorProviders.Providers.GetValidators(bindingContext.ModelMetadata, controllerContext))
            {
                IEnumerable<ModelValidationResult> modelValidationResults = modelValidator.Validate(null);
                foreach (ModelValidationResult modelValidationResult in modelValidationResults)
                {
                    bindingContext.ModelState.AddModelError(modelValidationResult.MemberName, modelValidationResult.Message);
                }
            }
        }
    }

 

有了代碼1-7中的Model綁定器就可以吻合我們前面所定義的Model驗證器以及提供程序了。

我要把MyCustomModelValidatorProvider類型和MyCustomModelValidatorDefaultModelBinder類型註冊到系統中,將示例代碼1-8部分添加至Global.asax文件的Application_Start()方法中。

代碼1-8

ModelBinders.Binders.Add(typeof(Models.RegistrationInformation), new Binders.MyCustomModelValidatorDefaultModelBinder());
ModelValidatorProviders.Providers.Insert(0, new ModelValidators.MyCustomModelValidatorProvider());

 

看下圖1,圖1爲初始的展示頁面,圖2爲圖1中點擊提交後執行驗證過後。

圖1

圖2


作者:金源


發佈了74 篇原創文章 · 獲贊 1 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章