在第三次迭代中,我们添加了基本表单验证。 我们会阻止用户在未填写必填表单字段的情况下提交表单。 我们还验证电子邮件地址和电话号码。

生成联系人管理 ASP.NET MVC 应用程序 (VB)

在本系列教程中,我们将从头到尾构建整个联系人管理应用程序。 通过 Contact Manager 应用程序,可以存储联系人列表的联系人信息(姓名、电话号码和电子邮件地址)。

我们通过多次迭代生成应用程序。 每次迭代后,我们都会逐步改进应用程序。 此多迭代方法的目标是使你能够了解每次更改的原因。

  • 迭代 #1 - 创建应用程序。 在第一次迭代中,我们将以最简单的方式创建联系人管理器。 添加了对基本数据库操作的支持:创建、读取、更新和删除 (CRUD) 。

  • 迭代 #2 - 使应用程序外观美观。 在此迭代中,我们通过修改默认 ASP.NET MVC 视图母版页和级联样式表来改进应用程序的外观。

  • 迭代 #3 - 添加表单验证。 在第三次迭代中,我们添加了基本表单验证。 我们会阻止用户在未填写必填表单字段的情况下提交表单。 我们还验证电子邮件地址和电话号码。

  • 迭代 #4 - 使应用程序松散耦合。 在第四次迭代中,我们将利用多种软件设计模式,以便更轻松地维护和修改 Contact Manager 应用程序。 例如,我们将应用程序重构为使用存储库模式和依赖关系注入模式。

  • 迭代 #5 - 创建单元测试。 在第五次迭代中,我们通过添加单元测试使应用程序更易于维护和修改。 我们将模拟数据模型类,并为控制器和验证逻辑生成单元测试。

  • 迭代 #6 - 使用测试驱动开发。 在第六次迭代中,我们通过先编写单元测试,然后针对单元测试编写代码,向应用程序添加新功能。 在此迭代中,我们将添加联系人组。

  • 迭代 #7 - 添加 Ajax 功能。 在第七次迭代中,我们通过添加对 Ajax 的支持来提高应用程序的响应能力和性能。

    在 Contact Manager 应用程序的第二次迭代中,我们添加了基本表单验证。 我们会阻止用户在未为必填表单域输入值的情况下提交联系人。 我们还验证电话号码和电子邮件地址 (见图 1) 。

    图 01 :具有验证 ( 的窗体单击以查看全尺寸图像 )

    在此迭代中,我们将验证逻辑直接添加到控制器操作。 通常,不建议使用此方法将验证添加到 ASP.NET MVC 应用程序。 更好的方法是将应用程序的验证逻辑放置在单独的 服务层 中。 在下一次迭代中,我们将重构 Contact Manager 应用程序,使应用程序更易于维护。

    在此迭代中,为了简单起见,我们手动编写所有验证代码。 我们可以利用验证框架,而不是自己编写验证代码。 例如,可以使用 Microsoft 企业库验证应用程序块 (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>
                    <label for="FirstName">First Name:</label>
                    <%= Html.TextBox("FirstName") %>
                    <%= Html.ValidationMessage("FirstName", "*") %>
                    <label for="LastName">Last Name:</label>
                    <%= Html.TextBox("LastName") %>
                    <%= Html.ValidationMessage("LastName", "*") %>
                    <label for="Phone">Phone:</label>
                    <%= Html.TextBox("Phone") %>
                    <%= Html.ValidationMessage("Phone", "*") %>
                    <label for="Email">Email:</label>
                    <%= Html.TextBox("Email") %>
                    <%= Html.ValidationMessage("Email", "*") %>
                <p class="submit">
                    <input type="submit" value="Create" />
            </fieldset>
        <% End Using %>
    </asp:Content>
    

    field-validation-error 类用于设置 Html.ValidationMessage () 帮助程序呈现的输出的样式。 input-validation-error 类用于设置 html.TextBox () 帮助程序呈现 (输入) 文本框的样式。 validation-summary-errors 类用于设置 Html.ValidationSummary () 帮助程序呈现的无序列表的样式。

    可以修改本节中所述的样式表类,以自定义验证错误消息的外观。

    将验证逻辑添加到创建操作

    现在,“创建”视图永远不会显示验证错误消息,因为我们尚未编写逻辑来生成任何消息。 若要显示验证错误消息,需要将错误消息添加到 ModelState。

    当向属性分配窗体域的值时,UpdateModel () 方法会自动将错误消息添加到 ModelState。 例如,如果尝试将字符串“apple”分配给接受 DateTime 值的 BirthDate 属性,则 UpdateModel () 方法会将错误添加到 ModelState。

    列表 2 中修改的 Create () 方法包含一个新节,该节在将新联系人插入数据库之前验证 Contact 类的属性。

    清单 2 - Controllers\ContactController.vb (Create with validation)

    <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
            _entities.AddToContactSet(contactToCreate)
            _entities.SaveChanges()
            Return RedirectToAction("Index")
        Catch
            Return View()
        End Try
    End Function
    

    验证部分强制实施四个不同的验证规则:

  • FirstName 属性的长度必须大于零 (,并且不能仅包含空格)
  • LastName 属性的长度必须大于零 (,并且不能仅包含空格)
  • 如果 Phone 属性的值 (长度大于 0) 则 Phone 属性必须与正则表达式匹配。
  • 如果 Email 属性的值 (长度大于 0) 则Email属性必须与正则表达式匹配。
  • 如果存在验证规则冲突,则会借助 AddModelError () 方法将错误消息添加到 ModelState。 将消息添加到 ModelState 时,需要提供属性的名称和验证错误消息的文本。 此错误消息由 Html.ValidationSummary () 和 Html.ValidationMessage () 帮助程序方法显示在视图中。

    执行验证规则后,将检查 ModelState 的 IsValid 属性。 将任何验证错误消息添加到 ModelState 时,IsValid 属性返回 false。 如果验证失败,则会重新显示“创建”窗体并显示错误消息。

    我从正则表达式存储库获取了用于验证电话号码和电子邮件地址的正则表达式 http://regexlib.com

    将验证逻辑添加到编辑操作

    编辑 () 操作更新联系人。 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
                _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
                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
                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 应用程序添加了验证逻辑。 但是,将验证逻辑混合到控制器逻辑中会长期给我们造成问题。 随着时间的推移,我们的应用程序将更难维护和修改。

    在下一次迭代中,我们将重构控制器中的验证逻辑和数据库访问逻辑。 我们将利用多个软件设计原则,使我们能够创建一个更松散耦合、更易于维护的应用程序。

    上一页下一页

  •