【问题标题】:How to change 'data-val-number' message validation in MVC while it is generated by @Html helper如何在 MVC 中更改由@Html helper 生成的“data-val-number”消息验证
【发布时间】:2011-06-17 05:32:03
【问题描述】:

假设这个模型:

Public Class Detail
    ...
    <DisplayName("Custom DisplayName")>
    <Required(ErrorMessage:="Custom ErrorMessage")>
    Public Property PercentChange As Integer
    ...
end class

和视图:

@Html.TextBoxFor(Function(m) m.PercentChange)

将继续这个 html:

   <input data-val="true" 
    data-val-number="The field 'Custom DisplayName' must be a number." 
    data-val-required="Custom ErrorMessage"     
    id="PercentChange" 
    name="PercentChange" type="text" value="0" />

我想自定义 data-val-number 错误消息,我猜这是因为 PercentChangeInteger。我一直在寻找这样的属性来更改它,range 或任何相关的都不起作用。
我知道有机会编辑不显眼的 js 文件本身或在客户端覆盖它。我想像服务器端的其他人一样更改data-val-number的错误消息。

【问题讨论】:

标签: asp.net-mvc asp.net-mvc-3 localization unobtrusive-validation validation


【解决方案1】:

您可以将ClientDataTypeModelValidatorProvider 类的ResourceKey 设置为包含FieldMustBeNumeric 键的全局资源的名称,以用您的自定义消息替换数字的MVC 验证错误消息。日期验证错误消息的关键也是FieldMustBeDate

ClientDataTypeModelValidatorProvider.ResourceKey="MyResources"; // MyResource is my global resource

有关如何将MyResources.resx 文件添加到您的项目的更多详细信息,请参阅here

【讨论】:

  • 太棒了,这确实有效!不过是ResourceClassKey,不是ResourceKey
【解决方案2】:

一个简单的方法是,在 ViewModel 上使用 dataannotation 更改消息:

[Required(ErrorMessage ="الزامی")]
[StringLength(maximumLength:50,MinimumLength =2)]
[Display(Name = "نام")]
public string FirstName { get; set; }

【讨论】:

    【解决方案3】:

    我把这个放在我的视野里

    @Html.DropDownListFor(m => m.BenefNamePos, Model.Options, new { onchange = "changePosition(this);", @class="form-control", data_val_number = "This is my custom message" })
    

    【讨论】:

      【解决方案4】:

      这是另一种在不更改 MVC3 源的情况下更改消息客户端的解决方案。此博文中的完整详细信息:

      https://greenicicle.wordpress.com/2011/02/28/fixing-non-localizable-validation-messages-with-javascript/

      简而言之,您需要在加载 jQuery 验证后包含以下脚本以及 appropriate localisation file

      (function ($) {
          // Walk through the adapters that connect unobstrusive validation to jQuery.validate.
          // Look for all adapters that perform number validation
          $.each($.validator.unobtrusive.adapters, function () {
              if (this.name === "number") {
                  // Get the method called by the adapter, and replace it with one 
                  // that changes the message to the jQuery.validate default message
                  // that can be globalized. If that string contains a {0} placeholder, 
                  // it is replaced by the field name.
                  var baseAdapt = this.adapt;
                  this.adapt = function (options) {
                      var fieldName = new RegExp("The field (.+) must be a number").exec(options.message)[1];
                      options.message = $.validator.format($.validator.messages.number, fieldName);
                      baseAdapt(options);
                  };
              }
          });
      } (jQuery));
      

      【讨论】:

      • 感谢菲尔,(+1 表示)很高兴知道这一点。但我个人宁愿在这个特定问题上更改 MVC3。我希望他们能在下一个版本中修复这个问题。
      【解决方案5】:

      也检查一下:

      The Complete Guide To Validation In ASP.NET MVC 3 - Part 2

      文章的主要部分如下(复制粘贴)。

      创建一个可在客户端和服务器上运行的功能齐全的自定义验证器有四个不同的部分。首先我们继承ValidationAttribute 并添加我们的服务器端验证逻辑。接下来,我们在属性上实现IClientValidatable,以允许将HTML5 data-* 属性传递给客户端。第三,我们编写了一个在客户端执行验证的自定义 JavaScript 函数。最后,我们创建一个适配器来将 HTML5 属性转换为我们的自定义函数可以理解的格式。虽然这听起来像是很多工作,但一旦开始,您会发现它相对简单。

      子类化 ValidationAttribute

      在这个例子中,我们将编写一个 NotEqualTo 验证器,它只检查一个属性的值是否不等于另一个属性的值。

      [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
      public sealed class NotEqualToAttribute : ValidationAttribute
      {
          private const string DefaultErrorMessage = "{0} cannot be the same as {1}.";
      
          public string OtherProperty { get; private set; }
      
          public NotEqualToAttribute(string otherProperty)
              : base(DefaultErrorMessage)
          {
              if (string.IsNullOrEmpty(otherProperty))
              {
                  throw new ArgumentNullException("otherProperty");
              }
      
              OtherProperty = otherProperty;
          }
      
          public override string FormatErrorMessage(string name)
          {
              return string.Format(ErrorMessageString, name, OtherProperty);
          }
      
          protected override ValidationResult IsValid(object value, 
              ValidationContext validationContext)
          {
              if (value != null)
              {
                  var otherProperty = validationContext.ObjectInstance.GetType()
                      .GetProperty(OtherProperty);
      
                  var otherPropertyValue = otherProperty
                      .GetValue(validationContext.ObjectInstance, null);
      
                  if (value.Equals(otherPropertyValue))
                  {
                      return new ValidationResult(
                          FormatErrorMessage(validationContext.DisplayName));
                  }
              }
          return ValidationResult.Success;
          }        
      }
      

      将新属性添加到 RegisterModel 的密码属性并运行应用程序。

      [Required]
      [DataType(DataType.Password)]
      [Display(Name = "Password")]
      [NotEqualTo("UserName")]
      public string Password { get; set; }
      ...
      

      实现 IClientValidatable

      ASP.NET MVC 2 有一个添加客户端验证的机制,但它不是很漂亮。值得庆幸的是,在 MVC 3 中,情况有所改善,该过程现在相当简单,并且值得庆幸的是涉及像以前的版本一样更改 Global.asax

      第一步是让您的自定义验证属性实现 IClientValidatable。这是一个简单的单一方法接口:

      public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
          ModelMetadata metadata,
          ControllerContext context)
      {
          var clientValidationRule = new ModelClientValidationRule()
          {
              ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
              ValidationType = "notequalto"
          };
      
          clientValidationRule.ValidationParameters.Add("otherproperty", OtherProperty);
      
          return new[] { clientValidationRule };
      }
      

      如果您现在运行应用程序并查看源代码,您将看到密码输入 html 现在包含您的 notequalto 数据属性:

      <div class="editor-field">
          <input data-val="true" data-val-notequalto="Password cannot be the same as UserName." 
          data-val-notequalto-otherproperty="UserName" 
          data-val-regex="Weak password detected." 
          data-val-regex-pattern="^(?!password$)(?!12345$).*" 
          data-val-required="The Password field is required." 
          id="Password" name="Password" type="password" />
          <span class="hint">Enter your password here</span>
          <span class="field-validation-valid" data-valmsg-for="Password" 
          data-valmsg-replace="true"></span>
      </div>
      

      创建自定义 jQuery 验证函数

      所有这些代码最好放在一个单独的 JavaScript 文件中。

      (function ($) {
          $.validator.addMethod("notequalto", function (value, element, params) {
              if (!this.optional(element)) {
                  var otherProp = $('#' + params);
                  return (otherProp.val() != 
              }
          return true;
      });
      
      $.validator.unobtrusive.adapters.addSingleVal("notequalto", "otherproperty");
      
      }(jQuery));
      

      根据您的验证要求,您可能会发现 jquery.validate 库已经包含验证所需的代码。 jquery.validate 中有很多验证器尚未实现或映射到数据注释,因此如果这些验证器满足您的需求,那么您需要用 javascript 编写的只是一个适配器,甚至是对内置适配器的调用,它可以少到只有一行。查看 jquery.validate.js 内部以了解可用的内容。

      使用现有的 jquery.validate.unobtrusive 适配器

      适配器的工作是读取表单元素上的 HTML5 data-* 属性,并将此数据转换为 jquery.validate 和您的自定义验证函数可以理解的表单。您不需要自己完成所有工作,在许多情况下,您可以调用内置适配器。 jquery.validate.unobtrusive 声明了三个可以在大多数情况下使用的内置适配器。它们是:

      jQuery.validator.unobtrusive.adapters.addBool - used when your validator does not need any additional data.
      jQuery.validator.unobtrusive.adapters.addSingleVal - used when your validator takes in one piece of additional data.
      jQuery.validator.unobtrusive.adapters.addMinMax - used when your validator deals with minimum and maximum values such as range or string length.
      

      如果您的验证器不属于这些类别之一,您需要使用jQuery.validator.unobtrusive.adapters.add 方法编写自己的适配器。这并不像听起来那么困难,我们将在本文后面看到一个示例。

      我们使用addSingleVal 方法,传入适配器的名称和我们要传递的单个值的名称。如果验证函数的名称与适配器不同,可以传入第三个参数(ruleName):

      jQuery.validator.unobtrusive.adapters.addSingleVal("notequalto", "otherproperty", "mynotequaltofunction");
      

      至此,我们的自定义验证器就完成了。

      为了更好地理解,请参阅article itself,它提供了更多描述和更复杂的示例。

      HTH。

      【讨论】:

        【解决方案6】:

        我在 KendoGrid 中遇到了这个问题,我在视图的末尾使用了一个脚本来覆盖 data-val-number:

        @(Html.Kendo().Grid<Test.ViewModel>(Model)
          .Name("listado")
          ...
          .Columns(columns =>
            {
                columns.Bound("idElementColumn").Filterable(false);
            ...
            }
        

        至少,在视图的末尾我放了:

        <script type="text/javascript">
                $("#listado").on("click", function (e) {
                    $(".k-grid #idElementColumn").attr('data-val-number', 'Ingrese un número.');
                });    
        </script>
        

        【讨论】:

          【解决方案7】:

          作为解决此问题的另一种方法,我应用了一个正则表达式属性来捕获无效条目并将我的消息设置在那里:

          [RegularExpression(@"[0-9]*$", ErrorMessage = "Please enter a valid number ")]
          

          这有点小技巧,但至少在我的特定情况下,这似乎比其他解决方案的复杂性更可取。

          编辑:这在 MVC3 中运行良好,但似乎 MVC4+ 可能有更好的解决方案。

          【讨论】:

          • 我还是更喜欢第一个解决方案,更整洁。 global.asax 中只有一行代码。这样做的问题是您必须将此属性添加到项目中的每个说整数属性中,并且它使用正则表达式,这让我对只需要 0.0001 秒即可对其进行正则表达式的过程感到不舒服;)
          • 没有任何论据表明它有点像黑客。我唯一的辩护是,覆盖框架中的任何错误消息应该非常容易。
          • 好吧,正确的做法是让它以一种简单的方式被覆盖......但既然我们不能这样做,我很高兴你发现我的想法很有用。跨度>
          • 似乎我不需要美元,也许 ^ 和 $ 是隐含的。
          • 我只知道 OP 是什么整数,但如果考虑十进制数和 i18n,这将很快失控?
          【解决方案8】:

          或者您可以简单地执行此操作。

          @Html.ValidationMessageFor(m => m.PercentChange, "Custom Message: Input value must be a number"), new { @style = "display:none" })
          

          希望这会有所帮助。

          【讨论】:

            【解决方案9】:

            你要做的是:

            Application_Start() 中的Global.asax 中添加以下代码:

             ClientDataTypeModelValidatorProvider.ResourceClassKey = "Messages";
             DefaultModelBinder.ResourceClassKey = "Messages";
            

            在 VS 中右键单击您的 ASP.NET MVC 项目。选择Add =&gt; Add ASP.NET Folder =&gt; App_GlobalResources

            在该文件夹中添加一个名为 Messages.resx.resx 文件。

            .resx文件中添加这些字符串资源:

            FieldMustBeDate        The field {0} must be a date.
            FieldMustBeNumeric     The field {0} must be a number.
            PropertyValueInvalid   The value '{0}' is not valid for {1}.
            PropertyValueRequired  A value is required.
            

            根据需要更改 FieldMustBeNumeric 值... :)

            你已经完成了。


            查看这篇文章了解更多详情:

            Localizing Default Error Messages in ASP.NET MVC and WebForms

            【讨论】:

            • 我没有收到 PropertyValueInvalid 的翻译,只有默认文本...有什么想法吗?
            • 虽然@DarinDimitrov 有一个有效的解决方案,但对我来说这应该是公认的答案,因为它使用内置的可扩展机制而不是复制整个.Net 类并对其进行更改,这要少得多可维护
            • 这确实是这个问题的最佳答案。
            • @Nick Coad。是的,它是最好的——而且对这个问题没有多余的回答。我可以确认它适用于 MVC 4 和 MVC 5
            【解决方案10】:

            如果您想在全局范围内指定消息而不是为每个项目自定义消息,这是纯 js 中的另一种解决方案。

            关键是验证消息是使用jquery.validation.unobtrusive.js 在每个元素上使用data-val-xxx 属性设置的,所以你所要做的就是在库使用它们之前替换这些消息,这有点脏但我只是想要快速完成工作,所以这里用于数字类型验证:

                $('[data-val-number]').each(function () {
                var el = $(this);
                var orig = el.data('val-number');
            
                var fieldName = orig.replace('The field ', '');
                fieldName = fieldName.replace(' must be a number.', '');
            
                el.attr('data-val-number', fieldName + ' باید عددی باشد')
            });
            

            好处是它不需要编译,您可以在以后轻松扩展它,虽然不健壮,但速度很快。

            【讨论】:

              【解决方案11】:

              您可以通过在呈现字段时自己提供 data-val-number 属性来覆盖消息。这会覆盖默认消息。这至少适用于 MVC 4。

              @Html.EditorFor(model => model.MyNumberField, new { data_val_number="提供一个整数,老兄!" })

              请记住,您必须在属性名称中使用下划线,Razor 才能接受您的属性。

              【讨论】:

              • 我不知道你是怎么让它工作的。在我的 MVC4-app 中以这种方式添加的属性被忽略了。
              • 这对我有用,但就我而言,我只是想将其关闭。 data_val="false" 就够了。
              • 我看不出这是如何工作的。如果这是一个新的htmlAttributes 对象,它可能会工作,但它是一个additionalViewData 对象,所以你只需要一个名为data_val_number 的额外属性。
              • @HenningJ 非常适合我,感谢您为我节省了大量时间 :)
              • 我同意@Sprintstar。它对我不起作用,但 @Html.EditorFor(model =&gt; model.age, new { htmlAttributes = new { @class = "form-control", data_val_required = "Field required"} }) 起作用了。
              【解决方案12】:

              我只是这样做了,然后使用了一个正则表达式:

              $(document).ready(function () {
                  $.validator.methods.number = function (e) {
                      return true;
                  };
              });
              
              
              [RegularExpression(@"^[0-9\.]*$", ErrorMessage = "Invalid Amount")]
              public decimal? Amount { get; set; }
              

              【讨论】:

                【解决方案13】:

                这并不容易。默认消息作为嵌入资源存储到 System.Web.Mvc 程序集中,并且获取的方法是内部密封内部类 (System.Web.Mvc.ClientDataTypeModelValidatorProvider+NumericModelValidator.MakeErrorString) 的私有静态方法。就好像微软编码的那个人隐藏了一个最高机密:-)

                您可以查看以下blog post,其中描述了一种可能的解决方案。您基本上需要用自定义替换现有的ClientDataTypeModelValidatorProvider

                如果你不喜欢你需要做的硬核编码,你也可以用一个字符串替换你的视图模型中的这个整数值,并在它上面有一个自定义验证属性,它可以进行解析并提供一个自定义错误消息(甚至可以本地化)。

                【讨论】:

                • 哇,你一定很擅长这些秘密的东西:)。非常艰难的代码。我不明白他们为什么这么难!我有点想他们在 MVC3 RTM 中修复了这些东西,但他们没有。我将它放在我的控制器中只是为了检查它是否工作。谢谢它就像一个魅力。但是我应该把这个remove 放在哪里?在我的global.asax?那还好吗?
                • @GtEx,是的,Providers.RemoveProviders.Add 应该放在 Global.asax 的 Application_Start 方法中。
                • 有没有人能够做类似的事情,但能够根据正在验证号码的对象的其他值创建消息?例如,如果我的对象有“Prompt”和“value”,我的消息会说“{Prompt} 必须是一个数字”。或者,我是否必须为此进行对象验证?
                • 查看 MVC 4 的源代码,它看起来正在修复,允许我们指定自己的 ResourceClassKey aspnetwebstack.codeplex.com/SourceControl/changeset/view/…
                • 是的。在这种情况下,替换 ResourceClassKey 并使用名称 FieldMustBeNumeric 有效。对于必需和无效的错误混乱,请使用 Darin Dimitrov stackoverflow.com/questions/12545176/… 提出的其他方法
                【解决方案14】:

                来自我拥有的关于 MVC 3 的这本书。您所要做的就是:

                public class ClientNumberValidatorProvider : ClientDataTypeModelValidatorProvider 
                { 
                   public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, 
                                                                          ControllerContext context) 
                   { 
                       bool isNumericField = base.GetValidators(metadata, context).Any(); 
                       if (isNumericField) 
                           yield return new ClientSideNumberValidator(metadata, context); 
                   } 
                } 
                
                public class ClientSideNumberValidator : ModelValidator 
                { 
                  public ClientSideNumberValidator(ModelMetadata metadata,  
                      ControllerContext controllerContext) : base(metadata, controllerContext) { } 
                
                  public override IEnumerable<ModelValidationResult> Validate(object container) 
                  { 
                     yield break; // Do nothing for server-side validation 
                  } 
                
                  public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() 
                  { 
                     yield return new ModelClientValidationRule { 
                        ValidationType = "number", 
                        ErrorMessage = string.Format(CultureInfo.CurrentCulture,  
                                                     ValidationMessages.MustBeNumber,  
                                                     Metadata.GetDisplayName()) 
                        }; 
                  } 
                } 
                
                protected void Application_Start() 
                { 
                    // Leave the rest of this method unchanged 
                
                    var existingProvider = ModelValidatorProviders.Providers 
                        .Single(x => x is ClientDataTypeModelValidatorProvider); 
                    ModelValidatorProviders.Providers.Remove(existingProvider); 
                    ModelValidatorProviders.Providers.Add(new ClientNumberValidatorProvider()); 
                } 
                

                注意 ErrorMessage 是如何产生的,你指定当前的文化,本地化的消息是从 ValidationMessages(这里是文化细节).resx 资源文件中提取的。如果您不需要,只需将其替换为您自己的消息即可。

                【讨论】:

                • 这听起来比我选择的解决方案复杂得多。更不用说我们在 vb.net 中没有yield return,尽管有一种解决方法。我仍然没有得到这个解决方案的优势。
                • 我真的很喜欢这个解决方案。一个问题:我们正在用一个新的供应商替换现有的供应商。如果现有的提供者过去做的不仅仅是这种验证怎么办?是否可以保证删除它不会破坏任何其他功能?
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-07-27
                • 1970-01-01
                相关资源
                最近更新 更多