对于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("密码")]

[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"

APS.NET MVC + EF (08)---数据注解和验证的更多相关文章

  1. asp.net mvc常用的数据注解和验证以及entity framework数据映射

    终于有时间整理一下asp.net mvc 和 entity framework 方面的素材了. 闲话少说,步入正题: 下面是model层的管理员信息表,也是大伙比较常用到的,看看下面的代码大伙应该不会 ...

  2. MVC中的数据注解和验证

    数据注解和验证 用户输入验证在客户端浏览器中需要执行验证逻辑. 在客户端也需要执行. 注解是一种通用机制, 可以用来向框架注入元数据, 同时, 框架不只驱动元数据的验证, 还可以在生成显示和编辑模型的 ...

  3. 【ASP.NET MVC系列】浅谈数据注解和验证

    [ASP.NET MVC系列]浅谈数据注解和验证   [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google C ...

  4. MVC学习手册之数据注解与验证

    MVC学习手册之数据注解与验证 新建一个MVC5的WEB应用程序,VS2013会自动生成一段代码,以下是Account控制器下Register.cshtml 页面的代码: @model WebAppl ...

  5. MVC5 数据注解和验证

    ①利用数据注解进行验证 ②创建自定义的验证逻辑 ③模型元数据注解的用法 ①先创建数据源 1,创建我们的Model  Order 2,创建控制器带EF 选择模型为Order 当你运行的时候会报错,需要代 ...

  6. EF CodeFirst数据注解特性详解

    数据注解特性是.NET特性,可以在EF或者EF Core中,应用于实体类上或者属性上,以重写默认的约定规则. 在EF 6和EF Core中,数据注解特性包含在System.ComponentModel ...

  7. Asp.net MVC 数据注解与验证

    数据注解特性定义在名称空间System.ComponentModel.DataAnnotations中(有些特性定义在其他名称空间中),它们提供了服务器端验证的功能,当在模型的属性上使用这些特性时,框 ...

  8. 数据注解和验证 – ASP.NET MVC 4 系列

           不仅在客户端浏览器中需要执行验证逻辑,在服务器端也需要执行.客户端验证能即时给出一个错误反馈(阻止请求发送至服务器),是时下 Web 应用程序所期望的特性.服务器端验证,主要是因为来自网 ...

  9. APS.NET MVC + EF (02)---ADO.NET Entity FrameWork

    2.1 Entity Framework简介 Ado.net Entity Framework 是Microsoft推出的ORM框架. 2.1.1 什么是ORM 对象关系映射(Object Relat ...

随机推荐

  1. linux上查看swf文件.靠谱

    在linux上查看swf文件,本来想用gnash 来看,可是有的电脑上看的时候只有声音,没有图像 所以用网页来查看,推荐谷歌 我们在和flash文件的同目录下新建一个文件名为:index.html 注 ...

  2. Game Engine Architecture 13

    [Game Engine Architecture 13] 1.describe an arbitrary signal x[n] as a linear combination of unit im ...

  3. redis常用操作(测试必备)

    连接redis redis的安装及基础配置,参考:https://www.cnblogs.com/UncleYong/p/9882843.html redis中,数据是key-value方式存储,ke ...

  4. hadoop java.io.EOFException: Unexpected end of input stream

    执行hadoop 报错 java.io.EOFException: Unexpected end of input stream at org.apache.hadoop.io.compress.De ...

  5. redhat quay 安装试用

    最近redhat 开源了quay 容器镜像管理平台,参考官方文档跑的时候需要订阅,各种不好使,然后就自己基于源码构建了 一个镜像(使用官方的dockerfile,构建出来的太大了1.9G 以及push ...

  6. 请指出document load和document ready的区别

    document load文档的所有内容都加载完成 document ready文档的DOM加载完成

  7. Problem C. 欧皇 ————2019.10.12

    题目: 再次感激土蛋 #include <bits/stdc++.h> using namespace std; typedef long long ll; ; ll C[][]; voi ...

  8. ES6基础-ES6 class

    作者 | Jeskson 来源 | 达达前端小酒馆 ES - Class 类和面向对象: 面向对象,即万物皆对象,面向对象是我们做开发一种的方式,开发思维,面向对象的思维中万物皆对象,以人作为例子,它 ...

  9. STRING Cytoscape 网络互作图

    网络图(Network)看似复杂,其实构成非常简单,网络图是一种图解模型,形状如同网络,故称网络图,由节点(node)和连线(edge)两个因素组成的.其中 node 又分为 source node( ...

  10. SpringMVC 字节流实现播放多媒体

    1.前言 在项目中,我们会遇到在线预览,播放MP3.图片.MP4等.用户上传文件后,将路径存储在数据库中,我们可动态读取数据库的数据,然后通过返回文件路径的字符串,在src中发送请求.当然这需要带参数 ...