MVC实现数据验证 2.0

1、MVC中的数据验证框架有何优点?   【本文示例源码
在Asp.net时代,或者没有使用MVC的验证框架,一般是在BLL层中进行数据验证,但是BLL层的返回值又只能返回一个东西,比如一个字符串,而实际情况中,数据验证是很复杂的。
这时候,BLL层和网站会分离的不彻底,因为很多代码不得不在网站中写。

而在MVC的数据验证框架中,甚至可以不用BLL层,而在比BLL层更底层的Model层书写数据验证的代码。
并且最后能在网页上显示出来。
此图这就是最后的效果 
MVC实现数据验证 2.0 - 郁郁 - 郁郁的博客
2、深入浅出?
此框架有个优点,非常灵活,我这里用正常的三层架构来写。
因为灵活,我可以把数据验证写在任何一层。
i)写在Controller里:这是最简单的方法,但是也是最不推荐的方法, 因为不能体现分层思想
ii)写在BLL中:如果对一个数据验证的时候,需要牵扯到别的数据,就应该把验证写在这一层,比如一个Article Model的Category值是1,查询这个分类是否存在
iii)写在Model中:一些底层的标准应该写在这一层,因为这些标准在任何情况下都不能违反,比如帐号名长度不能超过20个字符
下面,我会一步步讲3中验证方法介绍给大家

3、前端和后端的结合
完整地看过MVC教程的人应该都知道如何使用 ModelState,其实MVC验证框架就是利用它,将验证的结果显示在页面中。

下面看一个例子:
Controller
  1. [HttpPost]  
  2.        //如果表单中input的name属性和Model的字段一样,那可以直接以Model形式传入一个Action  
  3.        public ActionResult Exp1(Models.UserModel user)  
  4.        {  
  5.            //判断  
  6.            if (user.Name.Length > 20)  
  7.            {  
  8.                //如果错误,调用ModelState的AddModelError方法,第一个参数需要输入出错的字段名  
  9.                ModelState.AddModelError("Name", "名字不得超过20个字符");  
  10.            }  
  11.            //判断ModelState中是否有错误  
  12.            if (ModelState.IsValid)  
  13.            {  
  14.                //如果没错误,返回首页  
  15.                return RedirectToAction("Index");  
  16.            }  
  17.            else  
  18.            {  
  19.                //如果有错误,继续输入信息  
  20.                return View(user);  
  21.            }  
  22.        }  
这里在Controller中一个Action中进行了数据验证,并且把结果放入了ModelState中,那怎么在前端页面显示呢?

如果不了解MVC的验证框架,其实可以直接自动生成,看看标准做法
在代码上右击,点Add View
选择创建强类型View,并且在内容中选择Edit
MVC实现数据验证 2.0 - 郁郁 - 郁郁的博客
这是自动生成的View
MVC实现数据验证 2.0 - 郁郁 - 郁郁的博客
OK,下面我可以运行了。。。
MVC实现数据验证 2.0 - 郁郁 - 郁郁的博客
由于前端的页面View是自动生成的,所以有些读者可能没看懂,为什么我刚刚在后端的数据验证信息会显示到前端去了呢?
其实关键就是利用了这个:<%= Html.ValidationMessageFor(model => model.Name) %>
不理解强类型方法、或还在使用MVC1.0的读者可以看这个:<%= Html.ValidationMessage("Name") %>
(除了ValidationMessage函数外,还有其它几个函数,可以达到不同的效果,读者可以自行研究下,这几个函数都是以Validation开头的)

小结:在Controller中验证数据,放入ModelState(其核心是一个字典),然后在利用函数读取
这样,就达到了数据验证时前端和后端相结合的效果。


4、如何将数据验证代码放入业务逻辑层?
上面那部分,我们看到了MVC数据验证框架的核心,ModelState。
只要在ModelState中添加错误就可以在前端页面中显示了。

所以,这个部分的关键就是让BLL操作ModelState
这里,有2中方法可以参考,其中,第二种方案我是参考了xVal来实现的

i)方案一:在调用BLL函数的时候直接传入ModelState对象
优点:这个是最好理解的,我直接传入ModelState对象,让BLL操作它不就可以了?
缺点:BLL是业务逻辑层,它不应该知道自己被谁调用了,也就是说,BLL层中不应该出现任何MVC特有的东西(ModelState对象)

下面就让我来实现它:
BLL
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Web;  
  5. using System.Web.Mvc;  
  6.   
  7. namespace MvcApplication1.BLL  
  8. {  
  9.     public static class UserBLL  
  10.     {  
  11.         public static void Edit(Models.UserModel user, ModelStateDictionary ModelState)  
  12.         {  
  13.             if (user.Name.Length > 20)  
  14.             {  
  15.                 //如果错误,调用ModelState的AddModelError方法,第一个参数需要输入出错的字段名  
  16.                 ModelState.AddModelError("Name", "名字不得超过20个字符");  
  17.             }  
  18.             if (ModelState.IsValid)  
  19.             {  
  20.                 //在这里我可以写一些代码,因为完成了验证,我就可以开始更新数据库了  
  21.             }  
  22.         }  
  23.     }  
  24. }  
Controller
  1. [HttpPost]  
  2.        public ActionResult Exp2(Models.UserModel user)  
  3.        {  
  4.            //调用BLL中的函数  
  5.            BLL.UserBLL.Edit(user, ModelState);  
  6.   
  7.            if (ModelState.IsValid)  
  8.            {  
  9.                return RedirectToAction("Index");  
  10.            }  
  11.            else  
  12.            {  
  13.                //这里,前端页面不用改,所以我直接利用第一个例子中的前端页面  
  14.                return View("Exp1",user);  
  15.            }  
  16.        }  
OK,直接运行,结果和上一个方法一样

总结:在调用BLL的时候把ModelState传入


ii)方案二:通过错误捕捉,将BLL和Controller关联起来
如果使用方案一,会出现这样一个问题:
如果我的项目不用MVC了,要移植怎么办?
新的架构中也有类似于ModelState的东西。总不能把所有的BLL改一遍吧?
所以,我们需要方案二。

这里是通过编写一个自定义Exception类,然后这个自定义Exception类有2个功能:
1、加入错误
2、把错误转换到ModelState中(如果要转移到别的架构,可以再写一个新的转移方法)

下面的代码就是这个自定义Exception类
ModelExceptions
  1. //必须继承自Exception  
  2.     public class ModelExceptions : Exception  
  3.     {  
  4.         //存放错误信息的List  
  5.         List<string[]> errors = new List<string[]>();  
  6.   
  7.         //判断是否有错误  
  8.         public bool IsValid  
  9.         {  
  10.             get  
  11.             {  
  12.                 return errors.Count == 0 ? true : false;  
  13.             }  
  14.         }  
  15.   
  16.         //增加错误信息  
  17.         public void AddError(string name, string message)  
  18.         {  
  19.             this.errors.Add(new string[] { name, message });  
  20.         }  
  21.   
  22.         //填充ModelState  
  23.         public void FillModelState(ModelStateDictionary modelstate)  
  24.         {  
  25.             foreach (var e in this.errors)  
  26.             {  
  27.                 modelstate.AddModelError(e[0], e[1]);  
  28.             }  
  29.         }  
  30.     }  
接下来是在Controller中的代码
Controller
  1. [HttpPost]  
  2.        public ActionResult Exp3(Models.UserModel user)  
  3.        {  
  4.            //用try来捕捉错误  
  5.            try  
  6.            {  
  7.                BLL.UserBLL.Edit(user);  
  8.            }  
  9.            catch (ModelExceptions e)  
  10.            {  
  11.                //如果发生了错误,就填充到ModelState中  
  12.                e.FillModelState(ModelState);  
  13.            }  
  14.            if (ModelState.IsValid)  
  15.            {  
  16.                return RedirectToAction("Index");  
  17.            }  
  18.            else  
  19.            {  
  20.                //这里,前端页面不用改,所以我直接利用第一个例子中的前端页面  
  21.                return View("Exp1", user);  
  22.            }  
  23.        }  
然后是在BLL中的代码
BLL
  1. public static void Edit(Models.UserModel user)  
  2.        {  
  3.            var e = new ModelExceptions();  
  4.            if (user.Name.Length > 20)  
  5.            {  
  6.                //如果错误,调用ModelState的AddModelError方法,第一个参数需要输入出错的字段名  
  7.                e.AddError("Name", "名字不得超过20个字符");  
  8.            }  
  9.            if (e.IsValid)  
  10.            {  
  11.                //在这里我可以写一些代码,因为完成了验证,我就可以开始更新数据库了  
  12.            }  
  13.            else  
  14.            {  
  15.                //如果有错误,就抛出错误  
  16.                throw e;  
  17.            }  
  18.        }  
总结:简单的做法,在后期会反而会带来很多麻烦,所以推荐方案二。而且方案二也不是很麻烦,反而让人感觉很清晰


5、如何将数据验证代码放入Model中?
前面,在BLL中验证数据已经很好了,但是又出现了一个问题
一个Model,很多限制是固定的,比如长度不能超过20个字符
但是我在BLL中有很多过程,比如修改,删除等
那我岂不是要在所有的过程中都多这个进行验证?
其实你也可以通过写一个函数来解决这个问题

但是,我(Model)的名字有没有超过20个字符是我自己的事情,凭什么要你来鉴定?我自己说了算!
如何把数据验证交给Model呢?这里需要引用一个DLL
MVC实现数据验证 2.0 - 郁郁 - 郁郁的博客
然后在Model中这样做
Model
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Web;  
  5. using System.ComponentModel.DataAnnotations;  
  6.   
  7. namespace MvcApplication1.Models  
  8. {  
  9.     public class UserModel  
  10.     {  
  11.         public string Name { get; set; }  
  12.   
  13.   
  14.         //属性前加上Attribute  
  15.         [Required(ErrorMessage = "密码不能为空")]  
  16.         [StringLength(20, ErrorMessage = "密码长度不能超过20个字符")]  
  17.         public string Password { get; set; }  
  18.     }  
  19. }  
引用 System.ComponentModel.DataAnnotations 命名空间,并且在Model属性前加上Attribute(C# 新特性:特征),这样就可以了
请自行查看System.ComponentModel.DataAnnotations命名空间,看看可以有哪些验证方法
当然也可以自定义验证,查看默认验证的定义,看看它继承了哪个类,自己仿写就可以了

我这里没有取消掉BLL中的验证,2种验证可以混合使用

OK,那在Controller和BLL中需要做什么?我们需要做一定的修改
Controller
  1. [HttpPost]  
  2.         //MVC在传入这个Model的时候已经进行了验证,并且把错误放去了ModelState  
  3.         public ActionResult Exp4(Models.UserModel user)  
  4.         {  
  5.             try  
  6.             {  
  7.                 //别的不变,除了这里,我们需要传入ModelState.IsValid  
  8.                 BLL.UserBLL.Edit(user,ModelState.IsValid);  
  9.             }  
  10.             catch (ModelExceptions e)  
  11.             {  
  12.                 e.FillModelState(ModelState);  
  13.             }  
  14.             if (ModelState.IsValid)  
  15.             {  
  16.                 return RedirectToAction("Index");  
  17.             }  
  18.             else  
  19.             {  
  20.                 return View("Exp1", user);  
  21.             }  
  22.         }  
BLL
  1. public static void Edit(Models.UserModel user,bool IsValid)  
  2.        {  
  3.            var e = new ModelExceptions();  
  4.            if (user.Name.Length > 20)  
  5.            {  
  6.                e.AddError("Name", "名字不得超过20个字符");  
  7.            }  
  8.            //别的不变,但在这里,我除了要判断e中是否有错误外,还要判断ModelState中是否有错误  
  9.            if (e.IsValid && IsValid)  
  10.            {  
  11.                //在这里我可以写一些代码,因为完成了验证,我就可以开始更新数据库了  
  12.            }  
  13.            else  
  14.            {  
  15.                throw e;  
  16.            }  
  17.        }  
我注释了相对了上个例子改动的地方

并且混合使用了2中验证方法 MVC实现数据验证 2.0 - 郁郁 - 郁郁的博客
什么时候用Model验证? 验证Model固有的属性
什么时候用BLL验证? 当需要验证一些复杂关系的时候


另外,为什么要把ModelState.IsValid传入BLL?
因为Model验证是在这个Model传入这个方法的时候就已经完成的
MVC实现数据验证 2.0 - 郁郁 - 郁郁的博客
如果不传入,那BLL验证中虽然没错误,但不代表整个过程没有错误。
对数据库的操作要知道完整的验证信息,如果不传入,会导致程序BUG

总结:合理的根据情况来放置数据验证代码的位置才是王道



6、如何更改错误提示的样式?
我这里用了MVC基本的那个例程,里面包含了CSS样式表
而默认情况下,虽然会出现譬如“名称过长”这样的文字信息,但是却没有样式(默认是正常字体,正常颜色)

那如何修改?
其实打开MVC默认的CSS样式表就不难发现,这些错误信息都有固定的class,所以只要写一个CSS的class即可
MVC实现数据验证 2.0 - 郁郁 - 郁郁的博客
那怎么才能知道class名是什么呢? 最方便的方法,做好页面后在浏览器中看一下即可


7、Entity Framework中,如何在Model中编写数据验证?
Entity Framework会自动生成Model,虽然是可以修改的,但是强烈建议不要直接修改Model原始代码
其实微软早就想到这一点了,它生成的Model都是 partial class(部分类)
也就是说,同一个类的代码可以分几部分,写在不同的地方

具体写法如下,写在不同的地方,但需要在同一个命名空间下
User
  1. [MetadataType(typeof(UserMetaData))]  
  2. public partial class User { }  
  3. public class UserMetaData  
  4. {  
  5.     [Required(ErrorMessage = "名字为空")]  
  6.     [StringLength(10, ErrorMessage = "名字长度不得超过10个字符")]  
  7.     public string Name { get; set; }  
  8.   
  9.     [Required(ErrorMessage = "密码为空")]  
  10.     [StringLength(20, ErrorMessage = "密码长度不得超过20个字符")]  
  11.     public string Password { get; set; }  
  12.   
  13.     [Required(ErrorMessage = "帐号为空")]  
  14.     [StringLength(10, ErrorMessage = "帐号长度不得超过10个字符")]  
  15.     public string Passport { get; set; }  
  16. }  
这样写好后,便可以在Entity Framework中使用Model验证了


8、如何使用客户端验证
任何平台都可以靠js来实现客户端验证,但是我这里探讨的是MVC的数据验证。
那MVC的客户端数据验证有什么不同呢?

不同之处就在于,你可以不用写一行javascript代码!
下面让我们来实现它

先添加3个javascript文件,请按顺序添加:
MVC实现数据验证 2.0 - 郁郁 - 郁郁的博客
然后在View里添加一行代码:(注意要添加在Form前)
MVC实现数据验证 2.0 - 郁郁 - 郁郁的博客
注意点:这里,其实是这个函数把Model验证转换成了javascript代码,对!它只能转换Model验证,BLL验证无法转换,因为BLL验证涉及到复杂的代码,不可能全部转换成javascript吧?并且BLL验证很多还需要和数据库交互。
那如果想把BLL验证也做成“客户端”验证怎么办?(只有可能用ajax实现无刷新验证,而不是真正的客户端验证)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值