其实关于Mvc的验证在上一篇已经有讲过一些了,可以通过在我们定义的Model上面添加相应的System.ComponentModel.DataAnnotations空间下的验证属性。在服务器端通过ModelBinder来接受提交的数据就能实现自动验证。如下例子.

定义一个PriceModel

public class PriceModel
{
[Required]//通过定义这个实现Title字段为必输
[DisplayName("标题")]
public string Title { get; set; } [DisplayName("价格")]
public double Price { get; set; }
}

在页面我们定义的代码如下:

<% using (Html.BeginForm())
{ %>
<%= Html.TextBoxFor(m=>m.Title) %>
<%= Html.ValidationMessageFor(m => m.Title) %>
<%= Html.TextBoxFor(m => m.Price) %>
<%= Html.ValidationMessageFor(m => m.Price) %> <%--通过在前台添加Html.ValidationMessageFor能实现错误信息的显示--%>
<input type="submit" value="提交" />
<% } %>

在控制器定义的代码如下:

[HttpPost]
public ActionResult Index(PriceModel model)
{
return View(model);
}

当我们在页面上面不填写Title时,页面上会提示,标题字段必须填写。这就自动实现了验证。接下来对PriceModel的Price字段添加特殊的验证规则。添加一个PriceAttribute类。添加自定义的验证规则需要继承自ValidationAttribute,并且重写实现IsValid方法,在这个方法里面实现验证。定义完PriceAttribute后,在PriceModel的Price字段上面添加特性,这样就能启用对这个字段的验证,当在页面上面输入的数值后三位不是99到995之间时就会提示出错。

public class PriceAttribute : ValidationAttribute
{
public double MinPrice { get; set; } public override bool IsValid(object value)
{
if (value == null)
{
return true;
}
var price = (double)value;
if (price < MinPrice)
{
return false;
}
double cents = price - Math.Truncate(price);
if (cents < 0.99 || cents >= 0.995)
{
return false;
} return true;
} }

之所以能实现上面的验证是由于在使用DefualtModelBinder的时候会自动调用ModelValidatorProviders.Providers.GetValidators方法获取ModelValidator进行验证(疑问:如果使用自定义的ModelBinder验证能起作用吗?)。但是这里的验证会有一个问题,就是我们这样还是将数据发送到服务器端进行验证,验证失败向ModelState添加数据,通过Html.ValidationMessageFor获取验证失败的ErrorMessage然后再发送回客户端浏览器进行显示。我们能不能直接自动在客户端浏览器进行js验证?

Mvc提供了这样的功能。在前面一节我们提到过ClientDataTypeModelValidator ,其用于生成ModelClientValidationRules(这个验证规则只是针对类型而已,如向Price字段输入字符则会马上提示出错),这个类存放着要发送到客户端的验证规则。除了ClientDataTypeModelValidator能生成ModelClientValidationRules,继承自泛型类DataAnno-tationModelValidator<TAttribute>的类也会提供相应的客户端Rule(如:RangeAttributeAdapter会产生ModelClientValidationRangeRule),当我们在页面启用客户端验证的时候(添加这句代码:<%Html.EnableClientValidation(); %>),ModelClientValidationRules客户端验证规则将会被以json的格式发送到浏览器。例如当我在页面启用客户端验证后,会看到页面增加了如下的代码.

//<![CDATA[
if (!window.mvcClientValidationMetadata) { window.mvcClientValidationMetadata = []; }
window.mvcClientValidationMetadata.push({"Fields":[{"FieldName":"Title","ReplaceValidationMessageContents":true,"ValidationMessageId":"Title_validationMessage","ValidationRules":[{"ErrorMessage":"标题 字段是必需的。","ValidationParameters":{},"ValidationType":"required"}]},{"FieldName":"Price","ReplaceValidationMessageContents":true,"ValidationMessageId":"Price_validationMessage","ValidationRules":[{"ErrorMessage":"价格 字段是必需的。","ValidationParameters":{},"ValidationType":"required"},{"ErrorMessage":"字段 价格 必须是一个数字。","ValidationParameters":{},"ValidationType":"number"}]}],"FormId":"form0","ReplaceValidationSummary":false});
//]]>

通过启用验证规则,能对Title字段实现在客户端的自动验证,但是Price字段只能自动实现非空验证。对于自定义的PriceAttribute验证在客户端并不能实现验证。这个原因在上面一段已经说过,就是PriceAttribute没有相应的继承自DataAnnotationModelValidator<TAttribute>的类来产生相应的客户端验证规则。因此添加一个类来实现这样的功能。

public class PriceValidator : DataAnnotationsModelValidator<PriceAttribute>
{
double _minPrice;
string _message; public PriceValidator(ModelMetadata metadata, ControllerContext context
, PriceAttribute attribute)
: base(metadata, context, attribute)
{
_minPrice = attribute.MinPrice;
_message = attribute.ErrorMessage;
} public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var rule = new ModelClientValidationRule {
ErrorMessage = _message,
ValidationType = "price"
};
rule.ValidationParameters.Add("min", _minPrice); return new[] { rule };
}
}

接下来要做的事情就是在全局文件下,添加这个ModelValidator了。

protected void Application_Start() {
RegisterRoutes(RouteTable.Routes);
DataAnnotationsModelValidatorProvider
.RegisterAdapter(typeof(PriceAttribute), typeof(PriceValidator));
}

最后还要在客户端也要实现相应的Js逻辑。

<script type="text/javascript">
Sys.Mvc.ValidatorRegistry.validators["price"] = function(rule) {
// initialization code can go here.
var minValue = rule.ValidationParameters["min"]; // we return the function that actually does the validation
return function(value, context) {
if (value > minValue) {
var cents = value - Math.floor(value);
if (cents >= 0.99 && cents < 0.995) {
return true; /* success */
}
} return rule.ErrorMessage;
};
};
</script>

本文参考自(包括代码出处):http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx

后续讨论:在进行项目设计的时候我们通常会在客户端利用js进行提交表单前的数据验证,在服务器端我们再次会进行验证,理由是我们不相信任何来自客户端的数据。因此我们通常需要在服务器端和客户端实现相同的验证逻辑。我原来以为MVC框架能够实现当我们在服务器端实现相应的验证规则后,框架能够自动在客户端帮助我们自动实现脚本验证,不用自己书写代码。但是通过上面的PriceAttribute代码得知,对于自定义的验证规则,同样还要在页面添加脚本,而且还要实现PriceValidator这样的类并且在全局进行注册。这不是更加麻烦,而且一样要维护两份逻辑相同的代码?

如果在系统不复杂,验证数据合法性不是很复杂的时候,是可以通过Mvc框架自带的ModelBinder和验证功能帮助减少工作量。

其实系统的验证可以分为三个部分。

1.前端验证:就是利用js实现提交数据前的数据合法性验证。 还是利用js实现验证

2.服务器端数据合法性验证,这个实现逻辑基本和前者一致只是通过服务器端语言在服务器端进行验证.利用ModelBinder和System.ComponentModel.DataAnnotations,这样做的好处就是能让框架自动帮助我们实现验证。而且将验证规则的特性和模型放到一起更能体现模型的基本特征。

3.业务操作验证:在进行业务操作的时候进行验证,如权限商品数量是否足够出售等,这一点可以理解为业务操作是要在一定的操作条件下才能进行的,验证就是验证这些条件是否成立。感觉这些验证可以利用AOP来实现。

上面的观点还没有经过实践进行证明,只是猜想而已,做个备忘,等以后再回头来看。感觉MVC的有些功能有如鸡肋,食之无味,弃之可惜!

MVC系统学习5——验证的更多相关文章

  1. MVC系统学习6—Filter

    Mvc的过滤器是特性类,可以使我们在执行Action之前,执行Action之后,执行Action发生异常时,编写相关的处理代码实现某些逻辑.下面是四个基本的Filter接口. 上面这四个基本的Filt ...

  2. MVC系统学习4—ModelMetaData

    在Mvc R2中,新引入了一些扩展方法,如后面带一个for的方法,这些扩展方法会根据Model的属性自定生成相应的Html元素,如Html.EditFor(Model=>Model.IsAppr ...

  3. MVC系统学习1—MVC执行流程

    用MVC来做开发也有一段时间了,但是感觉一直没入门,就徘徊在似懂非懂的层次,和去年刚毕业学习WebForm时一样,当时通过张子阳老兄的几篇文章,明白了请求处理流程,页面生命周期才真正明白了WebFor ...

  4. MVC系统学习2—MVC路由

    在MVC下不是通过对物理文件的映射来实行访问的,而是通过定义后的路由Url来实现访问的.在前一篇讲到我们是在全局文件下进行路由配置. routes.MapRoute(                & ...

  5. MVC系统学习3—ModelBinder

    在ASP.NET MVC中,每个请求都被映射到一个Action方法,我们可以在action的方法中定义相应类型的参数,View中通过post.get方式提交的request参数,只要名称一致就会对应到 ...

  6. Mvc系统学习9——Areas学习

    在Mvc2.0中,新增加了一个特性就是Areas.在没有有使用Areas的情况下,我们的Mvc项目组织是下面这样的.当项目庞大的时候,Controllers,Model,View文件下下面势必会有很多 ...

  7. MVC系统学习7—Action的选择过程

    在Mvc源码的ControllerActionInvoker的InvokeAction方法里面有一个FindAction方法,FindAction方法在ControllerDescriptor里面定义 ...

  8. MVC系统学习8——AsyncController

    关于为什么使用异步Controller,这里不做备忘,三岁小孩都懂.主要的备忘是如何使用AsyncController. //这个action以Async结尾,并且返回值是void public vo ...

  9. ASP.NET MVC 5 学习教程:添加验证

    原文 ASP.NET MVC 5 学习教程:添加验证 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 通过控 ...

随机推荐

  1. 转 windows10 U盘系统启动盘怎么制作

    转 windows10 U盘系统启动盘怎么制作 现将http://jingyan.baidu.com/article/9f7e7ec05e24d56f29155455.html  将dvd 写入 is ...

  2. poj1715Hexadecimal Numbers(数位dp)

    链接 好久没写这种逐位计数的了. 先统计出总的数 ,s-n+1,倒着计算的 ,感觉倒着比较符合计算方式,总数为15*A(15,i) (1=<i<=8) 也就是1-8长度所有的排列总数 然后 ...

  3. Elixir安装

    参考:https://laravel.com/docs/5.2/elixir 1. 安装node 去这里下载 2.可以用淘宝的cnpm加速! npm install -g cnpm --registr ...

  4. Spring @Resource、@Autowired、@Qualifier区别

    @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入: @Autowired默认是按照类型装配注入的,如果想按照名称来转配注入,则需要结合@Qualif ...

  5. Map接口框架图

    Java集合大致可分为Set.List和Map三种体系,其中Set代表无序.不可重复的集合:List代表有序.重复的集合:而Map则代表具有映射关系的集合.Java 5之后,增加了Queue体系集合, ...

  6. ListView相关知识点

    最近开发接触到了ListView控件,其实简单的需求基本上源生的都可以满足,下面总结一下开发过程中所遇到的关键点: 1.多级ListView联动,保存位置:即切换第一层ListView的item过程中 ...

  7. mysql提升效率

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...

  8. COGS.1200 ganggang的烦恼

    背景 Zhang Gangrui 年纪大了,记性不好,保险箱的密码记不住了,他只记得密码是一个数的阶乘各个位的数相加的和,最后还有个T或F,代表这个数是否为素数,正好,你到他家去了,他请你帮他这个忙, ...

  9. cp - 复制文件和目录

    总览 cp [选项] 文件路径 cp [选项] 文件...目录 POSIX 选项: [-fipRr] GNU 参数(最短形式): [-abdfilprsuvxPR] [-S SUFFIX] [-V { ...

  10. Win10上 visual studio设置为本地IIS运行网站时 必须以管理员身份加载项目的解决方法

    右键,选择“兼容性疑难解答”. 选择“疑难解答程序” 选择“该程序需要附加权限” 点击测试程序 点击下一步 选择 是,为此程序保存这些设置