Модель — это набор классов, представляющих данные, которыми управляет приложение. В этой части мы познакомимся с тем, как устроена работа с моделями в ASP.NET Core – как приложение ASP.NET Core производит анализ запроса от клиента API, как можно передавать различные данные в контроллер и производить валидацию модели.
Привязка модели в контроллерах API
Контроллеры API работают с данными, поступающими из HTTP-запросов. Сведения о свойствах модели могут содержаться как в теле запроса, так и в данных маршрута или передаваться в параметрах запроса в URL. Если бы мы решились самостоятельно написать код по извлечению данных из всех этих источников, то неизбежно столкнулись бы с массой проблем и ошибок. Привязка модели автоматизирует процесс по извлечению данных из запроса следующим образом:
- Извлекает данные из различных источников, таких как данные маршрута, тело запроса, параметры запроса в URL и так далее.
- Предоставляет данные контроллерам в параметрах метода и общедоступных свойствах.
- Преобразует строковые данные в типы .NET.
- Обновляет свойства сложных типов.
В этой части мы познакомимся с работой системы привязки модели при создании приложений ASP.NET Core Web API.
Источники привязки
Чтобы продемонстрировать пример привязки модели, вернемся к действию контроллера, которое мы разработали в этой части. Вот как оно выглядело
[HttpGet("{id}")] public ActionResult<WeatherForecast> Get(int id) { var weather = Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); if ((id < 0) || (id > 5)) return BadRequest(); else return Ok(weather[id]); }
Как приложение понимает, что параметр метода id
необходимо связать с параметром маршрута? В данном случае работа приложения строится по следующему алгоритму:
- Приложение получает HTTP-запрос и выполняет поиск подходящего контроллера и действия для его выполнения. Соответственно, если клиент отправляет запрос по пути
/WeatherForecast/2
, то приложение выбирает представленное выше действие так как для действия указан двухсегментный путь (вспоминаем про маршрутизацию) и путь в запросе также состоит из двух сегментов. - Анализируются параметры метода. Приложение находит параметр
int id
- Просматриваются все доступные источники в HTTP-запросе и находится
id = "2"
в данных маршрута (на этом шаге приложение уже знает параметры маршрута – их имена, ограничения и т.д., а2
находится во втором сегменте, как и параметр маршрута). - Строка «2» преобразуется в число 2.
- Вызывается метод
Get(2)
.
Аналогичным образом приложение будет действовать и в случае наличия в методе нескольких параметров. В качестве доступных источников привязки модели в ASP.NET Core выступают (в порядке их приоритета):
- Поля формы
- Тело запроса (этот источник включается при добавлении к контроллеру атрибута
[ApiController]
) - Данные маршрута
- Параметры запроса (используются только для простых типов)
- Загруженные файлы (файлы привязываются только к целевым типам, которые реализуют
IFormFile
илиIEnumerable<IFormFile>
)
Использование нескольких источников и приоритетов при просмотре источников позволяет достаточно гибко организовывать работу приложения по привязке модели.
Boolean
, Byte
, SByte
, Char
, DateOnly
, DateTime
, DateTimeOffset
, Decimal
, Double
, Enum
, Guid
, Int16
, Int32
, Int64
, Single
, TimeOnly
, TimeSpan
, UInt16
, UInt32
, UInt64
, Uri
и Version
.Обратите внимание на то, что в представленном перечне списке отсутствует тип string. Поэтому, если мы хотим использовать параметр типа string
, передавая его, например, в параметрах запроса, то вот такое определение параметра key
в методе (см. листинг):
[HttpGet] public IActionResult Get(bool today, string key) { //код действия }
приведет к тому, что параметр запроса будет обязательным и попытка выполнения запроса без параметра приведет к ошибке выполнения запроса.
Если вам необходим строковый необязательный параметр, то в методе он должен быть типом, допускающим null
, то есть определяться следующим образом:
public IActionResult Get(bool today, string? key)
В этом случае, при отсутствии параметра в запросе, параметр метода будет равен null
, и ошибка с кодом 400 не будет генерироваться.
Атрибуты привязки
При разработке действий контроллеров API может возникнуть ситуация, при которой приоритет просмотра источников привязки потребуется изменить. Например, мы захотим, чтобы параметр метода получался только из заголовков запроса. По умолчанию такой источник привязки как заголовки запроса отсутствует и, чтобы привязать параметр метода к заголовку мы должны будем использовать специальный атрибут привязки
public IActionResult Get([FromHeader]string key) { //код метода }
В этом примере мы указали, что значение параметра метода (key
) должно получаться из заголовков запроса (и только из них). На данный момент, в ASP.NET Core определены следующие атрибуты, позволяющие указать источник привязки:
[FromQuery]
–получает значения из строки запросаURL.[FromRoute]
–получает значения из данных маршрута.[FromForm]
–получает значения из полей формы.[FromBody]
–получает значения из тела запроса.[FromHeader]
–получает значения из HTTP-заголовков.[FromServices]
– получает значения из контейнера DI[FromKeyedServices]
– получает значения из контейнера DI
Атрибуты [FromQuery]
, [FromRoute]
, [FromForm]
и [FromHeader]
позволяют указать параметр Name
, определяющее имя ключа в источнике привязки. Например, вот такое указание атрибута:
public IActionResult Get([FromHeader(“X-Key”)]string key)
говорит о том, что параметр key
должен быть получен из заголовка с именем X-Key
. Эти же атрибуты могут использоваться не только в параметрах методов, но и применяться к отдельным свойствам класса.
Итого
Привязка модели в контроллерах API позволяет автоматизировать процесс по извлечению данных из запроса, используя различные источники привязки в соответствии с их приоритетами. При необходимости, мы можем изменить приоритет привязки, используя специальные атрибуты, которые доступны для контроллеров с атрибутом [ApiController]
.