MVC中的数据验证

一  概述

关于数据验证和数据注解,是任何软件系统不可小觑的必要模块,在软件系统中起到举足轻重的作用,主要作用是为了保证数据安全性、防止漏洞注入和网络攻击。

从数据验证的验证方式来说,我们一般分为客户端验证和服务端验证(或者两种方式相结合),本篇文章主要讲解基于ASP.NET MVC框架的数据验证特性和数据注解。

二  数据验证

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

  在ASP.NET MVC中,验证特性定义在 System.ComponentModel.DataAnnotations 命名空间中,我们在使用验证特性前,首先需要引入命名空间

  ASP.NET MVC内置了六大验证特性:Required, StringLength, RegularExpression, Range, Compare和 Remote;

     //定义用户名必填
        [Required]
        public string UserName { get; set; }
        //定义密码是必填,且满足6-20位
        [Required]
        [StringLength(20, MinimumLength = 6, ErrorMessage = "密码必须是6-20位")]
        public string UserPass { get; set; }

(二)一个简单的例子

Models:UserInfo.cs

    public class UserInfo
    {
        //定义用户名必填
        [Required]
        public string UserName { get; set; }
        //定义密码是必填,且满足6-20位
        [Required]
        [StringLength(20, MinimumLength = 6, ErrorMessage = "密码必须是6-20位")]
        public string UserPass { get; set; }
        [Required]
        [Compare("UserPass", ErrorMessage = "两次密码不一致")]
        public string ConfirmUserPass { get; set; }
        //邮箱必填且满足,邮箱格式
        [Required]
        [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}", ErrorMessage = "邮箱格式不正确!")]
        public string Email { get; set; }
        //年龄必填,且满足1~120
        [Required]
        [Range(1, 120, ErrorMessage = "年龄为1到120岁之间")]
        public int Age { get; set; }
    }

Controller:DefaultController:

     public class UserInfoController : Controller
    {
        //暂时不用Ioc解耦,直接new一个服务实例
        UserInfoService userService = new UserInfoService();
        // GET: UserInfo
        public ActionResult Index()
        {
            //GetUserList:获取用户列表的
            List<UserInfo> userList = userService.GetUserList();
            return View(userList);
        }
        /// <summary>
        /// 添加用户
        /// </summary>
        /// <returns></returns>
        public ActionResult AddUserInfo()
        {
            return View();
        }
        [HttpPost]
        public ActionResult AddUserInfo(UserInfo user)
        {
            //使用ModelState.IsValid判断字段格式是否正确
            if (ModelState.IsValid)
            {
                //TryAddUserInfo:添加用户的服务方法,返回值为bool
                if (userService.TryAddUserInfo(user))
                {
                    return Json(new { IsSucess = 1, Message = "添加成功!" });
                    //return View("Index");前后端不分离时,直接跳转到列表页
                }
                else
                {
                    return Json(new { IsSucess = 0, Message = "添加失败" });
                }
            }
            else
            {
                //字段不符合格式
                string error = "";
                ViewBag.ModelState = ModelState;
                foreach (var item in ModelState)
                {
                    if (item.Value.Errors.Count > 0)
                    {
                        error += item.Value.Errors.FirstOrDefault().ErrorMessage;
                    }
                }
                return Json(new { IsSucess=0,Message=error});
                //return View("AddUserInfo");//一般前后端不分离的项目中这样使用最简单
            }
        }
    }
Controller
 1      public class UserInfoController : Controller
 2     {
 3         //暂时不用Ioc解耦,直接new一个服务实例
 4         UserInfoService userService = new UserInfoService();
 5         // GET: UserInfo
 6         public ActionResult Index()
 7         {
 8             //GetUserList:获取用户列表的
 9             List<UserInfo> userList = userService.GetUserList();
10             return View(userList);
11         }
12         /// <summary>
13         /// 添加用户
14         /// </summary>
15         /// <returns></returns>
16         public ActionResult AddUserInfo()
17         {
18             return View();
19         }
20         [HttpPost]
21         public ActionResult AddUserInfo(UserInfo user)
22         {
23             //使用ModelState.IsValid判断字段格式是否正确
24             if (ModelState.IsValid)
25             {
26                 //TryAddUserInfo:添加用户的服务方法,返回值为bool
27                 if (userService.TryAddUserInfo(user))
28                 {
29                     return Json(new { IsSucess = 1, Message = "添加成功!" });
30                     //return View("Index");前后端不分离时,直接跳转到列表页
31                 }
32                 else
33                 {
34                     return Json(new { IsSucess = 0, Message = "添加失败" });
35                 }
36             }
37             else
38             {
39                 //字段不符合格式
40                 string error = "";
41                 ViewBag.ModelState = ModelState;
42                 foreach (var item in ModelState)
43                 {
44                     if (item.Value.Errors.Count > 0)
45                     {
46                         error += item.Value.Errors.FirstOrDefault().ErrorMessage;
47                     }
48                 }
49                 return Json(new { IsSucess=0,Message=error});
50                 //return View("AddUserInfo");//一般前后端不分离的项目中这样使用最简单
51             }
52         }
53     }

View:Index.cshtml

    @model Model.UserInfo
@{
    ViewBag.Title = "AddUserInfo";
}
<h2>添加用户</h2>
@using (Html.BeginForm("AddUserInfo", "UserInfo",FormMethod.Post))
{
    <div>
        @Html.Label("用户名"): @Html.TextBoxFor(m => m.UserName)
        @Html.ValidationMessageFor(m => m.UserName)
    </div>
    <div>
        @Html.Label("密码"):@Html.TextBox("UserPass")
        @Html.ValidationMessageFor(m => m.UserPass)
    </div>

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

测试效果如下:

扫描二维码关注公众号,回复: 1870407 查看本文章

6.为什么要把Remote剔出来单独讲解呢?

 我们知道,除Remote以外的五大验证特性,命名空间均为System.ComponentModel.DataAnnotations,而Remote特性的命名空间却是System.Web.Mvc。

 Remote,从字面意思可以看出,“远程”,即远程验证。Remote特性指利用服务器端的回调函数执行客户端的验证逻辑(当执行到有Remote特性的元数据时,会自动地调用相应的控制器下的Action)。

 举个例子:新会员注册时,一般手机号是不允许重复的,检查DB中是否已存在手机号,可以使用Remote特性来验证。

Model:UserInfo.cs

  View Code

DefaultController

  View Code

Index.cshtml

  View Code

测试结果:

  给大家留一个思考题:如何验证多个参数?

      在实际项目开发中,一般我们验证的不仅仅是一个参数,而是多个参数,如用户名和手机号,身份证号等一起验证,关于多参数验证,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 著,潘丽丞 译)

猜你喜欢

转载自www.cnblogs.com/wyy1234/p/9264441.html
今日推荐