ASP.NET MVC - 模型验证(Model verification)

模型验证原理浅析

模型验证用到了模型绑定器、模型验证器(System.Web.Mvc.DataAnnotationsModelValidator)、模型状态(System.Web.Mvc.ModelState)。为模型应用验证特性(一些特性类型)可以使模型具有同时在服务端和客户端验证数据合法性的功能。

当一个请求到达Action后,Action所接收的模型参数会被实例化。实例化时会调用模型验证器。模型验证器会验证提交的数据是否可以成功转化为Action方法所接收的参数类型,如果不能转化,则模型绑定会失败。如果提交的数据能够成功转化为Action所接收的参数类型,那么模型绑定器就会创建参数的实例,接着会进入应用在模型上的验证特性,由验证特性类对模型的数据进行进一步验证,如果验证特性类对数据的验证是失败的,那么错误信息会被自动写入到ModelState字典中。

现在假设用户发起一个请求,提交一个空的查询字符,而Action接收一个DateTime类型的参数,由于用户提交的是空值,而DateTime是不能为空的struct类型,所以模型验证器会验证失败,异常会抛出到客户端。如果这个数据是有效的,那么模型绑定器就会创建参数类型的实例,并将提交的数据绑定到实例上。

再假如用户提交一个表单,Action方法接收一个Order自定义类型,Order具有x和y属性。同样的,模型验证器会验证数据的有效性,如果验证通过,则由模型绑定器创建参数类型的实例,假如Order模型上应用了验证特性类,那么验证特性类就会对模型进行进一步验证,如果Order的x和y验证未通过,则会自动将x和y作为key,将错误信息作为value写入到ModelSate[x].Errors和ModelSate[y].Errors中,同时还会将错误的数据写入ModelSate[x]和ModelSate[y]中。如果视图页面使用了Html.ValidationMessage或Html.ValidationMessageFor,那这两个方法也会将它们的name参数当做key去ModelState中获取key所对应的错误信息,于是,错误信息显示在了视图上,如图:

客户端验证需要的文件

启用客户端验证(包括失焦时触发验证、配合Remote特性的Ajax验证)这需要引入:

<script src="/Scripts/jquery-1.10.2.js"></script>
<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js"></script>

验证特性的本地化

只需要为每个验证特性的构造函数指定一个ErrorMessage的参数即可,如果需要多国语言的本地化验证提示信息,可参考:如何:为 ASP.NET 网页全球化设置区域性和 UI 区域性

各种验证特性


System.ComponentModel.DataAnnotations命名空间下的验证特性

[Required] 
//指出此属性是必填的;
[MinLength] 
//指出字符的最小长度;
[StringLength] 
//指出字符的最大长度,可在构造函数中指定最小长度;
[RegularExpression] 
//指出需要匹配的正则式;
[Range] 
//指出数字类型的属性的最小值和最大值;
[Compare] 
//指出此属性的值与参数指定的属性的值必须完全一样,比如用于验证两次输入的密码;
[HiddenInput] 
//指出这个属性是一个隐藏域;
[ScaffoldColumn] 
//指出客户端视图通过EditorForModel、DisplayForModel渲染出的Html的某些属性会被隐藏;
[ReadOnly] 
//指出服务端开始模型绑定时,不要把应用了此特性的属性更新为客户端提交的新值;
[UIHint] 
//指出一个模板名称,视图使用EditorFor、DispalyFor等模板辅助方法时,将应用此模板渲染Html,如果找不到指定的模板,ASP.NET MVC框架会自动使用默认模板进行替换,自定义模板参看:暂略;
[DisplayFormat] 
//指出格式化客户端输入框中的值;
[DataType] 
//指出输入框的输入模式;
//示例:
[DataType(DataType.Password)]
public string Password { get; set; }
DataType枚举可能的值如下
CreditCard
//表示信用卡卡号
Currency
//表示货币值
Custom
//表示自定义数据类型
Date
//表示的日期值
DateTime
//表示为日期和当天的时间
Duration
//表示存在的对象的持续时间
EmailAddress
//表示电子邮件地址
Html
//表示某一HTML文件
ImageUrl
//表示的图像的URL
MultilineText
//表示多行文本
Password
//表示密码值
PhoneNumber
//表示电话号码值
PostalCode
//表示邮政编码
Text
//表示显示文本
Time
//表示一个时间值
Upload
//表示文件上载的数据类型
Url
//表示一个URL值

DataType枚举可能的值如下


System.ComponentModel命名空间下的验证特性

[DisplayName] 
//指出此属性在视图的表单中显示的别名;
//示例:
[DisplayName("地址")]
public string Address { get; set; }

 


System.Web.Mvc命名空间下的验证特性

[Remote]
//指出客户端失焦时将路由到参数指定的控制器的Action中
//由Action做出处理,如果Action返回true则验证通过,否则验证失败
//如果失败,客户端会显示错误信息
//此特性专门用于客户端发起远程请求的Ajax验证
//客户端必须引入相应的Jquery文件
//注意,此特性只用于客户端Ajax的失焦验证,而并未执行正常流程的服务端验证
//所以如果需要服务端验证,则应在服务端应重写一个验证逻辑,因为假如js被禁用,
//此时如果点击提交,那么数据就不会经过Remoe指定的Action验证
//示例:
@{
        public class Order
    {
        public int OrderId { get; set; }
        [Remote("CheckUserName", "Account", ErrorMessage = "用户名已经存在")]
        public string UserName { get; set; }
    }     public class AccountController : Controller
    {
        public JsonResult CheckUserName(string userName)
        {
            List<string>
            userNameList = new List<string>
            {
                "sam","leo"
            };
            var result = !userNameList.Contains(userName);//数据库是否没有包含提交的用户名
            return Json(result, JsonRequestBehavior.AllowGet);
        }
    }
} <div class="form-group">
    @Html.LabelFor(model => model.UserName)
    @Html.EditorFor(model => model.UserName)
    @Html.ValidationMessageFor(model => model.UserName, "")
</div>

自定义验证

自定义验证特性 

推荐使用这种方式,因为可以结合客户端验证,而后面两种方式比较难用,权当了解。

using System.Web.Mvc;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations; namespace MusicStore.Models
{
    public class Order
    {
        public int OrderId { get; set; }
        public DateTime OrderDate { get; set; }
        [Remote("CheckUserName", "Account", ErrorMessage = "用户名已经存在")]
        public string UserName { get; set; }
        [Contains("中国", ErrorMessage = "字符中未包含中国二字")]
        [DisplayName("地址")]
        public string Address { get; set; }
    }     //验证提交的数据是否包含中国二字,从ValidationAttribute派生即可自定义验证特性,实现IClientValidatable即可注册客户端验证
    public class ContainsAttribute : ValidationAttribute, IClientValidatable
    {
        public string ContainsStr { get; set; }         public ContainsAttribute(string containsStr) : base($"字符中未包含{containsStr}")
        {
            ContainsStr = containsStr;
        }
        //为服务端验证编写逻辑
        protected override ValidationResult IsValid(object value, ValidationContext context)
        {
            return value == null ? ValidationResult.Success : value.ToString().Contains(ContainsStr) == false ? new ValidationResult($"字符中未包含{ContainsStr}") : ValidationResult.Success;
        }
        //为客户端验证进行注册
        //Contains特性验证可能应用在模型的多个属性或多个模型的属性上,所以此处以迭代器的方式返回多次调用
        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            //使用ModelClientValidationRule对象创建验证逻辑
            //ValidationType:指定客户端验证的函数的名称是什么,函数名称必须是小写
            //ErrorMessage:指定格式化的错误提示信息
            ModelClientValidationRule validationRule = new ModelClientValidationRule { ValidationType = "contains", ErrorMessage = FormatErrorMessage(metadata.DisplayName) };
            //客户端验证函数接收的参数,存储在params参数中,客户端通过params.containsvalue,参数名称必须是小写
            validationRule.ValidationParameters.Add("containsvalue", ContainsStr);
            yield return validationRule;
        }
    }
}
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript">
    $(document).ready(function () {         jQuery.validator.addMethod("contains",
            //测试输入的值是否包含服务端的特性验证指定的字符
            //inputValue:表单字段失焦后传递的输入的值
            //element:表单字段元素
            //params:通过服务端GetClientValidationRules方法生成的验证规则返回的验证特性参数的值
            function (inputValue, element, params) {
                var ContainsValue = params.containsvalue;               
                return inputValue.length ==  ? true : inputValue.indexOf(ContainsValue) == - ? false : true;
            }
        );         jQuery.validator.unobtrusive.adapters.add("contains", ["containsvalue"], function (options) {
            options.rules["contains"] = {
                containsvalue: options.params.containsvalue
            };            
            options.messages["contains"] = options.message;
        });     });   
</script>
<div class="form-group">
    @Html.LabelFor(model => model.Address)
    @Html.EditorFor(model => model.Address)
    @Html.ValidationMessageFor(model => model.Address, "")   
</div>

自定义自验证模型

自验证就是在模型中自定义一个验证逻辑,这比定义一个验证特性更简单,但在模型中定义的验证逻辑只适用于当前模型,不像验证特性可以应用在其它模型上,并且这种方式只能在服务端验证,无法实现客户端验证。

using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace MusicStore.Models
{
    public class Order:IValidatableObject
    {
        public int OrderId { get; set; }
        public string City { get; set; }
        public string Email { get; set; }

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            Order order = validationContext.ObjectInstance as Order;
            System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex("^\\s*([A-Za-z0-9_-]+(\\.\\w+)*@(\\w+\\.)+\\w{2,5})\\s*$");
            if (order == null)
            {
                yield break;
            }
            else if (!order.City.Contains("重庆"))
            {
                yield return new ValidationResult("城市中未包含重庆二字", new string[] { "City" });
            }
            else if (!r.IsMatch(order.Email))
            {
                yield return new ValidationResult("电邮格式错误", new string[] { "Email" });
            }
            //……
        }
    }
}

自定义参数验证

可以在控制器中编写一个验证方法,当Action接收到一个模型时,调用验证方法执行验证,如果验证失败则将错误信息写入ModelState中,此方法没有客户端验证。

[HttpPost]
public ActionResult Create(Order order)
{
    Validate(order);
    if (ModelState.IsValid)
    {
        //……
    }
    return View(order);
}
//自定义参数验证
private void Validate(Order order)
{
    System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex("^\\s*([A-Za-z0-9_-]+(\\.\\w+)*@(\\w+\\.)+\\w{2,5})\\s*$");
    if (!(order.City.Contains("重庆")))
    {
        ModelState.AddModelError("City", "城市中未包含重庆二字");
    }
    if (!(r.IsMatch(order.Email)))
    {
        ModelState.AddModelError("Email", "电邮格式错误");
    }
}

ASP.NET - 学习总目录

MVC - HtmlHelper类的更多相关文章

  1. 扩展ASP.NET MVC HtmlHelper类

    在这篇帖子中我会使用一个示例演示扩展ASP.NET MVC HtmlHelper类,让它们可以在你的MVC视图中工作.这个示例中我会提供一个简单的方案生成Html表格. HtmlHelper类 Htm ...

  2. ASP.NET MVC HtmlHelper 类的扩展方法

    再ASP.NET MVC编程中用到了R语法,在View页面编辑HTML标签的时候,ASP.NET MVC 为我们准备好了可以辅助我们写这些标签的办法,它们就是HtmlHelper.微软官方地址是:ht ...

  3. <一> MVC - HtmlHelper

    HtmlHelper类位于System.Web.Mvc.Html之中主要有七个静态类组成: FormExtensions - BeginForm, BeginRouteForm, EndForm In ...

  4. .net网站开发(前端):4.MVC HtmlHelper

    通过前面三节,已经大概理解MVC是怎样运作的了.MVC的一个特点就是可以很方便地控制视图效果,数据交互也很灵活.先讲一下视图控制的,HtmlHelper,看到Help就知道它是不知疲惫的好人啦(有点像 ...

  5. 【ASP.NET MVC系列】浅谈ASP.NET MVC八大类扩展(上篇)

    lASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操 ...

  6. MVC HtmlHelper扩展——实现分页功能

    MVC HtmlHelper扩展类(PagingHelper) using System; using System.Collections.Generic; using System.Collect ...

  7. ASP.NET MVC HtmlHelper用法集锦

    ASP.NET MVC HtmlHelper用法集锦 在写一个编辑数据的页面时,我们通常会写如下代码 1:<inputtype="text"value='<%=View ...

  8. MVC HtmlHelper用法大全

    MVC HtmlHelper用法大全HtmlHelper用来在视图中呈现 HTML 控件.以下列表显示了当前可用的一些 HTML 帮助器. 本主题演示所列出的带有星号 (*) 的帮助器. ·Actio ...

  9. mvc Controller类介绍

    1.Controller类 i.Controller必须为公开类: ii.必须以Controller结尾: iii.继承Controller基类或实现IController接口的类: iv.类中必须包 ...

随机推荐

  1. 多核CPU怎么理解

    简而言之,双核处理器即是基于单个半导体的一个处理器上拥有两个一样功能的处理器核心.换句话说,将两个物理处理器核心整合入一个核中.企业IT管理者们也一直坚持寻求增进性能而不用提高实际硬件覆盖区的方法.多 ...

  2. 登陆与注册以及Session

    Session  保存状态是在 登陆窗口  检查用户密码的动作上执行 .Models namespace 注册与登陆以及Session.Models { public class UserBF { p ...

  3. bzoj1143 2718

    最小可相交路径覆盖 先预处理可到达的点然后转化为最小不相交路径覆盖 type node=record        point,next:longint;      end; ..] of node; ...

  4. $apply() $digest()

    理解Angular中的$apply()以及$digest() <!DOCTYPE html> <html> <head> <meta charset=&quo ...

  5. 《C#并行编程高级教程》第6章 PLINQ:声明式数据并行 笔记

    PLINQ这个话题好多书都写到过,这本也没有什么特别好的地方. 几个有用和有趣的点记录一下.   顺序的不确定性 用PLINQ就一定要记住并行后会导致顺序不确定的问题.解决方案就是AsOrdered或 ...

  6. spring--资源--4

    4.1.1  概述 在日常程序开发中,处理外部资源是很繁琐的事情,我们可能需要处理URL资源.File资源资源.ClassPath相关资源.服务器相关资源(JBoss AS 5.x上的VFS资源)等等 ...

  7. 【HTML】Beginner4:Heading

    1.Headings h1 h2 h3 h4 h5 h6     h1 being the almighty emperor of headings     h6 being the lowest p ...

  8. wuzhicms后台菜单的添加

    红色部分都是从菜单管理处添加而来.只有少数是自定义的. 开发一个模块,首先就是菜单的管理.而,不需要的功能也可以在在菜单管理处隐藏或删除. 添加菜单:

  9. 支持度(support)和置信度(confidence)

      支持度(Support)的公式是:Support(A->B)=P(A U B).支持度揭示了A与B同时出现的概率.如果A与B同时出现的概率小,说明A与B的关系不大:如果A与B同时出现的非常频 ...

  10. Java 并发之共享对象

    上一篇文章说的是,避免多个线程在同一时间访问对象中的同一数据,这篇文章来详细说说共享和发布对象. 在没有同步的情况下,我们无法预料编译器.处理器安排操作执行的顺序,经常会发生以为“一定会”发生的动作实 ...