asp.net core系列 45 Web应用 模型绑定和验证
一. 模型绑定
ASP.NET Core MVC 中的模型绑定,是将 HTTP 请求中的数据映射到action方法参数。 这些参数可能是简单类型的参数,如字符串、整数或浮点数,也可能是复杂类型的参数。 当 MVC 收到 HTTP 请求时,它会将此请求路由定位到控制器的指定action方法。默认路由模板为 {controller=Home}/{action=Index}/{id?}
//例如:请求URL
http://contoso.com/movies/edit/2 //映射到movies/edit/2
public IActionResult Edit(int? id)
上面Url请求对应movies控制器下的Edit方法,该方法接受名为 id 的可选参数。MVC会将Edit中的id参数绑定到路由值中具有相同名称的值。 URL 路由中的字符串不区分大小写。
上面示例绑定的参数是简单类型,如果参数是一个类,例如Movie类型,该类包含简单和复杂类型作为属性,MVC的模型绑定仍然可以很好地处理它。它使用反射和递归来遍历寻找匹配的复杂类型的属性(如:Collection、Dictionary)。
如果模型绑定失败,MVC 不会引发错误,参数值会是null。 如果HTTP 请求中的数据是用户输入的值,在action中应使用 ModelState.IsValid 属性检查,不需要手动去检查。
注意:若要实现模型绑定,该类必须具有要绑定的公共默认构造函数和公共可写属性。 发生模型绑定时,在使用公共默认构造函数对类进行实例化后才可设置属性。
模型绑定完成后,将发生模型验证。 对于绝大多数开发方案,默认模型绑定效果极佳。还可以扩展,如果有特殊需求,则可自定义内置行为包括:模型绑定特性、全局自定义模型绑定和验证、绑定请求正文中的带格式数据(JSON、XML 和许多其他格式)、还有高级篇中自定义模型绑定。这里不在说明,请查看文档。
二.模型验证
在将数据存储到数据库之前,应用程序必须先验证数据。在 MVC 中,验证发生在客户端和服务器上。 模型验证使用自带的 System.ComponentModel.DataAnnotations类库来实现。 (不在本章范围:对于在类库中进行验证的实体可以通过开源插件FluentValidation 来实现 https://github.com/JeremySkinner/FluentValidation/wiki)。
2.1 验证属性
验证属性是模型验证的一种方法, 概念上类似于对数据库表中字段的验证, 验证属性在属性级别指定,下面是一个示例:
public class Movie
{
public int Id { get; set; } [Required]
[StringLength()]
public string Title { get; set; } [ClassicMovie()]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; } [Required]
[StringLength()]
public string Description { get; set; } [Range(, 999.99)]
public decimal Price { get; set; } [Required]
public Genre Genre { get; set; } public bool Preorder { get; set; }
}
常用的内置验证属性包括:[CreditCard] 信用卡格式、 [Compare]匹配两个属性、[EmailAddress] 邮件格式、[Phone] 电话格式、[Range] 给定范围内、[RegularExpression] 正则表达式、[Required]必须属性值、[StringLength] 最大长度、[Url] URL格式,还可以包括自定义验证属性(例如ClassicMovie)。所有的内置验证属性参考官网
2.2 自定义验证
上面的验证属性适用于大多数验证需求。 但是,某些验证规则特定于你的业务。在 MVC 中创建自定义验证属性很简单。只需从 ValidationAttribute 继承并重写 IsValid方法。 IsValid 方法采用两个参数,第一个是名为 value 的对象,第二个是名为 validationContext 的 ValidationContext 对象。 Value 引用自定义验证程序要验证的字段中的实际值。
/// <summary>
/// 自定义验证
/// </summary>
public class ClassicMovieAttribute : ValidationAttribute
{
private int _year; /// <summary>
/// 验证规则值
/// </summary>
/// <param name="year"></param>
public ClassicMovieAttribute(int year)
{
_year = year;
} protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
Movie movie = (Movie)validationContext.ObjectInstance; //用户不能将 1960 年以后发行的电影的流派设置为 Classic
if (movie.Genre == "Classic" && movie.ReleaseDate.Year > _year)
{
return new ValidationResult(GetErrorMessage());
} return ValidationResult.Success;
} private string GetErrorMessage()
{
return $"Classic movies must have a release year earlier than {_year}.";
}
}
运行程序,ReleaseDate是1989年,Genre是Classic,点击Save,验证是在服务端进行,显示错误消息,没有经过前端js验证,如下所示:

2.3 客户端js验证介绍
jQuery 非介入式验证脚本是一个自定义微软前端库,建立在流行的 jQuery Validate 插件。客户端验证原理是: MVC 的标记帮助程序和 HTML 帮助程序则能够使用模型属性中的验证特性和类型元数据,呈现需要验证的表单元素中的 HTML 5 data- 属性。MVC 为内置模型属性和自定义模型属性生成 data- 属性。然后,jQuery 非介入式验证分析 data- 属性并将逻辑传递给 jQuery Validate,从而将服务器端验证逻辑有效地“复制”到客户端。 可以使用相关标记帮助程序在客户端上显示验证错误。
下面示例表单中,asp- 标记帮助程序代码如下:
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
标记帮助程序将生成以下source html。请注意,HTML 输出中的 data-属性与 ReleaseDate 属性的验证特性相对应。下面的 data-val-required 属性包含在用户未填写发行日期字段时将显示的错误消息。jQuery 非介入式验证将此值传递给 jQuery Validate required() 方法,该方法随后在随附的 <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>
2.4 动态表单添加验证
在创建动态表单后,需要立即对其进行分析。 例如,下面的代码展示如何对通过 AJAX 添加的表单设置客户端验证。
$.get({
url: "https://url/that/returns/a/form",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add form. " + errorThrown);
},
success: function(newFormHTML) {
//添加表单newFormHTML
var container = document.getElementById("form-container");
container.insertAdjacentHTML("beforeend", newFormHTML);
//验证第一个表单
var forms = container.getElementsByTagName("form");
var newForm = forms[forms.length - 1];
//分析表单的 data- 属性
$.validator.unobtrusive.parse(newForm);
}
})
$.validator.unobtrusive.parse() 方法分析该选择器内表单的 data- 属性。当用户填写表单中的属性值提交时, 这些属性的值传递到 jQuery Validate 插件中,以便表单展示所需的客户端验证规则。
下面用一个简单示例来说明:
(1) 创建dynamic-form-validate.js文件,模拟动态生成表单,以及点击(#idbtn)按钮时验证:
var newFormHTML = "<form action=\"create\" method=\"post\">";
newFormHTML += "<div class=\"form-group\">";
newFormHTML += "<label asp-for=\"Title\" class=\"control- label\"></label>";
newFormHTML += "<input type=\"text\" data-val=\"true\" data-val-required=\"The Title field is required.\" id = \"Title\" name= \"Title\">";
newFormHTML += "<span class=\"text- danger field- validation - valid\" data-valmsg-for=\"Title\" data-valmsg-replace=\"true\"></span>";
newFormHTML += "</div>";
newFormHTML += "<div class=\"form-group\" >";
newFormHTML += "<input type=\"submit\" value=\"Save\" class=\"btn btn-primary\" />";
newFormHTML += "</div >";
newFormHTML += "</form>"; $("#idbtn").click(function () {
var container = document.getElementById("form-container");
container.insertAdjacentHTML("beforeend", newFormHTML); var forms = container.getElementsByTagName("form");
var newForm = forms[forms.length - 1];
//分析表单的 data- 属性
$.validator.unobtrusive.parse(newForm);
});
(2) 新建create页
@model StudyMVCDemo.Models.Movie
@{
ViewData["Title"] = "Create";
}
<h1>Create</h1> <div class="row">
<input value="动态加载表单" type="button" id="idbtn" />
<div id="form-container" class="col-md-4"> </div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script src="~/js/dynamic-form-validate.js"></script>
}
运行程序,点击"动态加载表单" 调用js将html表单添加到form-container元素容器中,点击save提示该字段不能为空,效果如下所示:

2.5 动态控件添加验证
当向表单动态添加控件(比如: <input/> 和 <select/>)时,需要更新表单上的验证规则。做法是应当先删除现有的验证数据,然后重新分析整个表单,如下js代码所示:
$.get({
url: "https://url/that/returns/a/control",
dataType: "html",
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add control. " + errorThrown);
},
success: function(newInputHTML) {
//向表单动态添加Input控件
var form = document.getElementById("my-form");
form.insertAdjacentHTML("beforeend", newInputHTML);
//移除现有的验证
$(form).removeData("validator") // Added by jQuery Validate
.removeData("unobtrusiveValidation"); // Added by jQuery Unobtrusive Validation
//重新分析整个表单
$.validator.unobtrusive.parse(form);
}
})
2.6 IClientModelValidator
在上面2.2自定义验证中,继承了ValidationAttribute进行服务端验证,还可以结合实现IClientModelValidator接口实现客户端验证,该接口用来控制要添加哪些 data- 属性。实现接口如下所示:
/// <summary>
/// 自定义验证
/// </summary>
public class ClassicMovieAttribute : ValidationAttribute,IClientModelValidator
{
private int _year; /// <summary>
/// 年份参考值
/// </summary>
/// <param name="year"></param>
public ClassicMovieAttribute(int year)
{
_year = year;
} protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
Movie movie = (Movie)validationContext.ObjectInstance; //用户不能将 1960 年以后发行的电影的流派设置为 Classic
if (movie.Genre == "Classic" && movie.ReleaseDate.Year > _year)
{
return new ValidationResult(GetErrorMessage());
} return ValidationResult.Success;
} private string GetErrorMessage()
{
return $"Classic movies must have a release year earlier than {_year}.";
} 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);
} private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
} attributes.Add(key, value);
return true;
}
}
生成的源html代码如下所示:
<input class="form-control" type="date"
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="1989-02-12">
在上面虽然实现了IClientModelValidator接口,但jQuery不了解规则或消息,还需要自定义classicmovie客户端验证方法,添加到jQuery validator对象。脚本如下所示:
//添加验证方法
$.validator.addMethod('classicmovie',function (value, element, params) {
//value ,是当前验证的元素的值。
//element 元素本身。
//params 是传入的参数(options.rules)
var genre = $("#form1").find("#Genre").val(),
year = params[0],
date = new Date(value);
if (genre.length > 0 && genre === 'Classic') {
// Since this is a classic movie, invalid if release date is after given year.
return date.getFullYear() <= year;
}
return true;
}); //注册一个适配器,参数1是适配器名称,参数2是验证规则的名称
$.validator.unobtrusive.adapters.add('classicmovie',['year'],function (options) {
//适配器规则绑定到jquery validation上面
options.rules['classicmovie'] = [parseInt(options.params['year'])];
options.messages['classicmovie'] = options.message;
});
运行程序,ReleaseDate是1989年,Genre是Classic,点击Save,客户端验证返回false,提示错误信息,如下所示:

参考文献
asp.net core系列 45 Web应用 模型绑定和验证的更多相关文章
- Asp.Net Core 入门(十)—— 模型绑定和验证
模型绑定时将Http请求中的数据映射到控制器操作方法上对应的参数,操作方法中的参数可以是简单类型,如整形,字符串等,也可以是复杂类型,如Product,Order等. Asp.Net Core MVC ...
- asp.net core系列 40 Web 应用MVC 介绍与详细示例
一. MVC介绍 MVC架构模式有助于实现关注点分离.视图和控制器均依赖于模型. 但是,模型既不依赖于视图,也不依赖于控制器. 这是分离的一个关键优势. 这种分离允许模型独立于可视化展示进行构建和测试 ...
- asp.net core系列 39 Web 应用Razor 介绍与详细示例
一. Razor介绍 在使用ASP.NET Core Web开发时, ASP.NET Core MVC 提供了一个新特性Razor. 这样开发Web包括了MVC框架和Razor框架.对于Razor来说 ...
- asp.net core 系列 18 web服务器实现
一. ASP.NET Core Module 在介绍ASP.NET Core Web实现之前,先来了解下ASP.NET Core Module.该模块是插入 IIS 管道的本机 IIS 模块(本机是指 ...
- asp.net core 系列 16 Web主机 IWebHostBuilder
一.概述 在asp.net core中,Host主机负责应用程序启动和生存期管理.host主机包括Web 主机(IWebHostBuilder)和通用主机(IHostBuilder).Web 主机是适 ...
- asp.net core系列 41 Web 应用 MVC视图
一.MVC视图 在Web开发的MVC和Razor中,都有使用视图,在Razor中称为"页"..cshtml视图是嵌入了Razor标记的HTML模板. Razor 标记使用C#代码, ...
- asp.net core系列 48 Identity 身份模型自定义
一.概述 ASP.NET Core Identity提供了一个框架,用于管理和存储在 ASP.NET Core 应用中的用户帐户. Identity添加到项目时单个用户帐户选择作为身份验证机制. 默认 ...
- asp.net core系列 44 Web应用 布局
一.概述 MVC的视图与Razor页面经常共享视觉和程序元素,通过使用布局来完成,布局还可减少重复代码.本章演示了以下内容的操作方法:(1)使用通用布局,(2)自定义布局,(3) 共享指令,(4)在呈 ...
- asp.net core系列 43 Web应用 Session分布式存储(in memory与Redis)
一.概述 HTTP 是无状态的协议. 默认情况下,HTTP 请求是不保留用户值或应用状态的独立消息. 本文介绍了几种保留请求间用户数据和应用状态的方法.下面以表格形式列出这些存储方式,本篇专讲Sess ...
随机推荐
- Linnux入门之简介
一.Linux简介 Minix(教授实验) -> Linux(大三学生Linus)企鹅作为吉祥物 linux主要分为内核版本和发行版本 linux 内核版本 :官网下载:https://www. ...
- hi-nginx-javascript vs node.js
hi-nginx-1.4.9已经支持javascript,这意味着把javascript应用于后端开发,将不再只有nodejs这唯一的途径和方法.由于java本身对javascript的极好支持,使得 ...
- 我的Python之旅第二天
一 .字符串操作 1单引号('').双引号("").三引号(""" """)的区别. 如果字符串中不包含单引号.双引号, ...
- (十分钟视频教程)nodejs基础实战教程3:react服务端渲染入门篇
视频截图如下: (具体视频见文末) 前言: 这是小猫的第三篇node教程,本篇内容是由公众号粉丝票选得出的,相信大家对这篇教程是抱有较大希望的,这篇教程由小猫和一位多年的好朋友合作完成(笔名:谷雨,博 ...
- .NET开发微信小程序-Template模块开发
1.添加一个文件目录,里面放模板信息 例:我在根目录添加一个文件夹:template 然后在这个文件夹下面添加相应的页面.比如我添加一个promodel.wxml文件.主要是放商品相关的模块信息(注: ...
- [CVPR2017] Weakly Supervised Cascaded Convolutional Networks论文笔记
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px "Helvetica Neue"; color: #042eee } p. ...
- java定时任务调度-Timer(1)
一.定义 有且仅有一个后台线程对多个业务线程进行定时定频率的调度 二. Timer ----> Timer Task (中有run();方法) 通过 new Timer().schedul ...
- 使用EHCache需要注意的几个问题(转)
最近做一个小项目,需要对一批数据进行缓存,且要求持久化到磁盘.使用ehcache非常简单和直观,一般来说只需要配置ehcache.xml文件,接着直接使用@Cacheable, @Cacheput, ...
- linux设备驱动之字符设备驱动模型(2)
在上一篇中我们已经了解了字符设备驱动的原理,也了解了应用层调用内核函数的机制,但是我们每次操作设备,都必须首先通过mknod命令创建一个设备文件名,比如说我们要打开u盘,硬盘等这些设备,难道我们还要自 ...
- 小黄鸡机器人和小I机器人的调用
<?php //---------------------------------聊天小机器人类---------------------------------------------- ...