Поделиться через


Добавление логики проверки в модель фильма

Рик Андерсон

Примечание.

Обновленная версия этого руководства доступна здесь , где используется ASP.NET MVC 5 и Visual Studio 2013. Это более безопасно, гораздо проще следовать и демонстрирует больше функций.

В этом разделе вы добавите логику проверки в Movie модель и убедитесь, что правила проверки применяются в любое время, когда пользователь пытается создать или изменить фильм с помощью приложения.

Сохранение вещей DRY

Одним из основных принципов дизайна ASP.NET MVC является DRY ("Не повторять себя"). ASP.NET MVC рекомендует указывать функциональные возможности или поведение только один раз, а затем отражать его везде в приложении. Это сокращает объем кода, который необходимо написать и делает код менее подверженным ошибкам и упрощает обслуживание.

Поддержка проверки, предоставляемая ASP.NET MVC и Entity Framework Code First, является отличным примером принципа DRY в действии. Правила проверки можно декларативно указывать в одном месте (в классе модели) и правила применяются везде в приложении.

Давайте рассмотрим, как воспользоваться этой поддержкой проверки в приложении фильма.

Добавление правил проверки в модель фильма

Начнем с добавления логики проверки в Movie класс.

Откройте файл Movie.cs. using Добавьте инструкцию в верхней части файла, ссылающегося System.ComponentModel.DataAnnotations на пространство имен:

using System.ComponentModel.DataAnnotations;

Обратите внимание, что пространство имен не содержит System.Web. DataAnnotations предоставляет встроенный набор атрибутов проверки, которые можно применять декларативно к любому классу или свойству.

Теперь обновите Movie класс, чтобы воспользоваться встроенными Requiredатрибутами и StringLengthRange атрибутами проверки. Используйте следующий код в качестве примера того, где применять атрибуты.

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; }
}

Запустите приложение, и вы снова получите следующую ошибку времени выполнения:

Модель, которая поддерживает контекст MovieDBContext, изменилась с момента создания базы данных. Рекомендуется использовать кодовые миграции для обновления базы данных (https://go.microsoft.com/fwlink/?LinkId=238269).

Мы будем использовать миграции для обновления схемы. Создайте решение, а затем откройте окно консоли диспетчер пакетов и введите следующие команды:

add-migration AddDataAnnotationsMig
update-database

После завершения этой команды Visual Studio открывает файл класса, определяющий новый DbMigration производный класс с указанным именем (AddDataAnnotationsMig), а в Up методе можно увидеть код, обновляющий ограничения схемы. Поля Title Genre больше не имеют значения NULL (т. е. необходимо ввести значение), а Rating поле имеет максимальную длину 5.

Атрибуты проверки определяют поведение для свойств модели, к которым они применяются. Атрибут указывает, что свойство должно иметь значение. В Required этом примере фильм должен иметь значения для Titleсвойств , ReleaseDateGenreи Price свойств, чтобы быть допустимыми. атрибут Range ограничивает значения указанным диапазоном. Атрибут StringLength позволяет задать максимальную и при необходимости минимальную длину строкового свойства. Встроенные типы (например decimal, int, float, DateTime) требуются по умолчанию и не требуют атрибута Required .

Code First гарантирует, что правила проверки, указанные в классе модели, применяются перед сохранением изменений в базе данных приложением. Например, приведенный ниже код создает исключение при SaveChanges вызове метода, так как отсутствуют несколько обязательных Movie значений свойств, а цена равна нулю (которая выходит из допустимого диапазона).

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  

Автоматическое применение правил проверки платформа .NET Framework помогает сделать приложение более надежным. Это также гарантирует, что в любом случае будут выполнены все проверки и в базе данных не будут случайно оставлены поврежденные данные.

Ниже приведен полный список кода для обновленного файла 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; }
    }
}

Пользовательский интерфейс ошибки проверки в ASP.NET MVC

Повторно запустите приложение и перейдите по URL-адресу /Movies .

Щелкните ссылку "Создать" , чтобы добавить новый фильм. Заполните форму недопустимыми значениями и нажмите кнопку "Создать ".

8_validationErrors

Примечание.

для поддержки проверки jQuery для языковых стандартов, отличных от английского языка, использующих запятую (",") для десятичной запятой, необходимо включить globalize.js и определенные язык и региональные параметры/globalize.cultures.js(из https://github.com/jquery/globalize ) и JavaScript для использованияGlobalize.parseFloat. В следующем коде показаны изменения файла Views\Movies\Edit.cshtml для работы с языком и региональными параметрами 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>
}

Обратите внимание, что форма автоматически использовала красный цвет границы, чтобы выделить текстовые поля, содержащие недопустимые данные и создав соответствующее сообщение об ошибке проверки рядом с каждым. Эти ошибки применяются как на стороне клиента (с помощью JavaScript и jQuery), так и на стороне сервера (если пользователь отключает JavaScript).

Реальное преимущество заключается в том, что вам не нужно изменять одну строку кода в MoviesController классе или в представлении Create.cshtml , чтобы включить этот пользовательский интерфейс проверки. В контроллере и представлениях, создаваемых в рамках этого руководства, автоматически применяются правила проверки, для определения которых к свойствам класса модели Movie были применены атрибуты.

Возможно, вы заметили для свойств Title и Genreобязательный атрибут не применяется, пока не отправьте форму (нажмите кнопку "Создать ") или введите текст в поле ввода и удалите его. Для поля, которое изначально пусто (например, поля в представлении create) и которое имеет только обязательный атрибут и никакие другие атрибуты проверки, можно выполнить следующее, чтобы активировать проверку:

  1. Вкладка в поле.
  2. Введите текст.
  3. Выйдите из поля с помощью клавиши TAB.
  4. Назад в поле перейдите на вкладку.
  5. Удалите текст.
  6. Выйдите из поля с помощью клавиши TAB.

Приведенная выше последовательность активирует необходимую проверку без нажатия кнопки отправки. Просто нажимая кнопку отправки, не вводя ни одного из полей, активирует проверку на стороне клиента. Данные формы передаются на сервер только после того, как будут устранены любые ошибки на стороне клиента. Это можно проверить, поместив точку останова в метод HTTP Post или используя средство fiddler или средства разработчика IE 9 F12.

Снимок экрана: страница создания фильма M V C. Оповещение рядом с заголовком указывает, что поле

Как выполняется проверка в методе создания представления и создания действия

Вам может быть интересно, как пользовательский интерфейс проверки создается без обновления кода контроллера или представлений. В следующем списке MovieController показано, как Create выглядят методы в классе. Они не изменяются из того, как вы создали их ранее в этом руководстве.

//
// 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);
}

Первый метод действия Create (HTTP GET) отображает исходную форму создания. Вторая версия ([HttpPost]) обрабатывает передачу формы. Второй метод Create (версия HttpPost) вызывает ModelState.IsValid, который определяет наличие ошибок проверки в фильме. При вызове этого метода оцениваются все атрибуты проверки, которые были применены к объекту. При наличии ошибок проверки в объекте метод Create повторно отображает форму. Если ошибок нет, метод сохраняет новый фильм в базе данных. В нашем примере фильма мы используем, форма не размещается на сервере, когда на стороне клиента обнаружены ошибки проверки; второй Createметод никогда не вызывается. Если вы отключите JavaScript в браузере, проверка клиента отключена, а метод HTTP POST Create вызывает ModelState.IsValid , чтобы проверить наличие ошибок проверки в фильме.

Вы можете установить точку останова в метод HttpPost Create и убедиться, что он не вызывается и данные формы не передаются, если на стороне клиента присутствуют ошибки проверки. Если отключить JavaScript в браузере и отправить форму с ошибками, будет достигнута точка останова. Без JavaScript вы по-прежнему будете получать полную проверку. На следующем рисунке показано, как отключить JavaScript в Internet Explorer.

Снимок экрана: окно

Снимок экрана: запись H t t p. Если точка состояния модели является допустимой, выделена.

На следующем рисунке показано, как отключить JavaScript в браузере FireFox.

Снимок экрана: окно

На следующем рисунке показано, как отключить JavaScript с помощью браузера Chrome.

Снимок экрана: страница

Ниже приведен шаблон представления Create.cshtml , который вы создали ранее в руководстве. Он используется в показанных выше методах действия для отображения исходной формы и повторного вывода формы в случае ошибки.

@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>

Обратите внимание, что код использует вспомогательный Html.EditorFor элемент для вывода <input> элемента для каждого Movie свойства. Рядом с этим вспомогательным методом является вызов вспомогательного Html.ValidationMessageFor метода. Эти два вспомогательных метода работают с объектом модели, передаваемым контроллером в представление (в данном случае Movie — объект). Они автоматически ищут атрибуты проверки, указанные в модели, и отображают сообщения об ошибках соответствующим образом.

Что действительно хорошо об этом подходе заключается в том, что ни контроллер, ни шаблон создания представления ничего не знает о фактических правилах проверки, применяемых или о конкретных сообщениях об ошибках, отображаемых. Правила проверки и строки ошибок указываются только в классе Movie. Эти же правила проверки автоматически применяются к представлению "Изменить" и к любым другим шаблонам представлений, которые можно создать, изменив модель.

Если вы хотите изменить логику проверки позже, это можно сделать ровно в одном месте, добавив атрибуты проверки в модель (в этом примере movie класс). Вам не придется беспокоиться о несогласованности применения правил в различных частях приложения, поскольку вся логика проверки будет определена в одном месте и начнет применяться по всему приложению. Это позволяет максимально оптимизировать код и обеспечить удобство его совершенствования и поддержки. Кроме того, таким образом вы будете полностью соблюдать требования принципа "Не повторяйся".

Добавление форматирования в модель фильма

Откройте файл Movie.cs и проверьте класс Movie. В пространстве имен System.ComponentModel.DataAnnotations в дополнение к набору встроенных атрибутов проверки предоставляются атрибуты форматирования. К полям с датой выпуска и ценой уже применено значение перечисления DataType. В следующем коде показаны свойства ReleaseDate и Price с соответствующим атрибутом DisplayFormat.

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

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

Атрибуты DataType не являются атрибутами проверки, они используются для того, чтобы сообщить обработчику представлений, как отрисовка HTML. В приведенном выше DataType.Date примере атрибут отображает даты фильма только в виде дат без времени. Например, следующие DataType атрибуты не проверяют формат данных:

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

Перечисленные выше атрибуты предоставляют только указания для обработчика представлений для форматирования данных (и предоставления атрибутов, таких как <> URL-адрес и <href="mailto:EmailAddress.com"> для электронной почты. Атрибут RegularExpression можно использовать для проверки формата данных.

Альтернативный подход к использованию DataType атрибутов можно явно задать DataFormatString значение. В следующем коде показано свойство даты выпуска со строкой формата даты (например, "d"). Это можно использовать для указания того, что вы не хотите время в рамках даты выпуска.

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

Movie Полный класс показан ниже.

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; }
}

Запустите приложение и перейдите к контроллеру Movies . Дата выпуска и цена хорошо отформатированы. На рисунке ниже показана дата выпуска и цена с помощью fr-FR в качестве языка и региональных параметров.

8_format_SM

На рисунке ниже показаны те же данные, которые отображаются с языком и региональными параметрами по умолчанию (английский язык США).

Снимок экрана: страница индекса фильма M V C с четырьмя фильмами, перечисленными в списке.

В следующей части этой серии мы рассмотрим приложение и внесем ряд изменений в автоматически создаваемые методы Details и Delete.