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 - 学习总目录

ASP.NET MVC - 模型验证的更多相关文章

  1. asp.net mvc 模型验证组件——FluentValidation

    asp.net mvc 模型验证组件——FluentValidation 示例 using FluentValidation; public class CustomerValidator: Abst ...

  2. Asp.Net MVC 模型验证详解-实现客户端、服务端双重验证

    概要 在asp.net webform开发中经常会对用户提交输入的信息进行校验,一般为了安全起见大家都会在客户端进行Javascript(利于交互).服务端双重校验(安全).书写校验代码是一个繁琐的过 ...

  3. ASP.NET没有魔法——ASP.NET MVC 模型验证

    在前面的文章中介绍了用户的注册及登录功能,在注册用户时可以通过代码的形式限制用户名及密码的格式,如果不符合要求那么就无法完成操作,如下图: 该功能的原理是Identity基于的Entity Frame ...

  4. asp.net mvc 模型验证注解,表单提交

    一.添加模型 public class Account { public int ID { get; set; } [Display(Name = "姓名")] //设置要显示的字 ...

  5. asp.net mvc 模型验证-最舒服的验证方式

    在院子里发现 http://www.cnblogs.com/yangecnu/p/3759784.html 模型验证方法 1. 一般方法 繁琐, 无数的if else, 在炎炎夏天,我见过一个验证方法 ...

  6. 当ASP.NET MVC模型验证遇上CKEditor

    项目需要,使用到了CKEditor编辑器.这是个很不错的富文本编辑器,但是当它绑定的字段需要进行模型验证的时候,却会出现验证失效的问题.因此本文旨在记录这个问题和给出解决办法.以下以Validatio ...

  7. ASP.NET没有魔法——ASP.NET MVC 模型绑定解析(下篇)

    上一篇<ASP.NET没有魔法——ASP.NET MVC 模型绑定解析(上篇)>文章介绍了ASP.NET MVC模型绑定的相关组件和概念,本章将介绍Controller在执行时是如何通过这 ...

  8. ASP.NET MVC Model验证(四)

    ASP.NET MVC Model验证(四) 前言 本篇主要讲解ModelValidatorProvider 和ModelValidator两种类型的自定义实现,前者是Model验证提供程序,而Mod ...

  9. ASP.NET MVC Model验证(二)

    ASP.NET MVC Model验证(二) 前言 上篇内容演示了一个简单的Model验证示例,然后在文中提及到Model验证在MVC框架中默认所处的位置在哪?本篇就是来解决这个问题的,并且会描述一下 ...

随机推荐

  1. linux基本

    一.初识 Linux与windows相比的优点是:长期稳定的运行,避免了因为系统的问题导致的项目运行中断:占用资源少:开源软件多. Centos(Community Enterprise Operat ...

  2. thinkphp 实现分页

    一.一个条件的查询数据 查询数据自然是先要显示出数据,然后根据条件进行查询数据 (1)显示出表的数据 这个方法我还是写在了HomeController.class控制器文件中 (1.1)写了一个方法s ...

  3. Python中集合的操作

    Python集合的基本详情 集合是无序的 集合是可变数据类型 集合属于不可哈希范围 集合自动去重 集合的操作 set1 = {1, 2, 3, 4, 5} set2 = {4, 5, 6, 7, 8} ...

  4. Hadoop记录-Hadoop jmx

    https://cwiki.apache.org/confluence/display/EAG/Hadoop+JMX+Monitoring+and+Alerting curl http://10.11 ...

  5. Hadoop记录-NameNode优化

    1.NameNode启动过程 加载FSImage: 回放EditLog: 执行CheckPoint(非必须步骤,结合实际情况和参数确定,后续详述): 收集所有DataNode的注册和数据块汇报. 采用 ...

  6. Linux记录-shell获取hdfs used使用

    #!/bin/bash export JAVA_HOME=/app/jdk/jdk1.8.0_92 export HADOOP_HOME=/app/hadoop export HADOOP_CONF_ ...

  7. linux xargs【转】

    -i -I 参数区别 http://man.linuxde.net/xargs 它们都是用来作参数扩展替换的,以下两句代码其实效果一样 cat arg.txt|xargs -i sh sk.sh -p ...

  8. Python高性能编程

    一.进程池和线程池 1.串行 import time import requests url_lists = [ 'http://www.baidu.com', 'http://fanyi.baidu ...

  9. impala系列: 同步Hive元数据和收集统计信息

    ---====================-- Impala 获取hive 的 metadata ---====================Impala 通常和Hive共用同一个metadat ...

  10. 【LeetCode】227. Basic Calculator

    Problem: Implement a basic calculator to evaluate a simple expression string. The expression string ...