文档章节

ASP.NET MVC下的四种验证编程方式

KavenSu
 KavenSu
发布于 2014/07/07 17:19
字数 3331
阅读 60
收藏 4

ASP.NET MVC下的四种验证编程方式

2014-04-09 10:18 蒋金楠 博客园 我要评论(0) 字号:T | T

一键收藏,随时查看,分享好友!

ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效性,我们将针对参数的验证成为Model绑定。总地来说,我们可以采用4种不同的编程模式来进行针对绑定参数的验证。

AD:WOT2014:用户标签系统与用户数据化运营培训专场


    ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效性,我们将针对参数的验证成为Model绑定。总地来说,我们可以采用4种不同的编程模式来进行针对绑定参数的验证。

    一、手工验证绑定的参数

    在定义具体Action方法的时候,对已经成功绑定的参数实施手工验证无疑是一种最为直接的编程方式,接下来我们通过一个简单的实例来演示如何将参数验证逻辑实现在对应的Action方法中,并在没有通过验证的情况下将错误信息响应给客户端。我们在一个ASP.NET MVC应用中定义了如下一个Person类作为被验证的数据类型,它的Name、Gender和Age三个属性分别表示一个人的姓名、性别和年龄。

    
      
      
    1. public class Person  

    2. {  

    3.    [DisplayName("姓名")]  

    4.    public string Name { get; set; }  

    5.    [DisplayName("性别")]  

    6.    public string Gender { get; set; }  

    7.    [DisplayName("年龄")]  

    8.    public int? Age { get; set; }  

    9. }

    接下来我们定义了如下一个HomeController。在针对GET请求的Action方法Index中,我们创建了一个Person对象并将其作为Model呈现在对应的View中。另一个支持POST请求的Index方法具有一个Person类型的参数,我们在该Action方法中先调用Validate方法对这个输入参数实施验证。如果验证成功(ModeState.IsValid属性返回True),我们返回一个内容为“输入数据通过验证”的ContentResult,否则将此参数作为Model呈现在对应的View中。

    
      
      
    1. public class HomeController : Controller  

    2. {  

    3.    [HttpGet]  

    4.    public ActionResult Index()  

    5.    {  

    6.        return View(new Person());  

    7.    }  

    8.    [HttpPost]  

    9.    public ActionResult Index(Person person)  

    10.    {  

    11.        Validate(person);  

    12.        if (!ModelState.IsValid)  

    13.        {  

    14.            return View(person);  

    15.        }  

    16.        else

    17.        {  

    18.            return Content("输入数据通过验证");  

    19.        }  

    20.    }  

    21.    private void Validate(Person person)  

    22.    {  

    23.        if (string.IsNullOrEmpty(person.Name))  

    24.        {  

    25.            ModelState.AddModelError("Name", "'Name'是必需字段");  

    26.        }  

    27.        if (string.IsNullOrEmpty(person.Gender))  

    28.        {  

    29.            ModelState.AddModelError("Gender", "'Gender'是必需字段");  

    30.        }  

    31.        else if (!new string[] { "M", "F" }.Any(  

    32.            g => string.Compare(person.Gender, g, true) == 0))  

    33.        {  

    34.            ModelState.AddModelError("Gender",  

    35.            "有效'Gender'必须是'M','F'之一");  

    36.        }  

    37.        if (null == person.Age)  

    38.        {  

    39.            ModelState.AddModelError("Age", "'Age'是必需字段");  

    40.        }  

    41.        else if (person.Age > 25 || person.Age < 18)  

    42.        {  

    43.            ModelState.AddModelError("Age", "有效'Age'必须在18到25周岁之间");  

    44.        }  

    45.    }  

    46. }

    如上面的代码片断所示,我们在Validate该方法中我们对作为参数的Person对象的3个属性进行逐条验证,如果提供的数据没有通过验证,我们会调用当前ModelState的AddModelError方法将指定的验证错误消息转换为ModelError保存起来。我们采用的具体的验证规则如下。

    • Person对象的Name、Gender和Age属性均为必需字段,不能为Null(或者空字符串)。

    • 表示性别的Gender属性的值必需是“M”(Male)或者“F”(Female),其余的均为无效值。

    • Age属性表示的年龄必须在18到25周岁之间。

    如下所示的是Action方法Index对应View的定义,这是一个Model类型为Person的强类型View,它包含一个用于编辑人员信息的表单。我们直接调用HtmlHelper<TModel> 的扩展方法EditorForModel将作为Model的Person对象以编辑模式呈现在表单之中。

    
      
      
    1. @model Person  

    2. <html>

    3. <head>

    4.    <title>编辑人员信息</title>

    5. </head>

    6. <body>

    7.    @using (Html.BeginForm())  

    8.    {  

    9.        @Html.EditorForModel()  

    10.        <input type="submit" value="保存"/>

    11.    }  

    12. </body>

    13. </html>

    直接运行该程序后,一个用于编辑人员基本信息的页面会被呈现出来,如果我们在输入不合法的数据并提交后,相应的验证信息会以图1所示的形式呈现出来。

    二、使用ValidationAttribute特性

    将针对输入参数的验证逻辑和业务逻辑定义在Action方法中并不是一种值得推荐的编程方式。在大部分情况下,同一个数据类型在不同的应用场景中具有相同的验证规则,如果我们能将验证规则与数据类型关联在一起,让框架本身来实施数据验证,那么最终的开发者就可以将关注点更多地放在业务逻辑的实现上面。实际上这也是ASP.NET MVC的Model验证系统默认支持的编程方式。当我们在定义数据类型的时候,可以在类型及其数据成员上面应用相应的ValidationAttribute特性来定义默认采用的验证规则。

    “System.ComponentModel.DataAnnotations”命名空间定义了一系列具体的ValidationAttribute特性类型,它们大都可以直接应用在自定义数据类型的某个属性上对目标数据成员实施验证。这些预定义验证特性不是本章论述的重点,我们会在“下篇”中对它们作一个概括性的介绍。

    常规验证可以通过上面列出的这些预定义ValidationAttribute特性来完成,但是在很多情况下我们需要通过创建自定义的ValidationAttribute特性来解决一些特殊的验证。比如上面演示实例中针对Person对象的验证中,我们要求Gender属性指定的表示性别的值必须是“M/m”和“F/f”两者之一,这样的验证就不得不通过自定义的ValidationAttribute特性来实现。

    针对 “某个值必须在指定的范围内”这样的验证规则,我们定义一个DomainAttribute特性。如下面的代码片断所示,DomainAttribute具有一个IEnumerable<string>类型的只读属性Values提供了一个有效值列表,该列表在构造函数中被初始化。具体的验证实现在重写的IsValid方法中,如果被验证的值在这个列表中,则视为验证成功并返回True。为了提供一个友好的错误消息,我们重写了方法FormatErrorMessage。

    
      
      
    1. [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field,   AllowMultiple = false)]  

    2. public class DomainAttribute : ValidationAttribute  

    3. {  

    4.    public IEnumerable<string> Values { get; private set; }  

    5.    public DomainAttribute(string value)  

    6.    {  

    7.        this.Values = new string[] { value };  

    8.    }  

    9.    public DomainAttribute(params string[] values)  

    10.    {  

    11.        this.Values = values;  

    12.    }  

    13.    public override bool IsValid(object value)  

    14.    {  

    15.        if (null == value)  

    16.        {  

    17.            return true;  

    18.        }  

    19.        return this.Values.Any(item => value.ToString() == item);  

    20.    }  

    21.    public override string FormatErrorMessage(string name)  

    22.    {  

    23.        string[] values = this.Values.Select(value => string.Format("'{0}'",  value)).ToArray();  

    24.        return string.Format(base.ErrorMessageString, name,string.Join(",",   values));  

    25.    }  

    26. }

    由于ASP.NET MVC在进行参数绑定的时候会自动提取应用在目标参数类型或者数据成员上的ValidationAttribute特性,并利用它们对提供的数据实施验证,所以我们不再需要像上面演示的实例一样自行在Action方法中实施验证,而只需要在定义参数类型Person的时候应用相应的ValidationAttribute特性将采用的验证规则与对应的数据成员相关联。

    如下所示的是属性成员上应用了相关ValidationAttribute特性的Person类型的定义。我们在三个属性上均应用了RequiredAttribute特性将它们定义成必需的数据成员,Gender和Age属性上则分别应用了DomainAttribute和RangeAttribute特性对有效属性值的范围作了相应限制。

    
      
      
    1. public class Person  

    2. {  

    3.    [DisplayName("姓名")]  

    4.    [Required(ErrorMessageResourceName = "Required",    ErrorMessageResourceType = typeof(Resources))]  

    5.    public string Name { get; set; }  

    6.    [DisplayName("性别")]  

    7.    [Required(ErrorMessageResourceName = "Required",   ErrorMessageResourceType = typeof(Resources))]  

    8.    [Domain("M", "F", "m", "f", ErrorMessageResourceName = "Domain",  ErrorMessageResourceType = typeof(Resources))]  

    9.    public string Gender { get; set; }  

    10.    [DisplayName("年龄")]  

    11.    [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources))]  

    12.    [Range(18, 25, ErrorMessageResourceName = "Range",  ErrorMessageResourceType = typeof(Resources))]  

    13.    public int? Age { get; set; }  

    14. }

    三个ValidationAttribute特性采用的错误消息均定义在项目默认的资源文件中(我们可以采用这样的步骤创建这个资源文件:右键选择Solution Exploror中的项目,并在上下文菜单中选择“属性”选项打开“项目属性”对象框。最后在对话框中选择“资源”Tab页面,通过点击页面中的链接创建一个资源文件),具体定义如图2所示。

    由于ASP.NET MVC会自动提取应用在绑定参数类型上的ValidationAttribute特性对绑定的参数实施自动化验证,所以我们根本不需要在具体的Action方法中来对参数作手工验证。如下面的代码片断所示,我们在Action方法Index中不再显式调用Validate方法,但是运行该程序并在输入不合法数据的情况下提交表单后依然会得到如图1所示的输出结果。

    
      
      
    1. public class HomeController : Controller  

    2. {  

    3.    //其他成员

    4.    [HttpPost]  

    5.    public ActionResult Index(Person person)  

    6.    {  

    7.        if (!ModelState.IsValid)  

    8.        {  

    9.            return View(person);  

    10.        }  

    11.        else

    12.        {  

    13.            return Content("输入数据通过验证");  

    14.        }  

    15.    }  

    16. }

    三、让数据类型实现IValidatableObject接口

    除了将验证规则通过ValidationAttribute特性直接定义在数据类型上并让ASP.NET MVC在进行参数绑定过程中据此来验证参数之外,我们还可以将验证操作直接定义在数据类型中。既然我们将验证操作直接实现在了数据类型上,意味着对应的数据对象具有“自我验证”的能力,我们姑且将这些数据类型称为“自我验证类型”。这些自我验证类型是实现了具有如下定义的接口IValidatableObject,该接口定义在“System.ComponentModel.DataAnnotations”命名空间下。

    
      
      
    1. public interface IValidatableObject  

    2. {  

    3.    IEnumerable<ValidationResult> Validate(  ValidationContext validationContext);  

    4. }

    如上面的代码片断所示,IValidatableObject接口具有唯一的方法Validate,针对自身的验证就实现在该方法中。对于上面演示实例中定义的数据类型Person,我们可以按照如下的形式将它定义成自我验证类型。

    
      
      
    1. public class Person: IValidatableObject  

    2. {  

    3.    [DisplayName("姓名")]  

    4.    public string Name { get; set; }  

    5.    [DisplayName("性别")]  

    6.    public string Gender { get; set; }  

    7.    [DisplayName("年龄")]  

    8.    public int? Age { get; set; }  

    9.    public IEnumerable<ValidationResult> Validate( ValidationContext validationContext)  

    10.    {  

    11.        Person person = validationContext.ObjectInstance as Person;  

    12.        if (null == person)  

    13.        {  

    14.            yield break;  

    15.        }  

    16.        if(string.IsNullOrEmpty(person.Name))  

    17.        {  

    18.            yield return new ValidationResult("'Name'是必需字段", new string[]{"Name"});  

    19.        }  

    20.        if (string.IsNullOrEmpty(person.Gender))  

    21.        {  

    22.            yield return new ValidationResult("'Gender'是必需字段", new string[] { "Gender" });  

    23.        }  

    24.        else if (!new string[]{"M","F"}.Any( g=>string.Compare(person.Gender,g, true) == 0))  

    25.        {  

    26.            yield return new ValidationResult("有效'Gender'必须是'M','F'之一",   new string[] { "Gender" });  

    27.        }  

    28.        if (null == person.Age)  

    29.        {  

    30.            yield return new ValidationResult("'Age'是必需字段",    new string[] { "Age" });  

    31.        }  

    32.        else if (person.Age > 25 || person.Age < 18)  

    33.        {  

    34.            yield return new ValidationResult("'Age'必须在18到25周岁之间",    new string[] { "Age" });  

    35.        }              

    36.    }  

    37. }

    如上面的代码片断所示,我们让Person类型实现了IValidatableObject接口。在实现的Validate方法中,我们从验证上下文中获取被验证的Person对象,并对其属性成员进行逐个验证。如果数据成员没有通过验证,我们通过一个ValidationResult对象封装错误消息和数据成员名称(属性名),该方法最终返回的是一个元素类型为ValidationResult的集合。在不对其他代码作任何改动的情况下,我们直接运行该程序并在输入不合法数据的情况下提交表单后依然会得到如图1所示的输出结果。

    四、让数据类型实现IDataErrorInfo接口

    上面我们让数据类型实现IValidatableObject接口并将具体的验证逻辑定义在实现的Validate方法中,这样的类型能够被ASP.NET MVC所识别,后者会自动调用该方法对绑定的数据对象实施验证。如果我们让数据类型实现IDataErrorInfo接口也能实现类似的自动化验证效果。

    IDataErrorInfo接口定义在“System.ComponentModel”命名空间下,它提供了一种标准的错误信息定制方式。如下面的代码片段所示,IDataErrorInfo具有两个成员,只读属性Error用于获取基于自身的错误消息,而只读索引用于返回指定数据成员的错误消息。

    
      
      
    1. public interface IDataErrorInfo  

    2. {  

    3.    string Error { get; }  

    4.    string this[string columnName] { get; }  

    5. }

    同样是针对上面演示的实例,现在我们对需要被验证的数据类型Person进行了重新定义。如下面的代码片断所示,我们让Person实现了IDataErrorInfo接口。在实现的索引中,我们将索引参数columnName视为属性名称,根据它按照上面的规则对相应的属性成员实施验证,并在验证失败的情况下返回相应的错误消息。在不对其他代码作任何改动的情况下,我们直接运行该程序并在输入不合法数据的情况下提交表单后依然会得到如图1所示的输出结果。

    
      
      
    1. public class Person : IDataErrorInfo  

    2. {  

    3.    [DisplayName("姓名")]  

    4.    public string Name { get; set; }  

    5.    [DisplayName("性别")]  

    6.    public string Gender { get; set; }  

    7.    [DisplayName("年龄")]  

    8.    public int? Age { get; set; }  

    9.    [ScaffoldColumn(false)]  

    10.    public string Error { get; private set; }  

    11.    public string this[string columnName]  

    12.    {  

    13.        get

    14.        {  

    15.            switch (columnName)  

    16.            {  

    17.                case "Name":  

    18.                    {  

    19.                        if(string.IsNullOrEmpty(this.Name))  

    20.                        {  

    21.                            return "'姓名'是必需字段";  

    22.                        }  

    23.                        return null;  

    24.                    }  

    25.                case "Gender":  

    26.                    {  

    27.                        if (string.IsNullOrEmpty(this.Gender))  

    28.                        {  

    29.                            return "'性别'是必需字段";  

    30.                        }  

    31.                        else if (!new string[] { "M", "F" }.Any(  

    32.                            g => string.Compare(this.Gender, g, true) == 0))  

    33.                        {  

    34.                            return "'性别'必须是'M','F'之一";  

    35.                        }  

    36.                        return null;  

    37.                    }  

    38.                case "Age":  

    39.                    {  

    40.                        if (null == this.Age)  

    41.                        {  

    42.                            return "'年龄'是必需字段";  

    43.                        }  

    44.                        else if (this.Age > 25 || this.Age < 18)  

    45.                        {  

    46.                            return "'年龄'必须在18到25周岁之间";  

    47.                        }  

    48.                        return null;  

    49.                    }  

    50.                default: return null;  

    51.            }  

    52.        }  

    53.    }  

    54. }

    原文链接:http://www.cnblogs.com/artech/p/asp-net-mvc-validation-programming.html

    本文转载自:

    KavenSu
    粉丝 6
    博文 66
    码字总数 16605
    作品 0
    深圳
    私信 提问
    ASP.NET MVC 随笔汇总

    ASP.NET MVC 随笔汇总 为了方便大家浏览所以整理一下,有的系列篇幅中不是很全面以后会慢慢的补全的。 学前篇之: ASP.NET MVC学前篇之扩展方法、链式编程 ASP.NET MVC学前篇之Lambda表达式、...

    jinyuan0829
    2014/08/03
    0
    0
    自学MVC看这里——全网最全ASP.NET MVC 教程汇总

    MVC架构已深得人心,微软也不甘落后,推出了Asp.net MVC。小编特意整理博客园乃至整个网络最具价值的MVC技术原创文章,为想要学习ASP.NET MVC技术的学习者提供一个整合学习入口。本文从Why,W...

    葡萄城控件技术团队
    2016/08/18
    567
    0
    ASP.NET MVC基于标注特性的Model验证:DataAnnotationsModelValidatorProvider

    DataAnnotationsModelValidator最终是通过它对应的ModelValidatorProvider,即DataAnnotationsModelValidatorProvider创建的。通过前面的介绍我们知道它是AssociatedValidatorProvider的子类...

    长平狐
    2012/09/04
    240
    0
    【ASP.NET MVC系列】浅谈ASP.NET MVC资源过滤和授权

    最近比较忙,博客很久没更新了,很多博友问何时更新博文,因此,今天就花了点时间,写了本篇文章,但愿大家喜欢。 本篇文章不适合初学者,需要对ASP.NET MVC具有一定基础。 本篇文章主要从A...

    Alan_beijing
    2018/07/22
    0
    0
    ASP.NET MVC的客户端验证:jQuery的验证

    之前我们一直讨论的Model验证仅限于服务端验证,即在Web服务器根据相应的规则对请求数据实施验证。如果我们能够在客户端(浏览器)对用户输入的数据先进行验证,这样会减少针对服务器请求的频...

    长平狐
    2012/09/04
    693
    0

    没有更多内容

    加载失败,请刷新页面

    加载更多

    OSChina 周六乱弹 —— 不要在领导修风扇的时候打开电扇

    Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @巴拉迪维 :《Whats Up》 主唱妹子 Lina Perry 的嗓音实在太有力了,收放自如的自信才能唱出这么优秀的歌吧!#今日歌曲推荐# 《Whats Up》-...

    小小编辑
    今天
    11
    0
    SpringBoot集成Elasticsearch并进行增删改查操作

    一、创建工程 使用IntelliJ创建SpringBoot工程 SpringBoot版本为2.0.4 ElasticSearch为5.6.10 删掉蓝框中的文件(如上) 最后我们的目录结构(如下) 下面pom文件主要修改的是把spring boot从Int...

    一字见心
    今天
    5
    0
    x001-版本介绍

    python版本介绍 目前有2 和 3 有很多企业用的代码是2版本,随着技术的发展,以后用3的会成为大趋势 python3的安装 yum -y install wget gcc zlib-devel bzip2-devel openssl-devel ncurses-d...

    侠客行之石头
    今天
    5
    0
    聊聊rocketmq的TransientStorePool

    序 本文主要研究一下rocketmq的TransientStorePool TransientStorePool rocketmq-all-4.6.0-source-release/store/src/main/java/org/apache/rocketmq/store/TransientStorePool.java publi......

    go4it
    昨天
    6
    0
    笔记

    场外借贷, 质押 ,托管, 永续合约. 场外借贷,n签合同. 新功能 证券交易组负责中信证券机构及个人投资交易相关系统,服务机构及个人投资客户, 涉及到两融、期权、 期货、做市等境内境外创新业...

    MtrS
    昨天
    6
    0

    没有更多内容

    加载失败,请刷新页面

    加载更多

    返回顶部
    顶部