• <ul id="cgeq2"></ul>
  • 歡迎您光臨深圳塔燈網絡科技有限公司!
    電話圖標 余先生:13699882642

    網站百科

    為您解碼網站建設的點點滴滴

    通過擴展改善ASP.NET MVC的驗證機制[實現篇]

    發表日期:2019-09 文章編輯:小燈 瀏覽次數:2236

    “基于某個規則的驗證”是本解決方案一個最大的賣點。為了保持以驗證規則名稱為核心的上下文信息,我定義了如下一個ValidatorContext(我們本打算將其命名為ValidationContext,無奈這個類型已經存在)。ValidatorContext的屬性RuleName和Culture表示當前的驗證規則和語言文化(默認值為當前線程的CurrentUICulture),而字典類型的屬性Properties用戶存放一些額外信息。當前ValidationContext的獲取與設置通過靜態Current完成。

     1: public class ValidatorContext
     2: {
     3: [ThreadStatic]
     4: private static ValidatorContext current;
     5:? 
     6: public string RuleName { get; private set; }
     7: public CultureInfo Culture { get; private set; }
     8: public IDictionary<string, object> Properties { get; private set; }
     9:? 
    10: public ValidatorContext(string ruleName, CultureInfo culture=null)
    11: {
    12: this.RuleName = ruleName;
    13: this.Properties = new Dictionary<string, object>();
    14: this.Culture = culture??CultureInfo.CurrentUICulture;
    15: }
    16:? 
    17: public static ValidatorContext Current
    18: {
    19: get { return current; }
    20: set { current = value; }
    21: }
    22: }

    我們為ValidatorContext定義了如下一個匹配的ValidatorContextScope對象用于設置ValidatorContext的作用范圍。

     1: public class ValidatorContextScope : IDisposable
     2: {
     3: private ValidatorContext current = ValidatorContext.Current;
     4: public ValidatorContextScope(string ruleName, CultureInfo culture = null)
     5: {
     6: ValidatorContext.Current = new ValidatorContext(ruleName, culture);
     7: }
     8: public void Dispose()
     9: {
    10: if (null == current)
    11: {
    12: foreach (object property in ValidatorContext.Current.Properties.Values)
    13: {
    14: IDisposable disposable = property as IDisposable;
    15: if (null != disposable)
    16: {
    17: disposable.Dispose();
    18: }
    19: }
    20: }
    21: ValidatorContext.Current = current;
    22: }
    23: }

    二、通過自定義ActionInvoker在進行操作執行之前初始化上下文

    通過《使用篇》中我們知道當前的驗證規則名稱是通過ValidationRuleAttribute來設置的,該特性不僅僅可以應用在Action方法上,也可以應用在Controller類型上。當然Action方法上的ValidationRuleAttribute具有更高的優先級。如下面的代碼片斷所示,ValidationRuleAttribute就是一個包含Name屬性的普通Attribute而已。

     1: [AttributeUsage( AttributeTargets.Class| AttributeTargets.Method)]
     2: public class ValidationRuleAttribute:Attribute
     3: {
     4: public string Name { get; private set; }
     5: public ValidationRuleAttribute(string name)
     6: {
     7: this.Name = name;
     8: }
     9: }

    很顯然,以當前驗證規則驗證規則為核心的ValidatorContext需要在Action操作之前設置(嚴格地說應該在進行Model綁定之前),而在Action操作完成后清除。很自然地,我們可以通過自定義ActionInvoker來完成,為此我定義了如下一個直接繼承自ControllerActionInvoker的ExtendedControllerActionInvoker類。

     1: public class ExtendedControllerActionInvoker : ControllerActionInvoker
     2: {
     3: public ExtendedControllerActionInvoker()
     4: { 
     5: this.CurrentCultureAccessor= (context=>
     6: {
     7: string culture = context.RouteData.GetRequiredString("culture");
     8: if(string.IsNullOrEmpty(culture))
     9: {
    10: return null;
    11: }
    12: else
    13: {
    14: return new CultureInfo(culture);
    15: }
    16: });
    17: }
    18: public virtual Func<ControllerContext, CultureInfo> CurrentCultureAccessor { get; set; }
    19: public override bool InvokeAction(ControllerContext controllerContext, string actionName)
    20: {
    21: CultureInfo originalCulture = CultureInfo.CurrentCulture;
    22: CultureInfo originalUICulture = CultureInfo.CurrentUICulture;
    23: try
    24: {
    25: CultureInfo culture = this.CurrentCultureAccessor(controllerContext);
    26: if (null != culture)
    27: {
    28: Thread.CurrentThread.CurrentCulture = culture;
    29: Thread.CurrentThread.CurrentUICulture = culture;
    30: }
    31: var controllerDescriptor = this.GetControllerDescriptor(controllerContext);
    32: var actionDescriptor = this.FindAction(controllerContext, controllerDescriptor, actionName);
    33: ValidationRuleAttribute attribute = actionDescriptor.GetCustomAttributes(true).OfType<ValidationRuleAttribute>().FirstOrDefault() as ValidationRuleAttribute;
    34: if (null == attribute)
    35: {
    36: attribute = controllerDescriptor.GetCustomAttributes(true).OfType<ValidationRuleAttribute>().FirstOrDefault() as ValidationRuleAttribute;
    37: }
    38: string ruleName = (null == attribute) ? string.Empty : attribute.Name;
    39: using (ValidatorContextScope contextScope = new ValidatorContextScope(ruleName))
    40: {
    41: return base.InvokeAction(controllerContext, actionName);
    42: }
    43: }
    44: catch
    45: {
    46: throw;
    47: }
    48: finally
    49: {
    50: Thread.CurrentThread.CurrentCulture = originalCulture;
    51: Thread.CurrentThread.CurrentUICulture = originalUICulture;
    52: }
    53: }
    54: }

    如上面的代碼片斷所示,在重寫的InvokeAction方法中我們通過ControllerDescriptor/ActionDescriptor得到應用在Controller類型/Action方法上的ValidationRuleAttribute特性,并或者到設置的驗證規則名稱。然后我們創建ValidatorContextScope對象,而針對基類InvokeAction方法的執行就在該ValidatorContextScope中執行的。初次之外,我們還對當前線程的Culture進行了相應地設置,默認的Culture 信息來源于當前RouteData。

    為了更方便地使用ExtendedControllerActionInvoker,我們定義了一個抽象的Controller基類:BaseController。BaseController是Controller的子類,在構造函數中我們將ActionInvoker屬性設置成我們自定義的ExtendedControllerActionInvoker對象。

     1: public abstract class BaseController: Controller
     2: {
     3: public BaseController()
     4: {
     5: this.ActionInvoker = new ExtendedControllerActionInvoker();
     6: }
     7: }

    三、為Validator創建基類:ValidatorBaseAttribute

    接下來我們才來看看真正用于驗證的驗證特性如何定義。我們的驗證特性都直接或者間接地繼承自具有如下定義的ValidatorBaseAttribute,而它使ValidationAttribute的子類。如下面的代碼片斷所示,ValidatorBaseAttribute還實現了IClientValidatable接口,以提供對客戶端驗證的支持。屬性RuleName、MessageCategory、MessageId和Culture分別代表驗證規則名稱、錯誤消息的類別和ID號(通過這兩個屬性通過MessageManager這個獨立的組件獲取完整的錯誤消息)和基于的語言文化。

     1: [AttributeUsage(AttributeTargets.Class|AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
     2: public abstract class ValidatorBaseAttribute : ValidationAttribute, IClientValidatable
     3: {
     4: 
     5: public string RuleName { get; set; }
     6: public string MessageCategory { get; private set; }
     7: public string MessageId { get; private set; }
     8: public string Culture { get; set; }
     9:? 
    10: public ValidatorBaseAttribute(MessageManager messageManager, string messageCategory, string messageId, params object[] args)
    11: : base(() => messageManager.FormatMessage(messageCategory, messageId, args))
    12: {
    13: this.MessageCategory = messageCategory;
    14: this.MessageId = messageId;
    15: }
    16:? 
    17: public ValidatorBaseAttribute(string messageCategory, string messageId, params object[] args)
    18: : this(MessageManagerFactory.GetMessageManager(), messageCategory, messageId, args)
    19: { }
    20:? 
    21: public virtual bool Match(ValidatorContext context, IEnumerable<ValidatorBaseAttribute> validators)
    22: {
    23: if (!string.IsNullOrEmpty(this.RuleName))
    24: {
    25: if (this.RuleName != context.RuleName)
    26: {
    27: return false;
    28: }
    29: }
    30:? 
    31: if (!string.IsNullOrEmpty(this.Culture))
    32: {
    33: if (string.Compare(this.Culture, context.Culture.Name, true) != 0)
    34: {
    35: return false;
    36: }
    37: }
    38:? 
    39: if (string.IsNullOrEmpty(this.Culture))
    40: {
    41: if (validators.Any(validator => validator.GetType() == this.GetType() && string.Compare(validator.Culture, context.Culture.Name, true) == 0))
    42: {
    43: return false;
    44: }
    45: }
    46: return true;
    47: }
    48: public abstract IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context);
    49: private object typeId;
    50: public override object TypeId
    51: {
    52: get { return (null == typeId) ? (typeId = new object()) : typeId; }
    53: }
    54: }

    由于我們需要將多個相同類型的Validator特性應用到某個類型或者字段/屬性上,我們需要通過AttributeUsageAttribute將AllowMultiple屬性設置為True,此外需要重寫TypeId屬性。至于為什么需需要這么做,可以參考我的上一篇文章《在ASP.NET MVC中如何應用多個相同類型的ValidationAttribute?》。對于應用在同一個目標元素的多個相同類型的Validator特性,只有與當前ValidatorContext相匹配的才能執行,我們通過Match方法來進行匹配性的判斷,具體的邏輯是這樣的:

    • 在顯式設置了RuleName屬性情況下,如果不等于當前驗證規則,直接返回False;
    • 在顯式設置了Culture屬性情況下,如果與當前語言文化不一致,直接返回False;
    • 在沒有設置Culture屬性(語言文化中性)情況下,如果存在另一個同類型的Validator與當前的語言文化一致,也返回False;
    • 其余情況返回True

    四、通過自定義ModelValidatorProvider在驗證之前將不匹配Validator移除

    應用在Model類型或其屬性/字段上的ValidationAttribute最終通過對應的ModelValidatorProvider(DataAnnotationsModelValidatorProvider)用于創建ModelValidator(DataAnnotationsModelValidator)。我們必須在ModelValidator創建之前將不匹配的Validator特性移除,才能確保只有與當前ValidatorContext相匹配的Validator特性參與驗證。為此我們通過繼承DataAnnotationsModelValidator自定義了如下一個ExtendedDataAnnotationsModelValidator。

     1: public class ExtendedDataAnnotationsModelValidatorProvider : DataAnnotationsModelValidatorProvider
     2: {
     3: protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
     4: {
     5: var validators = attributes.OfType<ValidatorBaseAttribute>();
     6: var allAttributes = attributes.Except(validators).ToList();
     7: foreach (ValidatorBaseAttribute validator in validators)
     8: {
     9: if (validator.Match(ValidatorContext.Current, validators))
    10: {
    11: allAttributes.Add(validator);
    12: }
    13: }
    14: return base.GetValidators(metadata, context, allAttributes);
    15: }
    16: }

    如上面的代碼片斷所示,在重寫的GetClientValidationRules方法中,輸入參數attributes表示所有的ValidationAttribute,在這里我們根據調用ValidatorBaseAttribute的Match方法將不匹配的Validator特性移除,然后根據余下的ValidationAttribute列表調用基類GetValidators方法創建ModelValidator列表。值得一提的是,關于System.Attribute的Equals/GetHashCode方法的問題就從這個方法中發現的(詳情參見《為什么System.Attribute的GetHashCode方法需要如此設計?》)。自定義ExtendedDataAnnotationsModelValidator在Global.asax的Application_Start方法中通過如下的方式進行注冊。

     1: protected void Application_Start()
     2: {
     3://...
     4: var provider = ModelValidatorProviders.Providers.OfType<DataAnnotationsModelValidatorProvider>().FirstOrDefault();
     5: if (null != provider)
     6: {
     7: ModelValidatorProviders.Providers.Remove(provider);
     8: }
     9: ModelValidatorProviders.Providers.Add(new ExtendedDataAnnotationsModelValidatorProvider());
    10: }

    五、RequiredValidatorAttribute的定義

    最后我們來看看用于驗證必需字段的RequiredValidatorAttribute如何定義。IsValid用于服務端驗證,而GetClientValidationRules生成調用客戶端驗證規則。

     1: [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]
     2: public class RequiredValidatorAttribute : ValidatorBaseAttribute
     3: {
     4: public RequiredValidatorAttribute(string messageCategory, string messageId, params object[] args)
     5: : base(messageCategory, messageId, args)
     6: { } 
     7:? 
     8: public override bool IsValid(object value)
     9: {
    10: if (value == null)
    11: {
    12: return false;
    13: }
    14: string str = value as string;
    15: if (str != null)
    16: {
    17: return (str.Trim().Length != 0);
    18: }
    19: return true;
    20: }
    21:? 
    22: public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    23: {
    24: return new ModelClientValidationRequiredRule[] { new ModelClientValidationRequiredRule(this.ErrorMessageString) };
    25: }
    26: }

    本頁內容由塔燈網絡科技有限公司通過網絡收集編輯所得,所有資料僅供用戶學習參考,本站不擁有所有權,如您認為本網頁中由涉嫌抄襲的內容,請及時與我們聯系,并提供相關證據,工作人員會在5工作日內聯系您,一經查實,本站立刻刪除侵權內容。本文鏈接:http://www.juherenli.com/19692.html
    相關開發語言
     八年  行業經驗

    多一份參考,總有益處

    聯系深圳網站公司塔燈網絡,免費獲得網站建設方案及報價

    咨詢相關問題或預約面談,可以通過以下方式與我們聯系

    業務熱線:余經理:13699882642

    Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.    

    • QQ咨詢
    • 在線咨詢
    • 官方微信
    • 聯系電話
      座機0755-29185426
      手機13699882642
    • 預約上門
    • 返回頂部
    奇米精品一区二区三区在| 久久国产精品成人无码网站| 国产精品99久久久久久董美香| 99久久精品午夜一区二区| 国产在线精品一区二区不卡| 国产成人精品福利网站在线观看| 亚洲av永久无码精品秋霞电影秋| 99久久精品免费精品国产| 国产精品亚洲精品日韩已满| 老司机免费午夜精品视频 | 91精品国产综合久久久久| 久久夜色精品国产www| 一本一本久久a久久精品综合麻豆| 精品久久久久久无码中文野结衣| 91精品婷婷国产综合久久| 国产精品久久久久久吹潮| 成人区人妻精品一区二区不卡视频 | 精品精品国产高清a毛片牛牛| 国产91精品不卡在线| 久久夜色精品国产噜噜亚洲a| 日韩精品少妇无码受不了| 精品乱人伦一区二区三区| 成人区人妻精品一区二区不卡网站| 日韩熟女精品一区二区三区| 国产精品亚洲五月天高清| 国产精品美女一区二区三区| 国产精品中文久久久久久久| 99RE6热在线精品视频观看| 日本亚洲精品色婷婷在线影院| 91精品国产色综合久久不| 99热在线精品免费播放6| 老司机午夜精品视频资源| 热re99久久6国产精品免费| 午夜精品久久久久久久久| 亚洲AV无码成人网站久久精品大 | 国产精品久久精品福利网站| 日本精品久久久久久福利| 无码日韩人妻精品久久| 国产成人高清精品免费软件| 国产主播精品福利19禁vip| 精品人妻一区二区三区四区|