Валидация

Rick Anderson

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

DRY

Одним из ключевых моментов в MVC является DRY (“Don’t Repeat Yourself” (Не повторяй сам себя)). ASP.NET MVC дает вам возможность определить функционал или поведение только один раз, а затем применять его во всем приложении, где это нужно. Это уменьшает количество кода, который вам нужно написать, снижает число ошибок в коде, и этот код легче тестировать и поддерживать.

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

Давайте рассмотрим валидацию в приложении с роликами.

Добавление правил валидации в класс модели

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

Обновите класс Movie, чтобы воспользоваться встроенными атрибутами валидации Required, StringLength, RegularExpression и Range.

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

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

    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
    [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''-'\s]*$")]
    [StringLength(5)]
    public string Rating { get; set; }
}

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

UI ошибок валидации в MVC

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

Нажмите на ссылку Create New, чтобы добавить новый ролик. Заполните форму какими-нибудь неверными значениями. Когда jQuery валидация на стороне клиента обнаружит ошибку, будет отображено соответствующее сообщение.

../../_images/val1.png

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

Существенным преимуществом является то, что вам не потребовалось менять ни единой строчки кода в классе MoviesController или представлении Create.cshtml, чтобы включить этот UI валидации. Контроллер и представления автоматически подхватили правила валидации, которые вы определили с помощью атрибутов валидации для свойств класса Movie. Протестируйте валидацию, используя метод действия Edit.

Данные из формы не отправляются на сервер, пока присутствуют ошибки валидации со стороны клиента. Вы можете проверить это, поставив точку останова в методе HTTP Post, используя Fiddler или инструменты для разработки F12.

Как проходит валидация в представлении Create и методе действия Create

Возможно, вы удивлены, как был сгенерирован UI валидации без внесения каких-либо изменений в код контроллера и представлений. В следующем листинге представлены два метода Create.

public IActionResult 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 http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID,Genre,Price,ReleaseDate,Title,Rating")] Movie movie)
{
    if (ModelState.IsValid)
    {
        _context.Add(movie);
        await _context.SaveChangesAsync();
        return RedirectToAction("Index");
    }
    return View(movie);
}
#region snippet_edit_get

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

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

../../_images/p8_IE9_disableJavaScript.png

Здесь показано, как отключить JavaScript в FireFox.

../../_images/ff.png

А здесь показано, как отключить JavaScript в Chrome.

../../_images/chrome.png

После отключения JavaScript отправьте неверные данные и зайдите в отладчик.

../../_images/ms.png

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

<form asp-action="Create">
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        <div asp-validation-summary="ModelOnly" class="text-danger"></div>
        <div class="form-group">
            <label asp-for="Genre" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="Genre" class="form-control" />
                <span asp-validation-for="Genre" class="text-danger"></span>
            </div>
        </div>
        @*Markup removed for brevity.*@
        <div class="form-group">
            <label asp-for="Rating" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="Rating" class="form-control" />
                <span asp-validation-for="Rating" class="text-danger"></span>
            </div>
        </div>
        <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>
</form>

Input тег-хелпер использует атрибуты DataAnnotations и создает HTML атрибуты, необходимые для jQuery валидации со стороны клиента. Validation тег-хелпер отображает ошибки валидации. См. валидация.

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

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

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

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

[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }

[Range(1, 100)]
[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 также предлагает включить в приложение функционал, характерный для конкретных типов. Например, для DataType.EmailAddress можно создать ссылку mailto:, а разделители в дате можно использовать для DataType.Date в браузерах, которые поддерживают HTML5. Атрибуты DataType предлагают HTML 5 data- атрибуты, которые понимают браузеры, поддерживающие HTML5. Атрибуты DataType не проводят валидацию.

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

Для указания формата даты напрямую используется атрибут DisplayFormat:

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

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

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

  • Браузер может включить возможности HTML5
  • По умолчанию браузер отобразит дату, используя корректный формат, основываясь на местоположении
  • С атрибутом DataType MVC может выбрать правильный шаблон с полями для представления даты (DisplayFormat, если используется сам по себе, использует строковый шаблон).

Примечание

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

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

Вам нужно отключить валидацию даты jQuery, чтобы использовать атрибут Range с DateTime.

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

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

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

    [Display(Name = "Release Date"), DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$"), 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''-'\s]*$"), StringLength(5)]
    public string Rating { get; set; }
}
Поделись хорошей новостью с друзьями!
Следи за новостями!