asp.net core系列之模型綁定和驗(yàn)證方法
一. 模型綁定
ASP.NET Core MVC 中的模型綁定,是將 HTTP 請(qǐng)求中的數(shù)據(jù)映射到 action方法參數(shù)。 這些參數(shù)可能是簡(jiǎn)單類型的參數(shù),如字符串、整數(shù)或浮點(diǎn)數(shù),也可能是復(fù)雜類型的參數(shù)。 當(dāng) MVC 收到 HTTP 請(qǐng)求時(shí),它會(huì)將此請(qǐng)求路由定位到控制器的指定 action方法。默認(rèn)路由模板為 {controller=Home}/{action=Index}/{id?}
//例如:請(qǐng)求URL http://contoso.com/movies/edit/2 //映射到movies/edit/2 public IActionResult Edit(int? id)
上面Url請(qǐng)求對(duì)應(yīng)movies控制器下的Edit方法,該方法接受名為 id
的可選參數(shù)。MVC會(huì)將Edit中的id參數(shù)綁定到路由值中 具有相同名稱 的值。 URL 路由中的字符串不區(qū)分大小寫。
上面示例綁定的參數(shù)是簡(jiǎn)單類型,如果參數(shù)是一個(gè)類,例如 Movie
類型,該類包含簡(jiǎn)單和復(fù)雜類型作為屬性,MVC的模型綁定仍然可以很好地處理它。它使用反射和遞歸來遍歷尋找匹配的復(fù)雜類型的屬性(如:Collection、Dictionary)。
如果模型綁定失敗,MVC 不會(huì)引發(fā)錯(cuò)誤,參數(shù)值會(huì)是null。 如果HTTP 請(qǐng)求中的數(shù)據(jù)是用戶輸入的值,在action中應(yīng)使用 ModelState.IsValid
屬性檢查,不需要手動(dòng)去檢查。
注意:若要實(shí)現(xiàn)模型綁定,該類必須具有要綁定的公共默認(rèn)構(gòu)造函數(shù)和公共可寫屬性。 發(fā)生模型綁定時(shí),在使用公共默認(rèn)構(gòu)造函數(shù)對(duì)類進(jìn)行實(shí)例化后才可設(shè)置屬性。
模型綁定完成后,將發(fā)生模型驗(yàn)證。 對(duì)于絕大多數(shù)開發(fā)方案,默認(rèn)模型綁定效果極佳。還可以擴(kuò)展,如果有特殊需求,則可自定義內(nèi)置行為包括:模型綁定特性、全局自定義模型綁定和驗(yàn)證、綁定請(qǐng)求正文中的帶格式數(shù)據(jù)(JSON、XML 和許多其他格式)、還有高級(jí)篇中自定義模型綁定。這里不在說明,請(qǐng)查看文檔。
二.模型驗(yàn)證
在將數(shù)據(jù)存儲(chǔ)到數(shù)據(jù)庫之前,應(yīng)用程序必須先驗(yàn)證數(shù)據(jù)。在 MVC 中,驗(yàn)證發(fā)生在客戶端和服務(wù)器上。
2.1 驗(yàn)證屬性
驗(yàn)證屬性是模型驗(yàn)證的一種方法, 概念上類似于對(duì)數(shù)據(jù)庫表中字段的驗(yàn)證, 驗(yàn)證屬性在屬性級(jí)別指定,下面是一個(gè)示例:
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; } }
常用的內(nèi)置驗(yàn)證屬性包括: [ CreditCard] 信用卡格式、 [Compare]匹配兩個(gè)屬性、 [ EmailAddress] 郵件格式、 [ Phone] 電話格式、 [Range] 給定范圍內(nèi)、 [RegularExpression] 正則表達(dá)式、 [Required]必須屬性值、 [StringLength] 最大長(zhǎng)度、 [Url] URL格式,還可以包括自定義驗(yàn)證屬性(例如 ClassicMovie )。 所有的內(nèi)置驗(yàn)證屬性 參考官網(wǎng)
2.2 自定義驗(yàn)證
上面的驗(yàn)證屬性適用于大多數(shù)驗(yàn)證需求。 但是,某些驗(yàn)證規(guī)則特定于你的業(yè)務(wù)。在 MVC 中創(chuàng)建自定義驗(yàn)證屬性很簡(jiǎn)單。只需從 ValidationAttribute
繼承并重寫 IsValid
方法。 IsValid
方法采用兩個(gè)參數(shù),第一個(gè)是名為 value 的對(duì)象,第二個(gè)是名為 validationContext 的 ValidationContext
對(duì)象。 Value 引用自定義驗(yàn)證程序要驗(yàn)證的字段中的實(shí)際值。
/// <summary> /// 自定義驗(yàn)證 /// </summary> public class ClassicMovieAttribute : ValidationAttribute { private int _year; /// <summary> /// 驗(yàn)證規(guī)則值 /// </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 年以后發(fā)行的電影的流派設(shè)置為 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}."; } }
運(yùn)行程序,ReleaseDate是1989年,Genre是Classic,點(diǎn)擊Save,驗(yàn)證是在服務(wù)端進(jìn)行,顯示錯(cuò)誤消息,沒有經(jīng)過前端js驗(yàn)證,如下所示:
2.3 客戶端js驗(yàn)證介紹
jQuery 非介入式驗(yàn)證腳本是一個(gè)自定義微軟前端庫,建立在流行的 jQuery Validate 插件??蛻舳蓑?yàn)證原理是: MVC 的標(biāo)記幫助程序和 HTML 幫助程序則能夠使用模型屬性中的驗(yàn)證特性和類型元數(shù)據(jù),呈現(xiàn)需要驗(yàn)證的表單元素中的 HTML 5 data- 屬性。MVC 為內(nèi)置模型屬性和自定義模型屬性生成 data- 屬性。然后,jQuery 非介入式驗(yàn)證分析 data-
屬性并將邏輯傳遞給 jQuery Validate,從而將服務(wù)器端驗(yàn)證邏輯有效地“復(fù)制”到客戶端。 可以使用相關(guān)標(biāo)記幫助程序在客戶端上顯示驗(yàn)證錯(cuò)誤。
下面示例表單中,asp- 標(biāo)記幫助程序代碼如下:
<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>
標(biāo)記幫助程序?qū)⑸梢韵聅ource html。請(qǐng)注意,HTML 輸出中的 data-
屬性與 ReleaseDate
屬性的驗(yàn)證特性相對(duì)應(yīng)。下面的 data-val-required
屬性包含在用戶未填寫發(fā)行日期字段時(shí)將顯示的錯(cuò)誤消息。jQuery 非介入式驗(yàn)證將此值傳遞給 jQuery Validate required() 方法,該方法隨后在隨附的 <span> 元素中顯示該錯(cuò)誤消息。
<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 動(dòng)態(tài)表單添加驗(yàn)證
在創(chuàng)建動(dòng)態(tài)表單后,需要立即對(duì)其進(jìn)行分析。 例如,下面的代碼展示如何對(duì)通過 AJAX 添加的表單設(shè)置客戶端驗(yàn)證。
$.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); //驗(yàn)證第一個(gè)表單 var forms = container.getElementsByTagName("form"); var newForm = forms[forms.length - 1]; //分析表單的 data- 屬性 $.validator.unobtrusive.parse(newForm); } })
$.validator.unobtrusive.parse()
方法分析該選擇器內(nèi)表單的 data-
屬性。當(dāng)用戶填寫表單中的屬性值提交時(shí), 這些屬性的值傳遞到 jQuery Validate 插件中,以便表單展示所需的客戶端驗(yàn)證規(guī)則。
下面用一個(gè)簡(jiǎn)單示例來說明:
(1) 創(chuàng)建dynamic-form-validate.js文件,模擬動(dòng)態(tài)生成表單,以及點(diǎn)擊(#idbtn)按鈕時(shí)驗(yàn)證:
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="動(dòng)態(tài)加載表單" 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> }
運(yùn)行程序,點(diǎn)擊"動(dòng)態(tài)加載表單" 調(diào)用js將html表單添加到form-container元素容器中,點(diǎn)擊save提示該字段不能為空,效果如下所示:
2.5 動(dòng)態(tài)控件添加驗(yàn)證
當(dāng)向表單動(dòng)態(tài)添加控件(比如: <input/>
和 <select/>
)時(shí),需要更新表單上的驗(yàn)證規(guī)則。做法是應(yīng)當(dāng)先刪除現(xiàn)有的驗(yàn)證數(shù)據(jù),然后重新分析整個(gè)表單,如下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) { //向表單動(dòng)態(tài)添加Input控件 var form = document.getElementById("my-form"); form.insertAdjacentHTML("beforeend", newInputHTML); //移除現(xiàn)有的驗(yàn)證 $(form).removeData("validator") // Added by jQuery Validate .removeData("unobtrusiveValidation"); // Added by jQuery Unobtrusive Validation //重新分析整個(gè)表單 $.validator.unobtrusive.parse(form); } })
2.6 IClientModelValidator
在上面2.2自定義驗(yàn)證中,繼承了ValidationAttribute進(jìn)行服務(wù)端驗(yàn)證,還可以結(jié)合實(shí)現(xiàn)IClientModelValidator接口實(shí)現(xiàn)客戶端驗(yàn)證,該接口用來控制要添加哪些 data- 屬性。實(shí)現(xiàn)接口如下所示:
/// <summary> /// 自定義驗(yàn)證 /// </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 年以后發(fā)行的電影的流派設(shè)置為 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">
在上面雖然實(shí)現(xiàn)了IClientModelValidator接口,但jQuery不了解規(guī)則或消息,還需要自定義 classicmovie
客戶端驗(yàn)證方法,添加到j(luò)Query validator
對(duì)象。腳本如下所示:
//添加驗(yàn)證方法 $.validator.addMethod('classicmovie',function (value, element, params) { //value ,是當(dāng)前驗(yàn)證的元素的值。 //element 元素本身。 //params 是傳入的參數(shù)(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; }); //注冊(cè)一個(gè)適配器,參數(shù)1是適配器名稱,參數(shù)2是驗(yàn)證規(guī)則的名稱 $.validator.unobtrusive.adapters.add('classicmovie',['year'],function (options) { //適配器規(guī)則綁定到j(luò)query validation上面 options.rules['classicmovie'] = [parseInt(options.params['year'])]; options.messages['classicmovie'] = options.message; });
運(yùn)行程序,ReleaseDate是1989年,Genre是Classic,點(diǎn)擊Save,客戶端驗(yàn)證返回false,提示錯(cuò)誤信息,如下所示:
參考文獻(xiàn)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- ASP.NET?Core?模型驗(yàn)證消息的本地化新姿勢(shì)詳解
- ASP.NET?Core?6.0?基于模型驗(yàn)證的數(shù)據(jù)驗(yàn)證功能
- ASP.NET?Core中Cookie驗(yàn)證身份用法詳解
- asp.net core3.1cookie和jwt混合認(rèn)證授權(quán)實(shí)現(xiàn)多種身份驗(yàn)證方案
- asp.net core配合vue實(shí)現(xiàn)后端驗(yàn)證碼邏輯
- [Asp.Net Core]用Blazor Server Side實(shí)現(xiàn)圖片驗(yàn)證碼
- ASP.NET Core實(shí)現(xiàn)自定義WebApi模型驗(yàn)證詳解
- ASP.NET Core WebApi中使用FluentValidation驗(yàn)證數(shù)據(jù)模型的方法
相關(guān)文章
運(yùn)行page頁面時(shí)的事件執(zhí)行順序及頁面的回發(fā)與否深度了解
page頁面時(shí)的事件執(zhí)行順序的了解對(duì)于一些.net開發(fā)者起到者尤關(guān)重要的作用;頁面的回發(fā)與否會(huì)涉及到某些事件執(zhí)行與不執(zhí)行,在本文中會(huì)詳細(xì)介紹,感興趣的朋友可以了解下2013-01-01asp.net讀取excel中的數(shù)據(jù)并綁定在gridview
這篇文章主要介紹了asp.net讀取excel中的數(shù)據(jù)并綁定在gridview上的方法,需要的朋友可以參考下2014-02-02Repeater綁定dictionary數(shù)據(jù)源代碼及報(bào)錯(cuò)解決
為大家講解下Repeater綁定dictionary數(shù)據(jù)源以及報(bào)錯(cuò)處理的方法,感興趣的朋友可以參考下哈,希望對(duì)你有所幫助2013-04-04C#實(shí)現(xiàn)pdf導(dǎo)出 .Net導(dǎo)出pdf文件
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)pdf導(dǎo)出 .Net導(dǎo)出pdf文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09Asp.net程序優(yōu)化js、css實(shí)現(xiàn)合并與壓縮的方法
這篇文章主要介紹了Asp.net程序優(yōu)化js、css實(shí)現(xiàn)合并與壓縮的方法,使用第三方組件實(shí)現(xiàn)訪問時(shí)將js與css文件壓縮并緩存在客戶端的效果,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-12-12