Добавление проверки

Рик Андерсон

Примечание

Обновленная версия этого руководства доступна здесь , используя последнюю версию Visual Studio. В новом руководстве используется ASP.NET Core MVC, который предоставляет множество улучшений по сравнению с этим руководством.

В этом руководстве описывается модель MVC ASP.NET Core с контроллерами и представлениями. Razor Pages — это новая альтернатива в ASP.NET Core, модель программирования на основе страниц, которая упрощает и повышает эффективность создания пользовательского веб-интерфейса. Мы рекомендуем ознакомиться с руководством по Razor Pages до версии MVC. Руководство по Razor Pages:

  • проще для выполнения;
  • охватывает дополнительные возможности;
  • Предпочтительный подход к разработке новых приложений.

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

Хранение вещей DRY

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

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

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

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

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

Откройте файл Movie.cs. Обратите внимание, что System.ComponentModel.DataAnnotations пространство имен не содержит System.Web. DataAnnotations предоставляет встроенный набор атрибутов проверки, которые можно декларативно применять к любому классу или свойству. (Он также содержит атрибуты форматирования, такие как DataType , которые помогают в форматировании и не обеспечивают никакой проверки.)

Теперь обновите класс, Movie чтобы воспользоваться встроенными Requiredатрибутами , StringLength, RegularExpression и Range validation. Замените класс Movie следующим кодом:

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

    [StringLength(60, MinimumLength = 3)]
    public string Title { get; set; }

    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    public DateTime ReleaseDate { get; set; }
  
    [RegularExpression(@"^[A-Z]+[a-zA-Z]*$")]
    [Required]
    [StringLength(30)]
    public string Genre { get; set; }

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

    [RegularExpression(@"^[A-Z]+[a-zA-Z]*$")]
    [StringLength(5)]
    public string Rating { get; set; }
}

Атрибут StringLength задает максимальную длину строки и устанавливает это ограничение для базы данных, поэтому схема базы данных изменится. Щелкните правой кнопкой мыши таблицу Movies в обозревателе сервера и выберите команду Открыть определение таблицы:

Снимок экрана: открытое окно серверного Обозреватель на вкладке

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

add-migration DataAnnotations
update-database

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

public override void Up()
{
    AlterColumn("dbo.Movies", "Title", c => c.String(maxLength: 60));
    AlterColumn("dbo.Movies", "Genre", c => c.String(nullable: false, maxLength: 30));
    AlterColumn("dbo.Movies", "Rating", c => c.String(maxLength: 5));
}

Поле Genre больше не допускает значения NULL (то есть необходимо ввести значение). Максимальная Rating длина поля — 5, максимальная Title длина — 60. Минимальная длина 3 в Title и диапазон в Price не создавали изменений схемы.

Изучите схему Movie:

Снимок экрана, на котором показана вкладка

В строковых полях отображаются новые ограничения длины и Genre они больше не проверяются как допускаемые значения NULL.

Атрибуты проверки определяют поведение для свойств модели, к которым они применяются. Атрибуты Required и MinimumLength указывают, что свойство должно иметь значение. Тем не менее, чтобы удовлетворить требованиям проверки, пользователю достаточно ввести пробел. Атрибут RegularExpression используется для ограничения того, какие символы могут быть введены. В приведенном выше коде в полях Genre и Rating можно использовать только буквы (пробелы, числа и специальные символы не допускаются). Атрибут Range ограничивает диапазон значений. Атрибут StringLength позволяет задать максимальную и при необходимости минимальную длину строкового свойства. Типы значений (например decimal, int, float, DateTime, ) являются обязательными и не нуждаются в атрибуте Required .

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

MovieDBContext db = new MovieDBContext();
Movie movie = new Movie();
movie.Title = "Gone with the Wind";
db.Movies.Add(movie);
db.SaveChanges();        // <= Will throw server side validation exception

Приведенный выше код создает следующее исключение:

Сбой проверки для одной или нескольких сущностей. Дополнительные сведения см. в разделе Свойство EntityValidationErrors.

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

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

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

Щелкните ссылку Создать, чтобы добавить новый фильм. Введите в форму какие-либо недопустимые значения. Если функция проверки jQuery на стороне клиента обнаруживает ошибку, сведения о ней отображаются в соответствующем сообщении.

8_validationErrors

Примечание

Для поддержки проверки jQuery для языковых стандартов, отличных от английского, которые используют запятую (",") для десятичной запятой, необходимо включить nuGet globalize, как описано выше в этом руководстве.

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

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

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

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

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

public ActionResult Create()
{
    return View();
}
// POST: /Movies/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
// more details see https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,Title,ReleaseDate,Genre,Price,Rating")] 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 в Обозреватель Интернета.

Снимок экрана: открытое окно

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

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

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

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

Снимок экрана: параметр Java Script и параметр для разрешения или отключения.

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

@model MvcMovie.Models.Movie
@{
    ViewBag.Title = "Create";
}
<h2>Create</h2>
@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()    
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        @Html.ValidationSummary(true)
        <div class="form-group">
            @Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Title)
                @Html.ValidationMessageFor(model => model.Title)
            </div>
        </div>
        @*Fields removed for brevity.*@        




        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

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

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

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

Использование атрибутов DataType

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

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

Атрибуты DataType предоставляют только указания для подсистемы представления о форматировании данных (и предоставляют атрибуты, например <a> для URL-адресов и <a href="mailto:EmailAddress.com"> электронной почты. Для проверки формата данных можно использовать атрибут RegularExpression . Атрибут DataType используется для указания типа данных, который более специфичен, чем встроенный тип базы данных. Они не являются атрибутами проверки. В этом случае требуется отслеживать только дату, а не дату и время. Перечисление DataType предоставляет множество типов данных, таких как Date, Time, PhoneNumber, Currency, EmailAddress и многое другое. Атрибут DataType также обеспечивает автоматическое предоставление функций для определенных типов в приложении. Например, mailto: можно создать ссылку для DataType.EmailAddress, а для DataType.Date можно предоставить селектор даты в браузерах, поддерживающих HTML5. Атрибуты DataType выдают атрибуты HTML 5 data- (произносится тире данных), которые могут понимать браузеры HTML 5. Атрибуты DataType не обеспечивают никакой проверки.

DataType.Date не задает формат отображаемой даты. По умолчанию поле данных отображается в соответствии с форматами по умолчанию на основе CultureInfo сервера.

С помощью атрибута DisplayFormat можно явно указать формат даты:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }

Параметр ApplyFormatInEditMode указывает, что указанное форматирование также должно применяться при отображении значения в текстовом поле для редактирования. (Это может не понадобиться для некоторых полей, например для значений валют, может не потребоваться символ валюты в текстовом поле для редактирования.)

Атрибут DisplayFormat можно использовать сам по себе, но, как правило, рекомендуется также использовать атрибут DataType . Атрибут DataType передает семантику данных, а не способ их отображения на экране, и предоставляет следующие преимущества, которые вы не получаете с помощью DisplayFormat:

  • В браузере можно включить функции HTML5 (например, для отображения элемента управления календарем, символа валюты, соответствующего языкового стандарта, ссылок на электронную почту и т. д.).
  • По умолчанию браузер будет отображать данные в правильном формате в зависимости от вашего языкового стандарта.
  • Атрибут DataType позволяет MVC выбрать правильный шаблон поля для отрисовки данных ( displayFormat , если используется сам по себе, использует шаблон строки). Дополнительные сведения см. в разделе Шаблоны ASP.NET MVC 2 Брэда Уилсона. (Хотя эта статья написана для MVC 2, эта статья по-прежнему относится к текущей версии ASP.NET MVC.)

Если вы используете DataType атрибут с полем даты, необходимо также указать DisplayFormat атрибут , чтобы убедиться, что поле правильно отображается в браузерах Chrome. Дополнительные сведения см. в этом потоке StackOverflow.

Примечание

Проверка jQuery не работает с атрибутом Range и DateTime. Например, следующий код всегда приводит к возникновению ошибки проверки на стороне клиента, даже если дата попадает в указанный диапазон:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

Чтобы использовать атрибут Range с DateTime, необходимо отключить проверку даты jQuery. Как правило, не рекомендуется компилировать жесткие даты в модели, поэтому использовать атрибут Range и DateTime не рекомендуется.

В следующем коде демонстрируется объединение атрибутов в одной строке:

public class Movie
{
   public int ID { get; set; }
   [Required,StringLength(60, MinimumLength = 3)]
   public string Title { get; set; }
   [Display(Name = "Release Date"),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; }
   [Required,StringLength(5)]
   public string Rating { get; set; }
}

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