由@Html帮助程序生成的MVC中如何更改“数据值编号”消息验证


76

假设这个模型:

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错误消息,因为它PercentChange是,所以我猜它已生成Integer。我一直在寻找这样的属性来更改它,range或者任何相关的不起作用。
我知道可以编辑unobtrusive的js文件本身或在客户端覆盖它。我想更改data-val-number的错误消息,就像服务器端的其他消息一样。


我已经使用了Griffin MVC Contrib项目来对没有难看属性的验证文本进行本地化
Eduardo Molteni 2012年

Answers:


39

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

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

如果您不喜欢需要的硬编码,则还可以用字符串替换视图模型中的该整数值,并在其上具有自定义验证属性,该属性将进行解析并提供自定义错误消息(甚至可以本地化)。


3
哇,您必须非常擅长于这些秘密事情:)。非常难的代码。我不明白他们为什么要这么难!我有点希望他们可以在MVC3 RTM中修复这些问题,但事实并非如此。我将其放在控制器中只是为了检查它是否正常工作。并感谢它的魅力。但是我应该把这个remove东西放在哪里?在我的global.asax?那可以吗
GtEx 2011年

2
@GtEx,是的Providers.RemoveProviders.Add应该放在Application_StartGlobal.asax的方法中。
Darin Dimitrov

有没有人能够做类似的事情,但是能够根据验证数字的对象的其他值创建消息?例如,如果我的对象具有“提示”和“值”,则我的消息将显示“ {提示}必须为数字”。还是我必须为此进行对象验证?
理查德B

8
展望MVC 4的源代码,它看起来像是被固定,允许我们指定我们自己ResourceClassKey aspnetwebstack.codeplex.com/SourceControl/changeset/view/...
MartinHN

3
是。在这种情况下,替换ResourceClassKey并使用名称FieldMustBeNumeric即可。对于必需和无效的错误消息,请使用达林·迪米特洛夫(Darin Dimitrov)提出的另一种方法stackoverflow.com/questions/12545176/…–
Eduardo

77

您可以通过在呈现字段时自行提供data-val-number属性来覆盖该消息。这将覆盖默认消息。至少与MVC 4兼容。

@ Html.EditorFor(model => model.MyNumberField,new {data_val_number =“提供一个整数,伙计!”})

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


我不知道你是怎么开始工作的。在我的MVC4-app属性中,以这种方式添加的属性将被忽略。
blazkovicz 2013年

这对我有用,尽管就我而言,我只是想将其关闭。data_val =“ false”已足够。
詹姆斯

4
我看不到这将如何工作。如果这是一个新htmlAttributes对象,它可能会起作用,但它是一个additionalViewData对象,因此您只需获得一个名为的额外属性data_val_number
Sprintstar 2014年

3
@HenningJ对我来说很棒,感谢您为我节省了很多时间:)
David Work

3
我同意@Sprintstar。它对我不起作用,但是对我@Html.EditorFor(model => model.age, new { htmlAttributes = new { @class = "form-control", data_val_required = "Field required"} })有用。
user1 2015年

52

您要做的是:

添加以下代码里面Application_Start()Global.asax

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

右键单击VS中的ASP.NET MVC项目。选择Add => Add ASP.NET Folder => App_GlobalResources

在该文件夹中添加一个.resx名为Messages.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根据需要更改值... :)

你完成了。


检查此帖子以获取更多详细信息:

在ASP.NET MVC和WebForms中本地化默认错误消息


我没有收到PropertyValueInvalid的翻译,只有默认文本...有任何想法吗?
user1073075 2014年

4
尽管@DarinDimitrov有一个有效的解决方案,但对我来说,这应该是一个可以接受的答案,因为它使用内置的可扩展机制,而不是复制整个.Net类并进行更改,因此,该类的维护性要差得多
2015年

5
这确实是这个问题的最佳答案。
尼克·科德

2
@尼克·科德(Nick Coad)。是的,这是最好的-而且没有多余的答案回答这个问题。我可以确认它可以在MVC 4和MVC 5中正常工作
netfed

23

作为解决此问题的一种替代方法,我应用了RegularExpression属性来捕获无效条目并在此处设置消息:

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

这样做虽然有些微不足道,但至少在我的特定情况下,似乎比其他解决方案提供的复杂性更好。

编辑:这在MVC3中效果很好,但似乎对于MVC4 +可能会有更好的解决方案。


1
我仍然更喜欢第一个解决方案,更加整洁。global.asax中只有一行代码。问题是您必须将此属性添加到项目中的每个单个说整数属性,并且使用正则表达式,这使我对仅花费0.0001秒进行regEx的过程感到不舒服;)
GtEx 2011年

2
毫无疑问,这有点hack。我唯一的辩护是,重写框架中的任何错误消息应该非常容易。
马修·尼科尔斯

正确的方法是以一种简单的方式使其可重写...但是由于我们无法做到这一点,所以我很高兴您发现我的想法很有用。
马修·尼科尔斯

好像我不需要美元,那么^和$可能是隐式的。
AndersLindén2014年

我只知道OP是什么整数,但是如果考虑十进制数字和i18n,它将很快失去作用?
phuzi

11

从我关于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资源文件中提取本地化消息。如果不需要,只需用您自己的消息替换它。


听起来比我选择的解决方案复杂得多。更不用说yield returnvb.net上没有的,尽管可以解决。我仍然没有得到这个解决方案的优势。
GtEx

我真的很喜欢这个解决方案。一个问题:我们正在用一个新的提供商来代替现有的提供商。如果现有提供者过去不仅要做这种验证,该怎么办?是否保证通过删除它不会破坏任何其他功能?
亚历克斯

7

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

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

简而言之,您需要做的是在加载jQuery验证后包括以下脚本以及适当的本地化文件

(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));

感谢Phil,(+ 1),我很高兴知道。但是我个人更愿意在这个特定问题上更改MVC3。我希望他们将此修补程序发布到下一个版本中。
GtEx 2011年

4

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

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

1
太神奇了,这确实有效!这ResourceClassKey虽然不是ResourceKey
文森特的SEL

3

这是纯js中的另一个解决方案,如果您要全局指定消息而不是为每个项目指定消息,则可以使用。

关键是验证消息是使用每个元素上jquery.validation.unobtrusive.jsdata-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 + ' باید عددی باشد')
});

好处是它不需要编译,您可以稍后轻松地对其进行扩展,虽然它不是很健壮,但是却很快。


2

也检查一下:

ASP.NET MVC 3中验证的完整指南-第2部分

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

创建在客户端和服务器上均可使用的功能齐全的自定义验证器有四个不同的部分。首先,我们子类化ValidationAttribute并添加我们的服务器端验证逻辑。接下来,我们IClientValidatable在属性上实现以允许将HTML5data-*属性传递给客户端。第三,我们编写了一个自定义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;
    }        
}

将新属性添加到R​​egisterModel的password属性中,然后运行该应用程序。

[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适配器

适配器的工作是读取data-*表单元素上的HTML5属性,并将此数据转换为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");

至此,我们的自定义验证程序已完成。

为了更好地理解,请参阅文章本身,该文章提供了更多描述和更复杂的示例。

HTH。


1

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

$(document).ready(function () {
    $.validator.methods.number = function (e) {
        return true;
    };
});


[RegularExpression(@"^[0-9\.]*$", ErrorMessage = "Invalid Amount")]
public decimal? Amount { get; set; }

1

或者您可以简单地做到这一点。

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

希望这可以帮助。


1

我把这个放在我的观点上

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

0

我在KendoGrid中有此问题,我在View的末尾使用了一个脚本来覆盖data-val-number:

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

至少在View的最后,我提出:

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

0

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

[Required(ErrorMessage ="الزامی")]
[StringLength(maximumLength:50,MinimumLength =2)]
[Display(Name = "نام")]
public string FirstName { get; set; }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.