• Asp.net MVC源码分析Model Validation(Client端)实现(1)


    前两篇我们介绍了ModelValidatoin Server 端的实现,那么我们知道在Web.config 中如果我们把ClientValidationEnabled 设置为true时,那么客户端也可以支持表单验证了. 那么这部份功能是如果实现的呢?今天让我们来一起学习Model validation client 端的实现.

    一.ModelClientValidationRule类

    这个类定义了如何输出客户端的一些信息:

    • ErrorMessage:取得或设定用户端验证规则的错误讯息。
    • ValidationParameters:取得验证参数清单
    • ValidationType:取得或设定验证类型。
     1   public class ModelClientValidationRule {
    2
    3 private readonly Dictionary<string, object> _validationParameters = new Dictionary<string, object>();
    4 private string _validationType;
    5
    6 public string ErrorMessage {
    7 get;
    8 set;
    9 }
    10
    11 public IDictionary<string, object> ValidationParameters {
    12 get {
    13 return _validationParameters;
    14 }
    15 }
    16
    17 public string ValidationType {
    18 get {
    19 return _validationType ?? String.Empty;
    20 }
    21 set {
    22 _validationType = value;
    23 }
    24 }
    25 }

    我们知道了这个类的数据结构,那么这些数据是如何输出的呢?我们看一下RequiredAttributeAdapter类,在这里定义了有一个GetClientValidationRules方法,这个方法返回了ModelClientValidationRequiredRule对象(包含了Required validation 需要输出到客户端的数据).

    1  public class RequiredAttributeAdapter : DataAnnotationsModelValidator<RequiredAttribute> {
    2 public RequiredAttributeAdapter(ModelMetadata metadata, ControllerContext context, RequiredAttribute attribute)
    3 : base(metadata, context, attribute) {
    4 }
    5
    6 public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
    7 return new[] { new ModelClientValidationRequiredRule(ErrorMessage) };
    8 }
    9 }

    二.如果让得到自定义Client Validaton 信息?

    上面我们介绍了一些系统内置(Required)的Validation 的输出,那么我们如果需要自定义验证规则如果来输出客户端数据呢?前面我们介绍了DataAnnotationsModelValidatorProvider.GetValidators 方法里自定义的验证是通过DefaultAttributeFactory委托来构造的,让我们顺着这个代码来继续我们的思路。

    1   internal static DataAnnotationsModelValidationFactory DefaultAttributeFactory =
    2 (metadata, context, attribute) => new DataAnnotationsModelValidator(metadata, context, attribute);

    让我接下来看一下DataAnnotationsModelValidator.GetClientValidationRules 方法的实现,我们看到下面代码第4行,说明只有我们自定义的ValidationAtribute只有实现了IClientValidatable接口才能够输出客户端数据。

     1   public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
    2 IEnumerable<ModelClientValidationRule> results = base.GetClientValidationRules();
    3
    4 IClientValidatable clientValidatable = Attribute as IClientValidatable;
    5 if (clientValidatable != null) {
    6 results = results.Concat(clientValidatable.GetClientValidationRules(Metadata, ControllerContext));
    7 }
    8
    9 return results;
    10 }

    三.如果输出自定义Client Validaton 信息到浏览器?

    以上的分析是我们得到Cient validation 的数据,那么我们怎么输出这些数据到浏览器呢? 

    一般我们在View 页面中的代码都是这样的:

    1   <div class="editor-label">
    2 Name
    3 </div>
    4 <div class="editor-field">
    5 @Html.TextBoxFor(m => m.UserName)
    6 @Html.ValidationMessageFor(m => m.UserName)
    7 </div>

    也就是说Client Validaton 信息是通过Html.TextBoxFor方法来输出的,那么我们接下来研究一下这个方法是如何实现的。

    在InputExtension.cs 方件的InputHelper方法我们找到了一些与Client Validaton有关的代码片段,下面第9行代码的GetUnobtrusiveValidationAttributes方法就实现了输出。

    1   // If there are any errors for a named field, we add the css attribute.
    2 ModelState modelState;
    3 if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState)) {
    4 if (modelState.Errors.Count > 0) {
    5 tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
    6 }
    7 }
    8
    9 tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata));

    GetUnobtrusiveValidationAttributes方法的源码:

     1  // Only render attributes if unobtrusive client-side validation is enabled, and then only if we've
    2 // never rendered validation for a field with this name in this form. Also, if there's no form context,
    3 // then we can't render the attributes (we'd have no <form> to attach them to).
    4 public IDictionary<string, object> GetUnobtrusiveValidationAttributes(string name, ModelMetadata metadata) {
    5 Dictionary<string, object> results = new Dictionary<string, object>();
    6
    7 // The ordering of these 3 checks (and the early exits) is for performance reasons.
    8 if (!ViewContext.UnobtrusiveJavaScriptEnabled) {
    9 return results;
    10 }
    11
    12 FormContext formContext = ViewContext.GetFormContextForClientValidation();
    13 if (formContext == null) {
    14 return results;
    15 }
    16
    17 string fullName = ViewData.TemplateInfo.GetFullHtmlFieldName(name);
    18 if (formContext.RenderedField(fullName)) {
    19 return results;
    20 }
    21
    22 formContext.RenderedField(fullName, true);
    23
    24 IEnumerable<ModelClientValidationRule> clientRules = ClientValidationRuleFactory(name, metadata);
    25 bool renderedRules = false;
    26
    27 foreach (ModelClientValidationRule rule in clientRules) {
    28 renderedRules = true;
    29 string ruleName = "data-val-" + rule.ValidationType;
    30
    31 ValidateUnobtrusiveValidationRule(rule, results, ruleName);
    32
    33 results.Add(ruleName, HttpUtility.HtmlEncode(rule.ErrorMessage ?? String.Empty));
    34 ruleName += "-";
    35
    36 foreach (var kvp in rule.ValidationParameters) {
    37 results.Add(ruleName + kvp.Key, kvp.Value ?? String.Empty);
    38 }
    39 }
    40
    41 if (renderedRules) {
    42 results.Add("data-val", "true");
    43 }
    44
    45 return results;
    46 }

    HtmlHelper 构造函数源码:

     1   public HtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection) {
    2 if (viewContext == null) {
    3 throw new ArgumentNullException("viewContext");
    4 }
    5 if (viewDataContainer == null) {
    6 throw new ArgumentNullException("viewDataContainer");
    7 }
    8 if (routeCollection == null) {
    9 throw new ArgumentNullException("routeCollection");
    10 }
    11
    12 ViewContext = viewContext;
    13 ViewDataContainer = viewDataContainer;
    14 RouteCollection = routeCollection;
    15 ClientValidationRuleFactory = (name, metadata) => ModelValidatorProviders.Providers.GetValidators(metadata ?? ModelMetadata.FromStringExpression(name, ViewData), ViewContext).SelectMany(v => v.GetClientValidationRules());
    16 }

    最终我们看到在GetUnobtrusiveValidationAttributes方法内部,MVC 调用了ClientValidationRuleFactory委托来得到clientRules对象它的类型是IEnumerable<ModelClientValidationRule>,这样我们在Model上标记的Client Validation数据就输出到了浏览器端。

    --------------------------------------风搔分割线-------------------------------------------------

    那么通过以上这些分析我们知道了Server 端如何输出Client Validation数据,那么JavaScript是怎么来进行验证的呢? 

    下一篇我们来分析jquery.validate.unobtrusive.js 的源码,这里面实现了MVC的客户端验证逻辑。

     

    转载请注明出处:http://www.cnblogs.com/RobbinHan/archive/2011/12/19/2293121.html

    本文作者: 十一月的雨 http://www.cnblogs.com/RobbinHan

  • 相关阅读:
    单反相机的传奇—佳能单反50年辉煌之路(连载十五)
    单反相机的传奇—佳能单反50年辉煌之路(连载十二)
    单反相机的传奇—佳能单反50年辉煌之路(连载十四)
    单反相机的传奇—佳能单反50年辉煌之路(连载十六)
    GUID的使用
    C#中的活动目录开发
    C# 窗体桌面定位问题
    C#TCPClient应用一个简单的消息发送和接收
    SQL表间连接
    放弃VMware改投VirtualBox的五个理由
  • 原文地址:https://www.cnblogs.com/RobbinHan/p/2293121.html
Copyright © 2020-2023  润新知