一. 模型绑定
ASP.NET Core MVC 中的模型绑定,是将 HTTP 请求中的数据映射到 action方法参数。 这些参数可能是简单类型的参数,如字符串、整数或浮点数,也可能是复杂类型的参数。 当 MVC 收到 HTTP 请求时,它会将此请求路由定位到控制器的指定 action方法。默认路由模板为 {controller=Home}/{action=Index}/{id?}
1
2
3
4
5
|
//例如:请求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 中,验证发生在客户端和服务器上。
2.1 验证属性
验证属性是模型验证的一种方法, 概念上类似于对数据库表中字段的验证, 验证属性在属性级别指定,下面是一个示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class Movie
{
public int Id { get ; set ; }
[Required]
[StringLength(100)]
public string Title { get ; set ; }
[ClassicMovie(1960)]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get ; set ; }
[Required]
[StringLength(1000)]
public string Description { get ; set ; }
[Range(0, 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 引用自定义验证程序要验证的字段中的实际值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
/// <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- 标记帮助程序代码如下:
1
2
3
4
5
|
< 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> 元素中显示该错误消息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
< 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 添加的表单设置客户端验证。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
$.get({
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)按钮时验证:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
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页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@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代码所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
$.get({
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- 属性。实现接口如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
/// <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代码如下所示:
1
2
3
4
5
6
|
< 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
对象。脚本如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//添加验证方法
$.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,提示错误信息,如下所示:
参考文献
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:http://www.cnblogs.com/MrHSR/p/10563157.html