Привязка модели

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

Привязка модели или Model binding представляет механизм сопоставления значений из HTTP-запроса с параметрами метода контроллера. При этом параметры могут представлять как простые типы (int, float и т.д.), так и более сложные типы данных, например, объекты классов.

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

public class HomeController : Controller
{
	public string Index(string name) => $"Name: {name}";
}

Допустим, на сервер приходит запрос https://localhost:7288/Home/Index?name=Tom.

При использовании стандартного маршрута для обслуживания данного запроса будет выбран метод Index контроллера Home. Поскольку данный метод принимает параметр с именем name, то механизм привязки по этому имени будет искать в среди пришедших данных значение с ключом name.

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

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

  • Данные форм. Хранятся в объекте Request.Form

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

  • Данные строки запроса. Хранятся в объекте Request.Query

Причем все эти источники данных представляют словари, в которых по ключу мы можем получить значение.

То есть в нашем случае, когда на сервер придет запрос https://localhost:7288/Home/Index?name=Tom, привязчик модели последовательно будет просматривать в поиске значения для параметра name следующие пути:

  • Request.Form["name"]

  • RouteData.Values["name"]

  • Request.Query["name"]

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

То есть, к примеру, пусть у нас есть следующая модель:

public record class Person(string Name, int Age, Company? Company);
public record class Company(string Name);

И пусть метод принимает в качестве параметра объект данной модели:

public class HomeController : Controller
{
    public string Index(Person person)
    {
        return $"{person.Name}({person.Age}) - {person.Company?.Name}";
    }
}

В этом случае привязчик модели последовательно будет просматривать те же источники в поиске значений для свойств объекта person. Например, чтобы найти значение для свойства Name, привязчик будет искать значение по следующим ключам:

  • Request.Form["person.Name"]

  • RouteData.Values["person.Name"]

  • Request.Query["person.Name"]

В случае если параметр сам хранит объект сложного типа, как выше класс Person ссылается на класс Company, то привязчик с помощью рекурсии спускается на уровень ниже - на уровень класса Company и пытается получить его свойства и найти для них значения.

Для таких типов как коллекции привязчик модели ищет значения с ключами имя_параметра[index] или просто по индексу [index]. Если параметр представляет объект Dictionary, то привязчик модели также ищет в источниках запроса значения с ключами имя_параметра[ключ] или просто ищет по ключу: [ключ].

При этом свойства, к которым осуществляется привязка, должны быть объявлены с модификатором public и быть доступными для записи.

Кроме того, если класс не представляет тип record, то он должен иметь конструктор без параметров. Поскольку в примере выше применются классы record, то конструктор без параметров можно не определять.

Например, если к вышеопреределенному методу контроллера мы обратимся с запросом

https://localhost:7288/Home/Index?person.Name=Tom&person.Age=37&person.Company.Name=Microsoft

то ASP.NET Core MVC вполне сможет связать передаваемые значения со свойствами объекта Person:

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

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

Однако если же параметр представляет значимый тип (например, int), то ему присваивается значение по умолчанию. И даже если ему явным образом не передано значение, то ModelState.IsValid возвратит true

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850