Iteracja 3 — Dodawanie weryfikacji formularza (C#)

autor: Microsoft

Pobierz kod

W trzeciej iteracji dodamy podstawową walidację formularza. Uniemożliwiamy użytkownikom przesyłanie formularza bez wypełniania wymaganych pól formularza. Weryfikujemy również adresy e-mail i numery telefonów.

Tworzenie aplikacji MVC do zarządzania kontaktami ASP.NET (C#)

W tej serii samouczków utworzymy całą aplikację do zarządzania kontaktami od początku do końca. Aplikacja Contact Manager umożliwia przechowywanie informacji kontaktowych — nazw, numerów telefonów i adresów e-mail — dla listy osób.

Tworzymy aplikację za pośrednictwem wielu iteracji. Po każdej iteracji stopniowo ulepszamy aplikację. Celem tego podejścia iteracji wielokrotnej jest umożliwienie zrozumienia przyczyny każdej zmiany.

  • Iteracja #1 — tworzenie aplikacji. W pierwszej iteracji utworzymy menedżera kontaktów w najprostszy możliwy sposób. Dodamy obsługę podstawowych operacji bazy danych: tworzenie, odczytywanie, aktualizowanie i usuwanie (CRUD).

  • Iteracja #2 — sprawia, że aplikacja wygląda ładnie. W tej iteracji poprawiamy wygląd aplikacji, modyfikując domyślną stronę wzorcową ASP.NET widoku MVC i kaskadowy arkusz stylów.

  • Iteracja #3 — dodawanie walidacji formularza. W trzeciej iteracji dodamy podstawową walidację formularza. Uniemożliwiamy użytkownikom przesyłanie formularza bez wypełniania wymaganych pól formularza. Weryfikujemy również adresy e-mail i numery telefonów.

  • Iteracja #4 — luźno połącz aplikację. W tej czwartej iteracji korzystamy z kilku wzorców projektowania oprogramowania, aby ułatwić konserwację i modyfikowanie aplikacji Contact Manager. Na przykład refaktoryzujemy naszą aplikację, aby używać wzorca repozytorium i wzorca wstrzykiwania zależności.

  • Iteracja #5 — tworzenie testów jednostkowych. W piątej iteracji ułatwiamy konserwację i modyfikowanie aplikacji przez dodanie testów jednostkowych. Wyśmiewamy nasze klasy modelu danych i kompilujemy testy jednostkowe dla naszych kontrolerów i logiki walidacji.

  • Iteracja nr 6 — korzystanie z programowania opartego na testach. W tej szóstej iteracji dodamy nową funkcjonalność do naszej aplikacji, pisząc najpierw testy jednostkowe i pisząc kod względem testów jednostkowych. W tej iteracji dodajemy grupy kontaktów.

  • Iteracja #7 — dodawanie funkcji Ajax. W siódmej iteracji poprawiamy czas reakcji i wydajność naszej aplikacji, dodając obsługę Ajax.

Ta iteracja

W tej drugiej iteracji aplikacji Contact Manager dodamy podstawową walidację formularza. Uniemożliwiamy osobom przesyłanie kontaktu bez wprowadzania wartości wymaganych pól formularza. Weryfikujemy również numery telefonów i adresy e-mail (zobacz Rysunek 1).

Okno dialogowe Nowy projekt

Rysunek 01. Formularz z walidacją (kliknij, aby wyświetlić obraz pełnowymiarowy)

W tej iteracji dodamy logikę weryfikacji bezpośrednio do akcji kontrolera. Ogólnie rzecz biorąc, nie jest to zalecany sposób dodawania weryfikacji do aplikacji ASP.NET MVC. Lepszym rozwiązaniem jest umieszczenie logiki walidacji aplikacji w oddzielnej warstwie usługi. W następnej iteracji refaktoryzujemy aplikację Contact Manager, aby zapewnić obsługę aplikacji.

W tej iteracji, aby zachować prostotę, napiszemy cały kod weryfikacji ręcznie. Zamiast pisać kod weryfikacyjny, możemy skorzystać z platformy weryfikacji. Można na przykład użyć bloku aplikacji weryfikacji biblioteki przedsiębiorstwa firmy Microsoft (VAB), aby zaimplementować logikę walidacji dla aplikacji ASP.NET MVC. Aby dowiedzieć się więcej na temat bloku aplikacji weryfikacji, zobacz:

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

Dodawanie walidacji do widoku tworzenia

Zacznijmy od dodania logiki walidacji do widoku Tworzenie. Na szczęście, ponieważ wygenerowaliśmy widok Tworzenie za pomocą programu Visual Studio, widok Tworzenie zawiera już całą niezbędną logikę interfejsu użytkownika do wyświetlania komunikatów weryfikacji. Widok Utwórz znajduje się na liście 1.

Lista 1 — \Views\Contact\Create.aspx

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<ContactManager.Models.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 class="fields">
            <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>

    <% } %>

</asp:Content>

Zwróć uwagę na wywołanie metody pomocnika Html.ValidationSummary(), która pojawia się bezpośrednio nad formularzem HTML. Jeśli istnieją komunikaty o błędach weryfikacji, ta metoda wyświetla komunikaty weryfikacji na liście punktowanej.

Zwróć uwagę na wywołania html.ValidationMessage(), które są wyświetlane obok każdego pola formularza. Pomocnik ValidationMessage() wyświetla indywidualny komunikat o błędzie weryfikacji. W przypadku listy 1 gwiazdka jest wyświetlana, gdy wystąpi błąd weryfikacji.

Na koniec pomocnik Html.TextBox() automatycznie renderuje klasę Arkusza stylów kaskadowych, gdy występuje błąd walidacji skojarzony z właściwością wyświetlaną przez pomocnika. Pomocnik Html.TextBox() renderuje klasę o nazwie input-validation-error.

Podczas tworzenia nowej aplikacji ASP.NET MVC zostanie automatycznie utworzony arkusz stylów o nazwie Site.css w folderze Content. Ten arkusz stylów zawiera następujące definicje klas CSS związane z pojawieniem się komunikatów o błędach weryfikacji:

.field-validation-error
{
    color: #ff0000;
}

.input-validation-error
{
    border: 1px solid #ff0000;
    background-color: #ffeeee;
}

.validation-summary-errors
{
    font-weight: bold;
    color: #ff0000;
}

Klasa field-validation-error służy do stylu danych wyjściowych renderowanych przez pomocnika Html.ValidationMessage(). Klasa input-validation-error służy do stylu pola tekstowego (danych wejściowych) renderowanych przez pomocnik Html.TextBox(). Klasa validation-summary-errors służy do stylu listy nieurządkowanej renderowanej przez pomocnik Html.ValidationSummary().

Uwaga

Klasy arkuszy stylów opisane w tej sekcji można zmodyfikować, aby dostosować wygląd komunikatów o błędach weryfikacji.

Dodawanie logiki walidacji do akcji tworzenia

W tej chwili widok Tworzenie nigdy nie wyświetla komunikatów o błędach weryfikacji, ponieważ nie napisaliśmy logiki w celu wygenerowania żadnych komunikatów. Aby wyświetlić komunikaty o błędach weryfikacji, należy dodać komunikaty o błędach do elementu ModelState.

Uwaga

Metoda UpdateModel() automatycznie dodaje komunikaty o błędach do elementu ModelState, gdy występuje błąd podczas przypisywania wartości pola formularza do właściwości. Jeśli na przykład próbujesz przypisać ciąg "apple" do właściwości BirthDate, która akceptuje wartości DateTime, metoda UpdateModel() dodaje błąd do modelu ModelState.

Zmodyfikowana metoda Create() w liście List 2 zawiera nową sekcję, która weryfikuje właściwości klasy Contact przed wstawieniem nowego kontaktu do bazy danych.

Lista 2 — Controllers\ContactController.cs (Utwórz przy użyciu walidacji)

//
// POST: /Contact/Create

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Contact contactToCreate)
{
    // Validation logic
    if (contactToCreate.FirstName.Trim().Length == 0)
        ModelState.AddModelError("FirstName", "First name is required.");
    if (contactToCreate.LastName.Trim().Length == 0)
        ModelState.AddModelError("LastName", "Last name is required.");
    if (contactToCreate.Phone.Length > 0 && !Regex.IsMatch(contactToCreate.Phone, @"((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}"))
        ModelState.AddModelError("Phone", "Invalid phone number.");
    if (contactToCreate.Email.Length > 0 && !Regex.IsMatch(contactToCreate.Email, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
        ModelState.AddModelError("Email", "Invalid email address.");
    if (!ModelState.IsValid)
        return View();

    // Database logic
    try
    {
        _entities.AddToContactSet(contactToCreate);
        _entities.SaveChanges();
        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}

Sekcja sprawdzania poprawności wymusza cztery odrębne reguły walidacji:

  • Właściwość FirstName musi mieć długość większą niż zero (i nie może składać się wyłącznie z spacji)
  • Właściwość LastName musi mieć długość większą niż zero (i nie może składać się wyłącznie z spacji)
  • Jeśli właściwość Phone ma wartość (ma długość większą niż 0), właściwość Phone musi być zgodna z wyrażeniem regularnym.
  • Jeśli właściwość Email ma wartość (ma długość większą niż 0), właściwość Email musi być zgodna z wyrażeniem regularnym.

W przypadku naruszenia reguły sprawdzania poprawności komunikat o błędzie jest dodawany do modelu ModelState przy użyciu metody AddModelError(). Po dodaniu komunikatu do elementu ModelState należy podać nazwę właściwości i tekst komunikatu o błędzie weryfikacji. Ten komunikat o błędzie jest wyświetlany w widoku przez metody pomocnika Html.ValidationSummary() i Html.ValidationMessage().

Po wykonaniu reguł walidacji właściwość IsValid elementu ModelState jest sprawdzana. Właściwość IsValid zwraca wartość false, gdy wszystkie komunikaty o błędach weryfikacji zostały dodane do elementu ModelState. Jeśli walidacja zakończy się niepowodzeniem, formularz Tworzenie zostanie ponownie wyeksponowy z komunikatami o błędach.

Uwaga

Mam wyrażenia regularne do weryfikowania numeru telefonu i adresu e-mail z repozytorium wyrażeń regularnych pod adresem http://regexlib.com

Dodawanie logiki walidacji do akcji edycji

Akcja Edit() aktualizuje kontakt. Akcja Edit() musi wykonać dokładnie tę samą walidację co akcja Create(). Zamiast duplikować ten sam kod weryfikacji, należy refaktoryzować kontroler kontaktów, aby akcje Create() i Edit() wywoływać tę samą metodę weryfikacji.

Zmodyfikowana klasa kontrolera kontaktów znajduje się na liście 3. Ta klasa ma nową metodę ValidateContact(), która jest wywoływana zarówno w ramach akcji Create() i Edit().

Lista 3 — Controllers\ContactController.cs

using System.Linq;
using System.Text.RegularExpressions;
using System.Web.Mvc;
using ContactManager.Models;

namespace ContactManager.Controllers
{
    public class ContactController : Controller
    {
        private ContactManagerDBEntities _entities = new ContactManagerDBEntities();

        protected void ValidateContact(Contact contactToValidate)
        {
            if (contactToValidate.FirstName.Trim().Length == 0)
                ModelState.AddModelError("FirstName", "First name is required.");
            if (contactToValidate.LastName.Trim().Length == 0)
                ModelState.AddModelError("LastName", "Last name is required.");
            if (contactToValidate.Phone.Length > 0 && !Regex.IsMatch(contactToValidate.Phone, @"((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}"))
                ModelState.AddModelError("Phone", "Invalid phone number.");
            if (contactToValidate.Email.Length > 0 && !Regex.IsMatch(contactToValidate.Email, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
                ModelState.AddModelError("Email", "Invalid email address.");
        }

        public ActionResult Index()
        {
            return View(_entities.ContactSet.ToList());
        }

        public ActionResult Create()
        {
            return View();
        } 

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create([Bind(Exclude = "Id")] Contact contactToCreate)
        {
            // Validation logic
            ValidateContact(contactToCreate);
            if (!ModelState.IsValid)
                return View();

            // Database logic
            try
            {
                _entities.AddToContactSet(contactToCreate);
                _entities.SaveChanges();
                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

        public ActionResult Edit(int id)
        {
            var contactToEdit = (from c in _entities.ContactSet
                                   where c.Id == id
                                   select c).FirstOrDefault();

            return View(contactToEdit);
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(Contact contactToEdit)
        {
            ValidateContact(contactToEdit);
            if (!ModelState.IsValid)
                return View();

            try
            {
                var 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();
            }
        }

        public ActionResult Delete(int id)
        {
            var contactToDelete = (from c in _entities.ContactSet
                                 where c.Id == id
                                 select c).FirstOrDefault();

            return View(contactToDelete);
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Delete(Contact contactToDelete)
        {
            try
            {
                var 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();
            }
        }

    }
}

Podsumowanie

W tej iteracji dodaliśmy podstawową walidację formularza do naszej aplikacji Contact Manager. Nasza logika walidacji uniemożliwia użytkownikom przesyłanie nowego kontaktu lub edytowanie istniejącego kontaktu bez podawania wartości właściwości FirstName i LastName. Ponadto użytkownicy muszą podać prawidłowe numery telefonów i adresy e-mail.

W tej iteracji dodaliśmy logikę walidacji do naszej aplikacji Contact Manager w najprostszy możliwy sposób. Jednak mieszanie logiki walidacji do logiki kontrolera spowoduje powstanie problemów w dłuższej perspektywie. Nasza aplikacja będzie trudniejsza do utrzymania i modyfikowania w czasie.

W następnej iteracji refaktoryzujemy logikę walidacji i logikę dostępu do bazy danych z naszych kontrolerów. Skorzystamy z kilku zasad projektowania oprogramowania, aby umożliwić nam tworzenie bardziej luźno sprzężonych i bardziej konserwowalnych aplikacji.