mvc 模型验证

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

显式模型验证

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

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视图中收集数据:html

@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" /> } 

 

 

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

[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();   } } ... 

 

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

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

 

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

一些浏览器好比Chrome和Firefox会忽略应用在复选框Checkbox上的CSS属性,咱们能够经过前面讲到的自定义模板来解决:正则表达式

@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) } }

 

 

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

显示验证消息

除了经过CSS风格提示错误,咱们能够将添加到modelstate的错误消息在视图中显示给用户:服务器

@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" /> } 

 

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

重载形式 说明
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(),咱们能够将错误消息紧邻输入控件挨个显示:

@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" /> } 

 

 

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在数据模型上添加验证规则:

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; } } 

 

这里使用了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验证特性扩展,在调用基类的验证后再作附加的检查。

以上的验证特性都是针对模型单个属性的,咱们还能够为整个模型建立自定义验证特性:

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); } } }

 

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

[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; } } 

 

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

... [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(); } } ...

 

自验证模型

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

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; } } }

 

模型绑定器在试图给模型对象赋值时调用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方法开始:

... 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); } } ...

 

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

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

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; } }

 

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

转自http://www.javashuo.com/article/p-ywcqkawn-t.html

相关文章
相关标签/搜索