Asp.Net MVC4 系列--進階篇之Model(2)
在上一章介紹了MVC中model binding,本章將介紹一下MVCmodel validation
和前幾章一樣,從一個例子開始
準備工作
準備model
public classAppointment
{
public string ClientName { get; set; }
[DataType(DataType.Date)]
public DateTime Date { get; set; }
public bool TermsAccepted { get; set; }
}
程式碼說明:一個預約model,包含了客戶姓名,預約日期,以及是否accept合約
準備controller
public class AppintmentController : Controller
{
public ViewResult MakeBooking()
{
return View("NewAppintment",new Appointment { Date = DateTime.Now });
}
[HttpPost]
public ViewResult MakeBooking(Appointment appt)
{
//statements to store new Appointment in a
//repository would go here in a real project
return View("Completed", appt);
}
}
程式碼說明:很經典的”Get-Post-action” 結構,同名方法兩個,Get版本用於服務第一次來到頁面的請求,post版本用於處理提交頁面的請求。
Get版本:render預約頁面,獲取預約資訊
Post版本:獲得預約的資訊,透傳(為了演示)給CompletedView表示預約成功
準備View
a.(NewAppintment.cshtml)
@model MVCModelValidation.Models.Appointment
@{
ViewBag.Title = "Make A Booking";
}
<h4>Book an Appointment</h4>
@using (Html.BeginForm()) {
<p>Your name: @Html.EditorFor(m =>m.ClientName)</p>
<p>Appointment Date:@Html.EditorFor(m => m.Date)</p>
<p>@Html.EditorFor(m =>m.TermsAccepted) I accept the terms & conditions</p>
<input type="submit" value="Make Booking" />
}
程式碼說明:預約View
b.Completed
@model MVCModelValidation.Models.Appointment
@{
ViewBag.Title = "Completed";
}
<h4>Your appointment is confirmed</h4>
<p>Your name is:<b>@Html.DisplayFor(m => m.ClientName)</b></p>
<p>The date of your appointment is:<b>@Html.DisplayFor(m => m.Date)</b></p>
程式碼說明:預約成功 View
執行
Make booking
可以看到,程式碼有幾個需要驗證的地方:
1.客戶姓名必填
2.預約日期必填
3.必須accept 合約
顯式驗證
由於我們的驗證是針對使用者提交表單的scenario,因此修改post版本的action就好了:
帶validation的action:
[HttpPost]
public ViewResult MakeBooking(Appointment appt)
{
if(string.IsNullOrEmpty(appt.ClientName))
{
ModelState.AddModelError("ClientName", "Please enter yourname");
}
if(ModelState.IsValidField("Date") && DateTime.Now >appt.Date)
{
ModelState.AddModelError("Date", "Please enter a date inthe future");
}
if (!appt.TermsAccepted)
{
ModelState.AddModelError("TermsAccepted", "You mustaccept the terms");
}
if (ModelState.IsValid)
{
// statements to store newAppointment in a
// repository would go here ina real project
return View("Completed", appt);
}
// statements to store newAppointment in a
// repository would go here in areal project
return View("NewAppintment", appt);
}
程式碼說明:由於到達這一步我們model已經被完成binding了(具體參見上一章asp.netMVC4系列—進階篇之Model(1)),因此我們拿出值新增我們的驗證邏輯,把相應錯誤資訊放在ModelError裡面讓MVCFramework拿走,最後判斷Model.IsValid,如果沒有任何錯誤,這個屬性為True。那麼這些資訊如何在View中顯示出來呢?
修改NewAppointment.cshtml
@model MVCModelValidation.Models.Appointment
@{
ViewBag.Title = "Make ABooking";
}
<h4>Book an Appointment</h4>
@using (Html.BeginForm()) {
@Html.ValidationSummary()
<p>Your name:@Html.EditorFor(m => m.ClientName)</p>
<p>Appointment Date:@Html.EditorFor(m => m.Date)</p>
<p>@Html.EditorFor(m =>m.TermsAccepted) I accept the terms & conditions</p>
<input type="submit" value="Make Booking" />
}
我們只新增了一行:
@Html.ValidationSummary()
這樣,就可以display我們action中新增的錯誤資訊了。
執行
不填名字也不勾選terms& conditions,make booking
可以看到,我們手動的驗證邏輯工作了,並且錯誤資訊也顯示了。
如果需要改顯示出來的錯誤資訊的css,可以修改:
~/Content/Site.css中的.validation-summary-errors
Html.ValidationSummary的其他過載
Html.ValidationSummary(bool) |
是否只顯示model級別的error(後面會演示) |
Html.ValidationSummary(string) |
在所有錯誤資訊顯示前,顯示一條總的錯誤資訊 |
Html.ValidationSummary(bool,string) |
前兩種情況的組合 |
Model 級別的錯誤
依舊修改剛才post版本的MakeBookingAction
新增一個驗證邏輯:
if (ModelState.IsValidField("ClientName") &&ModelState.IsValidField("Date") && appt.Date.DayOfWeek ==DayOfWeek.Monday)
{
ModelState.AddModelError("", "Sorry , Can notappointments on Mondays");
}
在View中使用另一個Html.ValidationSummary的過載(只顯示model error):
@Html.ValidationSummary(true)
填所有資訊(日期選一個monday),嘗試makebooking
可以看到,modellevel的驗證生效,並且只顯示了modellevel的錯誤資訊。
顯示Property級別的資訊
修改NewAppointment.cshtml(Form部分):
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<p>@Html.ValidationMessageFor(m=> m.ClientName)</p>
<p>Your name:@Html.EditorFor(m => m.ClientName)</p>
<p>@Html.ValidationMessageFor(m=> m.Date)</p>
<p>Appointment Date:@Html.EditorFor(m => m.Date)</p>
<p>@Html.ValidationMessageFor(m=> m.TermsAccepted)</p>
<p>@Html.EditorFor(m =>m.TermsAccepted) I accept the terms & conditions</p>
<input type="submit" value="MakeBooking" />
}
我們對每個需要顯示錯誤資訊的欄位新增了Html.ValidationMessageFor,這樣就可以了。
執行
為了驗證View的邏輯,把所有資訊清空
Make booking
可以看到我們為每個欄位新增的ValidationForMessage方法生效了,並顯示了property級別的錯誤資訊
把驗證放在metadata完成
MVC Framework把驗證作為了一個concern,也就是AOP的思想。MVCFramework 給我們提供了足夠的Validation的Attribute讓我們使用,我們可以在model裡面根據需要新增不同的Attribute就可以完成我們的驗證,避免在Controller中出現類似的重複的驗證邏輯,而controller只需要專注自己的職責(操作domainmodel,選擇View)
重構程式碼
1.Model
public class Appointment
{
[Required(ErrorMessage = "Please provide a name")]
public string ClientName { get; set; }
[Required(ErrorMessage = "Please enter a date")]
[DataType(DataType.Date)]
public DateTime Date { get; set; }
[Range(typeof(bool), "true", "true", ErrorMessage ="You must accept the terms")]
public bool TermsAccepted { get; set; }
}
2.把Controller的驗證邏輯拿掉
[HttpPost]
public ViewResult MakeBooking(Appointment appt)
{
if (ModelState.IsValid)
{
// statements to store newAppointment in a
// repository would go here ina real project
return View("Completed", appt);
}
// statements to store new Appointment in a
// repository would go here in a real project
return View("NewAppintment", appt);
}
3.View不用改
4.執行
可以看到,重構後(meta data版本)的程式碼工作了。
Validation Attribute
Compare |
傳入型別,指定一個值和要對比的值 |
Range |
傳入一個範圍 |
RegularExpression |
指定正規表示式 |
Required |
非空 |
StringLength |
限制字串長度 |
Customize Attribute
1.繼承Validation Attribute
public class MustBeTrueAttribute : ValidationAttribute {
public override bool IsValid(object value) {
return value is bool && (bool)value;
}
}
2.使用
public class Appointment
{
[Required(ErrorMessage ="Please provide a name")]
public string ClientName {get; set; }
[Required(ErrorMessage ="Please enter a date")]
[DataType(DataType.Date)]
public DateTime Date { get;set; }
[MustBeTrue(ErrorMessage ="You must accept the terms")]
public bool TermsAccepted {get; set; }
}
3.執行
Make booking
繼承built-in的attribute
1.實現
public class FutureDateAttribute : RequiredAttribute
{
public override bool IsValid(object value)
{
return base.IsValid(value) && ((DateTime)value) > DateTime.Now;
}
}
2.使用
public class Appointment
{
[Required(ErrorMessage = "Please provide a name")]
public string ClientName { get; set; }
[FutureDate(ErrorMessage = "Please Enter a Future Date")]
[DataType(DataType.Date)]
public DateTime Date { get; set; }
[MustBeTrue(ErrorMessage = "You must accept the terms")]
public bool TermsAccepted { get; set; }
}
3.執行
Make booking
可以看到futuredate 驗證生效,更新一下日期提交
可以看到預約成功。
Model level 驗證
1.實現
public class NoMondayBookingAttribute : ValidationAttribute
{
public NoMondayBookingAttribute()
{
ErrorMessage ="Can not book appointments on Mondays temporary";
}
public override bool IsValid(object value)
{
var app = value asAppointment;
if (app == null )
{
// we don't have amodel of the right type to validate, or we don't have
// the values forthe ClientName and Date properties we require
return true;
}
return app.Date.DayOfWeek != DayOfWeek.Monday;
}
}
程式碼說明:驗證是否為appointment型別,如果是,取出日期,如果為週一返回false,其他情況返回true
2.使用
[NoMondayBooking]
public class Appointment
{
[Required(ErrorMessage ="Please provide a name")]
public string ClientName {get; set; }
[FutureDate(ErrorMessage ="Please Enter a Future Date")]
[DataType(DataType.Date)]
public DateTime Date { get;set; }
[MustBeTrue(ErrorMessage ="You must accept the terms")]
public bool TermsAccepted {get; set; }
}
3.執行
輸入所有資訊,日期選擇下週一,符合所有property驗證的要求
Make booking
可以看到,由於預約為下週一,因此modellevel的驗證沒過,而我們View的實現沒改,我們現在是ValidateSummary(true),只顯示model level的錯誤資訊。
我們改到下週二重新提交
可以看到預約成功,通過了model level驗證。
IValidatableObject
如果希望把validation的職責加在model上,可以實現IValidatableObject介面,下一版model程式碼:
public class Appointment : IValidatableObject
{
public string ClientName {get; set; }
[DataType(DataType.Date)]
public DateTime Date { get;set; }
public bool TermsAccepted {get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var errors = new List<ValidationResult>();
if(string.IsNullOrEmpty(ClientName))
{
errors.Add(new ValidationResult("Please enter your name"));
}
if (DateTime.Now >Date)
{
errors.Add(new ValidationResult("Please enter a date in the future"));
}
if (errors.Count == 0&& Date.DayOfWeek == DayOfWeek.Monday)
{
errors.Add(
new ValidationResult("cannot book appointments on Mondays"));
}
if (!TermsAccepted)
{
errors.Add(new ValidationResult("You must accept the terms"));
}
return errors;
}
}
這樣就實現了和前面示例驗證邏輯一模一樣的驗證,可以仍推薦使用AOP的思路(Attribute)進行validate。
客戶端驗證
MVC framework提供unobstrusive javascript使得我們可以方便的進行客戶端驗證
1.Appsetting新增
<appSettings>
<add key="ClientValidationEnabled"value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
2.需要的js:
/Scripts/ jquery-1.7.1.min.js
/Scripts/ jquery.validate.min.js
/Scripts/ jquery.validate.unobtrusive.min.js
通常,建立basic的mvc sln預設的jsbundle已新增(BundleConfig.cs):
bundles.Add(newScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
在View中Render:
@Scripts.Render("~/bundles/jqueryval")
分析開啟客戶端驗證後生成了一個htmlinput
<input class="text-box single-line" data-val="true"
data-val-length="The field ClientName must be a string with aminimum length of 3 and
a maximum length of 10." data-val-length-max="10" data-val-length-min="3"
data-val-required="The ClientName field is required."id="ClientName"
name="ClientName" type="text" value=""/>
MVC Framework會找到data-val屬性,如果為true說明需要客戶端驗證,然後解析data-val開頭的驗證型別,例如data-val-length,data-val-length-min,
data-val-length-max . data-val-required 等等。
MVC framework的客戶端驗證部分,是基於jquery的。
相關文章
- Asp.Net MVC4 系列--進階篇之Model(1)ASP.NETMVC
- Asp.Net MVC4 系列--進階篇之Helper(2)ASP.NETMVC
- Asp.Net MVC4 系列--進階篇之Controller(2)ASP.NETMVCController
- Asp.Net MVC4系列--進階篇之AJAXASP.NETMVC
- Asp.Net MVC4 系列--進階篇之ViewASP.NETMVCView
- Asp.Net MVC4 系列--進階篇之路由 (2)ASP.NETMVC路由
- Asp.Net MVC4系列--進階篇之Helper(1)ASP.NETMVC
- Asp.Net MVC4 系列-- 進階篇之路由(1)ASP.NETMVC路由
- Asp.Net MVC 系列--進階篇之FilterASP.NETMVCFilter
- Asp.Net MVC系列--進階篇之controller(1)ASP.NETMVCController
- Asp.Net MVC4 系列--基礎篇(1)ASP.NETMVC
- Asp.Net MVC4系列---基礎篇(5)ASP.NETMVC
- Asp.Net MVC4系列---基礎篇(4)ASP.NETMVC
- 【webpack 系列】進階篇Web
- React進階篇2React
- Dagger 2 系列(五) -- 進階篇:@Scope 和 @Singleton
- ASP.NET MVC系列:ModelASP.NETMVC
- Asp.Net MVC 系列--基礎篇(2)ASP.NETMVC
- vue2進階篇:vue-router之命名路由Vue路由
- 測開之函式進階· 第2篇《純函式》函式
- Dagger2進階篇(二)
- .NET進階系列之四:深入DataTable
- 帶你深度解鎖Webpack系列(進階篇)Web
- 正規表示式系列之中級進階篇
- SpringCloud-OAuth2(三):進階篇SpringGCCloudOAuth
- Java多執行緒之進階篇Java執行緒
- Membership三步曲之進階篇
- Java進階篇 設計模式之十四 ----- 總結篇Java設計模式
- to debug asp.net mvc4ASP.NETMVC
- ASP.NET Core MVC 之模型(Model)ASP.NETMVC模型
- 你所不知道的ASP.NET Core進階系列(三)ASP.NET
- create table進階學習系列(十一)之cluster
- Linux ACL 許可權之進階篇Linux
- asp.net mvc原始碼分析-Action篇 DefaultModelBinderASP.NETMVC原始碼
- ASP.NET進階:認清控制元件 之 Button (轉)ASP.NET控制元件
- 高階前端進階系列 - webview前端WebView
- TypeScript極速完全進階指南-2中級篇TypeScript
- [一天一個進階系列] - MyBatis基礎篇MyBatis