一. 模型绑定

  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应用 模型绑定和验证的更多相关文章

  1. Asp.Net Core 入门(十)—— 模型绑定和验证

    模型绑定时将Http请求中的数据映射到控制器操作方法上对应的参数,操作方法中的参数可以是简单类型,如整形,字符串等,也可以是复杂类型,如Product,Order等. Asp.Net Core MVC ...

  2. asp.net core系列 40 Web 应用MVC 介绍与详细示例

    一. MVC介绍 MVC架构模式有助于实现关注点分离.视图和控制器均依赖于模型. 但是,模型既不依赖于视图,也不依赖于控制器. 这是分离的一个关键优势. 这种分离允许模型独立于可视化展示进行构建和测试 ...

  3. asp.net core系列 39 Web 应用Razor 介绍与详细示例

    一. Razor介绍 在使用ASP.NET Core Web开发时, ASP.NET Core MVC 提供了一个新特性Razor. 这样开发Web包括了MVC框架和Razor框架.对于Razor来说 ...

  4. asp.net core 系列 18 web服务器实现

    一. ASP.NET Core Module 在介绍ASP.NET Core Web实现之前,先来了解下ASP.NET Core Module.该模块是插入 IIS 管道的本机 IIS 模块(本机是指 ...

  5. asp.net core 系列 16 Web主机 IWebHostBuilder

    一.概述 在asp.net core中,Host主机负责应用程序启动和生存期管理.host主机包括Web 主机(IWebHostBuilder)和通用主机(IHostBuilder).Web 主机是适 ...

  6. asp.net core系列 41 Web 应用 MVC视图

    一.MVC视图 在Web开发的MVC和Razor中,都有使用视图,在Razor中称为"页"..cshtml视图是嵌入了Razor标记的HTML模板. Razor 标记使用C#代码, ...

  7. asp.net core系列 48 Identity 身份模型自定义

    一.概述 ASP.NET Core Identity提供了一个框架,用于管理和存储在 ASP.NET Core 应用中的用户帐户. Identity添加到项目时单个用户帐户选择作为身份验证机制. 默认 ...

  8. asp.net core系列 44 Web应用 布局

    一.概述 MVC的视图与Razor页面经常共享视觉和程序元素,通过使用布局来完成,布局还可减少重复代码.本章演示了以下内容的操作方法:(1)使用通用布局,(2)自定义布局,(3) 共享指令,(4)在呈 ...

  9. asp.net core系列 43 Web应用 Session分布式存储(in memory与Redis)

    一.概述 HTTP 是无状态的协议. 默认情况下,HTTP 请求是不保留用户值或应用状态的独立消息. 本文介绍了几种保留请求间用户数据和应用状态的方法.下面以表格形式列出这些存储方式,本篇专讲Sess ...

随机推荐

  1. 利用Python进行数据分析——pandas入门

    利用Python进行数据分析--pandas入门 基于NumPy建立的 from pandas importSeries,DataFrame,import pandas as pd 一.两种数据结构 ...

  2. c#学习笔记 day_one

    C#学习笔记 day one Chapter 1 c#概述 1.1 c#概述 C#是微软设计的,简洁的,类型安全的,面向对象的语言.它以c/c++作为基础.它的开发环境是visual studio,最 ...

  3. jQuery的学习笔记

    JQuery学习笔记 Chapter one初识jQuery 1.2测试jQuery 在jQuery库中,$是jQuery的别名,如:$()相当于jQuery() 注意:在使用JQuery进行开发的时 ...

  4. 自己用 Netty 实现一个简单的 RPC

    目录: 需求 设计 实现 创建 maven 项目,导入 Netty 4.1.16. 项目目录结构 设计接口 提供者相关实现 消费者相关实现 测试结果 总结 源码地址:github 地址 前言 众所周知 ...

  5. python3 分布式爬虫

    背景 部门(东方IC.图虫)业务驱动,需要搜集大量图片资源,做数据分析,以及正版图片维权.前期主要用node做爬虫(业务比较简单,对node比较熟悉).随着业务需求的变化,大规模爬虫遇到各种问题.py ...

  6. Spring4托管Hibernate5并利用HibernateTemplate进行数据库操作

    时隔半年,再次发布配置类的相关Blog,因为左手受伤原因先做一个简述. 首先利用idea创建一个Spring+SpringMVC+Hibernate项目,注意的是因为我们要完全放弃Hibernate以 ...

  7. cocos2d-x 开发常见问题:

    更改Andriod项目的显示横屏还是竖屏问题: 打开项目中的proj.android/AndroidManifest.xml文件中,更改screenOrientation配置信息: screenOri ...

  8. HTML 返回顶部

    每次看淘宝,看微信,都回有回到顶部的小logo,小图标,或者双击返回顶部.所以就学习了如何返回顶部的操作,一开始是联想html中的链接描点,在开头出设置个标签,下面点击另外一个标志回去.有三种觉得比较 ...

  9. 基于ASP.NET MVC 微信网页登录授权(scope为snsapi_base) 流程 上 获取OPENID

    流程图 我们需要判断是否存在OPENID  首先我们得先定义一个全局的OPENID 类似于普通账号密码登录系统的 当前登录用户ID  因为我是MVC 框架  我这里定义一个控制器基类 BaseCont ...

  10. 20.QT-Qpixmap实现图片鼠标缩放,鼠标拖动示例(详解)

    通过 QPainter 绘画实现,以本地图片985*740为例 如下图所示: 效果如下所示: 实现原理 主要通过以下函数实现: , ); //平铺显示pixmap //x y w h :表示绘画区域 ...