ASP.NET Core 中文文档 第四章 MVC(2.2)模型验证【转载】
http://www.cnblogs.com/dotNETCoreSG/p/aspnetcore-4_2_2-validation.html
介绍模型验证
在一个应用程序将数据存储到数据库之前,这个应用程序必须验证数据。数据必须检查潜在的安全隐患,验证类型和大小是正确并且符合你所制定的规则。尽管验证的实现可能会是冗余和繁琐的,却是有必要的。在 MVC 中,验证发生在客户端和服务器端。
幸运地是, .Net 有一些拥有抽象验证的验证 Attribute 。这些 Attribute 包含验证代码,从而减少你必须写的代码量。
验证 Attribute
验证 Attribute 是一种配置模型验证的方法,类似在数据库表中验证字段的概念。它包含了指定数据类型或者必填字段等约束。其它类型的验证包括将强制的业务规则应用到数据验证,比如验一个信用卡号,一个手机号码,或者一个 Email 地址。 验证 Attribute 使这些要求更简单,更容易使用。
下面是一个存储了电影和电视节目信息的应用程序中被注解的 Movie
模型。大部分属性是必填的,几个字符串类型的属性有长度限制。此外,在 Price
属性上通过自定义验证 Attribute 实现了 0 到 $999.99 的数字范围限制。
public class Movie
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; }
[Required]
[ClassicMovie(1960)]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; }
[Required]
[Range(0, 999.99)]
public decimal Price { get; set; }
[Required]
public Genre Genre { get; set; }
public bool Preorder { get; set; }
}
简单地通过阅读模型了解了这个应用程序的数据规则,(这种编码方式)让维护代码变得更简单。以下是几个常用的内置验证 Attribute :
[CreditCard]
: 验证属性是信号卡号格式。[Compare]
: 验证模型中的两个属性匹配。[EmailAddress]
: 验证属性是 Email 格式。[Phone]
: 验证属性是 电话号码 格式。[Range]
: 验证属性在指定的范围内。[RegularExpression]
: 验证数据匹配指定的正则表达式。[Required]
: 使属性成为必填。[StringLength]
: 验证字符串类型属性的最大长度。[Url]
: 验证属性是 URL 格式。
MVC 支持任何为了验证目的而从 ValidationAttribute
继承的 Attribute 。需要有用的验证 Attribute 可以在 System.ComponentModel.DataAnnotations 命名空间下找到。
可能在某些情况下,你需要使用比内置 Attribute 更多的验证功能。在那时,你可以通过创建继承自 ValidationAttribute
的自定义验证 Attribute 或者修改你的模型去实现 IValidatableObject
接口。
模型状态
模型状态表示在 HTML 表单提交值的一系列验证错误。
MVC 将持续验证字段直到错误数达到最大值(默认200)。你可以通过在 Startup.cs
文件下的 ConfigureServices
方法中插入以下代码来配置这个最大值:
services.AddMvc(options => options.MaxModelValidationErrors = 50);
处理模型状态异常
模型验证发生在每个控制器(Controller)的行为(Action)被调用之前,而检查 ModelState.IsValid
和做出适当的反应是行为(Action)方法的职责。在许多情况下,适当的反映是返回某种错误响应,理想情况下详细介绍了模型验证失败的原因。
一些应用程序将选择遵循一个标准的惯例来处理模型验证错误,在这种情况下,过滤器可能是一个适当的方式来实现这种策略。你需要分别用有效和无效的模型状态来测试 Action 的行为。
手动验证
当模型绑定和验证完成后,你也许想重复其中的部分操作。例如,用户可能输入了一个被期望为 integer 类型的字段的文本,或者你需要为模型中的一个属性计算一个值。
你需要手动去执行验证。像这样,调用 TryValidateModel
方法:
TryValidateModel(movie);
自定义验证
验证 Attribute 满足大多数的验证需求。然而你的业务存在一些特殊的验证规则,它们不仅仅是通用的数据验证,如确保字段必填或者符合一个值的范围之类的。对于这些情况,自定义验证 Attribute 是一个不错的解决方案。在 MVC 中创建你自己的自定义验证 Attribute 是非常容易的。只需要继承 ValidationAttribute
并且重写 IsValid
方法。 IsValid
方法接受两个参数,第一个是命名为 value
的 object 对象,第二个参数是一个命名为 validationContext
的 ValidationContext
对象。 Value
指的是你的自定义验证器验证的字段的值。
在下面的示例中,一个业务规则规定,用户可能不会将在1960年之后发布的电影的 Genre
设置为 Classic
。[ClassicMovie]
Attribute 首先检查 Genre
,如果它是 Genre.Classic
,接下来检查电影发布日期是否晚于1960年。如果发布晚于1960年,验证失败。这个 Attribute 接受一个 integer 类型的参数作为验证数据的年份。你可以在这个 Attribute 的构造函数中对这个值进行赋值,如同这里显示的:
public class ClassicMovieAttribute : ValidationAttribute, IClientModelValidator
{
private int _year;
public ClassicMovieAttribute(int Year)
{
_year = Year;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
Movie movie = (Movie)validationContext.ObjectInstance;
if (movie.Genre == Genre.Classic && movie.ReleaseDate.Year > _year)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
上面的 movie
变量代表一个包含了表单提交数据并等待验证的 Movie
的对象。在这个例子中,ClassicMovieAttribute
类的 IsValid
方法按照规定检查了日期和分类( Genre )。当验证成功, IsValid
方法返回一个 ValidationResult.Success
枚举码;当验证失败,返回一个带有错误消息的 ValidationResult
。当用户修改了 Genre
字段并且提交表单, ClassicMovieAttribute
中的 IsValid
方法将验证电影是否是经典( Classic )。如同其他内置的 Attribute 一样,应用 ClassicMovieAttribute
到比如 ReleaseDate
这个属性上来确保验证发生,如果之前例子中的演示代码一样。因为这个例子仅对 Movie
类型有效,一个更好的选择使用下面段落介绍的 IValidatableObject
。
另外,相同的代码可以放在模型里,通过去实现 IValidatableObject
接口中的 Validate
方法。当自定义验证 Attribute 能够很好的验证各个属性时,实现 IValidatableObject
接口可以用来实现类等级(Class-Level)的验证,如下。
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
{
yield return new ValidationResult(
"Classic movies must have a release year earlier than " + _classicYear,
new[] { "ReleaseDate" });
}
}
客户端验证
客户端验证为客户带了极大的便利。它可以节省时间而不用花费一个来回时间等待服务器的验证结果。在业务角度来看,一天中哪怕是几秒乘以数百次,都会增加很多工作时间、开支以及挫败感。直接和即时的验证,使用户能够更有效地工作,得到质量更好的投入和产出。
你必须适当的引用 JavaScript 脚本来进行客户端验证,如下。
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.3.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js"></script>
除了模型属性的类型元数据外,MVC还是用验证 Attribute 通过 JavaScript 验证数据并展示所有错误信息。当你使用 MVC 去渲染使用 Tag Helpers 或者 HTML helpers 的表单数据之时,它将在需要验证的表单元素中添加 HTML 5 data- attributes,如同下面看到的。 MVC 对所有内置验证 Attribute 和自定义验证 Attribute 生成 data-
特性。你可以通过相关的 Tag Helper 在客户端显示验证错误,如同这里展示的:
<div class="form-group">
<label asp-for="ReleaseDate" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
</div>
上面的 Tag Helper 渲染的 HTML 如下。 注意输出的 HTML 中 data-
特性对应 ReleaseDate
属性的验证 Attribute。下面的 data-val-required
特性包含一个用于展示的错误消息,如果用户没有填写 ReleaseDate 字段,错误消息将随着 <span>
元素一起显示。
<form action="/movies/Create" method="post">
<div class="form-horizontal">
<h4>Movie</h4>
<div class="text-danger"></div>
<div class="form-group">
<label class="col-md-2 control-label" for="ReleaseDate">ReleaseDate</label>
<div class="col-md-10">
<input class="form-control" type="datetime"
data-val="true" data-val-required="The ReleaseDate field is required."
id="ReleaseDate" name="ReleaseDate" value="" />
<span class="text-danger field-validation-valid"
data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
</div>
</div>
</div>
</form>
客户端验证防止表单提交直到有效为止。无论提交表单还是显示错误消息,提交按钮都会执行 JavaScript 代码。
MVC 基于 .NET 属性的数据类型决定类型特性值,可以使用 [DataType]
Attribute 来覆盖。基础的 [DataType]
Attribute 并不是真正的服务端认证。浏览器选择它们自己的错误消息并按照它们希望的那样显示这些错误,然而 jQuery Validation Unobtrusive 包可以重写这些消息并且让他们显示方式保持一致。当用户应用 [DataType]
的子类比如 [EmailAddress]
的时候,这种情况最明显。
客户端模型验证器
你也许会为你的自定义 Attribute 创建客户端逻辑,unobtrusive validation 会在客户端将它作为验证的一部分自动执行。第一步
是向下面一样,通过实现 IClientModelValidator
接口来控制那些被添加的 data- 特性:
public void AddValidation(ClientModelValidationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
var year = _year.ToString(CultureInfo.InvariantCulture);
MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
}
Attribute 实现这个接口后可以添加 HTML 特性到生成的字段。检查输出的 HTML 中的 ReleaseDate
元素,和上一个例子差不多,除了通过 IClientModelValidator
接口的 AddValidation
方法定义了一个 data-val-classicmovie
特性。
<input class="form-control" type="datetime"
data-val="true"
data-val-classicmovie="Classic movies must have a release year earlier than 1960"
data-val-classicmovie-year="1960"
data-val-required="The ReleaseDate field is required."
id="ReleaseDate" name="ReleaseDate" value="" />
Unobtrusive validation 使用 data-
特性中的数据来显示错误消息。然而 JQuery 在你添加 JQuery 的 validator
对象之前是不知道规则和消息的。在显示在下面的例子中将一个包含自定义客户端验证代码的命名为 classicmovie
的方法添加到 JQuery 的 validator
对象中。
$(function () {
jQuery.validator.addMethod('classicmovie',
function (value, element, params) {
// Get element value. Classic genre has value '0'.
var genre = $(params[0]).val(),
year = params[1],
date = new Date(value);
if (genre && genre.length > 0 && genre[0] === '0') {
// Since this is a classic movie, invalid if release date is after given year.
return date.getFullYear() <= year;
}
return true;
});
jQuery.validator.unobtrusive.adapters.add('classicmovie',
[ 'element', 'year' ],
function (options) {
var element = $(options.form).find('select#Genre')[0];
options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
options.messages['classicmovie'] = options.message;
});
}(jQuery));
现在 JQuery 拥有执行自定义 JavaScript 验证以及当验证代码返回 false 时用来显示的错误消息的信息了。
远程验证
当你需要在客户端上使用服务器上的数据进行验证的时候,远程验证是一个很棒的功能。比如,你的应用程序也许需要验证一个 Email 或者用户名是否已经被使用,这样做必须查询大量的数据。为了验证一个或几个字段下载大量的数据,消耗了过多的资源。并且可能会暴露敏感信息。另一个办法是使用回传请求来验证字段。
你可以用两个步骤实现远程验证。首先,你需要用 [Remote]
Attribute 注解你的模型。[Remote]
Attribute 接受多个重载可以直接使用客户端 JavaScript 到适当的代码来调用。下面的例子指向 Users
Controller 的 VerifyEmail
Action 。
public class User
{
[Remote(action: "VerifyEmail", controller: "Users")]
public string Email { get; set; }
}
第二步是将验证代码放到 [Remote]
Attribute 中定义的相应 Action 方法中。Action 方法返回一个 JsonResult
,如果需要,客户端可以用来继续或者暂停并显示错误。
[AcceptVerbs("Get", "Post")]
public IActionResult VerifyEmail(string email)
{
if (!_userRepository.VerifyEmail(email))
{
return Json(data: $"Email {email} is already in use.");
}
return Json(data: true);
}
现在当用户输入一个 Email ,View 中的 JavaScript 进行远程调用来检查 Email 是否被占用,如果被占用就显示错误消息。否则,用户可以和往常一样提交表单。
ASP.NET Core 中文文档 第四章 MVC(2.2)模型验证【转载】的更多相关文章
- ASP.NET Core 中文文档 第四章 MVC(3.6.2 )自定义标签辅助类(Tag Helpers)
原文:Authoring Tag Helpers 作者:Rick Anderson 翻译:张海龙(jiechen) 校对:许登洋(Seay) 示例代码查看与下载 从 Tag Helper 讲起 本篇教 ...
- ASP.NET Core 中文文档 第四章 MVC(4.2)控制器操作的路由
原文:Routing to Controller Actions 作者:Ryan Nowak.Rick Anderson 翻译:娄宇(Lyrics) 校对:何镇汐.姚阿勇(Dr.Yao) ASP.NE ...
- ASP.NET Core 中文文档 第四章 MVC(3.6.1 )Tag Helpers 介绍
原文:Introduction to Tag Helpers 作者:Rick Anderson 翻译:刘浩杨 校对:高嵩(Jack) 什么是 Tag Helpers? Tag Helpers 提供了什 ...
- ASP.NET Core 中文文档 第四章 MVC(3.8)视图中的依赖注入
原文:Dependency injection into views 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:孟帅洋(书缘) ASP.NET Core 支持在视图中使用 依赖 ...
- ASP.NET Core 中文文档 第四章 MVC(4.6)Areas(区域)
原文:Areas 作者:Dhananjay Kumar 和 Rick Anderson 翻译:耿晓亮(Blue) 校对:许登洋(Seay) Areas 是 ASP.NET MVC 用来将相关功能组织成 ...
- ASP.NET Core 中文文档 第四章 MVC(4.5)测试控制器逻辑
原文: Testing Controller Logic 作者: Steve Smith 翻译: 姚阿勇(Dr.Yao) 校对: 高嵩(Jack) ASP.NET MVC 应用程序的控制器应当小巧并专 ...
- ASP.NET Core 中文文档 第四章 MVC(4.4)依赖注入和控制器
原文: Dependency Injection and Controllers 作者: Steve Smith 翻译: 刘浩杨 校对: 孟帅洋(书缘) ASP.NET Core MVC 控制器应通过 ...
- ASP.NET Core 中文文档 第四章 MVC(4.1)Controllers, Actions 和 Action Results
原文:Controllers, Actions, and Action Results 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:许登洋(Seay) Action 和 acti ...
- ASP.NET Core 中文文档 第四章 MVC(3.9)视图组件
作者: Rick Anderson 翻译: 娄宇(Lyrics) 校对: 高嵩 章节: 介绍视图组件 创建视图组件 调用视图组件 演练:创建一个简单的视图组件 附加的资源 查看或下载示例代码 介绍视图 ...
- ASP.NET Core 中文文档 第四章 MVC(3.7 )局部视图(partial)
原文:Partial Views 作者:Steve Smith 翻译:张海龙(jiechen).刘怡(AlexLEWIS) 校对:许登洋(Seay).何镇汐.魏美娟(初见) ASP.NET Core ...
随机推荐
- [Java面试一]Spring总结以及在面试中的一些问题.(转发:http://www.cnblogs.com/wang-meng/p/5701982.html)
1.谈谈你对spring IOC和DI的理解,它们有什么区别? IoC Inverse of Control 反转控制的概念,就是将原本在程序中手动创建UserService对象的控制权,交由Spri ...
- IDEA运行后控制台输出乱码
1.点击 2.点击 3.添加:-Dfile.encoding=UTF-8 . 4.点击OK
- 海信电视 LED55K370 升级固件总结【含固件下载地址】
最早电视买回来,感觉垃圾软件太多,root后,删软件不小心删除了桌面,导致没桌面. 用ADB装了点软件,凑合可以用. 后来装了悟空遥控,然后装了沙发桌面,不影响使用了. 最近海信不停推送更新系统,改手 ...
- ACN经典例题1
1.韩信点兵 描述相传韩信才智过人,从不直接清点自己军队的人数,只要让士兵先后以三人一排.五人一排.七人一排地变换队形,而他每次只掠一眼队伍的排尾就知道总人数了.输入3个非负整数a,b,c ,表示每种 ...
- windows中检查端口占用
在cmd中怎么输入netstat -aon|findstr "9080" 返回: UDP 0.0.0.0:8001 *.* 其中的4220为进城PID
- LeetCode:快乐数【202】
LeetCode:快乐数[202] 题目描述 编写一个算法来判断一个数是不是“快乐数”. 一个“快乐数”定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数 ...
- LeetCode:移除K位数字【402】
LeetCode:移除K位数字[402] 题目描述 给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小. 注意: num 的长度小于 10002 且 ≥ k. nu ...
- 奥森图标和CSS特殊字体使用方法
作为第一篇博文,写这个 我快要被气炸,好吧,废话不说了 昨天在项目中发现有很多这些Awesome图标 也在网上找了下Font Awesome下载后这些文件,现在的版本是4.2,Font Awesome ...
- [原创]java WEB学习笔记37:EL表达式(简介,运算符,自动类型转换,保留字,隐含对象)
1.EL 简介 1)EL 全名为 Expression Language,它原本是 JSTL 1.0 为方便存取数据所自定义的语言 2)语法:EL 语法很简单,它最大的特点就是使用上很方便:${s ...
- 【leetcode刷题笔记】Decode Ways
A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' - ...