APS.NET MVC + EF (08)---数据注解和验证

对于Web开发人员来说,用户输入验证一直是一个挑战。不仅在客户端浏览器中需要执行验证逻辑,在服务器端也需要执行。如果觉得验证是令人望而生畏的繁杂琐事,ASP.NET MVC框架提供了数据注解的方式帮助我们处理这些琐事。

   

8.1 验证注解的使用

数据注解特性定义在名称空间System.ComponentModel.DataAnnotations中,它们提供了服务器端验证的功能,当在模型的属性上使用这些特性时,框架也支持客户端验证。在名称空间DataAnnotations中,有4个特性可以用来应对一般的验证场合。下面从Required特性开始对它们逐一介绍。

8.1.1 Required 验证特性

这个特性表必须的、不能为空的。用于不为空校验。

示例1

[Required(ErrorMessage="姓名不得为空")]

public string Name { get; set; } //姓名

   

8.1.2 StringLength 验证特性

这个特性可以验证字符串长度,一般用来验证属性的最大长度,也支持同时设置最小长度。如示例2所示

示例2

[DisplayName("密码")]

扫描二维码关注公众号,回复: 6096764 查看本文章

[StringLength(20,ErrorMessage = "{0}不能超过{1}个字符")]

public string Password { get; set; }

   

[DisplayName("密码")]

[StringLength(20,MinimumLength=6, ErrorMessage = "{0}长度必须在{2}和{1}之间")]

public string Password { get; set; }

  

使用ErrorMessage属性,一般都会在字符串中使用占位符,上述代码中,{0}对应属性的名称,{1}对应maximum数据,{2}对应minimum数据。

   

8.1.3 Range验证特性

Range 验证特性可以验证数字(整数和浮点数)、时间等类型的范围,对应的构造函数如下所示。

public RangeAttribute(int minimum, int maximum);

public RangeAttribute(double minimum, double maximum);

public RangeAttribute(Type type, string minimum, string maximum);

这三个构造函数主要用来验证时间等特殊类型。如示例3所示。

示例3

[DisplayName("年龄")]

[Range(18,35,ErrorMessage ="{0}必须在{1}和{2}之间")]

public int Age { get; set; }

[DisplayName("生日")]

[Range(typeof(DateTime),"2001-01-01","2019-12-31",ErrorMessage = "{0}必须在{1}和{2}之间"]

public DateTime? BornDate { get; set; }

上述代码中,{0}对应属性的名称,{1}对应minimum数据,{2}对应maximum数据。

   

8.1.4 Compare 验证特性

使用Compare验证特性一般用来验证两个属性的值是否一致。如示例4所示。

示例4

public string Passowrd { get; set; }

[Compare("Password",ErrorMessage ="两次输入的密码必须一致")]

public string PasswordConfig { get; set; }

   

8.1.5 RegularExpression验证特性

RegularExpression验证特性的关键是正则表达式的编写,如示例5所示。

示例5

[RegularExpression(@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$", ErrorMessage = "{0}格式不正确")]

public string Email { get; set; }

   

8.1.6 Remote验证特性

同其它几个验证特性不同,Remote特性的命名空间是System.Web.Mvc。Remote特性利用服务器端的回调函数执行客户端的验证逻辑。如示例6所示。

示例6

//控制器代码

public ActionResult CheckTelephone(string telephone)

{

if (telephone=="13636595489")

{

return Json("手机号"+telephone+ "已经存在", JsonRequestBehavior.AllowGet);

}

return Json(true, JsonRequestBehavior.AllowGet);

}

//实体类代码

[System.Web.Mvc.Remote("CheckTelephone", "Default", ErrorMessage ="手机号码已经存在")]

public string Telephone { get; set; }

   

8.2 自动生成客户端验证

8.2.1 使用视图模板创建视图

ASP.NET MVC 框架支持根据模型自动生成客户端验证代码或脚本,但必须满足以下条件:

  • 必须要用必须使用HtmlHelper扩展方法输出HTML表单。
  • 必须要用强类型页面。
  • 必须要引用Jquery库、Jquery.Validate、Jquery.Validate.unobtrusive这三个文件。
  • web.config文件中这两个值必须为True。默认为True

<appSettings>

<add key="ClientValidationEnabled" value="true" />

<add key="UnobtrusiveJavaScriptEnabled" value="true" />

</appSettings>

示例7

@using (Html.BeginForm())

{

<div class="form-horizontal">

<div class="form-group">

@Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })

<div class="col-md-10">

@Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })

@Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })

</div>

</div>

</div>

}

在示例7中,Html.EditorFor()方法和Html.TextBoxFor()方法类似,用来输出input标签(后面内容介绍区别)。的View视图会生成如下代码。

<form action="/Home/Login" method="post">

<div class="form-horizontal">

<div class="form-group">

<label class="control-label col-md-2" for="Name">姓名</label>

<div class="col-md-10">

<input class="form-control text-box single-line" data-val="true" data-val-required="姓名不得为空" id="Name" name="Name" type="text" value="" />

<span class="field-validation-valid text-danger" data-valmsg-for="Name" data-valmsg-replace="true"></span>

</div>

</div>

</div>

</form>

示例7中,Html.ValidationMessageFor()和Html.ValidationMessage()扩展方法用来生成HTML的验证消息。由HTML辅助方法默认生成的HTML都附加了符合HTML5标准的data-*属性,这些属性是根据模型自动生成的,引用的客户端脚本结合这些属性就构成了完整的客户端验证功能。

   

ASP.NET MV 还提供了 一些列视图模板,来简化示例8的复杂工作。在添加视图时,在"添加视图"对话框中的模板选项中选择不同的模板。如图8-1所示。

图8-1 选择视图模板

系统提供了Create、Delete、Details、Edit 和 List 模板,选择这些模板,系统会自动生成带有验证的、如示例8中所示的视图代码。在实际开发中,我们只需要做适当的修改即可。

   

8.2.2 ModelState 和服务器端验证

在模型中定义验证规则后,ASP.NET MVC 在将数据映射到模型时,会自动应用模型类上的验证规则。在验证的过程中,它会自动把验证错误信息添加到ModelState数据字典,这时只需要读取其IsVaild 属性,就可以判断是否通过。

另外也可以使用 ModeState 的 AddModelError()方法添加自定义的错误信息,用法如示例8所示。

   

示例8

public ActionResult RegisterUser(User user)

{

if (ModelState.IsValid)

{

UserManager manager = new UserManager();

if (!manager.Register(user))

{

ModelState.AddModelError("doubleUser", "用户名已使用,请重新输入!");

return View("Register2", user);

}

else//注册成功

{

return Redirect("~/");

}

}

return View("Register2", user);

}

   

当服务器端验证生效后,视图中使用 Html.ValidationMessage()来输出验证信息,除此之外还有Html.ValidationSummary()用来显示验证信息的汇总信息。

   

8.3 自定义验证规则

除了 ASP.NET MVC 中提供验证规则外,我们还可以自定义验证规则。例如,我们要求用户名不允许是"admin"、"sa"等特殊字符串。

要实现自定义验证规则,只要继承 ValidationAttribute类,并重写IsValid方法即可。如示例9所示。

示例9

public class UserNameAttribute:ValidationAttribute

{

protected override ValidationResult IsValid(object value, ValidationContext validationContext)

{

string[] keywords = { "admin", "sa" };

if (!keywords.Contains(value))

{

return ValidationResult.Success;

}

else

{

return new ValidationResult("不允许使用关键字!");

}

}

}

IsValid方法中的第一个参数 value 是要验证的对象的值,ValidationContext参数,提供了很多可在IsValid返回发内部使用的信息,如模型类型、模型对象实例、用来验证属性的人性化显示名称以及其他有用信息。

上面代码中的问题在于硬编码的错误提示消息那行代码。使用数据注解的开发人员希望可以使用ValidationAttribute的ErrorMessage属性来自定义错误提示消息。同时还要与其他验证特性一样,提供一个默认的错误提示消息(开发人员没有提供自定义的错误提示消息时使用)并且还要利用验证的属性名称生成错误提示消息。对示例9的完善如示例10所示。

示例10

public class UserNameAttribute:ValidationAttribute

{

public UserNameAttribute()

:base("{0}不允许使用关键字!")

{

}

protected override ValidationResult IsValid(object value, ValidationContext validationContext)

{

string[] keywords = { "admin", "sa" };

if (!keywords.Contains(value))

{

return ValidationResult.Success;

}

else

{

var errorMessage = FormatErrorMessage(validationContext.DisplayName);

return new ValidationResult(errorMessage);

}

}

}

前面的代码做了两处改动:

  • 首先,向基类的构造函数传递了一个默认的错误提示消息。如果正在面向国际开发应用程序的话,就应该从一个资源文件中提取这个默认的错误提示消息。
  • 注意,默认的错误提示消息中包含了一个参数占位符({0})。这个占位符之所以存在,是因为第二处改动,即调用继承的FormatErrorMessage方法会自动使用显示的属性名称来格式化这个字符串。

       

FormatErrorMessage可以确保我们使用合适的错误提示消息字符串。这条代码语句需要传递name属性的值,这个值可以通过validationContext参数的DisplayName属性获得。构造完验证逻辑后,就可以将其应用到任何模型属性上。

[UserName]

public string Name { get; set; }

   

[UserName(ErrorMessage=" {0}使用了关键字!")]

public string Name{get;set;}

   

   

8.4 显示和编辑注解

8.4.1 DisplayName 和 Display 特性

DisplayName用来在视图中通过 Html.LabelFor()方法显示属性的名称。如:

[DisplayName("姓名")]

public string Name { get; set; }

同样是为属性定义说明,还有另一种注解:Dispaly,它的作用并不仅仅是指定显示的内容,甚至能够指定显示的顺序。

[Display(Name="姓名",Order=15000)]

public string Name { get; set; }

order的默认参数是10000, Display在用于属性的显示名称上,具有更高的优先级。

   

8.4.2 HiddenInput 和 ScaffoldColumn 特性

我们的模型中经常需要定义一个名称中包含有Id的作为key的属性,但是我们在显示的时候又不想将这些属性显示出来,那该怎么办呢?就是对显示隐藏,我们可以使用HiddenInput。如示例11所示。

示例11

[HiddenInput]

public int Id{ get; set; }

   

[HiddenInput(DisplayValue = false)]

public int UserId { get; set; }

使用了HiddenInput,默认情况下属性会以只读形式显示出来,像是上面的Name,要想完全隐藏,就得将DisplayValue设置为false。

隐藏属性在HTML上的显示,我们还可以使用ScaffoldColumn特性。使用ScaffoldColumn并不是为属性设置type = "hidden",它是直接将该属性从基架中删除。如:

[ScaffoldColumn(false)]

public int UserId{ get; set; }

即使从基架中将该属性删除,模型绑定器仍然会试图为该属性赋值,这样就为典型的攻击"重复提交"提供了机会。要想防止这种攻击,我们可以利用Bind特性。

Bind特性可以选择模型绑定器要绑定的值。像是这样:

示例12

[Bind(Include = "Name, Email")]

public class User

{

public int UserId{ get; set;}

public string Name{ get; set;}

public string Email{ get; set;}

}

这样模型绑定器就只绑定Name和Email属性。 当然,我们也可以选择不绑定的属性:

[Bind(Exclude ="UserId")]

public class User

{

public int UserId{ get; set;}

public string Name{ get; set;}

public string Email{ get; set;}

}

这样,上面所讲的"重复提交"攻击就无法发挥作用了。但是必须注意,使用"Include"的白名单比起使用"Exclude"的黑名单更加安全,因为我们永远也不知道黑客会用怎样的方式来攻击我们。

Bind既可以用于模型,也可以用于控制器操作的参数。

   

8.4.3 ReadOnly特性

如果需要确保默认的模型绑定器不使用请求中的新值来更新属性,可在属性上添加ReadOnly特性。(并不会在Html代码中生成 ReadOnly 属性)

   

8.4.4 DataType 特性

DataType特性可为运行时提供关于属性的特定用途信息。例如,String类型的属性可应用于很多场合一一可以保存e-mail地址、URL或是密码。 如示例13所示。

示例13

DataType(DataType.Password)

public string Password{ get; set; }

除了 Password 类型外,还有Custom, DateTime, Date, Time, Duration, PhoneNumber, Currency, Text, Html, MultilineText, EmailAddress, Password, Url, ImageUrl, CreditCard,

PostalCode, Upload。

   

8.4.5 DisplayFormat 特性

DisplayForat特性可用来处理属性的各种格式化选项。如示例14所示。

示例14

[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:c}")]

public decimal Price { get; set; }

上面的代码可将模型的 Price 属性值格式化为货币值形式。ApplyFormatInEditMode参数的值默认是false,如果想把 Price 属性格式化为表单输入元素,需要将属性ApplyFormatInEditMode的值设置为true。例如,当把模型中decimal类型的 Price 属性值设置为12.1时,将在视图中看到¥12.10的输出结果。

DisplayFormat还有一个string属性:NullDisplayText,它表示针对空值(Null)对象的显示文本。

   

8.4.6 UIHint特性

UIHint特性给ASP.NET MVC运行时提供了一个模板名称,以备调用模板辅助方法,如(DisplayFor和EditorFor)渲染时输出使用。也可以定义自己的模板辅助方法来重写ASP.NET MVC的默认行为。

   

8.5 自定义模板

Html.EditorFor()和 Html.DisplayFor()的特殊之处在于它支持模板功能。

8.5.1 在Views/Shared目录下创建模板

示例15完成一个日历模板,该模板可以为DateTime类型的模型属性自动应用该模板。

步骤:

  • 在Views/Shared 目录下创建"EditorTemplates"文件夹。
  • 在"EditorTemplates"文件夹下按类型名称命名视图:DateTime.cshtml。
  • 实现模板代码。

示例15

@model DateTime?

@Html.TextBoxFor(m => m, "", new { @class = "Wdate", onfocus = "$(this).datepicker()" })

在视图中,下面代码将直接导致输出一个jQuery日历文本框。因为BornDate属性是DateTime 类型,将自动应用这个模板。(在视图中需要引入相关js文件)

@Html.EditorFor(m=>m.PublishDate)

如果不想让每一个DateTime编辑器都拥有DatePicker小部件,而是让一少部分特定的编辑器拥有。此时,可以把自定义的模板文件名改为"SpecialDateTime.cshtml",这样框架就不会为DateTime模型选择该模板,除非指定该模板名称。如下面代码所示。

@Html.EditorFor(m=>m.PublishDate,"SpecialDateTime")

另外,也可以在PublishDate属性上使用UIHint特性来指定模板名称:

[UIHint("SpecialDateTime")]

public DateTime? PublishDate { get;set;}

   

8.5.2 在Views/Model目录下创建模板

示例16完成将出版社显示成下拉框的模板。

步骤:

  • 在Views/Book 目录下创建"EditorTemplates"文件夹。
  • 在"EditorTemplates"文件夹下按类型名称命名视图:Publisher.cshtml。
  • 实现模板代码。

示例16

@model BookShop.Models.Publisher

   

@Html.DropDownListFor(m=>m.Id,(SelectList)ViewData["publisher"],"---请选择---")

在示例16的代码中,模板视图对应的类型是BookShop.Models.Publisher,在视图中下面的代码将自动将出版社显示为下拉框列表。

@Html.EditorFor(model=>model.Publisher)

   

Html.DisplayFor()使用自定义模板的方式和 Html.EditorFor类似,区别在于将模板文件夹改为"DisplayTemplates"

   

   

   

猜你喜欢

转载自www.cnblogs.com/mrfang/p/10802645.html
今日推荐