【ASP.NET MVC系列】浅谈数据注解和验证
【ASP.NET MVC系列】浅谈数据注解和验证

【02】浅谈Google Chrome浏览器(操作篇)(上)
【03】浅谈Google Chrome浏览器(操作篇)(下)
【04】浅谈ASP.NET框架
【07】浅谈ASP.NET MVC 路由
【08】浅谈ASP.NET MVC 视图
【10】浅谈jqGrid 在ASP.NET MVC中增删改查
【13】浅谈NuGet在VS中的运用
【14】浅谈ASP.NET 程序发布过程

一 概述
关于数据验证和数据注解,是任何软件系统不可小觑的必要模块,在软件系统中起到举足轻重的作用。
1.从数据验证的验证方式来说,我们一般分为客户端验证和服务端验证(或者两种方式相结合);
2.从数据验证的作用角度来说,数据验证起到很重要的作用,如防止漏洞注入,防止网络攻击(XSS等),确保数据安全,确保数据合理性,防止垃圾数据等作用;
3.从数据验证的种类来书,一般分为第三方验证(如我们用Jquery写好验证插件,在客户端用AJAX验证)和基于ASP.NET MVC框架的数据验证;
4.从数据注解的作用角度来说,如界面关键字段的友好设置和提示等;
说了那么多,那么本篇文章会讲解哪些内容呢?
本篇文章主要讲解基于ASP.NET MVC框架的数据验证特性和数据注解。

二 数据验证
(一)ASP.NET MVC 内置六大类数据验证特性

1.在ASP.NET MVC中,验证特性定义在System.ComponentModel.DataAnnotations命名空间中,因此我们在使用验证特性前,需要引入命名空间:
using System.ComponentModel.DataAnnotations;
2.ASP.NET MVC内置了六大验证特性:Required,StringLength,RegularExpression,Range,Compare和Remote;
3.数据验证使用单个验证特性:指数据验证只使用其中一个验证特性
1 [Required]
2 public string Username { get; set; }
4.有些属性,单个验证特性无法满足,需要两个及其以上验证特性组合,如密码,至少要满足两个条件:
(1)必填 (2)不少于6位
[Required]
[StringLength(6)]
public string Password { get; set; }
5.用代码演示一下五大验证特性(Remote除外)

Models:UserInfo.cs

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Web;
5
6 using System.ComponentModel.DataAnnotations;
7
8
9 namespace DataValidate.Models
10 {
11 public class UserInfo
12 {
13 //定义用户名必填
14 [Required]
15 public string UserName { get; set; }
16 //定义密码必填,且满足6位
17 [Required]
18 [StringLength(128,MinimumLength =6)]
19 public string Password { get; set; }
20 //验证两次输入的密码是否一致
21 [Required]
22 [Compare("Password", ErrorMessage = "两次密码输入不一致")]
23 public string ConfirmPassword { get; set; }
24 //定义邮件为必填,且满足邮件格式
25 [Required]
26 [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}")]
27 public string Email { get; set; }
28 //定义年龄为必填,且1-130岁之间
29 [Required]
30 [Range(1, 130)]
31 public int Age { get; set; }
32 }
33 }
34
35

Controller:DefaultController

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Web;
5 using System.Web.Mvc;
6
7 using DataValidate.Models;
8 namespace DataValidate.Controllers
9 {
10 public class DefaultController : Controller
11 {
12 // GET: Default
13
14 public ActionResult Index()
15 {
16 return View();
17 }
18
19 public ActionResult DataValidateDemo(UserInfo userInfo)
20 {
21 UserInfo _userInfo = new UserInfo();
22 _userInfo.UserName = userInfo.UserName;
23 return View("Index");
24 }
25 }
26 }

View:Index.cshtml

1 @model DataValidate.Models.UserInfo
2
3
4 @{
5 ViewBag.Title = "Index";
6 }
7
8 <h2>Index</h2>
9
10 @using (Html.BeginForm("DataValidateDemo", "Default"))
11 {
12 <div>@Html.Label("用户名"): @Html.TextBoxFor(m=>m.UserName)
13 @Html.ValidationMessageFor(m=>m.UserName)
14 </div>
15 <div>@Html.Label("密码"):@Html.TextBox("Password")
16 @Html.ValidationMessageFor(m=>m.Password)</div>
17
18 <div>@Html.Label("确认密码"):@Html.TextBox("ConfirmPassword")
19 @Html.ValidationMessageFor(m=>m.ConfirmPassword)</div>
20 <div>
21 @Html.Label("邮件"):@Html.TextBox("Email")
22 @Html.ValidationMessageFor(m => m.Email)
23 </div>
24 <div>
25 @Html.Label("年龄"):@Html.TextBox("Age")
26 @Html.ValidationMessageFor(m => m.Age)
27 </div>
28 <div><input type="submit" value="提交" /></div>
29 }

我们来看看测试结果

6.为什么要把Remote剔出来单独讲解呢?
我们知道,除Remote以外的五大验证特性,命名空间均为System.ComponentModel.DataAnnotations,而Remote特性的命名空间却是System.Web.Mvc。
Remote,从字面意思可以看出,“远程”,即远程验证。Remote特性指利用服务器端的回调函数执行客户端的验证逻辑(当执行到有Remote特性的元数据时,会自动地调用相应的控制器下的Action)。
举个例子:新会员注册时,一般手机号是不允许重复的,检查DB中是否已存在手机号,可以使用Remote特性来验证。

Model:UserInfo.cs

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Web;
5
6 using System.ComponentModel.DataAnnotations;
7
8
9 namespace DataValidate.Models
10 {
11 public class UserInfo
12 {
13 //定义用户名必填
14 [Required]
15 public string UserName { get; set; }
16 //定义密码必填,且满足6位
17 [Required]
18 [StringLength(128,MinimumLength =6)]
19 public string Password { get; set; }
20 //验证两次输入的密码是否一致
21 [Required]
22 [Compare("Password", ErrorMessage = "两次密码输入不一致")]
23 public string ConfirmPassword { get; set; }
24 //定义邮件为必填,且满足邮件格式
25 [Required]
26 [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}")]
27 public string Email { get; set; }
28 //定义年龄为必填,且1-130岁之间
29 [Required]
30 [Range(1, 130)]
31 public int Age { get; set; }
32
33 [Required]
34 [System.Web.Mvc.Remote("CheckTelephone", "Default", ErrorMessage ="手机号码已经存在")]
35 public string Telephone { get; set; }
36 }
37 }
38
39

DefaultController

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Web;
5 using System.Web.Mvc;
6
7 using DataValidate.Models;
8 namespace DataValidate.Controllers
9 {
10 public class DefaultController : Controller
11 {
12 // GET: Default
13
14 public ActionResult Index()
15 {
16 return View();
17 }
18
19 public ActionResult DataValidateDemo(UserInfo userInfo)
20 {
21 UserInfo _userInfo = new UserInfo();
22 _userInfo.UserName = userInfo.UserName;
23 return View("Index");
24 }
25
26 public ActionResult CheckTelephone(string telephone)
27 {
28 if (telephone=="13636595489")
29 {
30 return Json("手机号"+telephone+ "已经存在", JsonRequestBehavior.AllowGet);
31 }
32 return Json(true, JsonRequestBehavior.AllowGet);
33
34 }
35 }
36 }

Index.cshtml

1 @model DataValidate.Models.UserInfo
2
3
4 @{
5 ViewBag.Title = "Index";
6 Html.EnableClientValidation();
7 Html.EnableUnobtrusiveJavaScript();
8
9 }
10
11
12 <h2>Index</h2>
13
14 @using (Html.BeginForm("DataValidateDemo", "Default"))
15 {
16 <div>
17 @Html.Label("用户名"): @Html.TextBoxFor(m => m.UserName)
18 @Html.ValidationMessageFor(m => m.UserName)
19 </div>
20 <div>
21 @Html.Label("密码"):@Html.TextBox("Password")
22 @Html.ValidationMessageFor(m => m.Password)
23 </div>
24
25 <div>
26 @Html.Label("确认密码"):@Html.TextBox("ConfirmPassword")
27 @Html.ValidationMessageFor(m => m.ConfirmPassword)
28 </div>
29 <div>
30 @Html.Label("邮件"):@Html.TextBox("Email")
31 @Html.ValidationMessageFor(m => m.Email)
32 </div>
33 <div>
34 @Html.Label("年龄"):@Html.TextBox("Age")
35 @Html.ValidationMessageFor(m => m.Age)
36 </div>
37 <div>
38 @Html.Label("手机号码"):@Html.TextBox("Telephone")
39 @Html.ValidationMessageFor(m => m.Telephone)
40 </div>
41 <div><input type="submit" value="提交" /></div>
42 }
43 @section scripts{
44
45 <script src="~/Scripts/jquery.validate.js"></script>
46 <script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
47 }

测试结果:

给大家留一个思考题:如何验证多个参数?
在实际项目开发中,一般我们验证的不仅仅是一个参数,而是多个参数,如用户名和手机号,身份证号等一起验证,关于多参数验证,Remote验证特性又是怎么处理的呢?
(二) 验证错误提示

1.什么是验证错误提示?
指验证字段在验证不通过时,反馈给用户的提示信息,如密码不能低于6位,手机号必须为11位,年龄限制在1-130岁之间等,通过验证特性的ErroMessage实现。
[Required] [StringLength(128,MinimumLength =6,ErrorMessage ="密码不能低于6位数")]
2.错误验证提示大致分为两大类:默认错误提示和自定义错误提示。
(1)默认错误提示:当我们不指定ErroMessage的值时,ASP.NET MVC框架会指定默认值。
//定义密码必填,且满足6位
[Required]
[StringLength(128,MinimumLength =6)]
public string Password { get; set; }
Result:

(2)自定义值:我们为ErrorMessage指定具体自定义的值“密码不能低于6位数”
[Required]
[StringLength(128,MinimumLength =6,ErrorMessage ="密码不能低于6位数")]
public string Password { get; set; }
Result:

3.为什么要有自定义错误提示?
(1)为用户呈现友好提示,我们来看一下2中的默认值和自定义值;
默认值:字段Password必须是一个字符串,其最小长度为6,最大长度为128(这么一句话,要是给不懂程序的用户看了,肯定会疯掉,
很简单,对程序员来说,“字段”二字再基础不过,可对用户来说,他可能会问,字段是什么东东?)
自定义值:密码不能低于6位数(无论是程序员还是用户,都能看得明白)
(2)提高通用性,比如对美国提供英语提示,对俄罗斯提供俄语提示等;
4.如何实现通用性国际化?
在如上的自定义验证错误提示中,我们使用的是硬编码的形式,然而,面向国际市场开发的,这种硬编码错误消息提示是不实用的,因为我们要为不同地区显示
不同内容,实现国际化,庆幸的是,所有验证特性都允许为本地化的错误消息提示指定资源类型名称和资源名称,感兴趣的读者朋友,请参照How to:Set the
Cultrue and UI Cultrue for ASP.NET Page Globalization(sites:http://msdn.microsoft.com/en-us/library/bz9tc508.aspx)
思考题,如何实现错误消息通用性国际化?
(三) 验证原理
关于数据验证,我们思考这样一个问题:验证是什么时候发生的?如何才能知道验证失败?
本节我们将来回答这个问题。

1.要想充分理解验证原理,我们应该先熟悉几个基本概念:模型绑定器,模型元数据,模型验证器和模型状态(这部分内容,本篇文章不论述,大家知道这几个概念即可,具体详情内容,
将在接下来的文章中与大家分享:【ASP.NET MVC系列】浅谈ASP.NET MVC 模型)
2.默认情况下,ASP.NET MVC框架在模型绑定时就执行验证逻辑,在执行验证时,分为隐式执行和显示执行。
(1)隐式执行:一般指在控制器的Action中带有参数时,就会隐式执行模型验证。如下方法带有参数,因此就隐式执行模型绑定。
1 public ActionResult DataValidateDemo(UserInfo userInfo)
2 {
3 UserInfo _userInfo = new UserInfo();
4 _userInfo.UserName = userInfo.UserName;
5 return View("Index");
6 }
(2)显示执行:只利用控制器的UpdateModel或TryUpdateModel方式时,显示执行模型绑定。
3.模型绑定器一旦使用新值更新模型属性时,就会利用当前的模型元数据获得模型的所有验证器;
4.ASP.NET MVC运行时,DataAnnotationsModelValidator与数据验证一起工作;
5.DataAnnotationsModelValidator验证器会找到所有的验证特性并执行它所包含的验证逻辑;
6.模型绑定器捕获所有失败的验证规则,并把他们放入模型状态中;
7.模型绑定主要的副产品是模型状态,模型状态包含如下内容:
(1)包含用户放入模型属性中的所有值;
(2)包含每个属性相关联的所有错误;
(3)包含所有与模型对象本身有关的错误;
8.如果模型状态中存在错误,ModelState.IsValid就返回false;
9.控制操作和验证错误是怎样执行的?

控制器操作决定模型验证失败和验证成功时的执行流程。
(1)验证成功时:当验证成时,操作通常会执行必要的步骤来保存或更新用户信息;
(2)验证失败时:当验证失败时,操作一般会重新渲染提交模型值得视图;
(四)自定义验证
ASP.NET MVC之所以强大,在于其提供强大的自定义和扩展性,关于这个内容,会在后续的文章:“【SP.NET MVC系列】浅谈ASP.NET MVC八大类扩展”中深入讲解这两个强大的特性。

1.基于ASP.NET MVC的自定义验证,一般分为两大类型:将验证逻辑封装在自定义数据中和将验证逻辑封装在模型对象中。
(1)将验证逻辑封装在自定义数据中:复杂,但可复用性高;
(2)将验证逻辑封装在模型对象中:简单,但可复用性低;
2.将验证逻辑封装在自定数据中(会在后续的文章:“【ASP.NET MVC系列】浅谈ASP.NET MVC八大类扩展”中深入讲解)
3.将验证逻辑封装模型对象中(会在后续的文章:“【ASP.NET MVC系列】浅谈ASP.NET MVC八大类扩展”中深入讲解)
三 数据注解
(一)七大类型ASP.NET MVC内置数据注解

1.Dispaly特性:(1)模型属性设置友好的显示名称 (2)控制UI上属性的显示顺序;
2.ScaffoldColumn特性:隐藏HTML辅助方法;
3.DisplayFormat特性:处理属性的各种格式化选项;
4.ReadOnly特性:确保默认的模型绑定器不使用新值来更新;
5.DataType特性:提供关于属性的特定信息;
6.UIHint特性:(1)为ASP.NET MVC运行时提供模板名称,以备调用模板辅助方法渲染输出时使用 (2)自定义模板辅助方法;
7.HiddenInput特性:渲染type为hidden的元素;
四 参考文献
【01】ASP.NET MVC5 高级编程(Jon Galloway,Brad Wilson,K.Scott Allen,David Matson 著 ,孙远帅 译)
【02】ASP.NET MVC5编程实战(第3版)(Dino Esposite 著,潘丽丞 译)
五 版权区
- 感谢您的阅读,若有不足之处,欢迎指教,共同学习、共同进步。
- 博主网址:http://www.cnblogs.com/wangjiming/。
- 极少部分文章利用读书、参考、引用、抄袭、复制和粘贴等多种方式整合而成的,大部分为原创。
- 如您喜欢,麻烦推荐一下;如您有新想法,欢迎提出,邮箱:2098469527@qq.com。
- 可以转载该博客,但必须著名博客来源。
【ASP.NET MVC系列】浅谈数据注解和验证的更多相关文章
- 【第二篇】ASP.NET MVC快速入门之数据注解(MVC5+EF6)
目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...
- MVC学习手册之数据注解与验证
MVC学习手册之数据注解与验证 新建一个MVC5的WEB应用程序,VS2013会自动生成一段代码,以下是Account控制器下Register.cshtml 页面的代码: @model WebAppl ...
- ASP.NET MVC5高级编程 之 数据注解和验证
客户端验证逻辑会对用户向表单输入的数据给出一个即时反馈.而之所以需要服务器端验证,是因为来自网络的信息都是不能被信任的. 当在ASP.NET MVC设计模式上下文中谈论验证时,主要关注的是验证模型的值 ...
- 【ASP.NET MVC系列】浅谈ASP.NET MVC 视图与控制器传递数据
ASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作 ...
- 【ASP.NET MVC系列】浅谈NuGet在VS中的运用
一 概述 在我们讲解NuGet前,我们先来看看一个例子. 1.例子: 假设现在开发一套系统,其中前端框架我们选择Bootstrap,由于选择Bootstrap作为前端框架,因此,在项目中,我们 ...
- 【ASP.NET MVC系列】数据验证和注解
[01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作篇)(下) [04]浅谈ASP. ...
- 【ASP.NET MVC系列】浅谈表单和HTML辅助方法
[01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作篇)(下) [04]浅谈ASP. ...
- 【ASP.NET MVC系列】浅谈ASP.NET MVC八大类扩展(上篇)
lASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操 ...
- 【ASP.NET MVC系列】浅谈ASP.NET 页面之间传值的几种方式
ASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作 ...
随机推荐
- 一句话木马与caidao
实验吧有个试验环境:http://www.shiyanbar.com/experiment-course/experiment-course/vid/1812 菜刀的主要功能是用来连接一句话木马的,a ...
- 线段树【CF620E】The Child and Sequence
Description At the children's day, the child came to Picks's house, and messed his house up. Picks w ...
- 主席树【bzoj3524(p3567)】[POI2014]Couriers
Description 给一个长度为n的序列a.1≤a[i]≤n. m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r-l+1)/2.如果存在,输出这个数,否则输出0 ...
- 22、Flask实战第22天:Flask信号
Flask中的信号使用的是一个第三方插件blinker.通过pip list看一下是否安装,如果没有,则使用如下命令安装 pip install blinker 自定义信号 自定义信号分为3步: ①定 ...
- git log 查看某文件的修改历史
先进入此文件所在的目录下 1. git log --help 所有的git命令都可以通过git manual查看 在synopsis中可以看到公式 git log [<options>] ...
- luogu P1325 雷达安装
题目描述 描述: 假设海岸线是一条无限延伸的直线.它的一侧是陆地,另一侧是海洋.每一座小岛是在海面上的一个点.雷达必须安装在陆地上(包括海岸线),并且每个雷达都有相同的扫描范围d.你的任务是建立尽量少 ...
- [BZOJ4538]网络
今天打比赛,毒瘤yww把这题出到$n,m\leq 5\times10^5$,因为不会写整体二分所以来写坑爹的$O\left(n\log_2n\right)$做法 考虑按重要度建权值线段树(相同权值的请 ...
- 【树链剖分】【函数式权值分块】bzoj1146 [CTSC2008]网络管理Network
裸题,直接上.复杂度O(n*sqrt(n)*log(n)). //Num[i]表示树中的点i在函数式权值分块中对应的点 //Map[i]表示函数式权值分块中的点i在树中对应的点 #include< ...
- 【Trie+DP】BZOJ1212-[HNOI2004]L语言
[题目大意]给出字典和文章,求出文章能够被理解的最长前缀. [思路] 1A……!先用文章建立一棵Trie树,然后对于文章进行DP.f[i]表示文章中长度为i的前缀能否被理解,如果f[i]能理解,顺着下 ...
- 获取OS X中App Store更新后的安装包(如XCode)
如果宿舍有好几个人需要更新一些大的软件,如XCode,会占用很大的带宽. 为了节省带宽,我们可以在1台电脑上更新完后,获取存放在系统暂存区的更新的安装包,然后通过局域网或Airdrop的方式轻松分 ...