[ASP.NET MVC]如何定制Numeric属性/字段验证消息

对于一个Numeric属性/字段,ASP.NET MVC会自动进行数据类型的验证(客户端验证),以确保输入的是一个有效的数字,但是呈现在页面上的错误消息总是一段固定的文本:“The field {0} must be a number”,本篇提供一种解决方案使我们可以对此验证消息进行定制。[源代码从这里下载]

目录 一、针对Numeric属性/字段默认验证消息 二、默认的验证消息来源于何处? 三、通过自定义ModelValidatorProvider替换NumericModelValidator 四、注册自定义ModelValidatorProvider

一、针对Numeric属性/字段默认验证消息

我们先来通过一个简单的例子来验证这个问题,为此我们定义了如下一个表示员工信息的Employee类型,其中代表年龄的Age属性类型为整型。

   1: public class Employee
   2: {
   3:     [Display(Name = "姓名")]
   4:     public string Name { get; set; }
   5:  
   6:     [Display(Name = "性别")]
   7:     public string Gender { get; set; }
   8:  
   9:     [Display(Name = "年龄")]
  10:     public int Age { get; set; }
  11: }

我们创建一个Model类型为Employee的View对某个元员工的信息进行修改。如下图所示,当我们输入一个非数字字符串作为Age字段的时候,验证错误信息显示为“The field 年龄 must be a number”,值得一提的是:当前线程的CurrentUICulture为zh-CN。

二、默认的验证消息来源于何处?

针对数字类型字段进行验证的是一个名称为NumericModelValidator的ModelValidator,不过这是个定义在System.Web.Mvc程序集中俄内部类型。如果采用Reflector查看其定义,可以发现用于返回错误消息的方法是一个名为MakeErrorString的静态方法。如下面的代码所示,作为错误消息的字符串来源于内嵌于程序集中的资源文件。

   1: internal sealed class NumericModelValidator : ModelValidator
   2: {
   3:     //其他成员
   4:     private static string MakeErrorString(string displayName)
   5:     {
   6:         return string.Format(CultureInfo.CurrentCulture, MvcResources.ClientDataTypeModelValidatorProvider_FieldMustBeNumeric, new object[] { displayName });
   7:     }
   8: }

NumericModelValidator最终是通过ClientDataTypeModelValidatorProvider这个一个ModelValidatorProvider提供的。

三、通过自定义ModelValidatorProvider替换NumericModelValidator

如果我们想改变内部类型NumericModelValidator的错误消息,可以通过将ClientDataTypeModelValidatorProvider提供的NumericModelValidator替换成另一个ModelValidator。在这里我们替换的是一个DataAnnotationsModelValidator,而它基于的ValidationAttribute是我们自定义的NumericAttribute。

如下面的代码片断所示,内部类型NumericAttribute是ValidationAttribute的子类,并且实现了IClientValidatable接口。在这里我么只考虑客户端验证,所以重写的IsValid方法直接返回True,而GetClientValidationRules方法则返回一个包含一个验证类型为“number”的ModelClientValidationRule对象的集合。我们使用FormatErrorMessage方法格式化后的字符串作为ModelClientValidationRule的ErrorMessage属性。

   1: internal class NumericAttribute : ValidationAttribute, IClientValidatable
   2: {
   3:     public override bool IsValid(object value)
   4:     {
   5:         return true;
   6:     }
   7:     public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
   8:     {
   9:         yield return new ModelClientValidationRule { ValidationType = "number", ErrorMessage = this.FormatErrorMessage(metadata.DisplayName)};
  10:     }
  11: }

现在我们需要做的就是通过一个自定义ModelValidatorProvider将ClientDataTypeModelValidatorProvider提供的NumericModelValidator替换成基于NumericAttribute的DataAnnotationsModelValidator,所以我们定义了一个直接继承自ClientDataTypeModelValidatorProvider的FilterableClientDataTypeModelValidatorProvider。如下面的代码所示,在重写的GetValidators方法中我们完成了针对NumericModelValidator的替换。对于被创建的NumericAttribute来说,我们指定的错误消息也定义在Resource文件中(为了提供多语言的支持),而基于zh的内容为“{0}必须是一个数字!”。

   1: public class FilterableClientDataTypeModelValidatorProvider : ClientDataTypeModelValidatorProvider
   2: {
   3:     public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
   4:     {           
   5:         var allValidators = base.GetValidators(metadata, context);
   6:         var validators = new List<ModelValidator>();
   7:         foreach (var v in allValidators)
   8:         {
   9:             if (v.GetType().Name != "NumericModelValidator")
  10:             {
  11:                 validators.Add(v);
  12:             }
  13:             else
  14:             { 
  15:                 NumericAttribute attribute = new NumericAttribute{ ErrorMessage = Resources.FieldMustBeNumeric};
  16:                 DataAnnotationsModelValidator validator = new DataAnnotationsModelValidator(metadata, context, attribute);
  17:                 validators.Add(validator);
  18:             }
  19:         }
  20:         return validators;
  21:     }
  22: }

四、注册自定义ModelValidatorProvider

现在我们只需要通过ModelValidatorProvider的注册让我们自定义的FilterableClientDataTypeModelValidatorProvider替换默认的ClientDataTypeModelValidatorProvider。在Global.asax中,针对FilterableClientDataTypeModelValidatorProvider的注册可以通过如下的代码来完成。

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     //其他成员
   4:     protected void Application_Start()
   5:     { 
   6:          //其他操作
   7:         var clientDataTypeValidator = ModelValidatorProviders.Providers.OfType<ClientDataTypeModelValidatorProvider>().FirstOrDefault();
   8:         if (null != clientDataTypeValidator)
   9:         {
  10:             ModelValidatorProviders.Providers.Remove(clientDataTypeValidator);
  11:         }
  12:         ModelValidatorProviders.Providers.Add(new FilterableClientDataTypeModelValidatorProvider());
  13:     }
  14: }

现在运行我们的程序就可以得到我们定制的错误消息了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏GreenLeaves

Jquery ajax调用后台aspx后台文件方法(不是ashx)

在asp.net webForm开发中,用Jquery ajax调用aspx页面的方法常用的有两种:下面我来简单介绍一下。   (1)通过aspx.cs的静态方...

3235
来自专栏Google Dart

Dart 服务端开发 shelf_bind 包

shelf_bind倾向于约定优于配置,因此您可以编写必要的最小代码,但仍然可以根据需要覆盖默认值。

942
来自专栏跟着阿笨一起玩NET

Assembly.Load()方法,Assembly.LoadFrom()方法,Assembly.LoadFile()方法的区别!

         这个方法通过程序集的长名称(包括程序集名,版本信息,语言文化,公钥标记)来加载程序集的,会加载此程序集引用的其他程序集,一般情况下都应该优先...

2811
来自专栏FreeBuf

Python黑客学习笔记:从HelloWorld到编写PoC(上)

本系列文章适合CS在读学生和万年工具党,本文会在英文原文的基础上做些修改,并适当增加些解释说明。 ? 本篇包含原文的前几部分: 0x0 – Getting St...

28910
来自专栏技巅

Redis第一个版本源码分析-启动过程分析1

1865
来自专栏瓜大三哥

Yaffs_guts

Yaffs_guts ? 1.Chunk的读写擦除 2.文件地址映射 3.文件系统对象 1.Chunk的读写擦除 我们知道,NAND Flash的基本擦除单位是...

21310
来自专栏逆向技术

C++反汇编第六讲,认识C++中的Try catch语法,以及在反汇编中还原

      C++反汇编第六讲,认识C++中的Try catch语法,以及在反汇编中还原 我们以前讲SEH异常处理的时候已经说过了,C++中的Try catch...

23410
来自专栏令仔很忙

工厂模式的Assembly.Load(path).CreateInstance(className)出错解决方法

  下面咱们先了解Assembly.Load(path).CreateInstance(className)

942
来自专栏武培轩的专栏

京东面经汇总

一、Java Java的优势 平台无关性、垃圾回收 Java有哪些特性,举个多态的例子。 封装、继承、多态 abstract interface区别 含有abs...

5496
来自专栏程序员互动联盟

【编程基础】聊聊C语言-我的地盘我做主

上一篇我们讲了变量的寿命,知道了C语言的变量是有生命周期的。到了一定的时机它们所占用的内存就会被释放。接下来我们讲讲这些变量都存储在哪些地盘上以及它们各自的势力...

3425

扫码关注云+社区

领取腾讯云代金券