ASP.NET MVC 4 (十) 模型验证

时间:2022-09-07 16:43:40

模型验证是在模型绑定时检查从HTTP请求接收的数据是否合规以保证数据的有效性,在收到无效数据时给出提示帮助用户纠正错误的数据。

显式模型验证

验证数据最直接的方式就是在action方法中对接收的数据验证,以下面的Model为例:

public class Appointment {

        public string ClientName { get; set; }
public DateTime Date { get; set; }
public bool TermsAccepted { get; set; }
}

我们要求ClientName不能为空;约会日期Date不能早于当前日期,日期的格式可以在web.config中使 用<globalization culture="en-US" uiCulture="enUS"/>来指定,否则使用服务器默认的时区格式;TermsAccepted必须为true。我们在 MakeBooking.cshtml视图中收集数据:

ASP.NET MVC 4 (十) 模型验证
@model ModelValidation.Models.Appointment
@{
ViewBag.Title = "Make A Booking";
}
<h4>Book an Appointment</h4>
@using (Html.BeginForm()) {
  <p>Your name: @Html.EditorFor(m => m.ClientName)</p>
  <p>Appointment Date: @Html.EditorFor(m => m.Date)</p>
  <p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms & conditions</p>
  <input type="submit" value="Make Booking" />
}
ASP.NET MVC 4 (十) 模型验证

直接在action方法中验证请求的数据:

ASP.NET MVC 4 (十) 模型验证
...
[HttpPost]
public ViewResult MakeBooking(Appointment appt) {
  if (string.IsNullOrEmpty(appt.ClientName)) {
    ModelState.AddModelError("ClientName", "Please enter your name");
  }
  if (ModelState.IsValidField("Date") && DateTime.Now > appt.Date) {
    ModelState.AddModelError("Date", "Please enter a date in the future");
  }
  if (!appt.TermsAccepted) {
    ModelState.AddModelError("TermsAccepted", "You must accept the terms");
  }
  if (ModelState.IsValid) {
    // statements to store new Appointment in a
    // repository would go here in a real project
    return View("Completed", appt);
  } else {
    return View();
  }
}
...
ASP.NET MVC 4 (十) 模型验证

ModelState.IsValidField()检查模型绑定器能否成功绑定“Date”属性,如果数据不合法使用 ModelState.AddModelError()添加错误消息。如果没有任何错误,ModelState.IsValid=true,我们可以继续 正常操作,否则返回数据输入界面。HTML.EditFor()帮助函数会检查ModelState是否包含当前属性的错误,如果有错误会为生成的元素添 加CSS类input-validation-error,默认的input-validation-error类定义在~/Content /Site.css中:

...
.input-validation-error {
border: 1px solid #f00;
background-color: #fee;
}
...

其效果就是使得输入控件边框变红、背景变粉红以提示用户有错误发生。如果自己编写的HTML帮助函数要支持验证错误提示,可以参考System.Mvc.Web.Html.InputExtensions的源代码是如何实现的。

一些浏览器比如Chrome和Firefox会忽略应用在复选框Checkbox上的CSS属性,我们可以通过前面讲到的自定义模板来解决:

ASP.NET MVC 4 (十) 模型验证
@model bool?

@if (ViewData.ModelMetadata.IsNullableValueType) {
@Html.DropDownListFor(m => m, new SelectList(new [] {"Not Set", "True", "False"}, Model))
} else {
ModelState state = ViewData.ModelState[ViewData.ModelMetadata.PropertyName];
bool value = Model ?? false; if (state != null && state.Errors.Count > 0) {
<div class="input-validation-error" style="float:left">
@Html.CheckBox("", value)
</div>
} else {
@Html.CheckBox("", value)
}
}
ASP.NET MVC 4 (十) 模型验证

这里定义了一个bool类型专用的自定义模板,使用div标签包装checkbox,从modelstate检查当前属性是否有错误,有错误时添加错误提示的CSS类到div标签上。

显示验证消息

除了通过CSS风格提示错误,我们可以将添加到modelstate的错误消息在视图中显示给用户:

ASP.NET MVC 4 (十) 模型验证
@model ModelValidation.Models.Appointment
@{
ViewBag.Title = "Make A Booking";
}
<h4>Book an Appointment</h4>
@using (Html.BeginForm()) {
@Html.ValidationSummary()
<p>Your name: @Html.EditorFor(m => m.ClientName)</p>
<p>Appointment Date: @Html.EditorFor(m => m.Date)</p>
<p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms & conditions</p>
<input type="submit" value="Make Booking" />
}
ASP.NET MVC 4 (十) 模型验证

Html.ValidationSummary()帮助函数将ModelState中的错误消息以列表的方式罗列出来显示给用户。ValidationSummary有几种重载形式:

重载形式 说明
Html.ValidationSummary()  汇总显示所有的验证错误
Html.ValidationSummary(bool) 如果bool参数=true,只显示Model层次的错误,否则所有的验证错误都显示
Html.ValidationSummary(string)  在所有错误消息之前再显示string给出的字符串
Html.ValidationSummary(bool, string)  同Html.ValidationSummary(bool),只是在错误消息前多显示string给出的字符串

所谓Model层次的错误,其实就是使用ModelState.AddModelError()添加错误消息时第一个代表错误属性的参数留空,比如:

...
if (ModelState.IsValidField("ClientName") && ModelState.IsValidField("Date") && appt.ClientName == "Joe" && appt.Date.DayOfWeek == DayOfWeek.Monday) {
  ModelState.AddModelError("", "Joe cannot book appointments on Mondays");
}
...

除了Html.ValidationSummary(),我们可以将错误消息紧邻输入控件挨个显示:

ASP.NET MVC 4 (十) 模型验证
@model ModelValidation.Models.Appointment
@{
ViewBag.Title = "Make A Booking";
}
<h4>Book an Appointment</h4>
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
<p>@Html.ValidationMessageFor(m => m.ClientName)</p>
<p>Your name: @Html.EditorFor(m => m.ClientName)</p>
<p>@Html.ValidationMessageFor(m => m.Date)</p>
<p>Appointment Date: @Html.EditorFor(m => m.Date)</p>
<p>@Html.ValidationMessageFor(m => m.TermsAccepted)</p>
<p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms & conditions</p>
<input type="submit" value="Make Booking" />
}
ASP.NET MVC 4 (十) 模型验证

Html.ValidationMessageFor()在属性有错误时显示对应的错误消息,为避免在汇总消息中重复显示,这里使用true参数调用Html.ValidationSummary(true)。

模型绑定时验证

默认模型绑定器DefaultModelBinder内建在绑定时验证数据,比如我们输入非日期格式给Date属性,绑定器会给出“The value 'xxx' is not valid for Date.”的错误消息。我们可以重载DefaultModelBinder的一些方法来添加有用的信息:

方法 说明 默认实现的功能
OmModelUpdated 在绑定器试图给模型对象所有属性赋值时调用 根据模型metadata给出的验证规则验证数据添加错误消息到ModelState
SetProperty 在绑定器视图给模型对象的某个属性赋值时调用 如果模型属性不能是Null但是没有数据来绑定时添加“The <name> field is required”消息到ModelState,如果有数据但是处理错误比如类型转换失败添加“The value <value> is not valid for <name>”消息到ModelState

使用元数据指定验证规则

更多的时候我们不需要重载默认模型绑定器,因为我们可以更方便的使用metadata在数据模型上添加验证规则:

ASP.NET MVC 4 (十) 模型验证
public class Appointment {
  [Required]
  public string ClientName { get; set; }
  [DataType(DataType.Date)]
  [Required(ErrorMessage="Please enter a date")]
  public DateTime Date { get; set; }
  [Range(typeof(bool), "true", "true", ErrorMessage = "You must accept the terms")]
  public bool TermsAccepted { get; set; }
}
ASP.NET MVC 4 (十) 模型验证

这里使用了Required和Range两个特性,前者表示数据是必须的,后者指定了一个可用值范围;ErrorMessage则是错误时的提示消息,如果不指定则使用上表中的默认消息。可用的验证特性包括:

特性 示例 说明
Compare [Compare("MyOtherProperty")] 两个属性必须相同值,比如我们要求用户重复输入两次邮件地址时有用
Range [Range(10, 20)]  属性值必须在指定的数值范围内,可以使用数值类型的最大最小值比如int.MinValue、int.MaxValue
RegularExpression [RegularExpression("pattern")]  字符串值必须匹配正则表达式,默认大小写敏感,可以使用(?i)修饰符关闭大小写敏感,比如[RegularExpression("(?i)mypattern")]
Required [Required] 属性值必须非空或者不能只是空格,如果允许全空格可以[Required(AllowEmptyStrings = true)]
StringLength [StringLength(10)]  字符串长度不能超过给定的最大长度,也可以指定最小长度:[StringLength(10, MinimumLength=2)]

上面的例子中没有使用Required特性验证bool类型的TermsAccepted,这是因为EditFor()在渲染Checkbox会多 给出一个hidden的输入元素,即使我们没有选中checkbox返回的结果中仍然是有值的。使用Range看上去比较别扭,好在我们可以创建自定义的 验证特性类来改进:

public class MustBeTrueAttribute : ValidationAttribute {

        public override bool IsValid(object value) {
return value is bool && (bool)value;
}
}

这里验证输入数据是否是bool类型且为true,使用这个自定义验证特性很简单:

..
[MustBeTrue(ErrorMessage="You must accept the terms")]
public bool TermsAccepted { get; set; }
...

除了从ValidationAttribute扩展自定义特性,我们可以直接从内建的验证特性扩展:

public class FutureDateAttribute : RequiredAttribute {
public override bool IsValid(object value) {
return base.IsValid(value) && ((DateTime)value) > DateTime.Now;
}
}

这里从Require验证特性扩展,在调用基类的验证后再做附加的检查。

以上的验证特性都是针对模型单个属性的,我们还可以为整个模型创建自定义验证特性:

ASP.NET MVC 4 (十) 模型验证
public class NoJoeOnMondaysAttribute : ValidationAttribute {

        public NoJoeOnMondaysAttribute() {
ErrorMessage = "Joe cannot book appointments on Mondays";
} public override bool IsValid(object value) {
Appointment app = value as Appointment;
if (app == null || string.IsNullOrEmpty(app.ClientName) ||
app.Date == null) {
// we don't have a model of the right type to validate, or we don't have
// the values for the ClientName and Date properties we require
return true;
} else {
return !(app.ClientName == "Joe" &&
app.Date.DayOfWeek == DayOfWeek.Monday);
}
}
}
ASP.NET MVC 4 (十) 模型验证

这里检查客户名称和约定日期,不允许客户名称Joe在星期一预约,我们可以将这个特性应用在整个模型类上:

ASP.NET MVC 4 (十) 模型验证
[NoJoeOnMondays]
public class Appointment {
  [Required]
  public string ClientName { get; set; }
  [DataType(DataType.Date)]
  [FutureDate(ErrorMessage="Please enter a date in the future")]
  public DateTime Date { get; set; }
  [MustBeTrue(ErrorMessage="You must accept the terms")]
  public bool TermsAccepted { get; set; }
}
ASP.NET MVC 4 (十) 模型验证

有了这些验证规则特性,控制器类的action方法可以极大的简化为:

ASP.NET MVC 4 (十) 模型验证
...
[HttpPost]
public ViewResult MakeBooking(Appointment appt) {
if (ModelState.IsValid) {
// statements to store new Appointment in a
// repository would go here in a real project
return View("Completed", appt);
} else {
return View();
}
}
...
ASP.NET MVC 4 (十) 模型验证

自验证模型

模型验证的另外一种是为模型类实现IValidatableObject接口创建可自验证的模型类:

ASP.NET MVC 4 (十) 模型验证
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using ModelValidation.Infrastructure;
namespace ModelValidation.Models
{
public class Appointment : IValidatableObject
{
public string ClientName { get; set; }
[DataType(DataType.Date)]
public DateTime Date { get; set; }
public bool TermsAccepted { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> errors = new List<ValidationResult>();
if (string.IsNullOrEmpty(ClientName))
{
errors.Add(new ValidationResult("Please enter your name"));
}
if (DateTime.Now > Date)
{
errors.Add(new ValidationResult("Please enter a date in the future"));
}
if (errors.Count == 0 && ClientName == "Joe"
&& Date.DayOfWeek == DayOfWeek.Monday)
{
errors.Add(new ValidationResult("Joe cannot book appointments on Mondays"));
}
if (!TermsAccepted)
{
errors.Add(new ValidationResult("You must accept the terms"));
}
return errors;
}
}
}
ASP.NET MVC 4 (十) 模型验证

模型绑定器在试图给模型对象赋值时调用Validate(),返回结果是一个错误列表,使用这种方式我们可以在一个地方做完所有的数据验证。

客户端验证

以上讲的都是数据提交到服务器上后的验证,客户端我们可以通过脚本在数据提交到服务器前验证,MVC支持“unobtrusive client-side validation”,unobtrusive意指在输出HTML标签时添加特定HTML标签,过MVC的JAVA脚本验证库利用这些专用特性进行数据 验证。要使用客户端验证首先需要在web.config中启用:

...
<appSettings>
  <add key="ClientValidationEnabled" value="true"/>
  <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
...

上面的两个设置必须都为true,我们可以在Razor代码块中使用HtmlHelper.ClientValidationEnabled和 HtmlHelper.UnobtrusiveJavaScriptEnabled为单个视图配置是否使用客户端验证。我们还必须保证以下脚本文件被添加 到视图或者布局文件中:

  • /Scripts/ jquery-1.7.1.min.js
  • /Scripts/ jquery.validate.min.js
  • /Scripts/ jquery.validate.unobtrusive.min.js

可以看到客户端验证仍然是依赖于Jquery的。在启用客户端验证后,我们添加到模型类上的内建验证特性比如Requried、 StringLength就可以直接工作了,数据验证错误时java脚本会给出错误提示。具体来讲EditFor()这些模板帮助函数在启用客户端验证后 会输出一些额外的特性,比如上面ClientName属性:

...
<input class="text-box single-line" data-val="true" data-val-length="The field ClientName must be a string with a minimum length of 3 and a maximum length of 10." data-val-length-max="10" data-val-length-min="3" data-val-required="The ClientName field is required." id="ClientName" name="ClientName" type="text" value="" />
...

JQuery验证函数查找data-val=true的元素进行验证,data-val-<xxx>则是具体的验证规则,比如这里的 data-val-length和data-val-required。通过元数据指定的验证规则既可以在客户端使用,也可以在服务器端使用,为我们带来 了极大的方便,而且即使在客户端禁用了JAVA脚本,服务器端的数据验证仍然有效。

远程验证

远程验证是客户端验证和服务端验证的折中方式,客户端在背后通过Ajax请求向服务端验证数据,典型的应用场景可以是用户名的验证,在用户名验证成功后才允许用户继续后续的输入。使用远程验证是从控制器定义一个用于验证的action方法开始:

ASP.NET MVC 4 (十) 模型验证
...
public JsonResult ValidateDate(string Date) {
DateTime parsedDate; if (!DateTime.TryParse(Date, out parsedDate)) {
return Json("Please enter a valid date (mm/dd/yyyy)",
JsonRequestBehavior.AllowGet);
} else if (DateTime.Now > parsedDate) {
return Json("Please enter a date in the future",
JsonRequestBehavior.AllowGet);
} else {
return Json(true, JsonRequestBehavior.AllowGet);
}
}
...
ASP.NET MVC 4 (十) 模型验证

验证action方法必须有一个和要验证字段同名的参数,这里定义Date为字符串类型是有考虑的。模型绑定如果不能从请求数据中转换成日期类型会 发生异常,远程验证无法在客户端显示异常信息会被静悄悄的丢弃,所以一般我们使用字符串类型,在验证方法内部显式的转换数据类型。验证方法返回一个返回一 个JsonResult对象,验证成功我们封装true,不成功封装错误信息,无论哪种结果我们使用 JsonRequestBehavior.AllowGet标识验证结果可以通过GET请求。

有了远程验证action,我们需要添加remote验证特性到相应的模型类属性上:

ASP.NET MVC 4 (十) 模型验证
 public class Appointment {

        [Required]
[StringLength(10, MinimumLength = 3)]
public string ClientName { get; set; } [DataType(DataType.Date)]
[Remote("ValidateDate", "Home")]
public DateTime Date { get; set; } public bool TermsAccepted { get; set; }
}
ASP.NET MVC 4 (十) 模型验证

在Remote验证特性中指定用于验证的控制器名称和action,MVC的javascript验证库按此生成的URL请求并验证。远程验证会在 用户第一次提交表单时生效,以及此后的每一次编辑数据的动作,比如每一次的按键都会执行一次远程验证,这是我们在带宽有限时需要考虑的。

以上为对《Apress Pro ASP.NET MVC 4》第四版相关内容的总结,不详之处参见原版 http://www.apress.com/9781430242369。

转自:http://www.cnblogs.com/duanshuiliu/p/3708650.html

ASP.NET MVC 4 (十) 模型验证的更多相关文章

  1. ASP&period;NET MVC学习之模型验证详解

    ASP.NET MVC学习之模型验证篇 2014-05-28 11:36 by y-z-f, 6722 阅读, 13 评论, 收藏, 编辑 一.学习前的一句话 在这里要先感谢那些能够点开我随笔的博友们 ...

  2. ASP&period;NET MVC学习之模型验证篇

    一.学习前的一句话 在这里要先感谢那些能够点开我随笔的博友们.慢慢的已经在博客园中度过一年半了,伊始只是将博客园作为自己学习的记录本一样使用,也不敢将自己的随笔发表到博客园首页,生怕自己的技艺不高,反 ...

  3. ASP&period;NET MVC中使用窗体验证出现上下文的模型在数据库创建后发生更改,导致调试失败(一)

    在ASP.NET MVC中使用窗体验证.(首先要明白,验证逻辑是应该加在Model.View和Controller哪一个里面?由于Model的责任就是负责信息访问与商业逻辑验证的,所以我们把验证逻辑加 ...

  4. 【ASP&period;NET MVC系列】数据验证和注解

    [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作篇)(下) [04]浅谈ASP. ...

  5. ASP&period;NET MVC学习之模型绑定(1)

    一.前言 下面我们将开始学习模型绑定,通过下面的知识我们将能够理解ASP.NET MVC模型的模型绑定器是如何将http请求中的数据转换成模型的,其中我们重点讲述的是表单数据. 二.正文 1.简单类型 ...

  6. ASP&period;NET MVC如何实现自定义验证(服务端验证&plus;客户端验证)

    ASP.NET MVC通过Model验证帮助我们很容易的实现对数据的验证,在默认的情况下,基于ValidationAttribute的声明是验证被使用,我们只需 要将相应的ValidationAttr ...

  7. Asp&period;net Mvc 中的模型绑定

    asp.net mvc中的模型绑定可以在提交http请求的时候,进行数据的映射. 1.没有模型绑定的时候 public ActionResult Example0() { ) { string id ...

  8. ASP&period;NET MVC系列&colon;添加模型的验证规则

    首先,在模型类中引用 System.ComponentModel.DataAnnotations 命名空间;System.ComponentModel.DataAnnotations 命名空间提供定义 ...

  9. ASP&period;NET MVC学习之模型绑定(2)

    3.手工调用模型绑定 很多情况下我们都是通过形参的方式接收来自http流中的数据,这看似是完美的,但是缺少了很多过程中的控制,所以我们就需要使用手工的方式进行绑定.下面我们通过一个例子来说明,首先打开 ...

随机推荐

  1. 如何为Surface Dial设备开发自定义交互功能

    随着Surface Studio的发布,微软发布了与之相配套的外设硬件Surface Dial,用户可以将Surface Dail吸附在Surface Studio的屏幕上面,用旋转和点击的实体操作来 ...

  2. 16-1-26---图解HTTP(01)

    图解HTTP1.4.2确保可靠性的HTTP协议    按层次分,TCP位于传输层,提供可靠的字节流服务    所谓字节流服务,指为了方便传输,将大块数据分割成以报文为单位的数据包进行管理,而可靠的传输 ...

  3. 深入理解this机制系列第三篇——箭头函数

    × 目录 [1]痛点 [2]解决 [3]基本用法[4]回调函数[5]注意事项 前面的话 this机制与函数调用有关,而作用域则与函数定义有关.有没有什么是可以将this机制和作用域联系起来的呢?本文将 ...

  4. &lbrack;iOS&rsqb;技巧集锦:UICollectionView内容下沉64像素原因和解决方案

    现象 UICollectionView的内容在按Home键再回到APP时,会下沉64像素. 原因 页面有NavigationBar,正好是64像素,Controller勾选了Adjust Scroll ...

  5. C和指针 第九章 字符串 字符 字节

    C语言中没有字符串类型,字符串是以NUL结尾的字符数组组成的. 高级字符串查找: //计算字符串起始部分,有多少字符是在group中 size_t strspn(char const * str, c ...

  6. inode-软链接与硬链接

    一.inode是什么?理解inode,要从文件储存说起.文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector).每个扇区储存512字节(相当于0.5KB).操作系统读取硬 ...

  7. 第一个Polymer应用 - &lpar;2&rpar;创建你自己的元素

    原文链接: Step 2: Your own element翻译日期: 2014年7月6日翻译人员: 铁锚通过上一节的学习和实践, 您已经完成了一个基本的应用程序结构(application stru ...

  8. Qt 比对TreeItem节点

    void TreeModel::settingsUpdate(const QStringList &lines){ QList<TreeItem*> parents; TreeIt ...

  9. Quartz(一):Cron表达式

    正文 1. 表达式介绍 Cron:cron表达式用于配置cronTrigger的实例.cron表达式实际上是由七个子表达式组成.这些表达式之间用空格分隔. Cron格式:[秒] [分] [小时] [日 ...

  10. P4168 &lbrack;Violet&rsqb;蒲公英 区间众数

    $ \color{#0066ff}{ 题目描述 }$ 在乡下的小路旁种着许多蒲公英,而我们的问题正是与这些蒲公英有关. 为了简化起见,我们把所有的蒲公英看成一个长度为n的序列 \((a_1,a_2.. ...