反覆項目 #3 – 新增表單驗證 (VB)
由 Microsoft 提供
在第三個反覆項目中,我們新增基本表單驗證。 我們要防止人員提交未完成表單必填欄位的表單。 我們也驗證電子郵件地址和電話號碼。
建置連絡人管理 ASP.NET MVC 應用程式 (VB)
在此系列教學課程中,我們會從頭到尾建置整個連絡人管理應用程式。 Contact Manager 應用程式可讓您儲存連絡人資訊 (姓名、電話號碼和電子郵件地址) 以取得人員名單。
我們會透過多個反覆項目建置應用程式。 每次反覆運算時,我們會逐步改善應用程式。 這種多次反覆運算方法的目標是,讓您能夠瞭解每次變更的原因。
反覆項目 #1 – 建立應用程式 在第一個反覆項目中,我們以最簡單的方式建立 Contact Manager。 我們新增對基本資料庫作業的支援:建立、讀取、更新和刪除 (CRUD)。
反覆項目 #2 – 美化應用程式外觀。 在這個反覆項目中,我們修改預設 ASP.NET MVC 檢視主版頁面和串聯樣式表,以改善應用程式的外觀。
反覆項目 #3 – 新增表單驗證。 在第三個反覆項目中,我們新增基本表單驗證。 我們要防止人員提交未完成表單必填欄位的表單。 我們也驗證電子郵件地址和電話號碼。
反覆項目 #4 – 讓應用程式鬆散耦合。 在這個第四個反覆項目中,我們利用好幾種軟體設計模式,更輕鬆地維護和修改 Contact Manager 應用程式。 例如,我們將應用程式重構為使用存放庫模式和相依性插入模式。
反覆項目 #5 – 建立單元測試。 在第五個反覆項目中,我們會藉由新增單元測試,更容易維護和修改應用程式。 我們會模擬資料模型類別,並為控制器和驗證邏輯建置單元測試。
反覆項目 #6 – 使用測試導向的開發。 在這第六個反覆項目中,我們會先撰寫單元測試,再針對單元測試撰寫程式碼,藉此將新功能新增至應用程式。 在此反覆項目中,我們會新增連絡人群組。
反覆項目 #7 – 新增 Ajax 功能性。 在第七個反覆項目中,我們會藉由新增對 Ajax 的支援來改善應用程式的回應性和效能。
此反覆項目
在這個 Contact Manager 應用程式的第二個反覆項目中,我們會新增基本表單驗證。 我們避免使用者在未輸入必填表單欄位值的情況下提交連絡人。 我們也驗證電話號碼和電子郵件地址 (請參閱圖 1)。
圖 01:含有驗證的表單 (按一下以檢視完整大小的圖片)
在此反覆項目中,我們會將驗證邏輯直接新增至控制器動作。 一般來說,這不是向 ASP.NET MVC 應用程式新增驗證的建議方法。 更好的方法是將應用程式的驗證邏輯放置在單獨的服務層中。 在下一個反覆項目中,我們會重構 Contact Manager 應用程式,讓應用程式更容易維護。
在此反覆項目中,為了保持簡單,我們會手動撰寫所有驗證程式碼。 我們可以利用驗證架構,而不是自己編寫驗證程式碼。 例如,您可以使用 Microsoft Enterprise Library Validation Application Block (VAB) 來實作 ASP.NET MVC 應用程式的驗證邏輯。 若要深入瞭解驗證應用程式區塊,請參閱:
http://msdn.microsoft.com/library/dd203099.aspx
將驗證新增至建立檢視
讓我們從將驗證邏輯新增至 [建立] 檢視開始。 幸運的是,因為我們已使用 Visual Studio 產生建立檢視,因此建立檢視已經包含顯示驗證訊息所需的所有使用者介面邏輯。 [建立] 檢視包含在清單 1 中。
清單 1 - \Views\Contact\Create.aspx
<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of ContactManager.Contact)" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
<title>Create</title>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<%= Html.ValidationSummary() %>
<% Using Html.BeginForm()%>
<fieldset>
<legend>Create New Contact</legend>
<p>
<label for="FirstName">First Name:</label>
<%= Html.TextBox("FirstName") %>
<%= Html.ValidationMessage("FirstName", "*") %>
</p>
<p>
<label for="LastName">Last Name:</label>
<%= Html.TextBox("LastName") %>
<%= Html.ValidationMessage("LastName", "*") %>
</p>
<p>
<label for="Phone">Phone:</label>
<%= Html.TextBox("Phone") %>
<%= Html.ValidationMessage("Phone", "*") %>
</p>
<p>
<label for="Email">Email:</label>
<%= Html.TextBox("Email") %>
<%= Html.ValidationMessage("Email", "*") %>
</p>
<p class="submit">
<input type="submit" value="Create" />
</p>
</fieldset>
<% End Using %>
</asp:Content>
field-validation-error 類別可用來設定 Html.ValidationMessage() 協助程式所轉譯的輸出樣式。 input-validation-error 類別是用來設定 Html.TextBox() 協助程式所轉譯的文字方塊 (input) 樣式。 validation-summary-errors 類別可用來設定 Html.ValidationSummary() 協助程式所轉譯之未排序清單的樣式。
注意
您可以修改本節所述的樣式表類別,以自訂驗證錯誤訊息的外觀。
將驗證邏輯新增至建立動作
現在,[建立] 檢視永遠不會顯示驗證錯誤訊息,因為我們尚未撰寫邏輯來產生任何訊息。 若要顯示驗證錯誤訊息,您必須將錯誤訊息新增至 ModelState。
注意
UpdateModel() 方法會在將表單欄位的值指派給屬性時,自動將錯誤訊息新增至 ModelState。 例如,如果您嘗試將字串「apple」指派給接受 DateTime 值的 BirthDate 屬性,則 UpdateModel() 方法會將錯誤新增至 ModelState。
清單 2 中修改過的 Create() 方法包含新區段,在新的連絡人插入資料庫之前,會先驗證 Contact 類別的屬性。
清單 2 - Controllers\ContactController.vb (使用驗證建立)
<AcceptVerbs(HttpVerbs.Post)> _
Function Create(<Bind(Exclude:="Id")> ByVal contactToCreate As Contact) As ActionResult
' Validation logic
If contactToCreate.FirstName.Trim().Length = 0 Then
ModelState.AddModelError("FirstName", "First name is required.")
End If
If contactToCreate.LastName.Trim().Length = 0 Then
ModelState.AddModelError("LastName", "Last name is required.")
End If
If (contactToCreate.Phone.Length > 0 AndAlso Not Regex.IsMatch(contactToCreate.Phone, "((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}"))
ModelState.AddModelError("Phone", "Invalid phone number.")
End If
If (contactToCreate.Email.Length > 0 AndAlso Not Regex.IsMatch(contactToCreate.Email, "^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
ModelState.AddModelError("Email", "Invalid email address.")
End If
If Not ModelState.IsValid Then
Return View()
End If
' Database logic
Try
_entities.AddToContactSet(contactToCreate)
_entities.SaveChanges()
Return RedirectToAction("Index")
Catch
Return View()
End Try
End Function
validate 區段會強制執行四個不同的驗證規則:
- FirstName 屬性的長度必須大於零 (且不能只包含空格)
- LastName 屬性的長度必須大於零 (且不能只包含空格)
- 如果 Phone 屬性具有值 (長度大於 0),則 Phone 屬性必須符合規則運算式。
- 如果 Email 屬性具有值 (長度大於 0),則 Email 屬性必須符合規則運算式。
發生驗證規則違規時,會在 AddModelError() 方法的協助下,將錯誤訊息新增至 ModelState。 當您將訊息新增至 ModelState 時,您會提供屬性的名稱和驗證錯誤訊息的文字。 這個錯誤訊息會顯示在 Html.ValidationSummary() 和 Html.ValidationMessage() 協助程式方法的檢視中。
執行驗證規則之後,會檢查 ModelState 的 IsValid 屬性。 當任何驗證錯誤訊息已新增至 ModelState 時,IsValid 屬性會傳回 false。 如果驗證失敗,[建立] 表單會以錯誤訊息重新顯示。
注意
我從 http://regexlib.com 的規則運算式儲存庫中,取得用於驗證電話號碼和電子郵件地址的規則運算式
將驗證邏輯新增至編輯動作
Edit() 動作會更新 Contact。 Edit() 動作必須執行與 Create() 動作完全相同的驗證。 我們不應該限制相同的驗證碼,而是該重構 Contact 控制器,讓 Create() 和 Edit() 動作呼叫相同的驗證方法。
修改過的 Contact 控制器類別包含在清單 3 中。 這個類別有新的 ValidateContact() 方法,會在 Create() 和 Edit() 動作內呼叫。
清單 3 - Controllers\ContactController.vb
Public Class ContactController
Inherits System.Web.Mvc.Controller
Private _entities As New ContactManagerDBEntities()
Protected Sub ValidateContact(contactToValidate As Contact)
If contactToValidate.FirstName.Trim().Length = 0 Then
ModelState.AddModelError("FirstName", "First name is required.")
End If
If contactToValidate.LastName.Trim().Length = 0 Then
ModelState.AddModelError("LastName", "Last name is required.")
End If
If (contactToValidate.Phone.Length > 0 AndAlso Not Regex.IsMatch(contactToValidate.Phone, "((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}"))
ModelState.AddModelError("Phone", "Invalid phone number.")
End If
If (contactToValidate.Email.Length > 0 AndAlso Not Regex.IsMatch(contactToValidate.Email, "^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
ModelState.AddModelError("Email", "Invalid email address.")
End If
End Sub
'
' GET: /Contact
Function Index() As ActionResult
Return View(_entities.ContactSet.ToList())
End Function
'
' GET: /Contact/Create
Function Create() As ActionResult
Return View()
End Function
'
' POST: /Contact/Create
<AcceptVerbs(HttpVerbs.Post)> _
Function Create(<Bind(Exclude:="Id")> ByVal contactToCreate As Contact) As ActionResult
' Validation logic
ValidateContact(contactToCreate)
If Not ModelState.IsValid Then
Return View()
End If
' Database logic
Try
_entities.AddToContactSet(contactToCreate)
_entities.SaveChanges()
Return RedirectToAction("Index")
Catch
Return View()
End Try
End Function
'
' GET: /Contact/Edit/5
Function Edit(ByVal id As Integer) As ActionResult
Dim contactToEdit = (from c in _entities.ContactSet _
where c.Id = id _
select c).FirstOrDefault()
Return View(contactToEdit)
End Function
'
' POST: /Contact/Edit/5
<AcceptVerbs(HttpVerbs.Post)> _
Function Edit(ByVal contactToEdit As Contact) As ActionResult
' Validation logic
ValidateContact(contactToEdit)
If Not ModelState.IsValid Then
Return View()
End If
' Database logic
Try
Dim originalContact = (from c in _entities.ContactSet _
where c.Id = contactToEdit.Id _
select c).FirstOrDefault()
_entities.ApplyPropertyChanges(originalContact.EntityKey.EntitySetName, contactToEdit)
_entities.SaveChanges()
Return RedirectToAction("Index")
Catch
Return View()
End Try
End Function
'
' GET: /Contact/Delete/5
Function Delete(ByVal id As Integer) As ActionResult
Dim contactToDelete = (from c in _entities.ContactSet _
where c.Id = id _
select c).FirstOrDefault()
Return View(contactToDelete)
End Function
'
' POST: /Contact/Delete/5
<AcceptVerbs(HttpVerbs.Post)> _
Function Delete(ByVal contactToDelete As Contact) As ActionResult
Try
Dim originalContact = (from c in _entities.ContactSet _
where c.Id = contactToDelete.Id _
select c).FirstOrDefault()
_entities.DeleteObject(originalContact)
_entities.SaveChanges()
Return RedirectToAction("Index")
Catch
Return View()
End Try
End Function
End Class
摘要
在此反覆項目中,我們已將基本表單驗證新增至 Contact Manager 應用程式。 我們的驗證邏輯可防止使用者在未提供 FirstName 和 LastName 屬性值的情況下,提交新連絡人或編輯現有連絡人。 此外,使用者必須提供有效的電話號碼和電子郵件地址。
在此反覆項目中,我們會以最簡單的方式,將驗證邏輯新增至 Contact Manager 應用程式。 然而,從長遠來看,將驗證邏輯混合到控制器邏輯中會為我們帶來問題。 隨著時間推移,我們的應用程式將更加難以維護和修改。
在下一個反覆項目中,我們會將驗證邏輯和資料庫存取邏輯重構為控制器。 我們將利用數個軟體設計原則,讓我們能夠建立更鬆散耦合且更容易維護的應用程式。