Модели в ASP.NET Core MVC. Привязка модели

Привязка модели (model binding) в ASP.NET Core MVC — это механизм сопоставления параметров HTTP-запроса с параметрами методами контроллера. Мы уже сталкивались с работой этого механизма (просто не знали, что это он), когда разбирались с обработкой форм в контроллерах. Здесь мы вернемся к изучению работы механизма привязки модели, но уже изучим его более подробно.

Интерфейс IModelBinder

Чтобы найти и сопоставить данные из запроса с параметрами действия в контроллере используется привязчик модели (model binder), который представляет объект, реализующий интерфейс IModelBinder. Например, рассмотрим следующее действие контроллера:

public IActionResult Post(int id, bool draft)
{
    return Ok(new { Id=id, Draft=draft});
}

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

https://localhost:7092/Home/Post/1?draft=true

или так:

https://localhost:7092/Home/Post/?draft=true&id=1

в любом случае мы получим вот такой результат в браузере:

Поиск значений привязчиком модели осуществляется в следующих источниках в порядке их приоритета:

  • Данные форм. Хранятся в объекте Request.Form
  • Данные маршрута, то есть те данные, которые формируются в процессе сопоставления строки запроса маршруту. Хранятся в объекте RouteData.Values
  • Данные строки запроса. Хранятся в объекте Request.Query

Все эти источники данных представляют словари, в которых значения хранятся в виде пар «ключ-значение». То есть привязчик модели в приведенных выше вариантах запросов действовал по разному, а именно:

при запросе https://localhost:7092/Home/Post/1?draft=true выполнялись следующие действия:

  1. проверка значения Request.Form["id"].
  2. так как значения id в данных формы не было, то произошла проверка RouteData.Values["id"]. Такой параметр маршрута был найден в маршруте по умолчанию (последний необязательный параметр). Параметру метода id было присвоено значение 1.
  3. проверка значения Request.Form["draft"]. Значение не было обнаружено
  4. проверка значения RouteData.Values["draft"]. Значение не было обнаружено
  5. проверка значения Request.Query["draft"]. Значение обнаружено — параметру метода draft было присвоено значение true.

Проверить, что привязчик работает именно в такой последовательности можно выполнив вот такой запрос:

https://localhost:7092/Home/Post/5?draft=true&id=1

здесь используется И параметр id маршрута (значение 5) И параметр запроса (id=1). Так как приоритет проверки значений маршрута выше, чем параметров запроса, то в браузере мы увидим:

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

Например, создадим такое действие контроллера:

public IActionResult Post(Post post)
{
    return Ok(post);
}

где Post — это модель следующего вида:

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public string Body { get; set; }
}

здесь уже параметром метода выступает класс. Попробуем выполнить следующий запрос к контроллеру:

localhost:7163/home/post/10?Title=Привет,мир

в браузере мы увидим следующий результат:

в данном случае, привязчик обнаружил в параметрах маршрута значение для свойства Post.Id, а в параметрах запроса значение для Post.Title. Так как для двух других свойств значений не нашлось, то им было присвоено значение по умолчанию — null.

Свойство контроллера ModelState

Свойство ModelState хранит информацию о работе привязчика модели. Вполне возможна ситуация, при которой привязчик модели не найдет требуемое значение (см. пример выше) или же найденное значение не сможет быть сконвертировано в нужный тип. Например, если свойство имеет тип int, а в параметрах запроса будет содержаться строка.

Если параметр представляет ссылочный тип, свойство ModelState.IsValid возвратит false если привязчик не сможет найти для него значение. Это означает, что привязка завершилась с ошибкой и, возможно, что параметры метода мы не сможем использовать. Вернемся к нашему примеру. Когда мы выполнили запрос:

localhost:7163/home/post/10?Title=Привет,мир

то привязчик не нашел значения для двух параметров и присвоил им значение null. Добавим в действие контроллера проверку свойства ModelState.IsValid

public IActionResult Post(Post post)
{
    if (ModelState.IsValid)
      return Ok(post);
    else
        return BadRequest(ModelState);
}

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

Другой вариант, когда работа привязчика завершается ошибкой — невозможность сконвертировать значение из запроса в значение параметра:

localhost:7163/home/post/?Title=Привет,мир&id=три&body=text&author=admin

при таком запросе мы, вроде бы определили значения всех свойств модели, но значение id вместо числа содержит строку. В этом случае мы увидим следующую ошибку:

а значение свойства Id объекта типа Post будет установлено в значение по умолчанию, то есть 0. Мы можем не присваивать значения для значимых типов и в этом случае свойство ModelState.IsValidбудет возвращать true. Например, уберем из запрос параметр id:

localhost:7163/home/post/?Title=Привет,мир&body=text&author=admin

в этом случае мы увидим такой вывод в браузере

то есть, привязчик не обнаружил никаких ошибок.

Итого

В этой части мы более подробно познакомились с механизмом привязки модели. За привязку модели отвечает объект, реализующий интерфейс IModelBinder. Узнать состояние привязки модели мы можем из свойства ModelState.IsValid контроллера.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии