MVC 数据验证【转】

【转自】http://www.cnblogs.com/dozer/archive/2010/04/12/MVC-DataAnnotations.html 作者Dozer

今天在这里给大家介绍一下MVC的数据验证框架。

在1.0版中,很多朋友提出了怎么使用客户端验证,今天找了一些资料,发现了客户端验证的方法。

1、MVC中的数据验证框架有何优点?

在Asp.net时代,或者没有使用MVC的验证框架,一般是在BLL层中进行数据验证,但是BLL层的返回值又只能返回一个东西,比如一个字符串,而实际情况中,数据验证是很复杂的。

这时候,BLL层和网站会分离的不彻底,因为很多代码不得不在网站中写。

而在MVC的数据验证框架中,甚至可以不用BLL层,而在比BLL层更底层的Model层书写数据验证的代码。

并且最后能在网页上显示出来。

此图这就是最后的效果

2、深入浅出?

此框架有个优点,非常灵活,我这里用正常的三层架构来写。

因为灵活,我可以把数据验证写在任何一层。

i)写在Controller里:这是最简单的方法,但是也是最不推荐的方法, 因为不能体现分层思想

ii)写在BLL中:如果对一个数据验证的时候,需要牵扯到别的数据,就应该把验证写在这一层,比如一个Article Model的Category值是1,查询这个分类是否存在

iii)写在Model中:一些底层的标准应该写在这一层,因为这些标准在任何情况下都不能违反,比如帐号名长度不能超过20个字符

下面,我会一步步讲3中验证方法介绍给大家

3、前端和后端的结合

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

下面看一个例子:

Controller

[HttpPost]
//如果表单中input的name属性和Model的字段一样,那可以直接以Model形式传入一个Action
public ActionResult Exp1(Models.UserModel user)
{
//判断
if (user.Name.Length >20)
{
//如果错误,调用ModelState的AddModelError方法,第一个参数需要输入出错的字段名
ModelState.AddModelError("Name", "名字不得超过20个字符");
}
//判断ModelState中是否有错误
if (ModelState.IsValid)
{
//如果没错误,返回首页
return RedirectToAction("Index");
}
else
{
//如果有错误,继续输入信息
return View(user);
}
}

这里在Controller中一个Action中进行了数据验证,并且把结果放入了ModelState中,那怎么在前端页面显示呢?

如果不了解MVC的验证框架,其实可以直接自动生成,看看标准做法

在代码上右击,点Add View

选择创建强类型View,并且在内容中选择Edit

这是自动生成的View

OK,下面我可以运行了。。。

由于前端的页面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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication1.BLL
{
publicstaticclass UserBLL
{
publicstaticvoid Edit(Models.UserModel user, ModelStateDictionary ModelState)
{
if (user.Name.Length >20)
{
//如果错误,调用ModelState的AddModelError方法,第一个参数需要输入出错的字段名
ModelState.AddModelError("Name", "名字不得超过20个字符");
}
if (ModelState.IsValid)
{
//在这里我可以写一些代码,因为完成了验证,我就可以开始更新数据库了
}
}
}
}

Controller

[HttpPost]
public ActionResult Exp2(Models.UserModel user)
{
//调用BLL中的函数
BLL.UserBLL.Edit(user, ModelState);

if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
else
{
//这里,前端页面不用改,所以我直接利用第一个例子中的前端页面
return View("Exp1",user);
}
}

OK,直接运行,结果和上一个方法一样

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

ii)方案二:通过错误捕捉,将BLL和Controller关联起来

如果使用方案一,会出现这样一个问题:

如果我的项目不用MVC了,要移植怎么办?

新的架构中也有类似于ModelState的东西。总不能把所有的BLL改一遍吧?

所以,我们需要方案二。

这里是通过编写一个自定义Exception类,然后这个自定义Exception类有2个功能:

1、加入错误

2、把错误转换到ModelState中(如果要转移到别的架构,可以再写一个新的转移方法)

下面的代码就是这个自定义Exception类

ModelExceptions

//必须继承自Exception
publicclass ModelExceptions : Exception
{
//存放错误信息的List
List<string[]> errors =new List<string[]>();

//判断是否有错误
publicbool IsValid
{
get
{
return errors.Count ==0?true : false;
}
}

//增加错误信息
publicvoid AddError(string name, string message)
{
this.errors.Add(newstring[] { name, message });
}

//填充ModelState
publicvoid FillModelState(ModelStateDictionary modelstate)
{
foreach (var e inthis.errors)
{
modelstate.AddModelError(e[0], e[1]);
}
}
}

接下来是在Controller中的代码

Controller

[HttpPost]
public ActionResult Exp3(Models.UserModel user)
{
//用try来捕捉错误
try
{
BLL.UserBLL.Edit(user);
}
catch (ModelExceptions e)
{
//如果发生了错误,就填充到ModelState中
e.FillModelState(ModelState);
}
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
else
{
//这里,前端页面不用改,所以我直接利用第一个例子中的前端页面
return View("Exp1", user);
}
}

然后是在BLL中的代码

BLL

publicstaticvoid Edit(Models.UserModel user)
{
var e =new ModelExceptions();
if (user.Name.Length >20)
{
//如果错误,调用ModelState的AddModelError方法,第一个参数需要输入出错的字段名
e.AddError("Name", "名字不得超过20个字符");
}
if (e.IsValid)
{
//在这里我可以写一些代码,因为完成了验证,我就可以开始更新数据库了
}
else
{
//如果有错误,就抛出错误
throw e;
}
}

总结:简单的做法,在后期会反而会带来很多麻烦,所以推荐方案二。而且方案二也不是很麻烦,反而让人感觉很清晰

5、如何将数据验证代码放入Model中?

前面,在BLL中验证数据已经很好了,但是又出现了一个问题

一个Model,很多限制是固定的,比如长度不能超过20个字符

但是我在BLL中有很多过程,比如修改,删除等

那我岂不是要在所有的过程中都多这个进行验证?

其实你也可以通过写一个函数来解决这个问题

但是,我(Model)的名字有没有超过20个字符是我自己的事情,凭什么要你来鉴定?我自己说了算!

插播笑话一则:在我家,大事我说了算,小事我老婆说了算~ 那什么是大事?什么是小事?像美国打不打伊拉克,这就是大事;别的都是小事……

OK,言归正传…

如何把数据验证交给Model呢?这里需要引用一个DLL

然后在Model中这样做

Model

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace MvcApplication1.Models
{
publicclass UserModel
{
publicstring Name { get; set; }

//属性前加上Attribute
[Required(ErrorMessage ="密码不能为空")]
[StringLength(20, ErrorMessage ="密码长度不能超过20个字符")]
publicstring Password { get; set; }
}
}

引用 System.ComponentModel.DataAnnotations 命名空间,并且在Model属性前加上Attribute(C# 新特性:特征),这样就可以了

请自行查看System.ComponentModel.DataAnnotations命名空间,看看可以有哪些验证方法

当然也可以自定义验证,查看默认验证的定义,看看它继承了哪个类,自己仿写就可以了

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

OK,那在Controller和BLL中需要做什么?我们需要做一定的修改

Controller

[HttpPost]
//MVC在传入这个Model的时候已经进行了验证,并且把错误放去了ModelState
public ActionResult Exp4(Models.UserModel user)
{
try
{
//别的不变,除了这里,我们需要传入ModelState.IsValid
BLL.UserBLL.Edit(user,ModelState.IsValid);
}
catch (ModelExceptions e)
{
e.FillModelState(ModelState);
}
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
else
{
return View("Exp1", user);
}
}

BLL

publicstaticvoid Edit(Models.UserModel user,bool IsValid)
{
var e =new ModelExceptions();
if (user.Name.Length >20)
{
e.AddError("Name", "名字不得超过20个字符");
}
//别的不变,但在这里,我除了要判断e中是否有错误外,还要判断ModelState中是否有错误
if (e.IsValid && IsValid)
{
//在这里我可以写一些代码,因为完成了验证,我就可以开始更新数据库了
}
else
{
throw e;
}
}

我注释了相对了上个例子改动的地方

并且混合使用了2中验证方法

什么时候用Model验证?  验证Model固有的属性

什么时候用BLL验证? 当需要验证一些复杂关系的时候

另外,为什么要把ModelState.IsValid传入BLL?

因为Model验证是在这个Model传入这个方法的时候就已经完成的

如果不传入,那BLL验证中虽然没错误,但不代表整个过程没有错误。

对数据库的操作要知道完整的验证信息,如果不传入,会导致程序BUG

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

6、如何更改错误提示的样式?

我这里用了MVC基本的那个例程,里面包含了CSS样式表

而默认情况下,虽然会出现譬如“名称过长”这样的文字信息,但是却没有样式(默认是正常字体,正常颜色)

那如何修改?

其实打开MVC默认的CSS样式表就不难发现,这些错误信息都有固定的class,所以只要写一个CSS的class即可

那怎么才能知道class名是什么呢? 最方便的方法,做好页面后在浏览器中看一下即可

7、Entity Framework中,如何在Model中编写数据验证?

Entity Framework会自动生成Model,虽然是可以修改的,但是强烈建议不要直接修改Model原始代码

其实微软早就想到这一点了,它生成的Model都是 partial class(部分类)

也就是说,同一个类的代码可以分几部分,写在不同的地方

具体写法如下,写在不同的地方,但需要在同一个命名空间下

User

[MetadataType(typeof(UserMetaData))]
publicpartialclass User { }
publicclass UserMetaData
{
[Required(ErrorMessage ="名字为空")]
[StringLength(10, ErrorMessage ="名字长度不得超过10个字符")]
publicstring Name { get; set; }

[Required(ErrorMessage ="密码为空")]
[StringLength(20, ErrorMessage ="密码长度不得超过20个字符")]
publicstring Password { get; set; }

[Required(ErrorMessage ="帐号为空")]
[StringLength(10, ErrorMessage ="帐号长度不得超过10个字符")]
publicstring Passport { get; set; }
}

这样写好后,便可以在Entity Framework中使用Model验证了

1.非空和数据类型验证

 1         [Required]
 2         [Display(Name = "用户名")]
 3         public string UserName { get; set; }
 4
 5         [Required]
 6         [DataType(DataType.Password)]
 7         [Display(Name = "密码")]
 8         public string Password { get; set; }
 9
10         [Required]
11         [DataType(DataType.EmailAddress)]
12         [Display(Name = "电子邮件地址")]
13         public string Email { get; set; }

2.非空和字符长度验证
1         [Required(ErrorMessage="用户名不能为空!")]
2         [DisplayName("用户名")] 3         public string UserName { get; set; }
4
5         [DisplayName("密码")]
6         [StringLength(6,ErrorMessage="密码长度不能超过6个字符!")]
7         public string Password { get; set; }
3.值域验证
1         [DisplayName("年龄")]
2         [Range(1,int.MaxValue,ErrorMessage="年龄不能小于1!")]
3         public int Age { get; set; }
4.比较验证

1         [Required]
2         [DataType(DataType.Password)]
3         [DisplayName("密码")]
4         public string Password { get; set; }
5
6         [DataType(DataType.Password)]
7         [DisplayName("确认密码")]
8         [Compare("Password", ErrorMessage = "密码和确认密码不匹配!")]
9         public string ConfirmPassword { get; set; }

5.正则表达式验证

1         [DisplayName("联系电话")]
2         [RegularExpression(@"^((0\d{2,5}-)|\(0\d{2,5}\))?\d{7,8}(-\d{3,4})?$",ErrorMessage = "电话格式不正确!\n 有效格式为:\n①本区7或8位号码[-3或4位分机号码,可选]\n②(3~5位区号)7或8位号码[-3或4位分机号码,可选]\n③3~5位区号-7或8位号码[-3或4位分机号码,可选]\n示例:023-12345678;(023)1234567-1234")]
3         public string Phone { get; set; }
4         [DisplayName("电子邮件")]
5         [RegularExpression(@"^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$", ErrorMessage = "请输入正确的Email格式!\n示例:[email protected]")]
6         public string Email { get; set; }
7         [DisplayName("网址")]
8         [RegularExpression(@"http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?", ErrorMessage = "请输入合法的网址!\n示例:https://abc.com;http://www.abc.cn")]
9         public string Httpaddress { get; set; }

6.自定义验证
1         [Required]
2         [ValidatePasswordLength]
3         [DataType(DataType.Password)]
4         [DisplayName("密码")]
5         public string Password { get; set; }

 1     [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
 2     public sealed class ValidatePasswordLengthAttribute : ValidationAttribute, IClientValidatable
 3     {
 4         private const string _defaultErrorMessage = "‘{0}‘ 必须至少包含 {1} 个字符。";
 5         private readonly int _minCharacters = Membership.Provider.MinRequiredPasswordLength;
 6
 7         public ValidatePasswordLengthAttribute()
 8             : base(_defaultErrorMessage)
 9         {
10         }
11
12         public override string FormatErrorMessage(string name)
13         {
14             return String.Format(CultureInfo.CurrentCulture, ErrorMessageString,
15                 name, _minCharacters);
16         }
17
18         public override bool IsValid(object value)
19         {
20             string valueAsString = value as string;
21             return (valueAsString != null && valueAsString.Length >= _minCharacters);
22         }
23
24         public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
25         {
26             return new[]{
27                 new ModelClientValidationStringLengthRule(FormatErrorMessage(metadata.GetDisplayName()), _minCharacters, int.MaxValue)
28             };
29         }
30     }

  ※ ※ ※ ※ ※ ※ ※ ※ ※ ※

8、如何使用客户端验证

任何平台都可以靠js来实现客户端验证,但是我这里探讨的是MVC的数据验证。

那MVC的客户端数据验证有什么不同呢?

不同之处就在于,你可以不用写一行javascript代码!

下面让我们来实现它

先添加3个javascript文件,请按顺序添加:

然后在View里添加一行代码:(注意要添加在Form前)

注意点:这里,其实是这个函数把Model验证转换成了javascript代码,对!它只能转换Model验证,BLL验证无法转换,因为BLL验证涉及到复杂的代码,不可能全部转换成javascript吧?并且BLL验证很多还需要和数据库交互。

那如果想把BLL验证也做成“客户端”验证怎么办?(只有可能用ajax实现无刷新验证,而不是真正的客户端验证)

目前看来先只能手写了

如有收获,我会继续更新~

时间: 2024-10-14 04:15:51

MVC 数据验证【转】的相关文章

MVC 数据验证

前一篇说了MVC数据验证的例子,这次来详细说说各种各样的验证注解.System.ComponentModel.DataAnnotations 一.基础特性 一.Required 必填选项,当提交的表单缺少该值就引发验证错误. 二.StringLength 指定允许的长度 指定最大长度: [StringLength(20)] //最大长度不超过20个字符 指定最短于最长限制: [StringLength(20,MinimumLength=3)] //最大长度不超过20个字符,最短不能低于3个字符

【转】ASP.NET MVC 数据验证及相关内容

原文地址:http://www.jb51.net/article/56713.htm 一.数据验证 数据验证的步骤在模型类中添加与验证相关的特性标记在客户端导入与验证相关的js文件和css文件使用与验证相关的Html辅助方法在服务器端判断是否通过服务器端验证常用的验证标记 Required:非空验证StringLength:验证字符串的长度RegularExpression:正则表达式验证Compare:比较两个字段的值是否相等Range:范围验证Remote:服务器验证(需要在controll

Spring MVC 数据验证——validate编码方式

1.导入jar包 validation-api-1.0.0.GA.jar这是比較关键的一个jar包,主要用于解析注解@Valid. hibernate-validator-4.3.2.Final.jar能够下载最新的.这个包在注解方式编码中尤为重要. 其它的就是一些日志包(不一定全不须要):jboss-logging-3.1.3.GA.jar.slf4j-log4j12-1.6.1.jar 2.web项目的结构图 项目的主要结构图,不清楚web项目的环境的能够自己学一下.推荐去慕课网上找视频看

Spring MVC 数据验证——validate注解方式

1.说明 学习注解方式之前,应该先学习一下编码方式的spring注入.这样便于理解验证框架的工作原理.在出错的时候,也能更好的解决问题.所以本次博客教程也是基于编码方式,只是在原来的基础加上注解方式. 2.配置信息 web.xml不需要改变的 hello-servlet.xml将原来的加载方式,改为自动加入有hibernate和Spring提供的validate的默认类,配置如下: <?xml version="1.0" encoding="UTF-8"?&g

转:MVC 数据验证

一.基础特性 一.Required 必填选项,当提交的表单缺少该值就引发验证错误. 二.StringLength 指定允许的长度 指定最大长度: [StringLength(20)] //最大长度不超过20个字符 指定最短于最长限制: [StringLength(20,MinimumLength=3)] //最大长度不超过20个字符,最短不能低于3个字符 三.RegularExpression 正则表达式能够匹配的字符串,如果不能匹配,则报一个验证错误 [RegularExpression(@"

(十二)ASP.NET MVC 数据验证

ASP.NET MVC 会自动根据属性的类型进行基本的校验,比如 int 类型的属性,在提交非整数类型的数据的时候就会报错.在 Action 中可以根据 ModelState.IsValid 进行判断是否验证通过,如果没有通过,使用下面的方法可以获取到报错信息: public static string GetValidMsg(ModelStateDictionary modelState) { StringBuilder sb = new StringBuilder(); //遍历所有的属性

Spring mvc 数据验证框架注解

@AssertFalse 被注解的元素必须为false@AssertTrue 被注解的元素必须为false@DecimalMax(value) 被注解的元素必须为一个数字,其值必须小于等于指定的最小值@DecimalMin(Value) 被注解的元素必须为一个数字,其值必须大于等于指定的最小值@Digits(integer=, fraction=) 被注解的元素必须为一个数字,其值必须在可接受的范围内@Future 被注解的元素必须是日期,检查给定的日期是否比现在晚.@Max(value) 被注

Spring mvc 数据验证

加入jar包 bean-validator.jar 在实体类中加入验证Annotation和消息提示 package com.stone.model; import javax.validation.constraints.Size; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.NotEmpty; public class User { private i

MVC 3 数据验证 Model Validation 详解

续我们前面所说的知识点进行下一个知识点的分析,这一次我们来说明一下数据验证.其实这是个很容易理解并掌握的地方,但是这会浪费大家狠多的时间,所以我来总结整理一下,节约一下大家宝贵的时间. 在MVC 3中 数据验证,已经应用的非常普遍,我们在web form时代需要在View端通过js来验证每个需要验证的控件值,并且这种验证的可用性很低.但是来到了MVC 新时代,我们可以通过MVC提供的数据验证Attribute来进行我们的数据验证.并且MVC 提供了客户端和服务器端 双层的验证,只有我们禁用了客户