Метаданные и валидация модели

Валидация модели на стороне сервера

Последнее обновление: 07.04.2022

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

Для рассмотрения валидации возьмем самый простейший проект ASP.NET Core по типу по типу ASP.NET Core Empty.

Вначале добавим в проект новую папку Models для моделей и определим в ней одну единственную модель - класс Person:

using System.ComponentModel.DataAnnotations;

namespace MvcApp.Models
{
    public class Person
    {
        [Required]
        public string? Name { get; set; }
        [Required]
        public int Age { get; set; }
    }
}

Здесь каждое свойство модели помечено атрибутом Required, который находится в пространстве имен System.ComponentModel.DataAnnotations. Данный атрибут указывает, что этим свойствам необходимо обязательно передать некоторые значения. Если же им не передать значения, то свойства будут невалидны, некорректны и не пройдут валидацию.

Затем определим в проекте папку Controllers для контроллеров приложения и в нее добавим контроллер HomeController. В контроллере HomeController действие Create, через которое мы будем добавлять на сервер объект модели Person:

using Microsoft.AspNetCore.Mvc;
using MvcApp.Models;  // пространство имен класса Person

namespace MvcApp.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Create() => View();

        [HttpPost]
        public string Create(Person person)
        {
            if (ModelState.IsValid)
                return $"{person.Name} - {person.Age}";
            return "Данные не прошли валидацию";
        }
    }
}

Первый метод Create будет обрабатывать Get-запросы и будет отдавать пользователю представление с формой для добавления данных.

Post-метод Create получает введенные данные в виде модели Person и проверяет их корректность.

Валидация на стороне сервера, то есть в контроллере, осуществляется посредством помощью проверки свойства ModelState.IsValid. Объект ModelState сохраняет все значения, которые пользователь ввел для свойств модели, а также все ошибки, связанные с каждым свойством и с моделью в целом. Если в объекте ModelState имеются какие-нибудь ошибки, то свойство ModelState.IsValid возвратит false.

И в данном случае, если данные для модели были введены правильно, то данные возвращаеются в виде строки и далее будут выведены в браузере. Если же были введены некорректные данные, то возвращаем строку "Данные не прошли валидацию".

Теперь для метода Create в проекте в папке Home/Create определим представление Create.cshtml, которое будет содержать форму для ввода данных:

<form method="post">
    <div>
        <p>
            <label for="name">Name</label><br />
            <input type="text" id="name" name="name" />
        </p>
        <p>
            <label for="age">Age</label><br />
            <input id="age" name="age" />
        </p>
        <p>
            <input type="submit" value="Send"  />
        </p>
    </div>
</form>

Здесь определена простейшая форма html с полями для ввода данных для свойств модели Person. И по нажатию на кнопку Send эти введенные данные отправятся post-методу Create контроллера HomeController.

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

Основы валидации в ASP.NET Core MVC и C#

Теперь запустим приложение и обратимся к методу Create. И ничего не вводя в поля ввода, нажмем на кнопку отправки:

валидация на стороне сервера и ModelState.IsValid в ASP.NET Core MVC и C#

Поскольку к свойствам модели Person применяется атрибут Required, который требует обязательного наличия данных, то при отсутствии значений свойство ModelState.IsValid будет иметь значение false. Это будет означать, что данные не прошли валидацию, что они некорректны.

Однако, если мы введем какие-либо данные, тогда они пройдут валидацию, и мы увидим введенные данные:

проверка данных и ModelState.IsValid в ASP.NET Core MVC и C#

Получение данных из ModelState

Свойство ModelState представляет объект ModelStateDictionary, из которого мы можем получить различную информацию о состоянии модели, в частности, данные об ошибках. ModelStateDictionary представляет своего рода словарь, где ключами служат строки - названия свойств, а значениями - объекты ModelStateEntry, которые содержат детальную информацию о состоянии свойства. Например, мы можем пройтись по всем элементам в ModelState как в обычном словаре с помощью цикла foreach:

foreach(var item in ModelState)
{
    // item.Key  - название свойства
    // item.Value - объект ModelStateEntry
}

Надо понимать, что даже если модель проходит валидацию, и никаких ошибок нет, то ModelState все равно будет содержать в этом словаре соответствующие элементы. Чтобы узнать, прошло ли свойство валидацию или нет, у объекта ModelStateEntry можно использовать свойство ValidationState, которое представляет перечисление ModelValidationState. Данное перечисление может принимать следующие значения:

  • Valid: значение корректно

  • Invalid: значение некорректно

Если мы хотим непосредственно получить информацию об ошибках, то нам надо обратиться к свойству Errors объекта ModelStateEntry. Это свойство представляет объект ModelErrorCollection и хранит коллекцию ошибок, связанных с данным элементом.

foreach(var item in ModelState)
{
    // пробегаемся по всем ошибкам
    foreach (var error in item.Value.Errors)
    {
    }
}

Каждый элемент из коллекции ModelErrorCollection представляет объект ModelError. А его свойство ErrorMessage хранит собственно сообщение об ошибке.

Например, получим по валидации объекта Person детальную информацию. Для этого изменим код контроллера HomeController следующим образом:

using Microsoft.AspNetCore.Mvc;
using MvcApp.Models;  // пространство имен класса Person
using Microsoft.AspNetCore.Mvc.ModelBinding;  // пространство имен перечисления ModelValidationState

namespace MvcApp.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Create() => View();

        [HttpPost]
        public string Create(Person person)
        {
            if (ModelState.IsValid)
                return $"{person.Name} - {person.Age}";

            string errorMessages = "";
            // проходим по всем элементам в ModelState
            foreach(var item in ModelState)
            {
                // если для определенного элемента имеются ошибки
                if (item.Value.ValidationState == ModelValidationState.Invalid)
                {
                    errorMessages = $"{errorMessages}\nОшибки для свойства {item.Key}:\n";
                    // пробегаемся по всем ошибкам
                    foreach (var error in item.Value.Errors)
                    {
                        errorMessages = $"{errorMessages}{error.ErrorMessage}\n";
                    }
                }
            }
            return errorMessages;
        }
    }
}

И в данном случае post-метод Create возвращает все сообщения об ошибках для тех свойств, которые имеют некорректные значения:

получение данных их ModelState и валидация в ASP.NET Core MVC и C#

Также по названию свойства мы можем обращаться к соответствующей записи в ModelState:

var errorCounts = ModelState["Name"].Errors.Count;
var state = ModelState["Name"].ValidationState;

Изменение состояния модели

При необходимости мы сами можем валидировать значения и добавлять в ModelState информацию об ошибках. Для этого применяется метод ModelState.AddModelError. Одна из его версий:

ModelStateDictionary.AddModelError(key, errorMessage);

Первый параметр метода представляет ключ добавляемого в словарь элемента, а второй элемент - сообщение об ошибке

Например, дополнительно проверим полученные значения:

using Microsoft.AspNetCore.Mvc;
using MvcApp.Models;  // пространство имен класса Person
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace MvcApp.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Create() => View();

        [HttpPost]
        public string Create(Person person)
        {
            if (person.Age > 110 || person.Age < 0)
            {
                ModelState.AddModelError("Age", "Возраст должен находиться в диапазоне от 0 до 110");
            }
            if (person.Name?.Length < 3)
            {
                ModelState.AddModelError("Name", "Недопустимая длина строки. Имя должно иметь больше 2 символов");
            }

            if (ModelState.IsValid)
                return $"{person.Name} - {person.Age}";

            string errorMessages = "";
            // проходим по всем элементам в ModelState
            foreach(var item in ModelState)
            {
                // если для определенного элемента имеются ошибки
                if (item.Value.ValidationState == ModelValidationState.Invalid)
                {
                    errorMessages = $"{errorMessages}\nОшибки для свойства {item.Key}:\n";
                    // пробегаемся по всем ошибкам
                    foreach (var error in item.Value.Errors)
                    {
                        errorMessages = $"{errorMessages}{error.ErrorMessage}\n";
                    }
                }
            }
            return errorMessages;
        }
    }
}

В данном случае метод ModelState.AddModelError добавляет в ModelState ошибку, если значение свойства Age вне диапазона от 0 до 110 и если длина строки Name меньше 3 символов:

ModelState.AddModelError в ASP.NET Core MVC и C#

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

[HttpPost]
public string Create(Person person)
{
    if (person.Name == "admin" && person.Age == 25)
    {
        // добавляем ошибку уровня модели
        ModelState.AddModelError("", "Некорректные данные");
    }

    if (ModelState.IsValid) return $"{person.Name} - {person.Age}";

    string errorMessages = "";
    foreach(var item in ModelState)
    {
        foreach (var error in item.Value.Errors)
            errorMessages = $"{errorMessages}{error.ErrorMessage}\n";
    }
    return errorMessages;
}

В данном случае мы не хотим, чтобы имя было равно "admin" и одновременно возраст не был равен 25. И при их совпадении устанавливаем ошибку для всей модели. В качестве ключа для этой ошибки применяется пустая строка.

Количество ошибок

Фреймворк MVC валидирует свойства объекта, пока количество ошибок не достигнет предельного максимального количества (по умолчанию это 200 ошибок). Но мы можем настроить это поведение, определив нужное количество ошибок при подключении сервисов MVC в файле Program.cs:

builder.Services.AddControllersWithViews(options => options.MaxModelValidationErrors = 20);
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850