APS.NET MVC + EF (08)---數據註解和驗證

對於Web開發人員來講,用戶輸入驗證一直是一個挑戰。不只在客戶端瀏覽器中須要執行驗證邏輯,在服務器端也須要執行。若是以爲驗證是使人望而生畏的繁雜雜事,ASP.NET MVC框架提供了數據註解的方式幫助咱們處理這些雜事。html

   

8.1 驗證註解的使用

數據註解特性定義在名稱空間System.ComponentModel.DataAnnotations中,它們提供了服務器端驗證的功能,當在模型的屬性上使用這些特性時,框架也支持客戶端驗證。在名稱空間DataAnnotations中,有4個特性能夠用來應對通常的驗證場合。下面從Required特性開始對它們逐一介紹。web

8.1.1 Required 驗證特性

這個特性表必須的、不能爲空的。用於不爲空校驗。正則表達式

示例1 瀏覽器

[Required(ErrorMessage="姓名不得爲空")] 安全

public string Name { get; set; } //姓名服務器

   

8.1.2 StringLength 驗證特性

這個特性能夠驗證字符串長度,通常用來驗證屬性的最大長度,也支持同時設置最小長度。如示例2所示app

示例2 框架

[DisplayName("密碼")] 編輯器

[StringLength(20,ErrorMessage = "{0}不能超過{1}個字符")] ide

public string Password { get; set; }

   

[DisplayName("密碼")]

[StringLength(20,MinimumLength=6, ErrorMessage = "{0}長度必須在{2}和{1}之間")]

public string Password { get; set; }

  

使用ErrorMessage屬性,通常都會在字符串中使用佔位符,上述代碼中,{0}對應屬性的名稱,{1}對應maximum數據,{2}對應minimum數據。

   

8.1.3 Range驗證特性

Range 驗證特性能夠驗證數字(整數和浮點數)、時間等類型的範圍,對應的構造函數以下所示。

public RangeAttribute(int minimum, int maximum);

public RangeAttribute(double minimum, double maximum);

public RangeAttribute(Type type, string minimum, string maximum);

這三個構造函數主要用來驗證時間等特殊類型。如示例3所示。

示例3

[DisplayName("年齡")]

[Range(18,35,ErrorMessage ="{0}必須在{1}和{2}之間")]

public int Age { get; set; }

[DisplayName("生日")]

[Range(typeof(DateTime),"2001-01-01","2019-12-31",ErrorMessage = "{0}必須在{1}和{2}之間"]

public DateTime? BornDate { get; set; }

上述代碼中,{0}對應屬性的名稱,{1}對應minimum數據,{2}對應maximum數據。

   

8.1.4 Compare 驗證特性

使用Compare驗證特性通常用來驗證兩個屬性的值是否一致。如示例4所示。

示例4

public string Passowrd { get; set; }

[Compare("Password",ErrorMessage ="兩次輸入的密碼必須一致")]

public string PasswordConfig { get; set; }

   

8.1.5 RegularExpression驗證特性

RegularExpression驗證特性的關鍵是正則表達式的編寫,如示例5所示。

示例5

[RegularExpression(@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$", ErrorMessage = "{0}格式不正確")]

public string Email { get; set; }

   

8.1.6 Remote驗證特性

同其它幾個驗證特性不一樣,Remote特性的命名空間是System.Web.Mvc。Remote特性利用服務器端的回調函數執行客戶端的驗證邏輯。如示例6所示。

示例6

//控制器代碼

public ActionResult CheckTelephone(string telephone)

{

if (telephone=="13636595489")

{

return Json("手機號"+telephone+ "已經存在", JsonRequestBehavior.AllowGet);

}

return Json(true, JsonRequestBehavior.AllowGet);

}

//實體類代碼

[System.Web.Mvc.Remote("CheckTelephone", "Default", ErrorMessage ="手機號碼已經存在")]

public string Telephone { get; set; }

   

8.2 自動生成客戶端驗證

8.2.1 使用視圖模板建立視圖

ASP.NET MVC 框架支持根據模型自動生成客戶端驗證代碼或腳本,但必須知足如下條件:

  • 必需要用必須使用HtmlHelper擴展方法輸出HTML表單。
  • 必需要用強類型頁面。
  • 必需要引用Jquery庫、Jquery.Validate、Jquery.Validate.unobtrusive這三個文件。
  • web.config文件中這兩個值必須爲True。默認爲True

<appSettings>

<add key="ClientValidationEnabled" value="true" />

<add key="UnobtrusiveJavaScriptEnabled" value="true" />

</appSettings>

示例7

@using (Html.BeginForm())

{

<div class="form-horizontal">

<div class="form-group">

@Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })

<div class="col-md-10">

@Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })

@Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })

</div>

</div>

</div>

}

在示例7中,Html.EditorFor()方法和Html.TextBoxFor()方法相似,用來輸出input標籤(後面內容介紹區別)。的View視圖會生成以下代碼。

<form action="/Home/Login" method="post">

<div class="form-horizontal">

<div class="form-group">

<label class="control-label col-md-2" for="Name">姓名</label>

<div class="col-md-10">

<input class="form-control text-box single-line" data-val="true" data-val-required="姓名不得爲空" id="Name" name="Name" type="text" value="" />

<span class="field-validation-valid text-danger" data-valmsg-for="Name" data-valmsg-replace="true"></span>

</div>

</div>

</div>

</form>

示例7中,Html.ValidationMessageFor()和Html.ValidationMessage()擴展方法用來生成HTML的驗證消息。由HTML輔助方法默認生成的HTML都附加了符合HTML5標準的data-*屬性,這些屬性是根據模型自動生成的,引用的客戶端腳本結合這些屬性就構成了完整的客戶端驗證功能。

   

ASP.NET MV 還提供了 一些列視圖模板,來簡化示例8的複雜工做。在添加視圖時,在"添加視圖"對話框中的模板選項中選擇不一樣的模板。如圖8-1所示。

 

圖8-1 選擇視圖模板

系統提供了Create、Delete、Details、Edit 和 List 模板,選擇這些模板,系統會自動生成帶有驗證的、如示例8中所示的視圖代碼。在實際開發中,咱們只須要作適當的修改便可。

   

8.2.2 ModelState 和服務器端驗證

在模型中定義驗證規則後,ASP.NET MVC 在將數據映射到模型時,會自動應用模型類上的驗證規則。在驗證的過程當中,它會自動把驗證錯誤信息添加到ModelState數據字典,這時只須要讀取其IsVaild 屬性,就能夠判斷是否經過。

另外也可使用 ModeState 的 AddModelError()方法添加自定義的錯誤信息,用法如示例8所示。

   

示例8

public ActionResult RegisterUser(User user)

{

if (ModelState.IsValid)

{

UserManager manager = new UserManager();

if (!manager.Register(user))

{

ModelState.AddModelError("doubleUser", "用戶名已使用,請從新輸入!");

return View("Register2", user);

}

else//註冊成功

{

return Redirect("~/");

}

}

return View("Register2", user);

}

   

當服務器端驗證生效後,視圖中使用 Html.ValidationMessage()來輸出驗證信息,除此以外還有Html.ValidationSummary()用來顯示驗證信息的彙總信息。

   

8.3 自定義驗證規則

除了 ASP.NET MVC 中提供驗證規則外,咱們還能夠自定義驗證規則。例如,咱們要求用戶名不容許是"admin"、"sa"等特殊字符串。

要實現自定義驗證規則,只要繼承 ValidationAttribute類,並重寫IsValid方法便可。如示例9所示。

示例9

public class UserNameAttribute:ValidationAttribute

{

protected override ValidationResult IsValid(object value, ValidationContext validationContext)

{

string[] keywords = { "admin", "sa" };

if (!keywords.Contains(value))

{

return ValidationResult.Success;

}

else

{

return new ValidationResult("不容許使用關鍵字!");

}

}

}

IsValid方法中的第一個參數 value 是要驗證的對象的值,ValidationContext參數,提供了不少可在IsValid返回發內部使用的信息,如模型類型、模型對象實例、用來驗證屬性的人性化顯示名稱以及其餘有用信息。

上面代碼中的問題在於硬編碼的錯誤提示消息那行代碼。使用數據註解的開發人員但願可使用ValidationAttribute的ErrorMessage屬性來自定義錯誤提示消息。同時還要與其餘驗證特性同樣,提供一個默認的錯誤提示消息(開發人員沒有提供自定義的錯誤提示消息時使用)而且還要利用驗證的屬性名稱生成錯誤提示消息。對示例9的完善如示例10所示。

示例10

public class UserNameAttribute:ValidationAttribute

{

public UserNameAttribute()

:base("{0}不容許使用關鍵字!")

{

}

protected override ValidationResult IsValid(object value, ValidationContext validationContext)

{

string[] keywords = { "admin", "sa" };

if (!keywords.Contains(value))

{

return ValidationResult.Success;

}

else

{

var errorMessage = FormatErrorMessage(validationContext.DisplayName);

return new ValidationResult(errorMessage);

}

}

}

前面的代碼作了兩處改動:

  • 首先,向基類的構造函數傳遞了一個默認的錯誤提示消息。若是正在面向國際開發應用程序的話,就應該從一個資源文件中提取這個默認的錯誤提示消息。
  • 注意,默認的錯誤提示消息中包含了一個參數佔位符({0})。這個佔位符之因此存在,是由於第二處改動,即調用繼承的FormatErrorMessage方法會自動使用顯示的屬性名稱來格式化這個字符串。

       

FormatErrorMessage能夠確保咱們使用合適的錯誤提示消息字符串。這條代碼語句須要傳遞name屬性的值,這個值能夠經過validationContext參數的DisplayName屬性得到。構造完驗證邏輯後,就能夠將其應用到任何模型屬性上。

[UserName]

public string Name { get; set; }

   

[UserName(ErrorMessage=" {0}使用了關鍵字!")]

public string Name{get;set;}

   

   

8.4 顯示和編輯註解

8.4.1 DisplayName 和 Display 特性

DisplayName用來在視圖中經過 Html.LabelFor()方法顯示屬性的名稱。如:

[DisplayName("姓名")]

public string Name { get; set; }

一樣是爲屬性定義說明,還有另外一種註解:Dispaly,它的做用並不只僅是指定顯示的內容,甚至可以指定顯示的順序。

[Display(Name="姓名",Order=15000)]

public string Name { get; set; }

order的默認參數是10000, Display在用於屬性的顯示名稱上,具備更高的優先級。

   

8.4.2 HiddenInput 和 ScaffoldColumn 特性

咱們的模型中常常須要定義一個名稱中包含有Id的做爲key的屬性,可是咱們在顯示的時候又不想將這些屬性顯示出來,那該怎麼辦呢?就是對顯示隱藏,咱們可使用HiddenInput。如示例11所示。

示例11

[HiddenInput]

public int Id{ get; set; }

   

[HiddenInput(DisplayValue = false)]

public int UserId { get; set; }

使用了HiddenInput,默認狀況下屬性會以只讀形式顯示出來,像是上面的Name,要想徹底隱藏,就得將DisplayValue設置爲false。

隱藏屬性在HTML上的顯示,咱們還可使用ScaffoldColumn特性。使用ScaffoldColumn並非爲屬性設置type = "hidden",它是直接將該屬性從基架中刪除。如:

[ScaffoldColumn(false)]

public int UserId{ get; set; }

即便從基架中將該屬性刪除,模型綁定器仍然會試圖爲該屬性賦值,這樣就爲典型的攻擊"重複提交"提供了機會。要想防止這種攻擊,咱們能夠利用Bind特性。

Bind特性能夠選擇模型綁定器要綁定的值。像是這樣:

示例12

[Bind(Include = "Name, Email")]

public class User

{

public int UserId{ get; set;}

public string Name{ get; set;}

public string Email{ get; set;}

}

這樣模型綁定器就只綁定Name和Email屬性。 固然,咱們也能夠選擇不綁定的屬性:

[Bind(Exclude ="UserId")]

public class User

{

public int UserId{ get; set;}

public string Name{ get; set;}

public string Email{ get; set;}

}

這樣,上面所講的"重複提交"攻擊就沒法發揮做用了。可是必須注意,使用"Include"的白名單比起使用"Exclude"的黑名單更加安全,由於咱們永遠也不知道黑客會用怎樣的方式來攻擊咱們。

Bind既能夠用於模型,也能夠用於控制器操做的參數。

   

8.4.3 ReadOnly特性

若是須要確保默認的模型綁定器不使用請求中的新值來更新屬性,可在屬性上添加ReadOnly特性。(並不會在Html代碼中生成 ReadOnly 屬性)

   

8.4.4 DataType 特性

DataType特性可爲運行時提供關於屬性的特定用途信息。例如,String類型的屬性可應用於不少場合一一能夠保存e-mail地址、URL或是密碼。 如示例13所示。

示例13

DataType(DataType.Password)

public string Password{ get; set; }

除了 Password 類型外,還有Custom, DateTime, Date, Time, Duration, PhoneNumber, Currency, Text, Html, MultilineText, EmailAddress, Password, Url, ImageUrl, CreditCard,

PostalCode, Upload。

   

8.4.5 DisplayFormat 特性

DisplayForat特性可用來處理屬性的各類格式化選項。如示例14所示。

示例14

[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:c}")]

public decimal Price { get; set; }

上面的代碼可將模型的 Price 屬性值格式化爲貨幣值形式。ApplyFormatInEditMode參數的值默認是false,若是想把 Price 屬性格式化爲表單輸入元素,須要將屬性ApplyFormatInEditMode的值設置爲true。例如,當把模型中decimal類型的 Price 屬性值設置爲12.1時,將在視圖中看到¥12.10的輸出結果。

DisplayFormat還有一個string屬性:NullDisplayText,它表示針對空值(Null)對象的顯示文本。

   

8.4.6 UIHint特性

UIHint特性給ASP.NET MVC運行時提供了一個模板名稱,以備調用模板輔助方法,如(DisplayFor和EditorFor)渲染時輸出使用。也能夠定義本身的模板輔助方法來重寫ASP.NET MVC的默認行爲。

   

8.5 自定義模板

Html.EditorFor()和 Html.DisplayFor()的特殊之處在於它支持模板功能。

8.5.1 在Views/Shared目錄下建立模板

示例15完成一個日曆模板,該模板能夠爲DateTime類型的模型屬性自動應用該模板。

步驟:

  • 在Views/Shared 目錄下建立"EditorTemplates"文件夾。
  • 在"EditorTemplates"文件夾下按類型名稱命名視圖:DateTime.cshtml。
  • 實現模板代碼。

示例15

@model DateTime?

@Html.TextBoxFor(m => m, "", new { @class = "Wdate", onfocus = "$(this).datepicker()" })

在視圖中,下面代碼將直接致使輸出一個jQuery日曆文本框。由於BornDate屬性是DateTime 類型,將自動應用這個模板。(在視圖中須要引入相關js文件)

@Html.EditorFor(m=>m.PublishDate)

若是不想讓每個DateTime編輯器都擁有DatePicker小部件,而是讓一少部分特定的編輯器擁有。此時,能夠把自定義的模板文件名改成"SpecialDateTime.cshtml",這樣框架就不會爲DateTime模型選擇該模板,除非指定該模板名稱。以下面代碼所示。

@Html.EditorFor(m=>m.PublishDate,"SpecialDateTime")

另外,也能夠在PublishDate屬性上使用UIHint特性來指定模板名稱:

[UIHint("SpecialDateTime")]

public DateTime? PublishDate { get;set;}

   

8.5.2 在Views/Model目錄下建立模板

示例16完成將出版社顯示成下拉框的模板。

步驟:

  • 在Views/Book 目錄下建立"EditorTemplates"文件夾。
  • 在"EditorTemplates"文件夾下按類型名稱命名視圖:Publisher.cshtml。
  • 實現模板代碼。

示例16

@model BookShop.Models.Publisher

   

@Html.DropDownListFor(m=>m.Id,(SelectList)ViewData["publisher"],"---請選擇---")

在示例16的代碼中,模板視圖對應的類型是BookShop.Models.Publisher,在視圖中下面的代碼將自動將出版社顯示爲下拉框列表。

@Html.EditorFor(model=>model.Publisher)

   

Html.DisplayFor()使用自定義模板的方式和 Html.EditorFor相似,區別在於將模板文件夾改成"DisplayTemplates"

相關文章
相關標籤/搜索