一. 模型綁定
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