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("密码")] [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)---数据注解和验证的更多相关文章
- asp.net mvc常用的数据注解和验证以及entity framework数据映射
终于有时间整理一下asp.net mvc 和 entity framework 方面的素材了. 闲话少说,步入正题: 下面是model层的管理员信息表,也是大伙比较常用到的,看看下面的代码大伙应该不会 ...
- MVC中的数据注解和验证
数据注解和验证 用户输入验证在客户端浏览器中需要执行验证逻辑. 在客户端也需要执行. 注解是一种通用机制, 可以用来向框架注入元数据, 同时, 框架不只驱动元数据的验证, 还可以在生成显示和编辑模型的 ...
- 【ASP.NET MVC系列】浅谈数据注解和验证
[ASP.NET MVC系列]浅谈数据注解和验证 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google C ...
- MVC学习手册之数据注解与验证
MVC学习手册之数据注解与验证 新建一个MVC5的WEB应用程序,VS2013会自动生成一段代码,以下是Account控制器下Register.cshtml 页面的代码: @model WebAppl ...
- MVC5 数据注解和验证
①利用数据注解进行验证 ②创建自定义的验证逻辑 ③模型元数据注解的用法 ①先创建数据源 1,创建我们的Model Order 2,创建控制器带EF 选择模型为Order 当你运行的时候会报错,需要代 ...
- EF CodeFirst数据注解特性详解
数据注解特性是.NET特性,可以在EF或者EF Core中,应用于实体类上或者属性上,以重写默认的约定规则. 在EF 6和EF Core中,数据注解特性包含在System.ComponentModel ...
- Asp.net MVC 数据注解与验证
数据注解特性定义在名称空间System.ComponentModel.DataAnnotations中(有些特性定义在其他名称空间中),它们提供了服务器端验证的功能,当在模型的属性上使用这些特性时,框 ...
- 数据注解和验证 – ASP.NET MVC 4 系列
不仅在客户端浏览器中需要执行验证逻辑,在服务器端也需要执行.客户端验证能即时给出一个错误反馈(阻止请求发送至服务器),是时下 Web 应用程序所期望的特性.服务器端验证,主要是因为来自网 ...
- 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 ...
随机推荐
- 【Servlet】The servlets named [ByteServlet] and [content.ByteServlet] are both mapped to the url-pattern [ByteServlet] which is not permitted
创建时间:6.30 The servlets named [ByteServlet] and [content.ByteServlet] are both mapped to the url-patt ...
- Go如何使用数据库、缓存
database/sql 接口 Go官方没有提供数据库驱动,而是为开发数据库驱动定义了一些标准接口database/sql,开发者可以根据定义的接口来开发相应的数据库驱动,这样做有一个好处,只要是按照 ...
- Mybatis-plus中不列出全部字段
//不列出全部字段 @Test public void test10() { QueryWrapper<User> wrapper = new QueryWrapper<>() ...
- 前端(4)BOM与DOM
前端(4)BOM与DOM I/O前戏 到目前为止,我们已经学过了JavaScript的一些简单的语法.但是这些简单的语法,并没有和浏览器有任何交互. 也就是我们还不能制作一些我们经常看到的网页的一些交 ...
- nginx 其他配置语法
1.nginx 缓冲区配置 2.跳转重定向 3.头信息 4.超时 location / { proxy_pass http://127.0.0.1:8080;(代理跳转url) proxy_redir ...
- oracle在windows(含客户端工具pl/sql安装)下安装
安装Oracle服务器端 系统默认创建的数据库名称为orcl,可自行修改. 全局数据库名(用来唯一标示Oracle数据库,每个数据库至少由一个Oracle系统标识符(SID)引用),orcl,后面称为 ...
- tomcat 多实例部署
tomcat 配置多个实例 参考文档 聊聊 Tomcat 的单机多实例 https://www.cnblogs.com/mafly/p/tomcat.html 极客学院 Tomcat 8 权威指南 h ...
- (9-4 )deepsort在ubuntu1604下配置
Deep Sort with PyTorch YOLO https://github.com/ZQPei/deep_sort_pytorch 查看python版本 python3 --version ...
- c# HttpClient的HTTP/2支持
HTTP/2 是 HTTP 协议的主要修订版.HTTP/2 的一些显著功能是支持标头压缩和通过同一连接完全多路复用流.虽然 HTTP/2 保留了 HTTP 的语义(HTTP 标头.方法等),但它在数据 ...
- 网络协议 9 - TCP协议(下)
上次了解了 TCP 建立连接与断开连接的过程,我们发现,TCP 会通过各种“套路”来保证传输数据的安全.除此之外,我们还大概了解了 TCP 包头格式所对应解决的五个问题:顺序问题.丢包问题.连接维护. ...