ASP.NET MVC---內置的數據注解


       驗證是ASP.NET MVC開發中一個非常重要的環節,包括客戶端和服務端驗證。幸好,MVC提供了非常簡便的數據注解(Data Annotations)來幫助我們進行這項工作。

1.驗證性的數據注解

       MVC本身內置了一些常用的數據注解,像是Required,DisplayName等等,我會在下面一一講解。

       最常用的就是Required,像是下面這樣:

     

      使用Required可以指定錯誤消息:

[Required(ErrorMessage = "First Name is required")]
public string FirstName { get; set; }

      使用Required是不夠的,我們還需要規定用戶的輸入限制,像是字符串,就常有長度的限制,這時我們就可以利用StringLength。像是這樣:

[Required(ErrorMessage = "First Name is required")]
[StringLength(160)]
public string FirstName{ get; set;}

      它規定了我們輸入的最大長度是160個字符。如果想要規定最小長度,我們可以這樣寫:

[Required(ErrorMessage = "First Name is required")]
[StringLength(160, MinimumLength = 3)]
public string FirstName { get; set; }

      實際的效果如圖:


      字符串的要求是長度,而數字的要求則是數據范圍范圍:

 [Range(0.01, 100.0, ErrorMessage = "Price must be between 0.01 and 100.0")]
 public decimal Price { get; set; }

      Range()是一個雙閉區間,就是說,包含0.01和100.0。

      最后一個要講的,就是RegularExpression。

     我們可以這樣使用:

[Required(ErrorMessage = "Email is required")]
[RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+[A-Za-z]{2,4}", ErrorMessage = "Email is not valid")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }

    通過上面,我們可以知道,所有用於驗證用的數據注解都可以自定義自己的錯誤消息。

2.顯示性的數據注解

      我們都注意到,變量FirstName在頁面上的實際顯示是:First Name。這樣做才是符合實際需要的,要想這樣做,我們就必須利用DisplayName:

[Required(ErrorMessage = "First Name is required")]
[DisplayName("First Name")]
[StringLength(160, MinimumLength = 3)]
public string FirstName { get; set; }

     同樣是為屬性定義說明,還有另一種注解:Dispaly,它的作用並不僅僅是指定顯示的內容,甚至能夠指定顯示的順序,像是這樣:

[Required(ErrorMessage = "First Name is required")]
[Display(Name = "First Name", order = 15000)]
[StringLength(160)]
public string FirstName { get; set; }

       order的默認參數是10000,如果其他變量並沒有設置order值,FirstName就排在其他變量后面。但實際上是不用這么麻煩的,如果不指定該值,就會按照變量聲明的順序排列。

      Display在用於屬性的顯示名稱上,具有更高的優先級。

      DisplayName專門用於定義屬性的顯示名稱,但是Display並不僅僅是如此。它的內容非常豐富,有些在這里不好講,而且本人是MVC新手,所以還請感興趣的同學自己查一下相關資料。

      如果對數據庫有所了解的話,就會知道,數據庫需要key值用於搜索相應的元組。MVC經常與數據庫打交道,所以我們的模型中經常需要定義一個或幾個名稱中包含有Id的作為key的屬性,像是OrderId之類的,但是我們在顯示的時候又不想將這些屬性顯示出來,但正如我上面所講的,默認是會將該模型的所有屬性都顯示出來。那該怎么辦呢?就是對顯示隱藏,我們可以使用HiddenInput。

      像是這樣:

public class Person{
    [HiddenInput]
    public string Name { get; set; }

    [HiddenInput(DisplayValue = false)]
    public string Sex { get; set; }

    public int Age { get; set; }
}

      然后是控制器:

public ActionResult Index(){
     Person person = new Person() { Name = "Jos", Sex = "man", Age = 18 };
     return View(person);
}

      實際的效果如圖:
      

       我們可以看到,"Sex"完全被隱藏起來了!

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

       我們可以來看看使用了HiddenInput的HTML:

<div class = "editor-label"><label for = "Name">Name</label></div>
<div class = "editor-label">Jos<input id = "Name" name = "Name" type = "hidden" value = "Jos"/></div>

<input id = "Sex" name = "Sex" type = "hidden" value = "man"/>

<div class = "editor-label"><label for = "Age">Age</label></div>
<div class = "editor-field"?
    <input class = "text-box single-line" id = "Age" name = "Age" type = "text" value = "18"/>
<div>

      這些只要了解就好,MVC會自動幫我們處理。
      我們有時候會想,這樣做是好的嗎?畢竟,MVC會幫我們自動生成HTML文件,包括一些本該是由程序員自己設置的標記。但必須承認,這樣讓程序員從千篇一律的編碼中解放出來,讓我們能夠將關注點放在其他更值得關注的方面上,而且,我們程序員依然對生成的HTML文件具有很大的控制權。

      特別強調一下,HiddenInput的命名空間是System.Web.Mvc。

      同樣是隱藏屬性在HTML上的顯示,我們還可以使用ScaffoldColumn特性。

      使用ScaffoldColumn並不是為屬性設置type = "hidden",它是直接將該屬性從基架中刪除。我們知道,MVC可以通過預定義模板來自動生成HTML,這種方式就是基架(Scaffolding)。ScaffoldColumn表示存在於基架中並最終呈現在HTML中的字典。

      像是這樣:

[ScaffoldColumn(false)]
public int OrderId { get; set; }

     即使從基架中將該屬性刪除,模型綁定器仍然會試圖為該屬性賦值,這樣就為典型的攻擊“重復提交”提供了機會。黑客們可以試圖向我們的網頁中發送"OrderId = 100"這樣的字段,而我們的模型綁定器會為該屬性賦值並且有可能賦為黑客提供的值。要想防止這種攻擊,我們可以利用Bind特性。
     Bind特性可以選擇模型綁定器要綁定的值。像是這樣:

[Bind(Include = "Name, Comment")]
public class Review{
     public int ReviewId{ get; set;}
     public int ProductId{ get; set;}
     publc string Name{ get; set;}
     public string Comment{ get; set;}
}

      這樣模型綁定器就只綁定Name和Comment屬性。
      當然,我們也可以選擇不綁定的屬性:

[Bind(Exclude = "OrderId")]
    public partial class Order
    {
        [ScaffoldColumn(false)]
        public int OrderId { get; set; }
        
        [ScaffoldColumn(false)]
        public string UserName { get; set; }

     }

     這樣,上面所講的“重復提交”攻擊就無法發揮作用了。但是必須注意,使用"Include"的白名單比起使用"Exclude"的黑名單更加安全,因為我們永遠也不知道黑客會用怎樣的方式來攻擊我們,但確定的是,我們可以知道哪些被綁定的屬性是不會造成太大的危害的。

      使用白名單還是黑名單還是得看具體情況,畢竟有時候我們需要綁定的屬性非常多,如果采用白名單的方式,光是數據注解這里可能就要超過了我們該模型類的代碼總量了。

      Bind既可以用於模型,也可以用於控制器操作的參數,但更多時候還是用在模型上。

      提到模型綁定器,這里就必須得說一下屬性的更新問題。模型綁定器會更新被綁定的屬性的值,但有些值我們可能希望它永遠都不要被更新,那么我們就可以使用ReadOnly特性。

      像是這樣:

[ReadOnly(true)]
public decimal Total{ get; set;}

      模型綁定器就不會更新它的值,雖然實際運行的時候,我們的確可以在文本框里修改它的值,但是該值並不會被用來更新該屬性的值。

      ReadOnly的命名空間是在using System.ComponentModel。

      既然提到可讀,那么一定存在可寫。是的,在System.ComponentModel.DataAnnotations中就有一個Editable。

      它的使用方式和ReadOnly完全一樣,但我們會很好奇:如果同時將這兩個注解用在同一個屬性上,會怎么樣呢?事實上,Editable擁有更高的優先級。
      接下來講到的數據注解就真的非常重要。所有顯示性的數據注解,其實都是提供給HTML 輔助方法和ASP.NET MVC運行時的其他組件使用,這些都是靠模型元數據提供器來負責收集。所以,就有一種數據注解能夠為運行時提供關於屬性的特定用途。這就是DataType。

      我們會在用戶的登陸界面中要求用戶輸入登陸密碼,但是又必須保證,這些密碼不會顯示出來以免被別人看到,這時,DataType就發揮作用了:

[Required]
[DataType(DataType.Password)]
[DisplayName("Password")]
public string Password { get; set; }

      從上面可以看出,DataType所謂的數據類型,並不是我們常說的int,float等,事實上,DataType本身也有一個可支持的數據類型枚舉:

public enum DataType{
    Custom, DateTime, Date, Time, Duration, 
PhoneNumber, Currency, Text, Html, MultilineText,
EmailAddress, Password, Url, ImageUrl, CreditCard,
PostalCode, Upload }

     DataType實際上是一個驗證特性,它繼承自ValidationAttribute,但我之所以放在這里講,是因為所謂的"顯示"性,是指它的作用效果,像DataType的作用效果就是為它內置的數據類型提供不同的顯示效果。
     當然,我們還可以自定義自己的數據類型,不過這種情況下我們就必須自定義該數據類型的DisplayFormat。這些都在DataType的源碼中:

[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple=false)]  public class DataTypeAttribute : ValidationAttribute{
    public DataTypeAttribute(DataType dataType); 
    public DataTypeAttribute(string customDataType);
    public virtual string GetDataTypeName();
    public override bool IsValid(object value);
    public string CustomDataType { get; }
    public DataType DataType { get;  }
    public DisplayFormatAttribute DisplayFormat { get; }
}

      我們有時候想要對輸出進行格式化設置,這時我們就可以利用DisplayFormat。

      像是這樣:

[Required(ErrorMessage = "Price is required")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:c}")]
[Range(0.01, 100.0, ErrorMessage = "Price must be between 0.01 and 100.0")]
public decimal Price { get; set; }

      就會有這樣的效果:    

 

      話說,中文版的VS2012莫非真的是入鄉隨俗,我看到外國的教程都是美元標記,但這里卻是RMB標記!但這點確實真的很重要,畢竟,在中國發布的網站價格標注的要是美元的話,那還真的是沒有多少人會買了。

      這里必須注意,ApplyFormatInEditMode的默認值是false,之所以這樣,是因為我們模型綁定器無法解析格式化的值。

      查看DisplayFormat的源碼,我們就可以發現,它還有兩個個布爾類型的屬性:HtmlEncode和ConvertEmptyStringToNull,前者表示是否需要對目標內容進行HTML編碼,默認下是true(這樣是為了安全性的考慮),后者表示是否將傳入的空字符串轉換成Null。

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

      同樣的道理,因為DataType也對應着一個DisplayFormatAttribute,所以當這兩個特性同時應用在相同的元素上,后者具有更高級的優先級。

      最后一個要講的數據注解,因為不知道要放在哪里,但提到模板的話,就還是放在這里好了。它就是UIHintAttribute。

      我們知道,HTML輔助方法會幫我們選擇基於Model的模板方法。所謂的模板方法,指的是我們在通過調用這些方法來將Model的數據顯示在View中時,采用默認或者指定的模板來決定最終呈現在瀏覽器中的HTML。這點在我們使用MVC的時候就已經察覺了:模板名稱對應具體的Model元數據。所以,通過元數據,我們可以自定義要選擇的模板。像是這樣:

public class Person{
    [UIHint("Template A")]
    public string Name{ get; set;}

    [UIHint("Template B", "Mvc")]
    [UIHint("Template A")]
public string Sex{ get; set;} }

      它有兩個構造函數:

public UIHintAttribute(string uiHint); 
public UIHintAttribute(string uiHint, string presentationLayer);

      對於最后一個屬性Sex,最終采用的是Template B,因為它的presentationLayer的值為Mvc。

      本人完全是MVC新手,所講的均是自己的理解,如果有說得不對的地方,還請各位大神指出。

     

 

    

     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM