Dodawanie logiki weryfikacji do modelu filmowego

Autor: Rick Anderson

Uwaga

Zaktualizowana wersja tego samouczka jest dostępna tutaj, która używa ASP.NET MVC 5 i Visual Studio 2013. Jest ona bezpieczniejsza, znacznie łatwiejsza do naśladowania i demonstruje więcej funkcji.

W tej sekcji dodasz logikę walidacji do Movie modelu i upewnisz się, że reguły weryfikacji są wymuszane za każdym razem, gdy użytkownik próbuje utworzyć lub edytować film przy użyciu aplikacji.

Utrzymywanie suchych rzeczy

Jeden z podstawowych zestawów projektowych ASP.NET MVC to DRY ("Nie powtarzaj siebie"). ASP.NET MVC zachęca do określania funkcjonalności lub zachowania tylko raz, a następnie ich odzwierciedlenia wszędzie w aplikacji. Zmniejsza to ilość kodu potrzebnego do pisania i sprawia, że kod jest mniej podatny na błędy i ułatwia konserwację.

Obsługa walidacji zapewniana przez ASP.NET MVC i Entity Framework Code First to doskonały przykład akcji zasady DRY. Reguły weryfikacji można deklaratywne określić w jednym miejscu (w klasie modelu), a reguły są wymuszane wszędzie w aplikacji.

Przyjrzyjmy się, jak można skorzystać z tej obsługi walidacji w aplikacji filmowej.

Dodawanie reguł walidacji do modelu filmowego

Zaczniesz od dodania logiki weryfikacji do Movie klasy.

Otwórz plik Movie.cs . Dodaj instrukcję using w górnej części pliku, który odwołuje się do System.ComponentModel.DataAnnotations przestrzeni nazw:

using System.ComponentModel.DataAnnotations;

Zwróć uwagę, że przestrzeń nazw nie zawiera System.Webelementu . Funkcja DataAnnotations udostępnia wbudowany zestaw atrybutów weryfikacji, które można deklaratywnie zastosować do dowolnej klasy lub właściwości.

Teraz zaktualizuj klasę Movie , aby korzystać z wbudowanych Requiredatrybutów , StringLengthi Range walidacji. Użyj następującego kodu jako przykładu miejsca zastosowania atrybutów.

public class Movie {
    public int ID { get; set; }

    [Required]
    public string Title { get; set; }

    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [Required]
    public string Genre { get; set; }

    [Range(1, 100)]
    [DataType(DataType.Currency)]
    public decimal Price { get; set; }

    [StringLength(5)]
    public string Rating { get; set; }
}

Uruchom aplikację i ponownie zostanie wyświetlony następujący błąd czasu wykonywania:

Model tworzący kopię zapasową kontekstu "MovieDBContext" zmienił się od czasu utworzenia bazy danych. Rozważ użycie Migracje Code First do zaktualizowania bazy danych (https://go.microsoft.com/fwlink/?LinkId=238269).

Użyjemy migracji do zaktualizowania schematu. Skompiluj rozwiązanie, a następnie otwórz okno Konsola menedżera pakietów i wprowadź następujące polecenia:

add-migration AddDataAnnotationsMig
update-database

Po zakończeniu tego polecenia program Visual Studio otwiera plik klasy definiujący nową DbMigration klasę pochodną o określonej nazwie (AddDataAnnotationsMig), a w Up metodzie można zobaczyć kod, który aktualizuje ograniczenia schematu. Pola Title i Genre nie są już dopuszczające wartości null (oznacza to, że należy wprowadzić wartość), a Rating pole ma maksymalną długość 5.

Atrybuty weryfikacji określają zachowanie, do którego mają być wymuszane właściwości modelu. Atrybut wskazuje, że właściwość musi mieć wartość. W Required tym przykładzie film musi mieć wartości właściwości Title, ReleaseDate, Genrei Price , aby być prawidłowe. Atrybut Range ogranicza wartość do określonego zakresu. Atrybut StringLength umożliwia ustawienie maksymalnej długości właściwości ciągu i opcjonalnie jej minimalnej długości. Typy wewnętrzne (takie jak decimal, int, float, DateTime) są domyślnie wymagane i nie wymagają atrybutu Required .

Code First gwarantuje, że reguły weryfikacji określone w klasie modelu są wymuszane przed zapisaniem zmian w bazie danych przez aplikację. Na przykład poniższy kod zgłosi wyjątek po SaveChanges wywołaniu metody, ponieważ brakuje kilku wymaganych Movie wartości właściwości, a cena jest równa zero (która jest poza prawidłowym zakresem).

MovieDBContext db = new MovieDBContext();

Movie movie = new Movie();
movie.Title = "Gone with the Wind";
movie.Price = 0.0M;

db.Movies.Add(movie);  
db.SaveChanges();        // <= Will throw server side validation exception  

Automatyczne wymuszanie reguł weryfikacji przez .NET Framework pomaga zwiększyć niezawodność aplikacji. Gwarantuje to również, że nie można zapomnieć o zweryfikowaniu czegoś i przypadkowo zezwolić na nieprawidłowe dane w bazie danych.

Oto pełna lista kodu dla zaktualizowanego pliku Movie.cs :

using System;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models {
    public class Movie {
        public int ID { get; set; }

        [Required]
        public string Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }

        [Required]
        public string Genre { get; set; }

        [Range(1, 100)]
        [DataType(DataType.Currency)]
        public decimal Price { get; set; }

        [StringLength(5)]
        public string Rating { get; set; }
    }

    public class MovieDBContext : DbContext {
        public DbSet<Movie> Movies { get; set; }
    }
}

Interfejs użytkownika błędu walidacji w usłudze ASP.NET MVC

Uruchom ponownie aplikację i przejdź do adresu URL /Movies .

Kliknij link Utwórz nowy , aby dodać nowy film. Wypełnij formularz nieprawidłowymi wartościami, a następnie kliknij przycisk Utwórz .

8_validationErrors

Uwaga

aby obsługiwać walidację jQuery dla ustawień regionalnych innych niż angielski, które używają przecinka (",") dla punktu dziesiętnego, należy uwzględnić globalize.js i określone kultury/globalize.cultures.js plików(z https://github.com/jquery/globalize ) i JavaScript do użycia Globalize.parseFloat. Poniższy kod przedstawia modyfikacje pliku Views\Movies\Edit.cshtml do pracy z kulturą "fr-FR":

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
    <script src="~/Scripts/globalize.js"></script>
    <script src="~/Scripts/globalize.culture.fr-FR.js"></script>
    <script>
        $.validator.methods.number = function (value, element) {
            return this.optional(element) ||
                !isNaN(Globalize.parseFloat(value));
        }
        $(document).ready(function () {
            Globalize.culture('fr-FR');
        });
    </script>
    <script>
        jQuery.extend(jQuery.validator.methods, {    
            range: function (value, element, param) {        
                //Use the Globalization plugin to parse the value        
                var val = $.global.parseFloat(value);
                return this.optional(element) || (
                    val >= param[0] && val <= param[1]);
            }
        });

    </script>
}

Zwróć uwagę, że formularz automatycznie używał koloru czerwonego obramowania w celu wyróżnienia pól tekstowych zawierających nieprawidłowe dane i emitował odpowiedni komunikat o błędzie walidacji obok każdego z nich. Błędy są wymuszane zarówno po stronie klienta (przy użyciu języka JavaScript i jQuery) jak i po stronie serwera (w przypadku wyłączenia języka JavaScript przez użytkownika).

Prawdziwą korzyścią jest to, że nie trzeba zmieniać pojedynczego wiersza kodu w MoviesController klasie ani w widoku Create.cshtml w celu włączenia tego interfejsu użytkownika weryfikacji. Kontroler i widoki utworzone wcześniej w tym samouczku automatycznie odebrały reguły sprawdzania poprawności określone przy użyciu atrybutów weryfikacji we właściwościach Movie klasy modelu.

Być może zauważysz, że właściwości Title i Genre, wymagany atrybut nie jest wymuszany, dopóki formularz nie zostanie przesłany (naciśnij przycisk Utwórz ) lub wprowadź tekst w polu wejściowym i go usunął. W przypadku pola, które jest początkowo puste (takie jak pola w widoku Tworzenie) i które ma tylko wymagany atrybut i nie ma innych atrybutów weryfikacji, możesz wykonać następujące czynności, aby wyzwolić walidację:

  1. Karta w polu.
  2. Wprowadź jakiś tekst.
  3. Wyprzedaj kartę.
  4. Karta z powrotem do pola.
  5. Usuń tekst.
  6. Wyprzedaj kartę.

Powyższa sekwencja wyzwoli wymaganą walidację bez naciśnięcia przycisku przesyłania. Po prostu naciśnięcie przycisku przesyłania bez wprowadzania żadnego z pól spowoduje wyzwolenie weryfikacji po stronie klienta. Dane formularza nie są wysyłane do serwera, dopóki nie wystąpią żadne błędy weryfikacji po stronie klienta. Można to przetestować, umieszczając punkt przerwania w metodzie HTTP Post lub za pomocą narzędzia fiddler lub narzędzi deweloperskich IE 9 F12.

Zrzut ekranu przedstawiający stronę Tworzenie filmu W wersji C języka M. Alert obok pozycji Tytuł wskazuje, że pole Tytuł jest wymagane. Alert obok pozycji Gatunek wskazuje, że pole Gatunek jest wymagane.

Jak występuje walidacja w metodzie Create View and Create Action

Możesz się zastanawiać, w jaki sposób interfejs użytkownika weryfikacji został wygenerowany bez żadnych aktualizacji kodu w kontrolerze lub widokach. Następna lista pokazuje, jak Create wyglądają metody w MovieController klasie. Nie zmieniają się one w sposób, w jaki zostały utworzone wcześniej w tym samouczku.

//
// GET: /Movies/Create

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

//
// POST: /Movies/Create

[HttpPost]
public ActionResult Create(Movie movie)
{
    if (ModelState.IsValid)
    {
        db.Movies.Add(movie);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(movie);
}

Pierwsza metoda akcji (HTTP GET) Create wyświetla początkowy formularz Create. Druga wersja ([HttpPost]) obsługuje wpis formularza. Create Druga metoda (wersjaHttpPost) wywołuje, ModelState.IsValid aby sprawdzić, czy film ma jakiekolwiek błędy walidacji. Wywołanie tej metody ocenia wszystkie atrybuty weryfikacji, które zostały zastosowane do obiektu. Jeśli obiekt ma błędy walidacji, Create metoda ponownie wyświetla formularz. Jeśli nie ma żadnych błędów, metoda zapisuje nowy film w bazie danych. W naszym przykładzie filmu używanym formularz nie jest publikowany na serwerze po wykryciu błędów weryfikacji po stronie klienta; drugaCreatemetoda nigdy nie jest wywoływana. Jeśli wyłączysz język JavaScript w przeglądarce, walidacja klienta jest wyłączona i wywołania ModelState.IsValid metody HTTP POST Create w celu sprawdzenia, czy film ma jakiekolwiek błędy weryfikacji.

Można ustawić punkt przerwania w metodzie HttpPost Create i sprawdzić, czy metoda nigdy nie jest wywoływana, walidacja po stronie klienta nie będzie przesyłać danych formularza po wykryciu błędów weryfikacji. Jeśli wyłączysz język JavaScript w przeglądarce, prześlij formularz z błędami, punkt przerwania zostanie trafiony. Nadal uzyskujesz pełną walidację bez języka JavaScript. Na poniższej ilustracji pokazano, jak wyłączyć język JavaScript w programie Internet Explorer.

Zrzut ekranu przedstawiający okno Opcje internetowe otwarte na karcie Zabezpieczenia. Poziom niestandardowy jest zakreśliny na czerwono. W oknie Ustawienia zabezpieczeń aktywne wykonywanie skryptów jest wyłączone. Pasek przewijania jest zakreśliny na czerwono.

Zrzut ekranu przedstawiający wpis H t t p. Jeśli kropka stanu modelu jest prawidłowa, jest wyróżniona.

Na poniższej ilustracji pokazano, jak wyłączyć język JavaScript w przeglądarce FireFox.

Zrzut ekranu przedstawiający okno Opcje. Zawartość jest zaznaczona i zaznaczono pole wyboru Włącz skrypt Java.

Na poniższej ilustracji pokazano, jak wyłączyć język JavaScript za pomocą przeglądarki Chrome.

Zrzut ekranu przedstawiający stronę Opcje. Pod kapturem jest zaznaczona i okrągła w kolorze czerwonym. W obszarze Ustawienia zawartości skrypt java jest ustawiony na zezwalanie wszystkim witrynom na uruchamianie skryptu Java.

Poniżej znajduje się szablon widoku Create.cshtml , który został utworzony wcześniej w samouczku. Jest on używany przez metody akcji pokazane powyżej, aby wyświetlić formularz początkowy i odtworzyć go w przypadku błędu.

@model MvcMovie.Models.Movie

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"></script>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Movie</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.ReleaseDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.ReleaseDate)
            @Html.ValidationMessageFor(model => model.ReleaseDate)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Genre)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Genre)
            @Html.ValidationMessageFor(model => model.Genre)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Price)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Price)
            @Html.ValidationMessageFor(model => model.Price)
        </div>
        <div class="editor-label">
    @Html.LabelFor(model => model.Rating)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.Rating)
    @Html.ValidationMessageFor(model => model.Rating)
</div>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

Zwróć uwagę, jak kod używa Html.EditorFor pomocnika do wyprowadzania <input> elementu dla każdej Movie właściwości. Obok tego pomocnika jest wywołanie Html.ValidationMessageFor metody pomocnika. Te dwie metody pomocnicze działają z obiektem modelu przekazywanym przez kontroler do widoku (w tym przypadku obiektem Movie ). Automatycznie wyszukują atrybuty weryfikacji określone w modelu i wyświetlają odpowiednio komunikaty o błędach.

To, co jest naprawdę miłe w tym podejściu, to to, że ani kontroler, ani szablon Tworzenia widoku nie wie nic o rzeczywistych regułach weryfikacji, które są wymuszane, ani o wyświetlanych konkretnych komunikatach o błędach. Reguły walidacji i ciągi błędów są określone tylko w Movie klasie. Te same reguły sprawdzania poprawności są automatycznie stosowane do widoku Edytuj i innych szablonów widoków, które można utworzyć, aby edytować model.

Jeśli chcesz później zmienić logikę weryfikacji, możesz to zrobić w dokładnie jednym miejscu, dodając atrybuty weryfikacji do modelu (w tym przykładzie movie klasa). Nie trzeba martwić się o niespójność różnych części aplikacji ze sposobem wymuszania reguł — wszystkie logiki weryfikacji będą definiowane w jednym miejscu i używane wszędzie. Dzięki temu kod jest bardzo czysty i ułatwia konserwację i rozwijanie. Oznacza to, że będziesz w pełni honorować zasadę DRY.

Dodawanie formatowania do modelu filmowego

Otwórz plik Movie.cs i sprawdź klasę Movie . System.ComponentModel.DataAnnotations Przestrzeń nazw udostępnia atrybuty formatowania oprócz wbudowanego zestawu atrybutów weryfikacji. Do daty wydania i pól cen zastosowaliśmy DataType już wartość wyliczenia. Poniższy kod przedstawia ReleaseDate właściwości i Price z odpowiednim DisplayFormat atrybutem.

[DataType(DataType.Date)] 
public DateTime ReleaseDate { get; set; }

[DataType(DataType.Currency)] 
public decimal Price { get; set; }

Atrybuty nie są atrybutami weryfikacji. Są DataType one używane do określania aparatu wyświetlania sposobu renderowania kodu HTML. W powyższym DataType.Date przykładzie atrybut wyświetla daty filmu tylko jako daty bez godziny. Na przykład następujące DataType atrybuty nie weryfikują formatu danych:

[DataType(DataType.EmailAddress)]
[DataType(DataType.PhoneNumber)]
[DataType(DataType.Url)]

Atrybuty wymienione powyżej zawierają tylko wskazówki dla aparatu wyświetlania, aby sformatować dane (i podać atrybuty, takie jak <> adres URL i <href="mailto:EmailAddress.com"> dla wiadomości e-mail. Możesz użyć atrybutu RegularExpression , aby zweryfikować format danych.

Alternatywne podejście do używania DataType atrybutów można jawnie ustawić DataFormatString wartość. Poniższy kod przedstawia właściwość daty wydania z ciągiem formatu daty (czyli "d"). Użyjesz tego polecenia, aby określić, że nie chcesz używać godziny w ramach daty wydania.

[DisplayFormat(DataFormatString = "{0:d}")]
public DateTime ReleaseDate { get; set; }

Poniżej przedstawiono kompletną Movie klasę.

public class Movie {
    public int ID { get; set; }

    [Required]
    public string Title { get; set; }

    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [Required]
    public string Genre { get; set; }

    [Range(1, 100)]
    [DataType(DataType.Currency)]
    public decimal Price { get; set; }

    [StringLength(5)]
    public string Rating { get; set; }
}

Uruchom aplikację i przejdź do Movies kontrolera. Data wydania i cena są ładnie sformatowane. Na poniższej ilustracji przedstawiono datę i cenę wydania przy użyciu wartości "fr-FR" jako kultury.

8_format_SM

Na poniższej ilustracji przedstawiono te same dane wyświetlane z kulturą domyślną (angielski stany USA).

Zrzut ekranu przedstawiający stronę M V C Movie Index z czterema wymienionymi filmami.

W następnej części serii przejrzymy aplikację i wprowadzimy pewne ulepszenia automatycznie wygenerowane Details i Delete metody.