繰り返し #3 – フォーム検証を追加する (VB)

提供元: Microsoft

コードのダウンロード

3 番目のイテレーションでは、基本的なフォームの検証を追加します。 必須のフォーム フィールドを入力しないとユーザーがフォームを送信できないようにします。 また、メール アドレスと電話番号も検証します。

連絡先管理の ASP.NET MVC アプリケーションをビルドする (VB)

この一連のチュートリアルでは、連絡先管理アプリケーション全体を最初から最後までビルドします。 連絡先マネージャー アプリケーションを使用すると、人物の一覧に連絡先情報 (名前、電話番号、電子メール アドレス) を保存できます。

複数のイテレーションを通してアプリケーションをビルドします。 イテレーションのたびに、アプリケーションを徐々に改善します。 複数のイテレーションからなるこのアプローチの目的は、お客様が各変更の理由を理解できるようにすることです。

  • イテレーション #1 - アプリケーションを作成します。 最初のイテレーションでは、可能な限り簡単な方法で連絡先マネージャーを作成します。 基本的なデータベース操作 (生成、読み取り、更新、削除 (CRUD)) のサポートを追加します。

  • イテレーション #2 - アプリケーションの外観を良くします。 このイテレーションでは、既定の ASP.NET MVC ビュー マスター ページとカスケード スタイル シートを変更することで、アプリケーションの外観を向上させます。

  • イテレーション #3 - フォームの検証を追加します。 3 番目のイテレーションでは、基本的なフォームの検証を追加します。 必須のフォーム フィールドを入力しないとユーザーがフォームを送信できないようにします。 また、メール アドレスと電話番号も検証します。

  • イテレーション #4 - アプリケーションを疎結合します。 この 4 番目のイテレーションでは、いくつかのソフトウェア デザイン パターンを利用して、連絡先マネージャー アプリケーションを簡単に維持し、変更できるようにします。 たとえば、Repository パターンと Dependency Injection パターンを使用するようにアプリケーションをリファクターします。

  • イテレーション #5 - 単体テストを作成します。 5 番目のイテレーションでは、単体テストを追加することで、アプリケーションを簡単に維持し、変更できるようにします。 データ モデル クラスをモックし、コントローラーと検証ロジックの単体テストをビルドします。

  • イテレーション #6 - テスト駆動開発を使用します。 この 6 番目のイテレーションでは、最初に単体テストを記述し、この単体テストに対してコードを記述することにより、新しい機能をアプリケーションに追加します。 このイテレーションでは、連絡先グループを追加します。

  • イテレーション #7 - Ajax 機能を追加します。 7 番目のイテレーションでは、Ajax のサポートを追加することで、アプリケーションの応答性とパフォーマンスを向上させます。

このイテレーション

Contact Manager アプリケーションのこの 2 回目のイテレーションでは、基本的なフォームの検証を追加します。 必須のフォーム フィールドの値を入力しないとユーザーが連絡先を送信できないようにします。 電話番号とメール アドレスも検証します (図 1 を参照)。

The New Project dialog box

図 01: 検証を備えたフォーム (クリックするとフルサイズの画像が表示されます)

このイテレーションでは、検証ロジックをコントローラー アクションに直接追加します。 一般に、これは、ASP.NET MVC アプリケーションに検証を追加するのに推奨される方法ではありません。 より適切なアプローチは、アプリケーションの検証ロジックを個別のサービス レイヤー内に配置することです。 次のイテレーションでは、Contact Manager アプリケーションをリファクターして、アプリケーションをより維持しやすくします。

このイテレーションでは、物事をシンプルにするために、すべての検証コードを手動で記述します。 検証コードを自分で記述する代わりに、検証フレームワークを活用できます。 たとえば、Microsoft Enterprise Library Validation Application Block (VAB) を使用して、ASP.NET MVC アプリケーションの検証ロジックを実装できます。 Validation Application Block の詳細については、次を参照してください。

http://msdn.microsoft.com/library/dd203099.aspx

Create ビューへの検証の追加

まず、Create ビューに検証ロジックを追加します。 さいわい、Visual Studio を使用して Create ビューを生成したため、この Create ビューには検証メッセージを表示するために必要なすべてのユーザー インターフェイス ロジックが既に含まれています。 この Create ビューはリスト 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() ヘルパーによってレンダリングされる、テキスト ボックス (入力) のスタイルを設定するために使用されます。 validation-summary-errors クラスは、Html.ValidationSummary() ヘルパーによってレンダリングされる、順序指定されていないリストのスタイルを設定するために使用されます。

Note

このセクションで説明するスタイル シート クラスを変更して、検証エラー メッセージの外観をカスタマイズできます。

Create アクションへの検証ロジックの追加

現時点では、メッセージを生成するロジックを記述していないため、この Create ビューに検証エラー メッセージは表示されません。 検証エラー メッセージを表示するには、ModelState にエラー メッセージを追加する必要があります。

Note

UpdateModel() メソッドは、フォーム フィールドの値をプロパティに割り当てる際にエラーが発生した場合、ModelState にエラー メッセージを自動的に追加します。 たとえば、DateTime 値を受け取る BirthDate プロパティに文字列 "apple" を割り当てようとすると、UpdateModel() メソッドは ModelState にエラーを追加します。

リスト 2 の中の変更された Create() メソッドには、新しい連絡先がデータベースに挿入される前に Contact クラスのプロパティを検証する新しいセクションが含まれています。

リスト 2 - Controllers\ContactController.vb (検証を備えた Create)

<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

この検証セクションでは、次の 4 つの異なる検証規則が適用されます。

  • FirstName プロパティの長さは 0 より大きくする必要があります (かつスペースのみで構成することはできません)
  • LastName プロパティの長さは 0 より大きくする必要があります (かつスペースのみで構成することはできません)
  • Phone プロパティに値 (長さが 0 より大きい) がある場合、Phone プロパティは正規表現と一致する必要があります。
  • Email プロパティに値 (長さが 0 より大きい) がある場合、Email プロパティは正規表現と一致する必要があります。

検証規則違反がある場合は、AddModelError() メソッドを使用して、エラー メッセージを ModelState に追加します。 ModelState にメッセージを追加する場合は、プロパティの名前と検証エラー メッセージのテキストを指定します。 このエラー メッセージは、Html.ValidationSummary() および Html.ValidationMessage() ヘルパー メソッドによってビューの中に表示されます。

検証規則が実行されると、ModelState の IsValid プロパティがチェックされます。 IsValid プロパティは、何か検証エラー メッセージが ModelState に追加された場合に false を返します。 検証が失敗した場合、Create フォームはエラー メッセージと共に再表示されます。

Note

正規表現リポジトリ http://regexlib.com から電話番号とメールアドレスを検証するための正規表現を取得しました

Edit アクションへの検証ロジックの追加

Edit() アクションは連絡先を更新します。 Edit() アクションは、Create() アクションとまったく同じ検証を実行する必要があります。 同じ検証コードを複製するのではなく、Create() アクションと Edit() アクションの両方が同じ検証メソッドを呼び出すように、Contact コントローラーをリファクターする必要があります。

変更された Contact コントローラー クラスはリスト 3 の中に含まれています。 このクラスには、Create() および Edit() アクションの両方の中で呼び出される新しい ValidateContact() メソッドがあります。

リスト 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 アプリケーションに検証ロジックを追加しました。 ただし、検証ロジックとコントローラー ロジックを混在させると、長期的には問題が発生します。 このアプリケーションは、時間の経過と共に維持および変更することがより困難になります。

次のイテレーションでは、検証ロジックとデータベース アクセス ロジックをコントローラーからリファクターします。 いくつかのソフトウェア設計原則を活用して、より疎結合でより維持しやすいアプリケーションを作成できるようにします。