Управление привязкой

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

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

Фреймворк MVC предоставляет ряд атрибутов, с помощью которых мы можем изменить стандартный механизм привязки.

BindRequired и BindNever

Атрибут BindRequired требует обязательного наличия значения для свойства модели.

Атрибут BindNever указывает, что свойство модели надо исключить из механизма привязки.

Пусть у нас есть модель User:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
	public bool HasRight { get; set; }
}

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

public IActionResult AddUser(User user)
{
    string userInfo = $"Id: {user.Id}  Name: {user.Name}  Age: {user.Age}  HasRight: {user.HasRight}";
    return Content(userInfo);
}

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

http://localhost:54274/Home/AddUser?HasRight=true

И мы получим следующий вывод:

Атрибут BindRequired в ASP.NET Core

То есть это вполне валидный запрос, при обработке которого создается объект User. Для тех свойств, для которых не переданы значения, устанавливаются значения по умолчанию, например, для строковых свойств - пустые строки, для числовых свойств - число 0. Но вряд ли подобный объект User можно считать удовлетворительным, поскольку, у него должно быть установлено, как минимум, имя - свойство Name. То есть имя выступает в качестве обязательного критерия, и чтобы это указать, используем атрибут BindRequired.

А, к примеру, свойство HasRight не должно устанавливаться напрямую. Поэтому для него можно применить атрибут BindNever:

using Microsoft.AspNetCore.Mvc.ModelBinding;

public class User
{
    public int Id { get; set; }
    
	[BindRequired]
    public string Name { get; set; }
    
	public int Age { get; set; }
	[BindNever]
    public bool HasRight { get; set; }
}

Далее изменим метод, который получает объект User:

public IActionResult AddUser(User user)
{
    if(ModelState.IsValid)
    {
        string userInfo = $"Id: {user.Id}  Name: {user.Name}  Age: {user.Age}  HasRight: {user.HasRight}";
        return Content(userInfo);
    }
    return Content($"Количество ошибок: {ModelState.ErrorCount}");
}

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

Теперь нам обязательно надо будет указать значение для свойства Name, а свойство HasRight будет исключено из привязки:

BindNever в ASP.NET Core

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

  • Required: аналогично примению атрибута BindRequired

  • Never: аналогично примению атрибута BindNever

  • Optional: действие по умолчанию, мы можем передавать значение, а можем и не передавать, тогда будут применяться значения по умолчанию

Например, мы могли бы изменить модель User так:

public class User
{
    public int Id { get; set; }
    [BindingBehavior(BindingBehavior.Required)]
    public string Name { get; set; }
    [BindingBehavior(BindingBehavior.Optional)]
    public int Age { get; set; }
    [BindingBehavior(BindingBehavior.Never)]
    public bool HasRight { get; set; }
}

Атрибут Bind

Атрибут Bind позволяет установить выборочную привязку отдельных значений. Так, уберем из модели User атрибуты привязки:

public class User
{
	public int Id { get; set; }
	public string Name { get; set; }
	public int Age { get; set; }
	public bool HasRight { get; set; }
}

И применим атрибут Bind в методе AddUser:

public IActionResult AddUser([Bind("Name", "Age", "HasRight")] User user)
{
    string userInfo = $"Name: {user.Name}  Age: {user.Age}  HasRight: {user.HasRight}";
    return Content(userInfo);
}

В качестве параметра в атрибут Bind передается набор свойств объекта User, которые будут участвовать в процессе привязки. Здесь перечислены все свойства. Но, допустим, уберем пару свойств:

public IActionResult AddUser([Bind("Name")] User user)
{
    string userInfo = $"Name: {user.Name}  Age: {user.Age}  HasRight: {user.HasRight}";
    return Content(userInfo);
}

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

Атрибут Bind в ASP.NET Core MVC

Если мы используем атрибут Bind применительно к параметру метода, как в случае выше, то мы переопределяем привязку только для конкретного метода. Если нам надо глобально переопределить привязку для модели User во всех методах, то атрибут Bind применяется в целом к модели:

using Microsoft.AspNetCore.Mvc;

[Bind("Name")]
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public bool HasRight { get; set; }
}

Источники привязки

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

  • Данные отправленных форм в Request.Form

  • Данные маршрута в RouteData.Values

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

Но группа атрибутов позволяет переопределить это поведения, указав один целевой источник для поиска значений:

  • [FromHeader]: данные берутся из заголовков запроса

  • [FromQuery]: данные берутся из строки запроса

  • [FromRoute]: данные берутся из значений маршрута

  • [FromForm]: данные берутся из полученных форм

  • [FromBody]: данные берутся из тела запроса. Этот атрибут может применяться, когда в качестве источника данных выступает не форма и не строка запроса, а, скажем, данные отправляются через AJAX

    Атрибут FromBody может применяться, если метод имеет только один параметр, иначе будет сгенерировано исключение.

Например, получим данные о юзер-агенте из запроса:

public IActionResult GetUserAgent([FromHeader(Name="User-Agent")] string userAgent)
{
    return Content(userAgent);
}

В атрибут FromHeader передается строковый параметр, который указывает нужный заголовок.

Или используем атрибут [FromQuery]. Для этого возьмем модель User:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

В контроллере определим два метода:

public IActionResult AddUser()
{
    return View();
}

[HttpPost]
public IActionResult AddUser([FromQuery] User user)
{
    string userInfo = $"Name: {user.Name}  Age: {user.Age}";
    return Content(userInfo);
}

Как правило для post-запросов используются формы. И мы даже можем добавить форму. Так, для метода AddUser создадим представление AddUser.cshtml:

<form method="post">
    <p>
        <label>Имя</label><br />
        <input type="text" name="Name" />
    </p>
    <p>
        <label>Возраст</label><br />
        <input type="number" name="Age" />
    </p>
    <p>
        <input type="submit" value="Отправить" />
    </p>
</form>

При отправке формы по умолчанию привязчик вначале просматривает данные из полученной формы. Если каких-то данных там не окажется, то он просматривает данные маршрута и строки запроса.

Однако установленный атрибут FromQuery переопределяет это действие: теперь привязчик будет сразу просматривать данные из строки запроса. Даже если мы отправим вместе с формой какие-то данные, то они будут игнорироваться. Например, для обращения к get-версии метода AddUser исползуем адрес Home/AddUser?Name=Alice&Age=21:

Источники привязки модели в ASP.NET Core

В коде формы не установлен адрес, поэтому в качестве адреса отправки будет использоваться текущий адрес, то есть Home/AddUser?Name=Alice&Age=21, который содержит строку запроса с двумя параметрами. В итоге данные формы будут игнорироваться:

Настройка model binding в ASP.NET Core

Подобным образом применяются и все остальные атрибуты.

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