Валидация модели занимает ключевое место в разработке приложения в целом так как позволяет выявить какие-либо ошибки в модели до того как эти ошибки приведут к аварийной остановке приложения или получению некорректного результата. В ASP.NET Core MVC валидация модели базируется на общем для .NET механизме валидации, но, при этом дополняет этот механизм некоторыми другими возможностями, например, использованием атрибута Remote
.
Работа со свойством ModelState
Мы уже немного работали с этим свойством, когда разбирались с механизмом привязки моделей. Здесь мы познакомимся с этим свойством более подробно. ModelState
представляет объект ModelStateDictionary
, в котором содержится различная информация о состоянии модели, в том числе и сведения об ошибках. Данные в ModelStateDictionary
содержатся в виде пар «ключ-значение», где ключ — это название свойства, а значение — объект типа ModelStateEntry
, содержащий детальную информацию о состоянии свойства. Чтобы продемонстрировать работу со свойством ModelState, создадим новое приложение ASP.NET Core MVC и добавим в папку Models файл Person.cs со следующей моделью данных:
using System.ComponentModel.DataAnnotations; namespace AspModelState.Models { public class Person { [Required] public string Name { get; set; } [Required] public int Age { get; set; } } }
здесь оба свойства модели содержат атрибут Required
, указывающий на то, что свойства обязательно должны содержать значение. Теперь добавим тип этой модели в представление Views/Home/Index.cshtml
следующую форму и создадим в этом представлении следующую форму, используя для примера html-хэлперы:
@model Person @using (Html.BeginForm(FormMethod.Post)) { @Html.LabelFor(n=>n.Name, "Имя:") <br /> @Html.TextBoxFor(n=>n.Name); <br/> @Html.LabelFor(n=>n.Age, "Возраст:") <br /> @Html.TextBoxFor(n=>n.Age, new{type = "number"}) <br /> @Html.TextBox("btn", "Отправить", new{type = "submit"}) }
Теперь перейдем к контроллеру HomeController
и внесем в него следующие изменения:
[HttpGet] public IActionResult Index() { return View(); } [HttpPost] public IActionResult Index(Person person) { if (ModelState.IsValid) { return Ok(person); } else { StringBuilder stringBuilder = new StringBuilder(); foreach (var item in ModelState) { switch (item.Value.ValidationState) { case ModelValidationState.Unvalidated: { stringBuilder.Append($"Свойство {item.Key} не проверено\n"); break; } case ModelValidationState.Skipped: { stringBuilder.Append($"Свойство {item.Key} пропущено\n"); break; } case ModelValidationState.Invalid: { stringBuilder.Append($"Свойство {item.Key} содержит ошибки:\n"); foreach (var error in item.Value.Errors) { stringBuilder.Append($"\t{error.ErrorMessage}\n"); } break; } case ModelValidationState.Valid: { stringBuilder.Append($"Свойство {item.Key} успешно прошло проверку\n"); break; } } } return BadRequest(stringBuilder.ToString()); } }
здесь мы установили атрибут [HttpGet]
для метода Index()
без параметров, указав тем самым, что это действие будет выполняться только при GET-запросах, т.е., по сути, только выводить созданную нами форму.
Также мы создали ещё один метод Index()
с атрибутом [HttpPost]
, который будет обрабатывать POST-запрос, когда пользователь будет отправлять форму на сервер. В этом методе демонстрируется работа с различными свойствами ModelState
. Так, если модель пройдет валидацию (ModelState.IsValid
будет равно true
), то в браузере мы увидим JSON-объект, например,
В случае, если модель проверку не пройдет, то запустится цикл по всем элементам, содержащимся в ModelState
:
foreach (var item in ModelState)
Для каждого элемента item проверяется значение:
item.Value.ValidationState
то есть состояние проверки значения свойства. ValidationState
может принимать следующие значения:
ModelValidationState.Unvalidated
— свойство ещё не проверялосьModelValidationState.Skipped
— свойство исключено из проверкиModelValidationState.Invalid
— свойство содержит ошибкиModelValidationState.Valid
— свойство успешно прошло проверку
Если обнаруживается свойство с ошибками, то запускается цикл, который считывает все ошибки для свойства:
foreach (var error in item.Value.Errors) { stringBuilder.Append($"\t{error.ErrorMessage}\n"); }
в конце, вместе с ошибкой 400 возвращается информацию о проверке модели. В работающем приложении это будет выглядеть следующим образом:
или так (если одно из свойство пройдет проверку)
Изменение состояния модели
Вполне возможно, что при проверке модели нам может потребоваться какая-то дополнительная проверка свойств модели, которую мы не сможем осуществить, используя только штатные средства. Свойство ModelState
позволяет изменить состояние модели. Например, проведем дополнительную проверку свойства Age
:
public IActionResult Index(Person person) { if (person.Age < 18) ModelState.AddModelError("Age", "Возраст пользователя должен быть 18+"); .... }
Здесь мы проверяем, чтобы возраст пользователя был 18+ и, если пользователь не подходит под этот параметр, то вызывается метод ModelState.AddModelError()
, который добавляет для свойства Age
ошибку и, следовательно, свойство ModelState.IsValid
будет равно false
. В итоге, если мы попробует отправить вот такие данные формы:
то, в итоге, получим следующую ошибку:
Настройка проверки модели
При необходимости, мы можем задать настройки проверки модели отличные от тех, которые задаются ASP.NET Core MVC по умолчанию. Для этого необходимо задать настройки при добавлении сервисов MVC:
builder.Services.AddControllersWithViews(options => { options.MaxModelValidationErrors = 1;//максимальное количество ошибок, после которых проверка прерывается });
По умолчанию это свойство равно 200.
Итого
Валидация модели занимает важное место в разработке приложений. В ASP.NET Core MVC валидация модели базируется на общем механизме валидации в .NET, но, при этом, вносит в этот механизм свои дополнительные возможности, например, по изменению состояния модели через свойство ModelState
.