共用方式為


反覆項目 #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)。

[New Project] \(新增專案\) 對話方塊

圖 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 應用程式。 然而,從長遠來看,將驗證邏輯混合到控制器邏輯中會為我們帶來問題。 隨著時間推移,我們的應用程式將更加難以維護和修改。

在下一個反覆項目中,我們會將驗證邏輯和資料庫存取邏輯重構為控制器。 我們將利用數個軟體設計原則,讓我們能夠建立更鬆散耦合且更容易維護的應用程式。