摘要:ASP.NET MVC 2部落格系列之二:模型驗證
【原文位址】ASP.NET MVC 2: Model Validation
【原文發表日期】 Friday, January 15, 2010 4:14 AM
【除了寫部落格外,我現在還使用Twitter發短貼和共享連結。請透過twitter.com/scottgu跟隨我。】
這是我針對即將發佈的ASP.NET MVC 2所撰寫的文章系列的第二篇,這個部落格文章將討論 ASP.NET MVC 2中一些驗證方面的改進。
ASP.NET MVC 2 驗證
對用戶輸入的驗證以及強制業務規則/邏輯是大多數web應用的核心需求。ASP.NET MVC 2包含了一堆新的特性,顯著地簡化了對用戶輸入的驗證以及在Model/ViewModel中對驗證邏輯的強制實施。這些特性是這樣設計的,驗證邏輯總是在伺服器上執行的,也可以選擇在客戶端透過JavaScript來執行。ASP.NET MVC 2中的驗證設施和特性這般設計,以便:
1) 開發人員可以輕易地利用內建於.NET框架中的DataAnnotation驗證支援。DataAnnotation提供了一個非常簡便的方式,使用最少的程式碼在物件和屬性上用宣告的方式添加驗證規則。
2) 開發人員可以整合他們自己的驗證引擎,或者利用現有的驗證框架,像Castle驗證器或EntLib驗證庫。ASP.NET MVC 2的驗證特性是設計來在利用新的 ASP.NET MVC 2的驗證設施(包括客戶端驗證,模型繫結驗證等等)的同時,簡化任何型別的驗證架構的插入的。
這意味著,在常見的應用場景中啟用驗證是極其容易的,同時對更高級的場景則還能保持極好的彈性。
使用ASP.NET MVC 2 和 DataAnnotation來啟用驗證
讓我們在ASP.NET MVC 2中來全程示範一個簡單的CRUD場景,利用新的內建DataAnnotation驗證支援。具體來說,讓我們來實現一個「Create」表單來允許用戶輸入朋友的資料:
我們想要確保在保存到資料庫之前,輸入的資訊是合法的,如果不合法,就顯示合適的錯誤消息:
我們想要使得這個驗證同時在伺服端和客戶端(透過 JavaScript)發生。我們還想要確保我們的程式碼遵守DRY原則(Don』t Repeat Yourself,不重複自己),意味著我們應該只在一處實施驗證規則,然後使得我們的控制器,action方法和 View 來兌現這個承諾。
在下面,我將使用VS 2010,用ASP.NET MVC 2來實現上面的場景。你也可以使用VS 2008及ASP.NET MVC 2來實現完全一樣的場景。
第一步: 實現FriendsController (一開始沒有驗證)
我們首先在一個新的ASP.NET MVC 2專案中加一個簡單的「Person」類,像下面這樣:
它有四個屬性(是用C#的自動屬性支援實現的, 在VS 2010中VB也支援自動屬性了,哎!)。
然後在專案中加一個 「FriendsController」 控制器類,呈示2個 「Create」 action方法。第一個action方法是在對/Friends/Create URL的HTTP-GET請求進來時叫用的,它會顯示一個空白的表單,用來輸入個人資料。第二個action方法是在對/Friends/Create URL的HTTP-POST請求進來時叫用的。它會將提交的表單輸入映射到一個Person物件,核實沒有繫結錯誤發生,如果是合法的,最終會將資料保存到資料庫中去(在本教程的後面我們會實現相關的資料庫工作)。如果提交的表單輸入是不合法的,該action方法會重新顯示帶有錯誤的表單:
在實現了控制器之後,可以在Visual Studio中在其中一個action方法中右擊,選擇 「添加視圖」命令, 這會調出 「添加視圖」 對話框。選擇自動生成傳入物件為Person的「Create」視圖:
然後Visual Studio會在我們專案的\Views\Friends\目錄中,生成一個含有框架程式碼(scaffolded)的Create.aspx視圖文件。注意下面,它利用了ASP.NET MVC 2中新的強型別HTML輔助方法(促成了更好的intellisense和編譯時檢查支援):
現在,當我們執行該應用,存取 /Friends/Create URL時,我們將得到一張可以輸入資料的空白表單:
但,因為我們還沒有在應用中實現任何驗證,誰也無法阻止我們在表單中鍵入假的輸入,將其提交到伺服器去。
第二步: 使用DataAnnotation來啟用驗證
現在,讓我們來更新應用,執行一些基本的輸入驗證規則。我們將在我們的Person模型物件上實現這些規則,而不是在控制器或視圖中實現。在Person物件上實現這些規則的好處是,這將確保這些驗證在應用中任何使用Person物件的場景中都會被執行(例如,如果後來添加了編輯場景的話)。這將幫助確保我們將程式碼保持DRY,避免在多處重複這些規則。
ASP.NET MVC 2 允許開發人員輕鬆地在模型或視圖模型類上添加宣告式驗證特性,然後ASP.NET MVC在應用中實施模型繫結操作時,這些驗證規則就會被自動執行。為看其例子,讓我們更新Person類,在其中加幾個驗證特性。這麼做,在文件的頂部加一個對「System.Com
ponentModel.DataAnnotations」命名空間的 「using」 語句,然後在Person的屬性上飾於[Required], [StringLength], [Range], 和 [RegularExpression] 驗證特性(這幾個特性都是在那個命名空間中實現的):
註: 在上面我們明式指定了錯誤訊息字串,你也可以在資源文件中定義它們,或者按進來的用戶的語言/文化做本地化,你可以在這裡瞭解如何本地化驗證錯誤消息。
既然我們加了驗證特性到Person類上,讓我們來重新執行我們的應用,看在鍵入假的數值,將其提交回伺服器時會發生什麼:
注意上面我們的應用現在有一個蠻好的出錯體驗了。帶不合法輸入的文字元素以紅色高亮顯示,我們指定的驗證錯誤消息也顯示給了用戶。表單還保留用戶原先輸入的資料,這樣他們不用重新填寫什麼。但,你也許會問,怎麼會是這樣?
要理解這個行為,讓我們看一下處理我們表單的POST場景的Create action方法:
在我們的HTML表單被提交回伺服器時,上面的方法就會被叫用。因為該action方法接受一個「Person」 物件為參數,ASP.NET MVC會建立一個Person物件,自動地將進來的表單輸入數值映射到該物件上。作為該過程的一部分,ASP.NET MVC還會檢查該Person物件上的DataAnnotation驗證特性是否合法。如果一切都合法,那麼我們程式碼中的ModelState.IsValid檢查就會傳回真值,在這種情形下,我們(最終)將把該Person物件保存到資料庫中,然後重新定向回到主頁上去。
但如果Person物件上有任何驗證錯誤的話,我們的action方法就會以該不合法Person物件的資料重新顯示表單,這是透過上面程式碼片段中最後一行程式碼實現的。
然後,錯誤消息就會顯示在我們的視圖中,因為我們的Create表單在每一個<%= Html.TextBoxFor() %>輔助方法的叫用旁邊都有一個<%= Html.ValidationMessageFor() %>輔助方法叫用。Html.ValidationMessageFor() 輔助方法會針對傳入視圖的任何不合法的模型屬性輸出合適的錯誤消息:
這個模式/方式有一個好處,就是非常容易配置,它還允許我們輕鬆地添加或改變我們Person類上的驗證規則,而不必改變控制器或視圖中的任何程式碼。這個在一個地方指定驗證規則,然後在所有的地方都會被承諾和遵守的能力,允許我們以最少的努力快速地發展我們的應用和規則,並且將程式碼保持在非常DRY的程度。
第三步: 啟用客戶端驗證
目前我們的應用只能做伺服端的驗證,這意味著我們的終端用戶需要將表單提交到伺服器才能看到任何驗證錯誤消息。
ASP.NET MVC 2的驗證架構中一樣非常酷的東西是,它同時支援伺服端 和 客戶端驗證。為啟用這個功能,我們要做的就是在視圖中添加2個 JavaScript引用,編寫一行程式碼:
在我們添加了這三行後,ASP.NET MVC 2 就會使用我們加到Person類上的驗證元資料,為我們連接好客戶端JavaScript驗證邏輯。這意味著,當用戶使用tab鍵跳出一個不合法的輸入元素時,就會得到瞬時的驗證錯誤。
要在我們的朋友應用中看客戶端JavaScript支援的實戰例子的話,讓我們重新執行應用,在前三個文字框中填入合法的數值,然後嘗試點擊「Create(建立)」。注意,我們不必存取伺服器就會得到遺漏值的瞬時錯誤消息:
如果我們輸入一些不是合法的email的字元話,錯誤消息就會瞬時從「Email Required (Email是個必需值)」 變為 「Not a valid email (email不合法)」(這是我們將規則加到Person類上時指定的錯誤消息):
在輸入一個合法的email時,錯誤消息就是瞬時消失,文字框背景色也會恢復到正常的狀態:
好事是,我們不必編寫自己的任何定制JavaScript就能啟用上面的驗證邏輯。我們的驗證程式碼還是那麼DRY,我們可以在一個地方指定規則,然後在整個應用中得到執行,同時在客戶端和伺服端。
注意,為安全的原因,伺服端驗證規則總是執行的,即時你啟用了客戶端支援。這避免黑客嘗試繞過客戶端規則,哄騙攻擊(spoof)你的伺服器。
ASP.NET MVC 2中的客戶端JavaScript驗證支援可與你在ASP.NET MVC應用中使用的任何驗證框架/引擎協作,它並不要求你使用 DataAnnotation 驗證方式,所有的基礎設施是獨立於 DataAnnotation的,可以與Castle驗證器, EntLib驗證應用塊,或你選擇使用的任何定制驗證方案協作使用。
如果你不想使用我們的客戶端JavaScript文件,你也可以將其替換成jQuery驗證外掛,而使用那個庫。 ASP.NET MVC Futures下載還包括針對ASP.NET MVC 2伺服端驗證框架啟用jQuery驗證的支援。
第四步: 建立自訂的[Email]驗證特性
.NET框架中的System.ComponentModel.DataAnnotations命名空間包括了眾多可為你所用的內建驗證特性。我們在
上面的例子中使用了其中的四個:[Required], [StringLength], [Range], 和 [RegularExpression]。
你也可以定義自己的定制驗證特性,然後應用它們。你可以透過繼承自System.ComponentModel.DataAnnotations命名空間中的ValidationAttribute基底類別,定義完全定制的特性。或者,你也可以選擇繼承自任何現有的驗證特性,如果你只想要擴展它們的基本功能的話。
例如,為幫助清理我們Person類中的程式碼,我們也許想要建立一個新的[Email]驗證特性,將檢查合法email的正則運算式封裝起來。要這麼做的話,我們只要像這樣繼承自RegularExpressionAttribute基底類別,然後用合適的email正則運算式叫用RegularExpressionAttribute基底類別的建構式:
然後將Person類更新成使用我們新的[Email]驗證屬性,換掉我們先前使用的正則運算式,這使得我們的程式碼更乾淨,封裝也更好:
在建立定制的驗證特性時,你還可以指定在伺服端以及在客戶端透過JavaScript執行的驗證邏輯。
除了建立可施用於物件上個別屬性的驗證特性外,你還可以將驗證特性施用於類的層次,這允許你對一個物件中的多個屬性實施驗證邏輯。要看實戰例子的話,你可以參閱包含在預設ASP.NET MVC 2應用專案模板中AccountModels.cs/vb文件中的「PropertiesMustMatchAttribute」 定制特性(在VS 2010中做 文件->新ASP.NET MVC 2 Web專案,然後查詢該類)。
第五步: 持久化到資料庫中
現在讓我們來實現將朋友資料保存到資料庫所需的邏輯。
至此,我們只用了平白的(plain-old)C#類別(有時稱為「POCO」 類別, 即 「plain old CLR (or C#) object」)。我們可以使用的一個方案是,編寫一些單獨的持久程式碼,將這我們已經編寫好的現有類別映射到資料庫去。目前象NHibernate這樣的物件關係映射(Object relational mapping – ORM)方案非常地好支援這樣的POCO/PI風格的映射。隨.NET 4發佈的ADO.NET實體框架(Entity Framework – EF)也支援POCO / PI映射,而且就像NHibernate,EF也能啟用以「只用程式碼(code only)」的方式(沒有映射文件,也不需要設計器)定義持久性映射的能力。
如果我們的Person物件以這種方式映射到資料庫的話,我們不用對Person類別做任何改動,也不用修改任何驗證規則,它還會繼續完好地工作。
但假如我們要使用圖形工具來做ORM映射的話,怎麼辦?
今天使用Visual Studio的許多開發人員並不編寫他們自己的ORM映射/持久邏輯,而是使用Visual Studio中內建的設計器來幫助管理這樣的映射邏輯。
使用DataAnnotation(或者任何其他形式的基於特性的驗證)時一個經常問起的問題是,「如果你手頭的模型物件是由GUI設計器建立/維護的話,你該如何施用這些特性?」。例如,假如與類似我們至此為止一直在使用的POCO風格的Person類不同,我們而是在Visual Studio中透過像LINQ to SQL 或 ADO.NET EF設計器這樣的GUI映射工具定義/維護我們的Person類別的話,該怎麼辦呢:
上面是一張螢幕截圖,展示了在VS 2010中使用ADO.NET EF設計器定義的一個Person類別。上方的視窗定義了Person類別,下方的視窗展示了該類的屬性是如何映射到資料庫中的「People」表的映射編輯器。當你在設計器上點擊保存時,它會自動為你在專案中生成一個Person類別。這很棒,但每次你做了改動,點擊保存時,它就會重新生成 Person 類別,這會導致你在物件上面宣告的任何驗證特性的遺失。
將額外的基於特性的詮釋資料 (像驗證特性)施加到由VS設計器自動生成/維護的類別的一個方法是,採用一個我們稱之為「夥伴類別(buddy classes)」的技術。基本上來說,你建立另外一個類別,包含你的驗證特性和詮釋資料,然後透過將 「MetadataType」特性施加到一個與工具生成的類別一起編譯的partial類別上,將其與由設計器生成的類別連接起來。例如,如果我們想要將我們前面用到的驗證規則施加到由LINQ to SQL 或 ADO.NET EF設計器維護的Person類別上,我們可以更新我們的驗證程式碼,使其存在於一個單獨的「Person_Validation」類別上,使用象下面這樣的程式碼將其連接到由VS建立的「Person」類別上:
上面的做法沒有純粹的POCO方法那麼優雅,但其好處是,可以用於Visual Studio中任何工具或設計器生成的程式碼。
最後一步 – 將Friend保存到資料庫中
最後一步,不管是否採用了POCO或工具生成的Person類別,是將合法的朋友資料保存到資料庫中去。
這只要求我們用三行程式碼將FriendsControlle類別中的 「Todo」佔位語句替換掉,這三行程式碼將新朋友保存到資料庫。下面是整個FriendsController類別的完整程式碼(使用了ADO.NET EF做資料庫持久化):
現在,當我們存取 /Friends/Create URL時,我們可以輕鬆地添加新人到我們的朋友資料庫中去:
對所有資料的驗證都是同時在客戶端和伺服端執行的。我們可以輕易地在一個地方添加/修改/刪除驗證規則,而由整個應用中的所有的控制器和 View 來執行這些規則。
結語
ASP.NET MVC 2極大地簡化了web應用的驗證整合。它倡議一種基於模型的驗證方式,允許你將你的應用保持DRY,幫助確保驗證規則在整個應用中保持一致。ASP.NET MVC 2中內建的DataAnnotation支援,原本就使得對常見的驗證場景的支援非常容易。而且,ASP.NET MVC 2驗證服務中的擴充性支援允許你支援更大範圍的更高級的驗證場景,可以插入任何現有的或者定制的驗證框架/引擎。
希望本文對你有所幫助,
Scott