Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Привязка модели или Model binding представляет механизм сопоставления значений из HTTP-запроса с параметрами метода контроллера. При этом параметры могут представлять как простые типы (int, float и т.д.), так и более сложные типы данных, например, объекты классов.
Чтобы понять смысл привязки, посмотрим на примере. Пусть у нас есть следующий метод:
public class HomeController : Controller { public IActionResult Index(string name) { // ... } }
Допустим, на сервер приходит запрос http://localhost:8000/home/index?name=volga.
Для обслуживания данного запроса будет выбран метод Index
контроллера Home
. Поскольку данный метод принимает параметр с
именем name, то механизм привязки по этому имени будет искать в среди пришедших данных значение с ключом name
.
Чтобы найти и сопоставить данные из запроса с параметрами метода используется привязчик модели (model binder), который представляет объект интерфейса IModelBinder.
Для поиска значений привязчик модели используется следующие источники в порядке приоритета:
Данные форм. Хранятся в объекте Request.Form
Данные маршрута, то есть те данные, которые формируются в процессе сопоставления строки запроса маршруту.
Хранятся в объекте RouteData.Values
Данные строки запроса. Хранятся в объекте Request.Query
Причем все эти источники данных представляют словари, в которых по ключу мы можем получить значение.
То есть в нашем случае, когда на сервер придет запрос http://localhost:8000/home/index?name=volga, привязчик модели последовательно будет просматривать в поиске значения для параметра name следующие пути:
Request.Form["name"]
RouteData.Values["name"]
Request.Query["name"]
В случае, если параметры метода представляют сложные данные, например, класс, привязчик модели будет действовать подобным образом.
Он использует рефлексию и рекурсию для прохода по всем свойствам параметра сложного типа для сопоставления свойств со значениями из запроса.
В частности, привязки модели ищет значения с ключами наподобие [имя_параметра].[имя_свойства]
.
Если подобных значений не будет найдено, то привязчик ищет значения просто по имени свойства.
То есть, к примеру, пусть у нас есть следующая модель:
public class Phone { public int Id { get; set; } public string Name { get; set; } public Company Manufacturer { get; set; } public decimal Price { get; set; } } public class Company { public int Id { get; set; } public string Name { get; set; } }
И пусть метод принимает в качестве параметра объект данной модели:
public IActionResult Index(Phone myPhone) { // ... }
В этом случае привязчик модели последовательно будет просматривать те же источники в поиске значений для свойств объекта myPhone. Например, чтобы найти значение для свойства Name, привязчик будет искать значение по следующим ключам:
Request.Form["myPhone.Name"]
RouteData.Values["myPhone.Name"]
Request.Query["myPhone.Name"]
В случае если параметр сам хранит объект сложного типа, как выше класс Phone ссылается на класс Company, то привязчик с помощью рекурсии спускается на уровень ниже - на уровень класса Company и пытается получить его свойства и найти для них значения.
Для таких типов как коллекции привязчик модели ищет значения с ключами имя_параметра[index]
или просто по индексу [index]
.
Если параметр представляет объект Dictionary, то привязчик модели также ищет в источниках запроса значения с ключами имя_параметра[ключ]
или просто ищет по ключу: [ключ]
.
При этом свойства, к которым осуществляется привязка, должны быть объявлены с модификатором public и быть доступными для записи. А сам класс должен иметь общедоступный конструктор по умолчанию. И когда будет осуществляться механизм привязки для создания объекта будет использоваться этот стандартный конструктор, и затем у созданного объекта будут устанавливаться свойства.
Когда значение для параметра метода найдено, привязчик модели прекращает поиск значений для этого параметра и переходит к поиску значений для следующего параметра.
Вполне возможна ситуация, когда привязчик не найдет требуемое значение или найденное значение не сможет быть сконвертировано в нужный тип. Если параметр представляет ссылочный тип, свойство
ModelState.IsValid
в этом случае возвратит false
. Это будет значить, что привязка завершилась с ошибкой, и
полноценно параметры метода мы использовать не сможем.
Однако если же параметр представляет значимый тип (например, int), то ему присваивается значение по умолчанию. И даже если ему явным образом
не передано значение, то ModelState.IsValid
возвратит true