ASP.NET Core でのモデル バインド
この記事では、モデル バインドとは何か、そのしくみ、その動作のカスタマイズ方法を説明します。
モデル バインドとは何か
コントローラーと Razor Pages では、HTTP 要求からのデータが処理されます。 たとえば、ルート データからはレコード キーが提供され、ポストされたフォーム フィールドからはモデルのプロパティ用の値が提供されます。 これらの各値を取得してそれらを文字列から .NET 型に変換するためのコードを記述するのは、面倒で間違いも起こりやすいでしょう。 モデル バインドを使用すれば、このプロセスを自動化できます。 モデル バインド システムでは次のことが行われます。
- ルート データ、フォーム フィールド、クエリ文字列などのさまざまなソースからデータを取得します。
- メソッド パラメーターとパブリック プロパティによりコントローラーと Razor Pages にデータが提供されます。
- 文字列データを .NET 型に変換します。
- 複合型のプロパティを更新します。
例
次のアクション メソッドがあるとします。
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
さらにアプリでは、この URL を使用して要求が受信されます。
https://contoso.com/api/pets/2?DogsOnly=true
ルーティング システムでアクション メソッドが選択されたら、モデル バインドでは次の手順が実行されます。
GetById
の最初のパラメーター (id
という名前の整数型) を検索します。- HTTP 要求内で利用可能なソースを調べ、ルート データ内で
id
= "2" を検索します。 - 文字列 "2" を整数 2 に変換します。
GetById
の次のパラメーター (dogsOnly
という名前のブール型) を検索します。- 該当するソース内を調べ、クエリ文字列内で "DogsOnly=true" を検索します。 名前の照合では大文字と小文字が区別されません。
- 文字列 "true" をブール型の
true
に変換します。
次にフレームワークによって GetById
メソッドが呼び出され、id
パラメーターには 2 が、dogsOnly
パラメーターには true
が渡されます。
前の例では、モデル バインド ターゲットは 単純 型のメソッド パラメーターです。 ターゲットは複合型のプロパティになる場合もあります。 各プロパティが正常にバインドされたら、そのプロパティに対してモデル検証が行われます。 どのようなデータがモデルにバインドされているかを示す記録、バインド エラー、または検証のエラーは、ControllerBase.ModelState または PageModel.ModelState に格納されます。 このプロセスが正常終了したかどうかを確認するために、アプリでは ModelState.IsValid フラグが調べられます。
対象サーバー
モデル バインドでは、次の種類のターゲットの値について検索が試みられます。
- 要求のルーティング先であるコントローラー アクション メソッドのパラメーター。
- 要求のルーティング先である Razor Pages ハンドラー メソッドのパラメーター。
- 属性によって指定されている場合は、コントローラーまたは
PageModel
クラスのパブリック プロパティ。
[BindProperty] 属性
コントローラーまたは PageModel
クラスのパブリック プロパティに適用できます。これによってモデル バインドはそのプロパティをターゲットとするようになります。
public class EditModel : PageModel
{
[BindProperty]
public Instructor? Instructor { get; set; }
// ...
}
[BindProperties] 属性
コントローラーまたは PageModel
クラスに適用できます。これによってモデル バインドはクラスのすべてのパブリック プロパティをターゲットとするように指示されます。
[BindProperties]
public class CreateModel : PageModel
{
public Instructor? Instructor { get; set; }
// ...
}
HTTP GET 要求のモデル バインド
既定では、プロパティは HTTP GET 要求にバインドされません。 通常、GET 要求に必要なのはレコード ID パラメーターのみです。 レコード ID は、データベース内の項目の検索に使用されます。 そのため、モデルのインスタンスを保持するプロパティをバインドする必要はありません。 GET 要求からのデータにプロパティがバインドされるようにするシナリオでは、SupportsGet
プロパティを true
に設定します。
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
単純型と複合型のモデル バインド
モデル バインドは、操作の対象とする型に特定の定義を使用します。 単純型は、 または TryParse
メソッドを使用して TypeConverter 1 つの文字列から変換されます。 複合型は、複数の入力値から変換されます。 フレームワークは、 または TryParse
の存在に基づいて違いをTypeConverter
決定します。 外部リソースや複数の入力を必要としない変換へのSomeType
変換には、型コンバーターを作成するか、 を使用TryParse
string
することをお勧めします。
変換元
既定では、モデル バインドでは HTTP 要求内の次のソースからキーと値のペアの形式でデータが取得されます。
- フォーム フィールド
- 要求本文 ([ApiController] 属性を持つコントローラー の場合)。
- ルート データ
- クエリ文字列パラメーター
- アップロード済みのファイル
ターゲット パラメーターまたはプロパティごとに、前述の一覧に示されている順序でソースがスキャンされます。 次のようにいくつかの例外があります。
- ルート データとクエリ文字列の値は、 単純型 にのみ使用されます。
- アップロード済みのファイルは、
IFormFile
またはIEnumerable<IFormFile>
を実装するターゲットの種類にのみバインドされます。
既定のソースが正しくない場合は、次のいずれかの属性を使用してソースを指定します。
[FromQuery]
- クエリ文字列から値を取得します。[FromRoute]
- ルート データから値を取得します。[FromForm]
- ポストされたフォーム フィールドから値を取得します。[FromBody]
- 要求本文から値を取得します。[FromHeader]
- HTTP ヘッダーから値を取得します。
これらの属性:
次の例のように、モデル クラスではなく、モデル プロパティに個別に追加されます。
public class Instructor { public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ... }
必要に応じて、コンストラクター内のモデル名の値を受け取ります。 このオプションは、プロパティ名と要求内の値とが一致しない場合に指定されます。 たとえば、要求内の値は、次の例のようにその名前にハイフンが含まれている場合、ヘッダーである可能性があります。
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
[FromBody] 属性
[FromBody]
属性をパラメーターに適用すると、HTTP 要求の本文からそのプロパティが設定されます。 ASP.NET Core ランタイムでは、本文を読み取る責任が入力フォーマッタに委任されます。 入力フォーマッタについては、この記事で後ほど説明します。
[FromBody]
を複合型パラメーターに適用すると、そのプロパティに適用されているバインディング ソース属性はいずれも無視されます。 たとえば、次の Create
アクションでは、その pet
パラメーターを本文から設定するように指定されています。
public ActionResult<Pet> Create([FromBody] Pet pet)
Pet
クラスでは、Breed
プロパティをクエリ文字列パラメーターから設定するように指定されています。
public class Pet
{
public string Name { get; set; } = null!;
[FromQuery] // Attribute is ignored.
public string Breed { get; set; } = null!;
}
前の例の場合:
[FromQuery]
属性は無視されます。Breed
プロパティは、クエリ文字列パラメーターから設定されません。
入力フォーマッタでは本文のみが読み取られ、バインディング ソース属性は認識されません。 本文内で適切な値が見つかった場合は、その値を使用して Breed
プロパティが設定されます。
アクション メソッドごとに [FromBody]
を複数のパラメーターに適用しないでください。 入力フォーマッタによって要求ストリームが読み取られると、他の [FromBody]
パラメーターをバインドするためにそれを再度読み取ることはできません。
その他のソース
ソース データは、"値プロバイダー" によってモデル バインド システムに提供されます。 モデル バインド用に、他のソースからデータを取得するカスタムの値プロバイダーを作成して登録することができます。 たとえば、cookie またはセッション状態からのデータが必要だとします。 新しいソースからデータを取得するには:
IValueProvider
を実装するクラスを作成します。IValueProviderFactory
を実装するクラスを作成します。Program.cs
内のファクトリ クラスを登録します。
このサンプルには、 からcookie値を取得する値プロバイダーとファクトリの例が含まれています。 でカスタム値プロバイダー ファクトリを登録します Program.cs
。
builder.Services.AddControllers(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});
上記のコードは、すべての組み込み値プロバイダーの後にカスタム値プロバイダーを配置します。 それをリストの最初に持ってくるには、Add
ではなく Insert(0, new CookieValueProviderFactory())
を呼び出します。
モデル プロパティ用のソースがない
既定では、モデル プロパティ用の値が見つからない場合、モデル状態エラーは作成されません。 プロパティは次のように null 値または既定値に設定されます。
- Null 許容 単純 型は に
null
設定されます。 - null 非許容値型は
default(T)
に設定されます。 たとえば、パラメーターint id
は 0 に設定されます。 - 複合型の場合、モデル バインドでは、プロパティを設定せずに既定のコンストラクターを使用して、インスタンスが作成されます。
- 配列は
Array.Empty<T>()
に設定されます。例外として、byte[]
配列はnull
に設定されます。
モデル プロパティ用のフォーム フィールド内で何も見つからないときモデル状態を無効にする必要がある場合は、[BindRequired]
属性を使用します。
この動作は [BindRequired]
、要求本文の ON または XML データではなく JS、投稿されたフォーム データからのモデル バインドに適用されることに注意してください。 要求本文データは、入力フォーマッタによって処理されます。
型変換エラー
ソースは見つかってもそれをターゲットの種類に変換できない場合、無効であることを示すフラグがモデル状態に付けられます。 前のセクションで説明したように、ターゲットのパラメーターまたはプロパティは null または既定値に設定されます。
[ApiController]
属性を持つ API コントローラーでは、モデル状態が無効であると、HTTP 400 の自動応答が生成されます。
Razor ページでは、エラー メッセージを含むページが再表示されます。
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
ページが上記のコードによって再表示されると、無効な入力はフォーム フィールドに表示されません。 これは、モデル プロパティが null または既定値に設定されているためです。 無効な入力はエラー メッセージに表示されます。 フォーム フィールドの不適切なデータを再表示する場合は、model プロパティを文字列にし、データ変換を手動で行うことを検討してください。
型変換エラーが結果的にモデル状態エラーになることを望まない場合も同じ方法をお勧めします。 その場合は、モデル プロパティを文字列にします。
単純型
単純型と複合型の詳細については、「モデル バインドの単純型と複合型」を参照してください。
モデル バインダーでソース文字列の変換先とすることができる単純型には次のものがあります。
- Boolean
- Byte、SByte
- Char
- DateOnly
- DateTime
- DateTimeOffset
- Decimal
- Double
- Enum
- Guid
- Int16、Int32、Int64
- Single
- TimeOnly
- TimeSpan
- UInt16、UInt32、UInt64
- Uri
- バージョン
とバインドする IParsable<T>.TryParse
API では IParsable<TSelf>.TryParse
、バインディング コントローラー のアクション パラメーター値がサポートされています。
public static bool TryParse (string? s, IFormatProvider? provider, out TSelf result);
次 DateRange
のクラスは、日付範囲の IParsable<TSelf>
バインドをサポートするために を実装します。
public class DateRange : IParsable<DateRange>
{
public DateOnly? From { get; init; }
public DateOnly? To { get; init; }
public static DateRange Parse(string value, IFormatProvider? provider)
{
if (!TryParse(value, provider, out var result))
{
throw new ArgumentException("Could not parse supplied value.", nameof(value));
}
return result;
}
public static bool TryParse(string? value,
IFormatProvider? provider, out DateRange dateRange)
{
var segments = value?.Split(',', StringSplitOptions.RemoveEmptyEntries
| StringSplitOptions.TrimEntries);
if (segments?.Length == 2
&& DateOnly.TryParse(segments[0], provider, out var fromDate)
&& DateOnly.TryParse(segments[1], provider, out var toDate))
{
dateRange = new DateRange { From = fromDate, To = toDate };
return true;
}
dateRange = new DateRange { From = default, To = default };
return false;
}
}
上記のコードでは次の操作が行われます。
- 2 つの日付を表す文字列を オブジェクトに
DateRange
変換します。 - モデル バインダーでは、 メソッドを
IParsable<TSelf>.TryParse
使用して をバインドしますDateRange
。
次のコントローラー アクションでは、 クラスを DateRange
使用して日付範囲をバインドします。
// GET /WeatherForecast/ByRange?range=7/24/2022,07/26/2022
public IActionResult ByRange([FromQuery] DateRange range)
{
if (!ModelState.IsValid)
return View("Error", ModelState.Values.SelectMany(v => v.Errors));
var weatherForecasts = Enumerable
.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
&& DateOnly.FromDateTime(wf.Date) <= range.To)
.Select(wf => new WeatherForecastViewModel
{
Date = wf.Date.ToString("d"),
TemperatureC = wf.TemperatureC,
TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
Summary = wf.Summary
});
return View("Index", weatherForecasts);
}
次 Locale
のクラスは、 IParsable<TSelf>
へのバインドをサポートするために を CultureInfo
実装します。
public class Locale : CultureInfo, IParsable<Locale>
{
public Locale(string culture) : base(culture)
{
}
public static Locale Parse(string value, IFormatProvider? provider)
{
if (!TryParse(value, provider, out var result))
{
throw new ArgumentException("Could not parse supplied value.", nameof(value));
}
return result;
}
public static bool TryParse([NotNullWhen(true)] string? value,
IFormatProvider? provider, out Locale locale)
{
if (value is null)
{
locale = new Locale(CurrentCulture.Name);
return false;
}
try
{
locale = new Locale(value);
return true;
}
catch (CultureNotFoundException)
{
locale = new Locale(CurrentCulture.Name);
return false;
}
}
}
次のコントローラー アクションでは、 クラスを Locale
使用して文字列を CultureInfo
バインドします。
// GET /en-GB/WeatherForecast
public IActionResult Index([FromRoute] Locale locale)
{
var weatherForecasts = Enumerable
.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.Select(wf => new WeatherForecastViewModel
{
Date = wf.Date.ToString("d", locale),
TemperatureC = wf.TemperatureC,
TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
Summary = wf.Summary
});
return View(weatherForecasts);
}
次のコントローラー アクションでは、 クラスと Locale
クラスをDateRange
使用して、 を使用して日付範囲をCultureInfo
バインドします。
// GET /af-ZA/WeatherForecast/RangeByLocale?range=2022-07-24,2022-07-29
public IActionResult RangeByLocale([FromRoute] Locale locale, [FromQuery] string range)
{
if (!ModelState.IsValid)
return View("Error", ModelState.Values.SelectMany(v => v.Errors));
if (!DateRange.TryParse(range, locale, out DateRange rangeResult))
{
ModelState.TryAddModelError(nameof(range),
$"Invalid date range: {range} for locale {locale.DisplayName}");
return View("Error", ModelState.Values.SelectMany(v => v.Errors));
}
var weatherForecasts = Enumerable
.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.Where(wf => DateOnly.FromDateTime(wf.Date) >= rangeResult.From
&& DateOnly.FromDateTime(wf.Date) <= rangeResult.To)
.Select(wf => new WeatherForecastViewModel
{
Date = wf.Date.ToString("d", locale),
TemperatureC = wf.TemperatureC,
TemperatureF = 32 + (int) (wf.TemperatureC / 0.5556),
Summary = wf.Summary
});
return View("Index", weatherForecasts);
}
GitHub の API サンプル アプリには、API コントローラーの前のサンプルが示されています。
とバインドする TryParse
API では TryParse
、バインディング コントローラー のアクション パラメーター値がサポートされています。
public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);
IParsable<T>.TryParse
は、 とは異なり TryParse
、リフレクションに依存しないため、パラメーター バインドに推奨されるアプローチです。
次 DateRangeTP
のクラスは を実装します TryParse
。
public class DateRangeTP
{
public DateOnly? From { get; }
public DateOnly? To { get; }
public DateRangeTP(string from, string to)
{
if (string.IsNullOrEmpty(from))
throw new ArgumentNullException(nameof(from));
if (string.IsNullOrEmpty(to))
throw new ArgumentNullException(nameof(to));
From = DateOnly.Parse(from);
To = DateOnly.Parse(to);
}
public static bool TryParse(string? value, out DateRangeTP? result)
{
var range = value?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (range?.Length != 2)
{
result = default;
return false;
}
result = new DateRangeTP(range[0], range[1]);
return true;
}
}
次のコントローラー アクションでは、 クラスを DateRangeTP
使用して日付範囲をバインドします。
// GET /WeatherForecast/ByRangeTP?range=7/24/2022,07/26/2022
public IActionResult ByRangeTP([FromQuery] DateRangeTP range)
{
if (!ModelState.IsValid)
return View("Error", ModelState.Values.SelectMany(v => v.Errors));
var weatherForecasts = Enumerable
.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.Where(wf => DateOnly.FromDateTime(wf.Date) >= range.From
&& DateOnly.FromDateTime(wf.Date) <= range.To)
.Select(wf => new WeatherForecastViewModel
{
Date = wf.Date.ToString("d"),
TemperatureC = wf.TemperatureC,
TemperatureF = 32 + (int)(wf.TemperatureC / 0.5556),
Summary = wf.Summary
});
return View("Index", weatherForecasts);
}
複合型
複合型には、バインドする既定のパブリック コンストラクターと書き込み可能なパブリック プロパティが必要です。 モデル バインドが行われると、クラスは既定のパブリック コンストラクターを使用してインスタンス化されます。
複合型の各プロパティについて、 モデル バインドはソースを検索して名前パターンprefix.property_nameします。 何も見つからない場合は、プレフィックスなしで property_name だけが探索されます。 プレフィックスを使用する決定は、プロパティごとに行われません。 たとえば、 メソッドにOnGet(Instructor instructor)
バインドされた を含む?Instructor.Id=100&Name=foo
クエリでは、 型Instructor
の結果のオブジェクトに次のものが含まれます。
Id
を100
に設定する。Name
をnull
に設定する。 前のInstructor.Name
Instructor.Id
クエリ パラメーターで が使用されたため、モデル バインドは を想定しています。
パラメーターにバインドする場合、プレフィックスはパラメーター名です。 PageModel
パブリック プロパティにバインドする場合、プレフィックスはパブリック プロパティ名です。 一部の属性には、パラメーター名またはプロパティ名の既定の使用をオーバーライドするための Prefix
プロパティがあります。
たとえば、複合型が次の Instructor
クラスであるとします。
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
プレフィックス = パラメーター名
バインドされるモデルが instructorToUpdate
という名前のパラメーターである場合:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
モデル バインドでは、キー instructorToUpdate.ID
がないかソースを調べることから始まります。 見つからない場合は、プレフィックスなしで ID
が探索されます。
プレフィックス = プロパティ名
バインドされるモデルがコントローラーの Instructor
という名前のプロパティか、または PageModel
クラスである場合:
[BindProperty]
public Instructor Instructor { get; set; }
モデル バインドでは、キー Instructor.ID
がないかソースを調べることから始まります。 見つからない場合は、プレフィックスなしで ID
が探索されます。
カスタム プレフィックス
バインドされるモデルが instructorToUpdate
という名前のパラメーターであり、かつ Bind
属性でプレフィックスとして Instructor
が指定されている場合:
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
モデル バインドでは、キー Instructor.ID
がないかソースを調べることから始まります。 見つからない場合は、プレフィックスなしで ID
が探索されます。
複合型のターゲットの属性
複合型のモデル バインドを制御するために利用できる組み込みの属性がいくつかあります。
警告
ポストされたフォーム データが値のソースである場合、これらの属性はモデル バインドに影響します。 入力フォーマッタには影響 しません 。入力フォーマッタは、ON および XML 要求本文を JS処理します。 入力フォーマッタについては、この記事で後ほど説明します。
[Bind] 属性
クラスまたはメソッド パラメーターに適用できます。 モデルのどのプロパティをモデル バインドに含めるかを指定します。 [Bind]
は入力フォーマッタ には影響しません 。
次の例では、任意のハンドラーまたはアクション メソッドが呼び出されると、Instructor
モデルの指定されたプロパティのみがバインドされます。
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
次の例では、OnPost
メソッドが呼び出されると、Instructor
モデルの指定されたプロパティのみがバインドされます。
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
[Bind]
属性を使用すれば、"作成" シナリオにおいて過剰ポスティングから保護することができます。 除外されたプロパティはそのままにしておくのではなく null または既定値に設定されるので、この属性は編集シナリオではうまく機能しません。 過剰ポスティングを防ぐ場合は、[Bind]
属性ではなくビュー モデルをお勧めします。 詳細については、「過剰ポスティングに関するセキュリティの注意事項」を参照してください。
[ModelBinder] 属性
ModelBinderAttribute は、型、プロパティ、またはパラメーターに適用されます。 これにより、特定のインスタンスまたは型をバインドするために使用されるモデル バインダーの種類を指定できます。 次に例を示します。
[HttpPost]
public IActionResult OnPost(
[ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
[ModelBinder]
属性を使用して、モデル バインド時にプロパティまたはパラメーターの名前を変更することもできます。
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
// ...
}
[BindRequired] 属性
モデルのプロパティに対してバインドを実行できない場合に、モデル バインドがモデル状態エラーを追加できるようにします。 次に例を示します。
public class InstructorBindRequired
{
// ...
[BindRequired]
public DateTime HireDate { get; set; }
}
モデル検証に関するページにある [Required]
属性の説明も参照してください。
[BindNever] 属性
プロパティまたは型に適用できます。 モデル バインドがモデルのプロパティを設定できないようにします。 型に適用すると、モデル バインド システムによって、型によって定義されるすべてのプロパティが除外されます。 次に例を示します。
public class InstructorBindNever
{
[BindNever]
public int Id { get; set; }
// ...
}
コレクション
ターゲットが単純型のコレクションである場合、モデル バインドでは parameter_name または property_name との一致が探索されます。 一致が見つからない場合は、サポートされているいずれかの形式がプレフィックスなしで探索されます。 次に例を示します。
バインドされるパラメーターが
selectedCourses
という名前の配列であるとした場合:public IActionResult OnPost(int? id, int[] selectedCourses)
フォームまたはクエリ文字列データは、次のいずれかの形式とすることができます。
selectedCourses=1050&selectedCourses=2000
selectedCourses[0]=1050&selectedCourses[1]=2000
[0]=1050&[1]=2000
selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
[a]=1050&[b]=2000&index=a&index=b
パラメーターまたは という名前
index
のプロパティ、またはIndex
コレクション値に隣接している場合は、バインドしないでください。 モデル バインドでは、コレクションのインデックスとして を使用index
しようとすると、バインドが正しくない可能性があります。 たとえば、次のアクションを考えてみましょう。public IActionResult Post(string index, List<Product> products)
前のコードでは、
index
クエリ文字列パラメーターは メソッド パラメーターにindex
バインドされ、製品コレクションのバインドにも使用されています。 パラメーターの名前をindex
変更するか、モデル バインド属性を使用してバインドを構成すると、この問題は回避されます。public IActionResult Post(string productIndex, List<Product> products)
次の形式は、フォーム データでのみサポートされます。
selectedCourses[]=1050&selectedCourses[]=2000
上記のすべてのフォーマット例において、モデル バインドでは 2 つの項目から成る配列が
selectedCourses
パラメーターに渡されます。- selectedCourses[0]=1050
- selectedCourses[1]=2000
添え字番号 (... [0] ... [1] ...) を使用するデータ フォーマットでは、確実にそれらがゼロから始まる連続した番号になるようにする必要があります。 添え字の番号付けで欠落している番号がある場合、欠落している番号の後の項目はすべて無視されます。 たとえば、添え字が 0、1 の並びではなく、0、2 の並びで振られている場合、2 番目の項目は無視されます。
辞書
Dictionary
ターゲットの場合、モデル バインドでは parameter_name または property_name との一致が探索されます。 一致が見つからない場合は、サポートされているいずれかの形式がプレフィックスなしで探索されます。 次に例を示します。
ターゲット パラメーターが
selectedCourses
という名前のDictionary<int, string>
であるとします:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
ポストされたフォームまたはクエリ文字列データは、次のいずれかの例のようになります。
selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
[1050]=Chemistry&selectedCourses[2000]=Economics
selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
上記のすべてのフォーマット例において、モデル バインドでは 2 つの項目から成る辞書が
selectedCourses
パラメーターに渡されます。- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
コンストラクターのバインドとレコードの種類
モデルバインドでは、複合型にパラメーターなしのコンストラクターが含まれている必要があります。 System.Text.Json
と Newtonsoft.Json
ベースの入力フォーマッタは両方とも、パラメーターなしのコンストラクターを持たないクラスの逆シリアル化をサポートします。
レコードの種類は、ネットワーク経由でデータを簡潔に表す優れた方法です。 ASP.NET Coreでは、単一のコンストラクターを使用したモデル バインドとレコード型の検証がサポートされています。
public record Person(
[Required] string Name, [Range(0, 150)] int Age, [BindNever] int Id);
public class PersonController
{
public IActionResult Index() => View();
[HttpPost]
public IActionResult Index(Person person)
{
// ...
}
}
Person/Index.cshtml
:
@model Person
Name: <input asp-for="Name" />
<br />
Age: <input asp-for="Age" />
レコードの種類を検証するとき、ランタイムは、プロパティではなく、特にパラメーターに対してバインドと検証のメタデータを検索します。
フレームワークでは、レコードの種類へのバインディングと検証を行うことができます。
public record Person([Required] string Name, [Range(0, 100)] int Age);
前述の内容を機能させるには、次のような型である必要があります。
- レコードの種類である。
- パブリック コンストラクターを 1 つだけ持つ。
- 同じ名前および型のプロパティを持つパラメーターを含む。 名前は大文字と小文字を区別しないようにする必要がある。
パラメーターなしのコンストラクターを持たない POCO
パラメーターなしのコンストラクターを持たない POCO はバインドできません。
次のコードでは、型にパラメーターなしのコンストラクターが必要であるという例外が発生します。
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
手動で作成されるコンストラクターを含むレコードの種類
プライマリ コンストラクターが動作するように見える、手動で作成されたコンストラクターを含むレコードの種類
public record Person
{
public Person([Required] string Name, [Range(0, 100)] int Age)
=> (this.Name, this.Age) = (Name, Age);
public string Name { get; set; }
public int Age { get; set; }
}
レコードの種類、検証、メタデータのバインディング
レコードの種類については、パラメーターの検証とバインドのメタデータが使用されます。 プロパティのメタデータはすべて無視されます。
public record Person (string Name, int Age)
{
[BindProperty(Name = "SomeName")] // This does not get used
[Required] // This does not get used
public string Name { get; init; }
}
検証とメタデータ
検証では、パラメーターのメタデータを使用しますが、プロパティを使用して値を読み取ります。 通常のプライマリ コンストラクターの場合、2 つは同じになります。 ただし、これを打破する方法があります。
public record Person([Required] string Name)
{
private readonly string _name;
// The following property is never null.
// However this object could have been constructed as "new Person(null)".
public string Name { get; init => _name = value ?? string.Empty; }
}
TryUpdateModel によって、レコードの種類のパラメーターは更新されません。
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
この場合、MVC により再度 Name
のバインドが試行されることはありません。 ただし、Age
は更新されます。
モデル バインド ルート データとクエリ文字列のグローバリゼーション動作
ASP.NET Core ルート値プロバイダーとクエリ文字列値プロバイダーでは、次のことが行われます。
- 値をインバリアント カルチャとして扱います。
- URL はカルチャに依存しないものと想定します。
これに対し、フォーム データからの値は、カルチャに依存した変換にかけられます。 URL がロケール間で共有可能なように、設計上そのようになっています。
ASP.NET Core ルート値プロバイダーとクエリ文字列値プロバイダーでカルチャ依存の変換が行われるようにするには、次のようにします。
- IValueProviderFactory から継承します
- QueryStringValueProviderFactory または RouteValueValueProviderFactory からコードをコピーします。
- 値プロバイダー コンストラクターに渡されたカルチャ値を CultureInfo.CurrentCulture に置き換えます。
- MVC オプション内の既定値プロバイダー ファクトリを、新しいものに置き換えます。
public class CultureQueryStringValueProviderFactory : IValueProviderFactory
{
public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
{
_ = context ?? throw new ArgumentNullException(nameof(context));
var query = context.ActionContext.HttpContext.Request.Query;
if (query?.Count > 0)
{
context.ValueProviders.Add(
new QueryStringValueProvider(
BindingSource.Query,
query,
CultureInfo.CurrentCulture));
}
return Task.CompletedTask;
}
}
builder.Services.AddControllers(options =>
{
var index = options.ValueProviderFactories.IndexOf(
options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>()
.Single());
options.ValueProviderFactories[index] =
new CultureQueryStringValueProviderFactory();
});
特別なデータ型
モデル バインドで処理できる特殊なデータ型がいくつかあります。
IFormFile と IFormFileCollection
HTTP 要求に含まれたアップロード済みファイル。 また、複数のファイルに対して IEnumerable<IFormFile>
もサポートされています。
CancellationToken
アクションでは、オプションで CancellationToken
をパラメーターとしてバインドすることができます。 これにより、HTTP 要求の基になる接続が中断されたときに、それを通知する RequestAborted がバインドされます。 アクションでは、このパラメーターを使用して、コントローラー アクションの一部として実行される実行時間の長い非同期操作を取り消すことができます。
FormCollection
ポストされたフォーム データからすべての値を取得するために使用します。
入力フォーマッタ
要求本文のデータは、ON、XML、またはその他の JS形式にすることができます。 このデータを解析するために、モデル バインドでは、特定のコンテンツの種類を処理するように構成された "入力フォーマッタ" が使用されます。 既定では、ASP.NET Coreには ON データを処理JSするための ON ベースの入力フォーマッタが含まれていますJS。 他のコンテンツの種類については対応する他のフォーマッタを追加することができます。
ASP.NET Core では、Consumes 属性に基づいて入力フォーマッタが選択されます。 属性が存在しない場合は、Content-Type ヘッダーが使用されます。
組み込みの XML 入力フォーマッタを使用するには:
Program.cs
で、AddXmlSerializerFormatters または AddXmlDataContractSerializerFormatters を呼び出します。builder.Services.AddControllers() .AddXmlSerializerFormatters();
要求本文で XML を必要とするコントローラー クラスまたはアクション メソッドに
Consumes
属性を適用します。[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)
詳細については、「XML シリアル化の概要」を参照してください。
入力フォーマッタを使用してモデル バインドをカスタマイズする
入力フォーマッタは、要求本文からデータを読み取るためのすべての役割を担います。 このプロセスをカスタマイズするには、入力フォーマッタによって使用される API を構成します。 このセクションでは、ObjectId
という名前のカスタム型を理解するために、System.Text.Json
ベースの入力フォーマッタをカスタマイズする方法について説明します。
カスタム ObjectId
プロパティを含む次のモデルについて考えてみましょう。
public class InstructorObjectId
{
[Required]
public ObjectId ObjectId { get; set; } = null!;
}
System.Text.Json
を使用する際のモデル バインド プロセスをカスタマイズするために、JsonConverter<T> から派生するクラスを作成します。
internal class ObjectIdConverter : JsonConverter<ObjectId>
{
public override ObjectId Read(
ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> new(JsonSerializer.Deserialize<int>(ref reader, options));
public override void Write(
Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
=> writer.WriteNumberValue(value.Id);
}
カスタム コンバーターを使用するために、型に JsonConverterAttribute 属性を適用します。 次の例では、ObjectId
型は、ObjectIdConverter
をそのカスタム コンバーターとして構成されています。
[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);
詳細については、カスタム コンバーターを記述する方法に関する記事をご覧ください。
指定された型をモデル バインドから除外する
モデル バインドと検証システムの動作は によって駆動されます ModelMetadata。 ModelMetadata
については、詳細プロバイダーを MvcOptions.ModelMetadataDetailsProviders に追加してカスタマイズできます。 組み込みの詳細プロバイダーは、指定された型に対してモデル バインドまたは検証を無効にする場合に使用できます。
指定された型のすべてのモデルに対してモデル バインドを無効にするには、Program.cs
に ExcludeBindingMetadataProvider を追加します。 たとえば、System.Version
型のすべてのモデルに対してモデル バインドを無効にするには、次のようにします。
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
指定された型のプロパティに対して検証を無効にするには、Program.cs
に SuppressChildValidationMetadataProvider を追加します。 たとえば、System.Guid
型のプロパティに対して検証を無効にするには、次のようにします。
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
カスタム モデル バインダー
モデル バインドを拡張するには、カスタム モデル バインダーを記述し、[ModelBinder]
属性を使用してそれを特定のターゲット向けに選択します。 詳細については、「custom model binding」 (カスタム モデル バインド) を参照してください。
手動によるモデル バインド
モデル バインドは、TryUpdateModelAsync メソッドを使用して手動で呼び出すことができます。 このメソッドは ControllerBase
クラスと PageModel
クラスの両方で定義されています。 メソッドのオーバーロードにより、使用するプレフィックスと値プロバイダーを指定できます。 モデル バインドが失敗した場合は、メソッドから false
が返されます。 次に例を示します。
if (await TryUpdateModelAsync(
newInstructor,
"Instructor",
x => x.Name, x => x.HireDate!))
{
_instructorStore.Add(newInstructor);
return RedirectToPage("./Index");
}
return Page();
TryUpdateModelAsync では、値プロバイダーを使用して、フォーム本文、クエリ文字列、およびルート データからデータを取得します。 TryUpdateModelAsync
は通常、次のようになります。
- 過剰な投稿を防止するために、Razor Pages と MVC アプリ (コントローラーとビューを使用) で使用されます。
- フォーム データ、クエリ文字列、およびルート データから使用される場合を除き、Web API では使用されません。 ON を使用 JSする Web API エンドポイントでは 、入力フォーマッタ を使用して要求本文を オブジェクトに逆シリアル化します。
詳細については、「TryUpdateModelAsync」をご覧ください。
[FromServices] 属性
この属性の名前は、データ ソースを指定するモデル バインド属性のパターンに従います。 ただし、それは、値プロバイダーからのデータ バインドを説明するものではありません。 依存関係挿入コンテナーから型のインスタンスが取得されます。 その目的は、特定のメソッドが呼び出された場合にのみサービスを必要するときにコンストラクターの挿入の代替手段を提供することにあります。
型のインスタンスが依存関係挿入コンテナーに登録されていない場合、アプリは パラメーターをバインドしようとしたときに例外をスローします。 パラメーターを省略可能にするには、次のいずれかの方法を使用します。
- パラメーターを null 許容にします。
- パラメーターの既定値を設定します。
null 許容パラメーターの場合は、パラメーターにアクセスする前に パラメーターがないことを null
確認します。
その他のリソース
この記事では、モデル バインドとは何か、そのしくみ、その動作のカスタマイズ方法を説明します。
モデル バインドとは何か
コントローラーと Razor Pages では、HTTP 要求からのデータが処理されます。 たとえば、ルート データからはレコード キーが提供され、ポストされたフォーム フィールドからはモデルのプロパティ用の値が提供されます。 これらの各値を取得してそれらを文字列から .NET 型に変換するためのコードを記述するのは、面倒で間違いも起こりやすいでしょう。 モデル バインドを使用すれば、このプロセスを自動化できます。 モデル バインド システムでは次のことが行われます。
- ルート データ、フォーム フィールド、クエリ文字列などのさまざまなソースからデータを取得します。
- メソッド パラメーターとパブリック プロパティによりコントローラーと Razor Pages にデータが提供されます。
- 文字列データを .NET 型に変換します。
- 複合型のプロパティを更新します。
例
次のアクション メソッドがあるとします。
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
さらにアプリでは、この URL を使用して要求が受信されます。
https://contoso.com/api/pets/2?DogsOnly=true
ルーティング システムでアクション メソッドが選択されたら、モデル バインドでは次の手順が実行されます。
GetById
の最初のパラメーター (id
という名前の整数型) を検索します。- HTTP 要求内で利用可能なソースを調べ、ルート データ内で
id
= "2" を検索します。 - 文字列 "2" を整数 2 に変換します。
GetById
の次のパラメーター (dogsOnly
という名前のブール型) を検索します。- 該当するソース内を調べ、クエリ文字列内で "DogsOnly=true" を検索します。 名前の照合では大文字と小文字が区別されません。
- 文字列 "true" をブール型の
true
に変換します。
次にフレームワークによって GetById
メソッドが呼び出され、id
パラメーターには 2 が、dogsOnly
パラメーターには true
が渡されます。
上記の例で、モデル バインディング ターゲットは単純型のメソッド パラメーターになっています。 ターゲットは複合型のプロパティになる場合もあります。 各プロパティが正常にバインドされたら、そのプロパティに対してモデル検証が行われます。 どのようなデータがモデルにバインドされているかを示す記録、バインド エラー、または検証のエラーは、ControllerBase.ModelState または PageModel.ModelState に格納されます。 このプロセスが正常終了したかどうかを確認するために、アプリでは ModelState.IsValid フラグが調べられます。
対象サーバー
モデル バインドでは、次の種類のターゲットの値について検索が試みられます。
- 要求のルーティング先であるコントローラー アクション メソッドのパラメーター。
- 要求のルーティング先である Razor Pages ハンドラー メソッドのパラメーター。
- 属性によって指定されている場合は、コントローラーまたは
PageModel
クラスのパブリック プロパティ。
[BindProperty] 属性
コントローラーまたは PageModel
クラスのパブリック プロパティに適用できます。これによってモデル バインドはそのプロパティをターゲットとするようになります。
public class EditModel : PageModel
{
[BindProperty]
public Instructor? Instructor { get; set; }
// ...
}
[BindProperties] 属性
コントローラーまたは PageModel
クラスに適用できます。これによってモデル バインドはクラスのすべてのパブリック プロパティをターゲットとするように指示されます。
[BindProperties]
public class CreateModel : PageModel
{
public Instructor? Instructor { get; set; }
// ...
}
HTTP GET 要求のモデル バインド
既定では、プロパティは HTTP GET 要求にバインドされません。 通常、GET 要求に必要なのはレコード ID パラメーターのみです。 レコード ID は、データベース内の項目の検索に使用されます。 そのため、モデルのインスタンスを保持するプロパティをバインドする必要はありません。 GET 要求からのデータにプロパティがバインドされるようにするシナリオでは、SupportsGet
プロパティを true
に設定します。
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string? ApplicationInsightsCookie { get; set; }
変換元
既定では、モデル バインドでは HTTP 要求内の次のソースからキーと値のペアの形式でデータが取得されます。
- フォーム フィールド
- 要求本文 ([ApiController] 属性を持つコントローラー の場合)。
- ルート データ
- クエリ文字列パラメーター
- アップロード済みのファイル
ターゲット パラメーターまたはプロパティごとに、前述の一覧に示されている順序でソースがスキャンされます。 次のようにいくつかの例外があります。
- ルート データとクエリ文字列の値は単純型にのみ使用されます。
- アップロード済みのファイルは、
IFormFile
またはIEnumerable<IFormFile>
を実装するターゲットの種類にのみバインドされます。
既定のソースが正しくない場合は、次のいずれかの属性を使用してソースを指定します。
[FromQuery]
- クエリ文字列から値を取得します。[FromRoute]
- ルート データから値を取得します。[FromForm]
- ポストされたフォーム フィールドから値を取得します。[FromBody]
- 要求本文から値を取得します。[FromHeader]
- HTTP ヘッダーから値を取得します。
これらの属性:
次の例のように、モデル クラスではなく、モデル プロパティに個別に追加されます。
public class Instructor { public int Id { get; set; } [FromQuery(Name = "Note")] public string? NoteFromQueryString { get; set; } // ... }
必要に応じて、コンストラクター内のモデル名の値を受け取ります。 このオプションは、プロパティ名と要求内の値とが一致しない場合に指定されます。 たとえば、要求内の値は、次の例のようにその名前にハイフンが含まれている場合、ヘッダーである可能性があります。
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
[FromBody] 属性
[FromBody]
属性をパラメーターに適用すると、HTTP 要求の本文からそのプロパティが設定されます。 ASP.NET Core ランタイムでは、本文を読み取る責任が入力フォーマッタに委任されます。 入力フォーマッタについては、この記事で後ほど説明します。
[FromBody]
を複合型パラメーターに適用すると、そのプロパティに適用されているバインディング ソース属性はいずれも無視されます。 たとえば、次の Create
アクションでは、その pet
パラメーターを本文から設定するように指定されています。
public ActionResult<Pet> Create([FromBody] Pet pet)
Pet
クラスでは、Breed
プロパティをクエリ文字列パラメーターから設定するように指定されています。
public class Pet
{
public string Name { get; set; } = null!;
[FromQuery] // Attribute is ignored.
public string Breed { get; set; } = null!;
}
前の例の場合:
[FromQuery]
属性は無視されます。Breed
プロパティは、クエリ文字列パラメーターから設定されません。
入力フォーマッタでは本文のみが読み取られ、バインディング ソース属性は認識されません。 本文内で適切な値が見つかった場合は、その値を使用して Breed
プロパティが設定されます。
アクション メソッドごとに [FromBody]
を複数のパラメーターに適用しないでください。 入力フォーマッタによって要求ストリームが読み取られると、他の [FromBody]
パラメーターをバインドするためにそれを再度読み取ることはできません。
その他のソース
ソース データは、"値プロバイダー" によってモデル バインド システムに提供されます。 モデル バインド用に、他のソースからデータを取得するカスタムの値プロバイダーを作成して登録することができます。 たとえば、cookie またはセッション状態からのデータが必要だとします。 新しいソースからデータを取得するには:
IValueProvider
を実装するクラスを作成します。IValueProviderFactory
を実装するクラスを作成します。Program.cs
内のファクトリ クラスを登録します。
このサンプルには、 からcookie値を取得する値プロバイダーとファクトリの例が含まれています。 でカスタム値プロバイダー ファクトリを登録します Program.cs
。
builder.Services.AddControllers(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
});
上記のコードは、すべての組み込み値プロバイダーの後にカスタム値プロバイダーを配置します。 それをリストの最初に持ってくるには、Add
ではなく Insert(0, new CookieValueProviderFactory())
を呼び出します。
モデル プロパティ用のソースがない
既定では、モデル プロパティ用の値が見つからない場合、モデル状態エラーは作成されません。 プロパティは次のように null 値または既定値に設定されます。
- null 許容単純型は
null
に設定されます。 - null 非許容値型は
default(T)
に設定されます。 たとえば、パラメーターint id
は 0 に設定されます。 - 複合型の場合、モデル バインドでは、プロパティを設定せずに既定のコンストラクターを使用して、インスタンスが作成されます。
- 配列は
Array.Empty<T>()
に設定されます。例外として、byte[]
配列はnull
に設定されます。
モデル プロパティ用のフォーム フィールド内で何も見つからないときモデル状態を無効にする必要がある場合は、[BindRequired]
属性を使用します。
この動作は [BindRequired]
、要求本文の ON または XML データではなく JS、ポストされたフォーム データからのモデル バインドに適用されることに注意してください。 要求本文データは、入力フォーマッタによって処理されます。
型変換エラー
ソースは見つかってもそれをターゲットの種類に変換できない場合、無効であることを示すフラグがモデル状態に付けられます。 前のセクションで説明したように、ターゲットのパラメーターまたはプロパティは null または既定値に設定されます。
[ApiController]
属性を持つ API コントローラーでは、モデル状態が無効であると、HTTP 400 の自動応答が生成されます。
Razor ページでは、エラー メッセージを含むページが再表示されます。
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// ...
return RedirectToPage("./Index");
}
前のコードによってページが再表示されると、無効な入力はフォーム フィールドに表示されません。 これは、モデル プロパティが null または既定値に設定されているためです。 無効な入力はエラー メッセージに表示されます。 フォーム フィールドで不適切なデータを再表示する場合は、model プロパティを文字列にし、データ変換を手動で行うことを検討してください。
型変換エラーが結果的にモデル状態エラーになることを望まない場合も同じ方法をお勧めします。 その場合は、モデル プロパティを文字列にします。
単純型
モデル バインダーでソース文字列の変換先とすることができる単純型には次のものがあります。
- Boolean
- Byte、SByte
- Char
- DateTime
- DateTimeOffset
- Decimal
- Double
- Enum
- Guid
- Int16、Int32、Int64
- Single
- TimeSpan
- UInt16、UInt32、UInt64
- Uri
- バージョン
複合型
複合型には、バインドする既定のパブリック コンストラクターと書き込み可能なパブリック プロパティが必要です。 モデル バインドが行われると、クラスは既定のパブリック コンストラクターを使用してインスタンス化されます。
複合型の各プロパティについて、 モデル バインドは名前パターン prefix.property_nameのソースを調べます。 何も見つからない場合は、プレフィックスなしで property_name だけが探索されます。 プレフィックスを使用する決定は、プロパティごとに行われません。 たとえば、 メソッドにOnGet(Instructor instructor)
バインドされた を含む?Instructor.Id=100&Name=foo
クエリでは、 型Instructor
の結果のオブジェクトには次のものが含まれます。
Id
を100
に設定する。Name
をnull
に設定する。 前のInstructor.Name
クエリ パラメーターで が使用されたためInstructor.Id
、モデル バインドは を想定しています。
パラメーターにバインドする場合、プレフィックスはパラメーター名です。 PageModel
パブリック プロパティにバインドする場合、プレフィックスはパブリック プロパティ名です。 一部の属性には、パラメーター名またはプロパティ名の既定の使用をオーバーライドするための Prefix
プロパティがあります。
たとえば、複合型が次の Instructor
クラスであるとします。
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
プレフィックス = パラメーター名
バインドされるモデルが instructorToUpdate
という名前のパラメーターである場合:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
モデル バインドでは、キー instructorToUpdate.ID
がないかソースを調べることから始まります。 見つからない場合は、プレフィックスなしで ID
が探索されます。
プレフィックス = プロパティ名
バインドされるモデルがコントローラーの Instructor
という名前のプロパティか、または PageModel
クラスである場合:
[BindProperty]
public Instructor Instructor { get; set; }
モデル バインドでは、キー Instructor.ID
がないかソースを調べることから始まります。 見つからない場合は、プレフィックスなしで ID
が探索されます。
カスタム プレフィックス
バインドされるモデルが instructorToUpdate
という名前のパラメーターであり、かつ Bind
属性でプレフィックスとして Instructor
が指定されている場合:
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
モデル バインドでは、キー Instructor.ID
がないかソースを調べることから始まります。 見つからない場合は、プレフィックスなしで ID
が探索されます。
複合型のターゲットの属性
複合型のモデル バインドを制御するために利用できる組み込みの属性がいくつかあります。
警告
ポストされたフォーム データが値のソースである場合、これらの属性はモデル バインドに影響します。 入力フォーマッタには影響 しません 。入力フォーマッタは、ON 要求本文と XML 要求本文を JS処理します。 入力フォーマッタについては、この記事で後ほど説明します。
[Bind] 属性
クラスまたはメソッド パラメーターに適用できます。 モデルのどのプロパティをモデル バインドに含めるかを指定します。 [Bind]
は、入力フォーマッタ には影響しません 。
次の例では、任意のハンドラーまたはアクション メソッドが呼び出されると、Instructor
モデルの指定されたプロパティのみがバインドされます。
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
次の例では、OnPost
メソッドが呼び出されると、Instructor
モデルの指定されたプロパティのみがバインドされます。
[HttpPost]
public IActionResult OnPost(
[Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
[Bind]
属性を使用すれば、"作成" シナリオにおいて過剰ポスティングから保護することができます。 除外されたプロパティはそのままにしておくのではなく null または既定値に設定されるので、この属性は編集シナリオではうまく機能しません。 過剰ポスティングを防ぐ場合は、[Bind]
属性ではなくビュー モデルをお勧めします。 詳細については、「過剰ポスティングに関するセキュリティの注意事項」を参照してください。
[ModelBinder] 属性
ModelBinderAttribute は、型、プロパティ、またはパラメーターに適用されます。 これにより、特定のインスタンスまたは型をバインドするために使用されるモデル バインダーの種類を指定できます。 次に例を示します。
[HttpPost]
public IActionResult OnPost(
[ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
[ModelBinder]
属性を使用して、モデル バインド時にプロパティまたはパラメーターの名前を変更することもできます。
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
// ...
}
[BindRequired] 属性
モデルのプロパティに対してバインドを実行できない場合に、モデル バインドがモデル状態エラーを追加できるようにします。 次に例を示します。
public class InstructorBindRequired
{
// ...
[BindRequired]
public DateTime HireDate { get; set; }
}
モデル検証に関するページにある [Required]
属性の説明も参照してください。
[BindNever] 属性
プロパティまたは型に適用できます。 モデル バインドがモデルのプロパティを設定できないようにします。 型に適用すると、モデル バインド システムは、型が定義するすべてのプロパティを除外します。 次に例を示します。
public class InstructorBindNever
{
[BindNever]
public int Id { get; set; }
// ...
}
コレクション
ターゲットが単純型のコレクションである場合、モデル バインドでは parameter_name または property_name との一致が探索されます。 一致が見つからない場合は、サポートされているいずれかの形式がプレフィックスなしで探索されます。 次に例を示します。
バインドされるパラメーターが
selectedCourses
という名前の配列であるとした場合:public IActionResult OnPost(int? id, int[] selectedCourses)
フォームまたはクエリ文字列データは、次のいずれかの形式とすることができます。
selectedCourses=1050&selectedCourses=2000
selectedCourses[0]=1050&selectedCourses[1]=2000
[0]=1050&[1]=2000
selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
[a]=1050&[b]=2000&index=a&index=b
パラメーターまたは という名前
index
のプロパティ、またはIndex
コレクション値に隣接している場合はバインドしないでください。 モデル バインドは、コレクションのインデックスとして を使用index
しようとします。これにより、バインドが正しくない可能性があります。 たとえば、次のアクションを考えてみましょう。public IActionResult Post(string index, List<Product> products)
前のコードでは、クエリ文字列パラメーターは
index
メソッド パラメーターにindex
バインドされ、製品コレクションのバインドにも使用されています。 パラメーターの名前をindex
変更するか、モデル バインド属性を使用してバインドを構成すると、この問題は回避されます。public IActionResult Post(string productIndex, List<Product> products)
次の形式は、フォーム データでのみサポートされます。
selectedCourses[]=1050&selectedCourses[]=2000
上記のすべてのフォーマット例において、モデル バインドでは 2 つの項目から成る配列が
selectedCourses
パラメーターに渡されます。- selectedCourses[0]=1050
- selectedCourses[1]=2000
添え字番号 (... [0] ... [1] ...) を使用するデータ フォーマットでは、確実にそれらがゼロから始まる連続した番号になるようにする必要があります。 添え字の番号付けで欠落している番号がある場合、欠落している番号の後の項目はすべて無視されます。 たとえば、添え字が 0、1 の並びではなく、0、2 の並びで振られている場合、2 番目の項目は無視されます。
辞書
Dictionary
ターゲットの場合、モデル バインドでは parameter_name または property_name との一致が探索されます。 一致が見つからない場合は、サポートされているいずれかの形式がプレフィックスなしで探索されます。 次に例を示します。
ターゲット パラメーターが
selectedCourses
という名前のDictionary<int, string>
であるとします:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
ポストされたフォームまたはクエリ文字列データは、次のいずれかの例のようになります。
selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
[1050]=Chemistry&selectedCourses[2000]=Economics
selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
上記のすべてのフォーマット例において、モデル バインドでは 2 つの項目から成る辞書が
selectedCourses
パラメーターに渡されます。- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
コンストラクターのバインドとレコードの種類
モデルバインドでは、複合型にパラメーターなしのコンストラクターが含まれている必要があります。 System.Text.Json
と Newtonsoft.Json
ベースの入力フォーマッタは両方とも、パラメーターなしのコンストラクターを持たないクラスの逆シリアル化をサポートします。
レコード型は、ネットワーク経由でデータを簡潔に表す優れた方法です。 ASP.NET Coreでは、1 つのコンストラクターを使用したモデル バインドとレコード型の検証がサポートされています。
public record Person(
[Required] string Name, [Range(0, 150)] int Age, [BindNever] int Id);
public class PersonController
{
public IActionResult Index() => View();
[HttpPost]
public IActionResult Index(Person person)
{
// ...
}
}
Person/Index.cshtml
:
@model Person
Name: <input asp-for="Name" />
<br />
Age: <input asp-for="Age" />
レコードの種類を検証するとき、ランタイムは、プロパティではなく、特にパラメーターに対してバインドと検証のメタデータを検索します。
フレームワークでは、レコードの種類へのバインディングと検証を行うことができます。
public record Person([Required] string Name, [Range(0, 100)] int Age);
前述の内容を機能させるには、次のような型である必要があります。
- レコードの種類である。
- パブリック コンストラクターを 1 つだけ持つ。
- 同じ名前および型のプロパティを持つパラメーターを含む。 名前は大文字と小文字を区別しないようにする必要がある。
パラメーターなしのコンストラクターを持たない POCO
パラメーターなしのコンストラクターを持たない POCO はバインドできません。
次のコードでは、型にパラメーターなしのコンストラクターが必要であるという例外が発生します。
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
手動で作成されるコンストラクターを含むレコードの種類
プライマリ コンストラクターが動作するように見える、手動で作成されたコンストラクターを含むレコードの種類
public record Person
{
public Person([Required] string Name, [Range(0, 100)] int Age)
=> (this.Name, this.Age) = (Name, Age);
public string Name { get; set; }
public int Age { get; set; }
}
レコードの種類、検証、メタデータのバインディング
レコードの種類については、パラメーターの検証とバインドのメタデータが使用されます。 プロパティのメタデータはすべて無視されます。
public record Person (string Name, int Age)
{
[BindProperty(Name = "SomeName")] // This does not get used
[Required] // This does not get used
public string Name { get; init; }
}
検証とメタデータ
検証では、パラメーターのメタデータを使用しますが、プロパティを使用して値を読み取ります。 通常のプライマリ コンストラクターの場合、2 つは同じになります。 ただし、これを打破する方法があります。
public record Person([Required] string Name)
{
private readonly string _name;
// The following property is never null.
// However this object could have been constructed as "new Person(null)".
public string Name { get; init => _name = value ?? string.Empty; }
}
TryUpdateModel によって、レコードの種類のパラメーターは更新されません。
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
この場合、MVC により再度 Name
のバインドが試行されることはありません。 ただし、Age
は更新されます。
モデル バインド ルート データとクエリ文字列のグローバリゼーション動作
ASP.NET Core ルート値プロバイダーとクエリ文字列値プロバイダーでは、次のことが行われます。
- 値をインバリアント カルチャとして扱います。
- URL はカルチャに依存しないものと想定します。
これに対し、フォーム データからの値は、カルチャに依存した変換にかけられます。 URL がロケール間で共有可能なように、設計上そのようになっています。
ASP.NET Core ルート値プロバイダーとクエリ文字列値プロバイダーでカルチャ依存の変換が行われるようにするには、次のようにします。
- IValueProviderFactory から継承します
- QueryStringValueProviderFactory または RouteValueValueProviderFactory からコードをコピーします。
- 値プロバイダー コンストラクターに渡されたカルチャ値を CultureInfo.CurrentCulture に置き換えます。
- MVC オプション内の既定値プロバイダー ファクトリを、新しいものに置き換えます。
public class CultureQueryStringValueProviderFactory : IValueProviderFactory
{
public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
{
_ = context ?? throw new ArgumentNullException(nameof(context));
var query = context.ActionContext.HttpContext.Request.Query;
if (query?.Count > 0)
{
context.ValueProviders.Add(
new QueryStringValueProvider(
BindingSource.Query,
query,
CultureInfo.CurrentCulture));
}
return Task.CompletedTask;
}
}
builder.Services.AddControllers(options =>
{
var index = options.ValueProviderFactories.IndexOf(
options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>()
.Single());
options.ValueProviderFactories[index] =
new CultureQueryStringValueProviderFactory();
});
特別なデータ型
モデル バインドで処理できる特殊なデータ型がいくつかあります。
IFormFile と IFormFileCollection
HTTP 要求に含まれたアップロード済みファイル。 また、複数のファイルに対して IEnumerable<IFormFile>
もサポートされています。
CancellationToken
アクションでは、オプションで CancellationToken
をパラメーターとしてバインドすることができます。 これにより、HTTP 要求の基になる接続が中断されたときに、それを通知する RequestAborted がバインドされます。 アクションでは、このパラメーターを使用して、コントローラー アクションの一部として実行される実行時間の長い非同期操作を取り消すことができます。
FormCollection
ポストされたフォーム データからすべての値を取得するために使用します。
入力フォーマッタ
要求本文のデータは、ON、XML、またはその他の JS形式にすることができます。 このデータを解析するために、モデル バインドでは、特定のコンテンツの種類を処理するように構成された "入力フォーマッタ" が使用されます。 既定では、ASP.NET Coreには ON データを処理JSするための ON ベースの入力フォーマッタが含まれますJS。 他のコンテンツの種類については対応する他のフォーマッタを追加することができます。
ASP.NET Core では、Consumes 属性に基づいて入力フォーマッタが選択されます。 属性が存在しない場合は、Content-Type ヘッダーが使用されます。
組み込みの XML 入力フォーマッタを使用するには:
Program.cs
で、AddXmlSerializerFormatters または AddXmlDataContractSerializerFormatters を呼び出します。builder.Services.AddControllers() .AddXmlSerializerFormatters();
要求本文で XML を必要とするコントローラー クラスまたはアクション メソッドに
Consumes
属性を適用します。[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)
詳細については、「XML シリアル化の概要」を参照してください。
入力フォーマッタを使用してモデル バインドをカスタマイズする
入力フォーマッタは、要求本文からデータを読み取るためのすべての役割を担います。 このプロセスをカスタマイズするには、入力フォーマッタによって使用される API を構成します。 このセクションでは、ObjectId
という名前のカスタム型を理解するために、System.Text.Json
ベースの入力フォーマッタをカスタマイズする方法について説明します。
カスタム ObjectId
プロパティを含む次のモデルについて考えてみましょう。
public class InstructorObjectId
{
[Required]
public ObjectId ObjectId { get; set; } = null!;
}
System.Text.Json
を使用する際のモデル バインド プロセスをカスタマイズするために、JsonConverter<T> から派生するクラスを作成します。
internal class ObjectIdConverter : JsonConverter<ObjectId>
{
public override ObjectId Read(
ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> new(JsonSerializer.Deserialize<int>(ref reader, options));
public override void Write(
Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
=> writer.WriteNumberValue(value.Id);
}
カスタム コンバーターを使用するために、型に JsonConverterAttribute 属性を適用します。 次の例では、ObjectId
型は、ObjectIdConverter
をそのカスタム コンバーターとして構成されています。
[JsonConverter(typeof(ObjectIdConverter))]
public record ObjectId(int Id);
詳細については、カスタム コンバーターを記述する方法に関する記事をご覧ください。
指定された型をモデル バインドから除外する
モデル バインドと検証システムの動作は、 によって ModelMetadata駆動されます。 ModelMetadata
については、詳細プロバイダーを MvcOptions.ModelMetadataDetailsProviders に追加してカスタマイズできます。 組み込みの詳細プロバイダーは、指定された型に対してモデル バインドまたは検証を無効にする場合に使用できます。
指定された型のすべてのモデルに対してモデル バインドを無効にするには、Program.cs
に ExcludeBindingMetadataProvider を追加します。 たとえば、System.Version
型のすべてのモデルに対してモデル バインドを無効にするには、次のようにします。
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
指定された型のプロパティに対して検証を無効にするには、Program.cs
に SuppressChildValidationMetadataProvider を追加します。 たとえば、System.Guid
型のプロパティに対して検証を無効にするには、次のようにします。
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(Guid)));
});
カスタム モデル バインダー
モデル バインドを拡張するには、カスタム モデル バインダーを記述し、[ModelBinder]
属性を使用してそれを特定のターゲット向けに選択します。 詳細については、「custom model binding」 (カスタム モデル バインド) を参照してください。
手動によるモデル バインド
モデル バインドは、TryUpdateModelAsync メソッドを使用して手動で呼び出すことができます。 このメソッドは ControllerBase
クラスと PageModel
クラスの両方で定義されています。 メソッドのオーバーロードにより、使用するプレフィックスと値プロバイダーを指定できます。 モデル バインドが失敗した場合は、メソッドから false
が返されます。 次に例を示します。
if (await TryUpdateModelAsync(
newInstructor,
"Instructor",
x => x.Name, x => x.HireDate!))
{
_instructorStore.Add(newInstructor);
return RedirectToPage("./Index");
}
return Page();
TryUpdateModelAsync では、値プロバイダーを使用して、フォーム本文、クエリ文字列、およびルート データからデータを取得します。 TryUpdateModelAsync
は通常、次のようになります。
- 過剰な投稿を防止するために、Razor Pages と MVC アプリ (コントローラーとビューを使用) で使用されます。
- フォーム データ、クエリ文字列、およびルート データから使用される場合を除き、Web API では使用されません。 ON を使用 JSする Web API エンドポイントでは 、Input フォーマッタ を使用して、要求本文を オブジェクトに逆シリアル化します。
詳細については、「TryUpdateModelAsync」をご覧ください。
[FromServices] 属性
この属性の名前は、データ ソースを指定するモデル バインド属性のパターンに従います。 ただし、それは、値プロバイダーからのデータ バインドを説明するものではありません。 依存関係挿入コンテナーから型のインスタンスが取得されます。 その目的は、特定のメソッドが呼び出された場合にのみサービスを必要するときにコンストラクターの挿入の代替手段を提供することにあります。
型のインスタンスが依存関係挿入コンテナーに登録されていない場合、アプリは パラメーターをバインドしようとしたときに例外をスローします。 パラメーターを省略可能にするには、次のいずれかの方法を使用します。
- パラメーターを null 許容にします。
- パラメーターの既定値を設定します。
null 許容パラメーターの場合は、 パラメーターがアクセスする前にないことを null
確認します。
その他のリソース
この記事では、モデル バインドとは何か、そのしくみ、その動作のカスタマイズ方法を説明します。
サンプル コードを表示またはダウンロードします (ダウンロード方法)。
モデル バインドとは何か
コントローラーと Razor Pages では、HTTP 要求からのデータが処理されます。 たとえば、ルート データからはレコード キーが提供され、ポストされたフォーム フィールドからはモデルのプロパティ用の値が提供されます。 これらの各値を取得してそれらを文字列から .NET 型に変換するためのコードを記述するのは、面倒で間違いも起こりやすいでしょう。 モデル バインドを使用すれば、このプロセスを自動化できます。 モデル バインド システムでは次のことが行われます。
- ルート データ、フォーム フィールド、クエリ文字列などのさまざまなソースからデータを取得します。
- メソッド パラメーターとパブリック プロパティによりコントローラーと Razor Pages にデータが提供されます。
- 文字列データを .NET 型に変換します。
- 複合型のプロパティを更新します。
例
次のアクション メソッドがあるとします。
[HttpGet("{id}")]
public ActionResult<Pet> GetById(int id, bool dogsOnly)
さらにアプリでは、この URL を使用して要求が受信されます。
http://contoso.com/api/pets/2?DogsOnly=true
ルーティング システムでアクション メソッドが選択されたら、モデル バインドでは次の手順が実行されます。
GetById
の最初のパラメーター (id
という名前の整数型) を検索します。- HTTP 要求内で利用可能なソースを調べ、ルート データ内で
id
= "2" を検索します。 - 文字列 "2" を整数 2 に変換します。
GetById
の次のパラメーター (dogsOnly
という名前のブール型) を検索します。- 該当するソース内を調べ、クエリ文字列内で "DogsOnly=true" を検索します。 名前の照合では大文字と小文字が区別されません。
- 文字列 "true" をブール型の
true
に変換します。
次にフレームワークによって GetById
メソッドが呼び出され、id
パラメーターには 2 が、dogsOnly
パラメーターには true
が渡されます。
上記の例で、モデル バインディング ターゲットは単純型のメソッド パラメーターになっています。 ターゲットは複合型のプロパティになる場合もあります。 各プロパティが正常にバインドされたら、そのプロパティに対してモデル検証が行われます。 どのようなデータがモデルにバインドされているかを示す記録、バインド エラー、または検証のエラーは、ControllerBase.ModelState または PageModel.ModelState に格納されます。 このプロセスが正常終了したかどうかを確認するために、アプリでは ModelState.IsValid フラグが調べられます。
対象サーバー
モデル バインドでは、次の種類のターゲットの値について検索が試みられます。
- 要求のルーティング先であるコントローラー アクション メソッドのパラメーター。
- 要求のルーティング先である Razor Pages ハンドラー メソッドのパラメーター。
- 属性によって指定されている場合は、コントローラーまたは
PageModel
クラスのパブリック プロパティ。
[BindProperty] 属性
コントローラーまたは PageModel
クラスのパブリック プロパティに適用できます。これによってモデル バインドはそのプロパティをターゲットとするようになります。
public class EditModel : InstructorsPageModel
{
[BindProperty]
public Instructor Instructor { get; set; }
[BindProperties] 属性
ASP.NET Core 2.1 以降で使用できます。 コントローラーまたは PageModel
クラスに適用できます。これによってモデル バインドはクラスのすべてのパブリック プロパティをターゲットとするように指示されます。
[BindProperties(SupportsGet = true)]
public class CreateModel : InstructorsPageModel
{
public Instructor Instructor { get; set; }
HTTP GET 要求のモデル バインド
既定では、プロパティは HTTP GET 要求にバインドされません。 通常、GET 要求に必要なのはレコード ID パラメーターのみです。 レコード ID は、データベース内の項目の検索に使用されます。 そのため、モデルのインスタンスを保持するプロパティをバインドする必要はありません。 GET 要求からのデータにプロパティがバインドされるようにするシナリオでは、SupportsGet
プロパティを true
に設定します。
[BindProperty(Name = "ai_user", SupportsGet = true)]
public string ApplicationInsightsCookie { get; set; }
変換元
既定では、モデル バインドでは HTTP 要求内の次のソースからキーと値のペアの形式でデータが取得されます。
- フォーム フィールド
- 要求本文 ([ApiController] 属性を持つコントローラー の場合)。
- ルート データ
- クエリ文字列パラメーター
- アップロード済みのファイル
ターゲット パラメーターまたはプロパティごとに、前述の一覧に示されている順序でソースがスキャンされます。 次のようにいくつかの例外があります。
- ルート データとクエリ文字列の値は単純型にのみ使用されます。
- アップロード済みのファイルは、
IFormFile
またはIEnumerable<IFormFile>
を実装するターゲットの種類にのみバインドされます。
既定のソースが正しくない場合は、次のいずれかの属性を使用してソースを指定します。
[FromQuery]
- クエリ文字列から値を取得します。[FromRoute]
- ルート データから値を取得します。[FromForm]
- ポストされたフォーム フィールドから値を取得します。[FromBody]
- 要求本文から値を取得します。[FromHeader]
- HTTP ヘッダーから値を取得します。
これらの属性:
次の例のように、(モデル クラスにではなく) モデル プロパティに個別に追加されます。
public class Instructor { public int ID { get; set; } [FromQuery(Name = "Note")] public string NoteFromQueryString { get; set; }
必要に応じて、コンストラクター内のモデル名の値を受け取ります。 このオプションは、プロパティ名と要求内の値とが一致しない場合に指定されます。 たとえば、要求内の値は、次の例のようにその名前にハイフンが含まれている場合、ヘッダーである可能性があります。
public void OnGet([FromHeader(Name = "Accept-Language")] string language)
[FromBody] 属性
[FromBody]
属性をパラメーターに適用すると、HTTP 要求の本文からそのプロパティが設定されます。 ASP.NET Core ランタイムでは、本文を読み取る責任が入力フォーマッタに委任されます。 入力フォーマッタについては、この記事で後ほど説明します。
[FromBody]
を複合型パラメーターに適用すると、そのプロパティに適用されているバインディング ソース属性はいずれも無視されます。 たとえば、次の Create
アクションでは、その pet
パラメーターを本文から設定するように指定されています。
public ActionResult<Pet> Create([FromBody] Pet pet)
Pet
クラスでは、Breed
プロパティをクエリ文字列パラメーターから設定するように指定されています。
public class Pet
{
public string Name { get; set; }
[FromQuery] // Attribute is ignored.
public string Breed { get; set; }
}
前の例の場合:
[FromQuery]
属性は無視されます。Breed
プロパティは、クエリ文字列パラメーターから設定されません。
入力フォーマッタでは本文のみが読み取られ、バインディング ソース属性は認識されません。 本文内で適切な値が見つかった場合は、その値を使用して Breed
プロパティが設定されます。
アクション メソッドごとに [FromBody]
を複数のパラメーターに適用しないでください。 入力フォーマッタによって要求ストリームが読み取られると、他の [FromBody]
パラメーターをバインドするためにそれを再度読み取ることはできません。
その他のソース
ソース データは、"値プロバイダー" によってモデル バインド システムに提供されます。 モデル バインド用に、他のソースからデータを取得するカスタムの値プロバイダーを作成して登録することができます。 たとえば、cookie またはセッション状態からのデータが必要だとします。 新しいソースからデータを取得するには:
IValueProvider
を実装するクラスを作成します。IValueProviderFactory
を実装するクラスを作成します。Startup.ConfigureServices
内のファクトリ クラスを登録します。
サンプル アプリには、cookie から値を取得する値プロバイダーとファクトリの例が含まれています。 Startup.ConfigureServices
内の登録コードを次に示します。
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(System.Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(System.Guid)));
})
.AddXmlSerializerFormatters();
表示したコードでは、すべての組み込み値プロバイダーの後にカスタムの値プロバイダーが配置されています。 それをリストの最初に持ってくるには、Add
ではなく Insert(0, new CookieValueProviderFactory())
を呼び出します。
モデル プロパティ用のソースがない
既定では、モデル プロパティ用の値が見つからない場合、モデル状態エラーは作成されません。 プロパティは次のように null 値または既定値に設定されます。
- null 許容単純型は
null
に設定されます。 - null 非許容値型は
default(T)
に設定されます。 たとえば、パラメーターint id
は 0 に設定されます。 - 複合型の場合、モデル バインドでは、プロパティを設定せずに既定のコンストラクターを使用して、インスタンスが作成されます。
- 配列は
Array.Empty<T>()
に設定されます。例外として、byte[]
配列はnull
に設定されます。
モデル プロパティ用のフォーム フィールド内で何も見つからないときモデル状態を無効にする必要がある場合は、[BindRequired]
属性を使用します。
この動作は [BindRequired]
、要求本文の ON または XML データではなく JS、投稿されたフォーム データからのモデル バインドに適用されることに注意してください。 要求本文データは、入力フォーマッタによって処理されます。
型変換エラー
ソースは見つかってもそれをターゲットの種類に変換できない場合、無効であることを示すフラグがモデル状態に付けられます。 前のセクションで説明したように、ターゲットのパラメーターまたはプロパティは null または既定値に設定されます。
[ApiController]
属性を持つ API コントローラーでは、モデル状態が無効であると、HTTP 400 の自動応答が生成されます。
Razor ページでは、エラー メッセージを含むページが再表示されます。
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
_instructorsInMemoryStore.Add(Instructor);
return RedirectToPage("./Index");
}
クライアント側の検証では、それが行われなかった場合に Razor Pages フォームに送信されてしまう不適切なデータのほとんどがキャッチされます。 この検証により、前の強調表示されたコードをトリガーするのが難しくなります。 サンプル アプリには、[Submit with Invalid Date] ボタンが含まれており、これを使用すると、[Hire Date] フィールドに不適切なデータが入力され、そのフォームが送信されます。 このボタンを使用すると、データ変換エラーが発生したときにページを再表示するためのコードがどのように機能するかを表示できます。
上のコードでページが再表示されると、無効な入力はフォーム フィールドに表示されません。 これは、モデル プロパティが null または既定値に設定されているためです。 無効な入力はエラー メッセージに表示されます。 しかし、フォーム フィールドに不適切なデータを再表示したい場合は、モデル プロパティを文字列にしてデータ変換を手動で行うことを検討してください。
型変換エラーが結果的にモデル状態エラーになることを望まない場合も同じ方法をお勧めします。 その場合は、モデル プロパティを文字列にします。
単純型
モデル バインダーでソース文字列の変換先とすることができる単純型には次のものがあります。
- Boolean
- Byte、SByte
- Char
- DateTime
- DateTimeOffset
- Decimal
- Double
- Enum
- Guid
- Int16、Int32、Int64
- Single
- TimeSpan
- UInt16、UInt32、UInt64
- Uri
- バージョン
複合型
複合型には、バインドする既定のパブリック コンストラクターと書き込み可能なパブリック プロパティが必要です。 モデル バインドが行われると、クラスは既定のパブリック コンストラクターを使用してインスタンス化されます。
複合型のプロパティごとに、モデル バインドでは名前パターン prefix.property_name がないかソースが調べられます。 何も見つからない場合は、プレフィックスなしで property_name だけが探索されます。
パラメーターにバインドする場合、プレフィックスはパラメーター名です。 PageModel
パブリック プロパティにバインドする場合、プレフィックスはパブリック プロパティ名です。 一部の属性には、パラメーター名またはプロパティ名の既定の使用をオーバーライドするための Prefix
プロパティがあります。
たとえば、複合型が次の Instructor
クラスであるとします。
public class Instructor
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
プレフィックス = パラメーター名
バインドされるモデルが instructorToUpdate
という名前のパラメーターである場合:
public IActionResult OnPost(int? id, Instructor instructorToUpdate)
モデル バインドでは、キー instructorToUpdate.ID
がないかソースを調べることから始まります。 見つからない場合は、プレフィックスなしで ID
が探索されます。
プレフィックス = プロパティ名
バインドされるモデルがコントローラーの Instructor
という名前のプロパティか、または PageModel
クラスである場合:
[BindProperty]
public Instructor Instructor { get; set; }
モデル バインドでは、キー Instructor.ID
がないかソースを調べることから始まります。 見つからない場合は、プレフィックスなしで ID
が探索されます。
カスタム プレフィックス
バインドされるモデルが instructorToUpdate
という名前のパラメーターであり、かつ Bind
属性でプレフィックスとして Instructor
が指定されている場合:
public IActionResult OnPost(
int? id, [Bind(Prefix = "Instructor")] Instructor instructorToUpdate)
モデル バインドでは、キー Instructor.ID
がないかソースを調べることから始まります。 見つからない場合は、プレフィックスなしで ID
が探索されます。
複合型のターゲットの属性
複合型のモデル バインドを制御するために利用できる組み込みの属性がいくつかあります。
[Bind]
[BindRequired]
[BindNever]
警告
ポストされたフォーム データが値のソースである場合、これらの属性はモデル バインドに影響します。 入力フォーマッタには影響 しません 。入力フォーマッタは、ON および XML 要求本文を JS処理します。 入力フォーマッタについては、この記事で後ほど説明します。
[Bind] 属性
クラスまたはメソッド パラメーターに適用できます。 モデルのどのプロパティをモデル バインドに含めるかを指定します。 [Bind]
は入力フォーマッタ には影響しません 。
次の例では、任意のハンドラーまたはアクション メソッドが呼び出されると、Instructor
モデルの指定されたプロパティのみがバインドされます。
[Bind("LastName,FirstMidName,HireDate")]
public class Instructor
次の例では、OnPost
メソッドが呼び出されると、Instructor
モデルの指定されたプロパティのみがバインドされます。
[HttpPost]
public IActionResult OnPost([Bind("LastName,FirstMidName,HireDate")] Instructor instructor)
[Bind]
属性を使用すれば、"作成" シナリオにおいて過剰ポスティングから保護することができます。 除外されたプロパティはそのままにしておくのではなく null または既定値に設定されるので、この属性は編集シナリオではうまく機能しません。 過剰ポスティングを防ぐ場合は、[Bind]
属性ではなくビュー モデルをお勧めします。 詳細については、「過剰ポスティングに関するセキュリティの注意事項」を参照してください。
[ModelBinder] 属性
ModelBinderAttribute は、型、プロパティ、またはパラメーターに適用されます。 これにより、特定のインスタンスまたは型をバインドするために使用されるモデル バインダーの種類を指定できます。 次に例を示します。
[HttpPost]
public IActionResult OnPost([ModelBinder(typeof(MyInstructorModelBinder))] Instructor instructor)
[ModelBinder]
属性を使用して、モデル バインド時にプロパティまたはパラメーターの名前を変更することもできます。
public class Instructor
{
[ModelBinder(Name = "instructor_id")]
public string Id { get; set; }
public string Name { get; set; }
}
[BindRequired] 属性
モデルのプロパティにのみに適用でき、メソッドのパラメーターには適用できません。 モデルのプロパティに対してバインドを実行できない場合に、モデル バインドがモデル状態エラーを追加できるようにします。 次に例を示します。
public class InstructorWithCollection
{
public int ID { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Hire Date")]
[BindRequired]
public DateTime HireDate { get; set; }
モデル検証に関するページにある [Required]
属性の説明も参照してください。
[BindNever] 属性
モデルのプロパティにのみに適用でき、メソッドのパラメーターには適用できません。 モデル バインドがモデルのプロパティを設定できないようにします。 次に例を示します。
public class InstructorWithDictionary
{
[BindNever]
public int ID { get; set; }
コレクション
ターゲットが単純型のコレクションである場合、モデル バインドでは parameter_name または property_name との一致が探索されます。 一致が見つからない場合は、サポートされているいずれかの形式がプレフィックスなしで探索されます。 次に例を示します。
バインドされるパラメーターが
selectedCourses
という名前の配列であるとした場合:public IActionResult OnPost(int? id, int[] selectedCourses)
フォームまたはクエリ文字列データは、次のいずれかの形式とすることができます。
selectedCourses=1050&selectedCourses=2000
selectedCourses[0]=1050&selectedCourses[1]=2000
[0]=1050&[1]=2000
selectedCourses[a]=1050&selectedCourses[b]=2000&selectedCourses.index=a&selectedCourses.index=b
[a]=1050&[b]=2000&index=a&index=b
パラメーターまたは という名前
index
のプロパティ、またはIndex
コレクション値に隣接している場合は、バインドしないでください。 モデル バインドでは、コレクションのインデックスとして を使用index
しようとすると、バインドが正しくない可能性があります。 たとえば、次のアクションを考えてみましょう。public IActionResult Post(string index, List<Product> products)
前のコードでは、
index
クエリ文字列パラメーターは メソッド パラメーターにindex
バインドされ、製品コレクションのバインドにも使用されています。 パラメーターの名前をindex
変更するか、モデル バインド属性を使用してバインドを構成すると、この問題は回避されます。public IActionResult Post(string productIndex, List<Product> products)
次の形式は、フォーム データでのみサポートされます。
selectedCourses[]=1050&selectedCourses[]=2000
上記のすべてのフォーマット例において、モデル バインドでは 2 つの項目から成る配列が
selectedCourses
パラメーターに渡されます。- selectedCourses[0]=1050
- selectedCourses[1]=2000
添え字番号 (... [0] ... [1] ...) を使用するデータ フォーマットでは、確実にそれらがゼロから始まる連続した番号になるようにする必要があります。 添え字の番号付けで欠落している番号がある場合、欠落している番号の後の項目はすべて無視されます。 たとえば、添え字が 0、1 の並びではなく、0、2 の並びで振られている場合、2 番目の項目は無視されます。
辞書
Dictionary
ターゲットの場合、モデル バインドでは parameter_name または property_name との一致が探索されます。 一致が見つからない場合は、サポートされているいずれかの形式がプレフィックスなしで探索されます。 次に例を示します。
ターゲット パラメーターが
selectedCourses
という名前のDictionary<int, string>
であるとします:public IActionResult OnPost(int? id, Dictionary<int, string> selectedCourses)
ポストされたフォームまたはクエリ文字列データは、次のいずれかの例のようになります。
selectedCourses[1050]=Chemistry&selectedCourses[2000]=Economics
[1050]=Chemistry&selectedCourses[2000]=Economics
selectedCourses[0].Key=1050&selectedCourses[0].Value=Chemistry& selectedCourses[1].Key=2000&selectedCourses[1].Value=Economics
[0].Key=1050&[0].Value=Chemistry&[1].Key=2000&[1].Value=Economics
上記のすべてのフォーマット例において、モデル バインドでは 2 つの項目から成る辞書が
selectedCourses
パラメーターに渡されます。- selectedCourses["1050"]="Chemistry"
- selectedCourses["2000"]="Economics"
コンストラクターのバインドとレコードの種類
モデルバインドでは、複合型にパラメーターなしのコンストラクターが含まれている必要があります。 System.Text.Json
と Newtonsoft.Json
ベースの入力フォーマッタは両方とも、パラメーターなしのコンストラクターを持たないクラスの逆シリアル化をサポートします。
C# 9 では、ネットワーク上のデータを簡潔に表現するための優れた方法であるレコードの種類が導入されています。 ASP.NET Core により、1 つのコンストラクターを使用した、モデル バインドとレコードの種類の検証のサポートが加えられます。
public record Person([Required] string Name, [Range(0, 150)] int Age, [BindNever] int Id);
public class PersonController
{
public IActionResult Index() => View();
[HttpPost]
public IActionResult Index(Person person)
{
...
}
}
Person/Index.cshtml
:
@model Person
Name: <input asp-for="Name" />
...
Age: <input asp-for="Age" />
レコードの種類を検証するとき、ランタイムは、プロパティではなく、特にパラメーターに対してバインドと検証のメタデータを検索します。
フレームワークでは、レコードの種類へのバインディングと検証を行うことができます。
public record Person([Required] string Name, [Range(0, 100)] int Age);
前述の内容を機能させるには、次のような型である必要があります。
- レコードの種類である。
- パブリック コンストラクターを 1 つだけ持つ。
- 同じ名前および型のプロパティを持つパラメーターを含む。 名前は大文字と小文字を区別しないようにする必要がある。
パラメーターなしのコンストラクターを持たない POCO
パラメーターなしのコンストラクターを持たない POCO はバインドできません。
次のコードでは、型にパラメーターなしのコンストラクターが必要であるという例外が発生します。
public class Person(string Name)
public record Person([Required] string Name, [Range(0, 100)] int Age)
{
public Person(string Name) : this (Name, 0);
}
手動で作成されるコンストラクターを含むレコードの種類
プライマリ コンストラクターが動作するように見える、手動で作成されたコンストラクターを含むレコードの種類
public record Person
{
public Person([Required] string Name, [Range(0, 100)] int Age) => (this.Name, this.Age) = (Name, Age);
public string Name { get; set; }
public int Age { get; set; }
}
レコードの種類、検証、メタデータのバインディング
レコードの種類については、パラメーターの検証とバインドのメタデータが使用されます。 プロパティのメタデータはすべて無視されます。
public record Person (string Name, int Age)
{
[BindProperty(Name = "SomeName")] // This does not get used
[Required] // This does not get used
public string Name { get; init; }
}
検証とメタデータ
検証では、パラメーターのメタデータを使用しますが、プロパティを使用して値を読み取ります。 通常のプライマリ コンストラクターの場合、2 つは同じになります。 ただし、これを打破する方法があります。
public record Person([Required] string Name)
{
private readonly string _name;
public Name { get; init => _name = value ?? string.Empty; } // Now this property is never null. However this object could have been constructed as `new Person(null);`
}
TryUpdateModel によって、レコードの種類のパラメーターは更新されません。
public record Person(string Name)
{
public int Age { get; set; }
}
var person = new Person("initial-name");
TryUpdateModel(person, ...);
この場合、MVC により再度 Name
のバインドが試行されることはありません。 ただし、Age
は更新されます。
モデル バインド ルート データとクエリ文字列のグローバリゼーション動作
ASP.NET Core ルート値プロバイダーとクエリ文字列値プロバイダーでは、次のことが行われます。
- 値をインバリアント カルチャとして扱います。
- URL はカルチャに依存しないものと想定します。
これに対し、フォーム データからの値は、カルチャに依存した変換にかけられます。 URL がロケール間で共有可能なように、設計上そのようになっています。
ASP.NET Core ルート値プロバイダーとクエリ文字列値プロバイダーでカルチャ依存の変換が行われるようにするには、次のようにします。
- IValueProviderFactory から継承します
- QueryStringValueProviderFactory または RouteValueValueProviderFactory からコードをコピーします。
- 値プロバイダー コンストラクターに渡されたカルチャ値を CultureInfo.CurrentCulture に置き換えます。
- MVC オプション内の既定値プロバイダー ファクトリを、新しいものに置き換えます。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
var index = options.ValueProviderFactories.IndexOf(
options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>().Single());
options.ValueProviderFactories[index] = new CulturedQueryStringValueProviderFactory();
});
}
public class CulturedQueryStringValueProviderFactory : IValueProviderFactory
{
public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var query = context.ActionContext.HttpContext.Request.Query;
if (query != null && query.Count > 0)
{
var valueProvider = new QueryStringValueProvider(
BindingSource.Query,
query,
CultureInfo.CurrentCulture);
context.ValueProviders.Add(valueProvider);
}
return Task.CompletedTask;
}
}
特別なデータ型
モデル バインドで処理できる特殊なデータ型がいくつかあります。
IFormFile と IFormFileCollection
HTTP 要求に含まれたアップロード済みファイル。 また、複数のファイルに対して IEnumerable<IFormFile>
もサポートされています。
CancellationToken
アクションでは、オプションで CancellationToken
をパラメーターとしてバインドすることができます。 これにより、HTTP 要求の基になる接続が中断されたときに、それを通知する RequestAborted がバインドされます。 アクションでは、このパラメーターを使用して、コントローラー アクションの一部として実行される実行時間の長い非同期操作を取り消すことができます。
FormCollection
ポストされたフォーム データからすべての値を取得するために使用します。
入力フォーマッタ
要求本文のデータは、ON、XML、またはその他の JS形式にすることができます。 このデータを解析するために、モデル バインドでは、特定のコンテンツの種類を処理するように構成された "入力フォーマッタ" が使用されます。 既定では、ASP.NET Coreには ON データを処理JSするための ON ベースの入力フォーマッタが含まれますJS。 他のコンテンツの種類については対応する他のフォーマッタを追加することができます。
ASP.NET Core では、Consumes 属性に基づいて入力フォーマッタが選択されます。 属性が存在しない場合は、Content-Type ヘッダーが使用されます。
組み込みの XML 入力フォーマッタを使用するには:
Microsoft.AspNetCore.Mvc.Formatters.Xml
NuGet パッケージをインストールします。Startup.ConfigureServices
で、AddXmlSerializerFormatters または AddXmlDataContractSerializerFormatters を呼び出します。services.AddRazorPages() .AddMvcOptions(options => { options.ValueProviderFactories.Add(new CookieValueProviderFactory()); options.ModelMetadataDetailsProviders.Add( new ExcludeBindingMetadataProvider(typeof(System.Version))); options.ModelMetadataDetailsProviders.Add( new SuppressChildValidationMetadataProvider(typeof(System.Guid))); }) .AddXmlSerializerFormatters();
要求本文で XML を必要とするコントローラー クラスまたはアクション メソッドに
Consumes
属性を適用します。[HttpPost] [Consumes("application/xml")] public ActionResult<Pet> Create(Pet pet)
詳細については、「XML シリアル化の概要」を参照してください。
入力フォーマッタを使用してモデル バインドをカスタマイズする
入力フォーマッタは、要求本文からデータを読み取るためのすべての役割を担います。 このプロセスをカスタマイズするには、入力フォーマッタによって使用される API を構成します。 このセクションでは、ObjectId
という名前のカスタム型を理解するために、System.Text.Json
ベースの入力フォーマッタをカスタマイズする方法について説明します。
Id
という名前のカスタム ObjectId
プロパティが含まれている、次のモデルを考えてみます。
public class ModelWithObjectId
{
public ObjectId Id { get; set; }
}
System.Text.Json
を使用する際のモデル バインド プロセスをカスタマイズするために、JsonConverter<T> から派生するクラスを作成します。
internal class ObjectIdConverter : JsonConverter<ObjectId>
{
public override ObjectId Read(
ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return new ObjectId(JsonSerializer.Deserialize<int>(ref reader, options));
}
public override void Write(
Utf8JsonWriter writer, ObjectId value, JsonSerializerOptions options)
{
writer.WriteNumberValue(value.Id);
}
}
カスタム コンバーターを使用するために、型に JsonConverterAttribute 属性を適用します。 次の例では、ObjectId
型は、ObjectIdConverter
をそのカスタム コンバーターとして構成されています。
[JsonConverter(typeof(ObjectIdConverter))]
public struct ObjectId
{
public ObjectId(int id) =>
Id = id;
public int Id { get; }
}
詳細については、カスタム コンバーターを記述する方法に関する記事をご覧ください。
指定された型をモデル バインドから除外する
モデル バインドと検証システムの動作は、 によって ModelMetadata駆動されます。 ModelMetadata
については、詳細プロバイダーを MvcOptions.ModelMetadataDetailsProviders に追加してカスタマイズできます。 組み込みの詳細プロバイダーは、指定された型に対してモデル バインドまたは検証を無効にする場合に使用できます。
指定された型のすべてのモデルに対してモデル バインドを無効にするには、Startup.ConfigureServices
に ExcludeBindingMetadataProvider を追加します。 たとえば、System.Version
型のすべてのモデルに対してモデル バインドを無効にするには、次のようにします。
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(System.Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(System.Guid)));
})
.AddXmlSerializerFormatters();
指定された型のプロパティに対して検証を無効にするには、Startup.ConfigureServices
に SuppressChildValidationMetadataProvider を追加します。 たとえば、System.Guid
型のプロパティに対して検証を無効にするには、次のようにします。
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.ValueProviderFactories.Add(new CookieValueProviderFactory());
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(System.Version)));
options.ModelMetadataDetailsProviders.Add(
new SuppressChildValidationMetadataProvider(typeof(System.Guid)));
})
.AddXmlSerializerFormatters();
カスタム モデル バインダー
モデル バインドを拡張するには、カスタム モデル バインダーを記述し、[ModelBinder]
属性を使用してそれを特定のターゲット向けに選択します。 詳細については、「custom model binding」 (カスタム モデル バインド) を参照してください。
手動によるモデル バインド
モデル バインドは、TryUpdateModelAsync メソッドを使用して手動で呼び出すことができます。 このメソッドは ControllerBase
クラスと PageModel
クラスの両方で定義されています。 メソッドのオーバーロードにより、使用するプレフィックスと値プロバイダーを指定できます。 モデル バインドが失敗した場合は、メソッドから false
が返されます。 次に例を示します。
if (await TryUpdateModelAsync<InstructorWithCollection>(
newInstructor,
"Instructor",
i => i.FirstMidName, i => i.LastName, i => i.HireDate))
{
_instructorsInMemoryStore.Add(newInstructor);
return RedirectToPage("./Index");
}
PopulateAssignedCourseData(newInstructor);
return Page();
TryUpdateModelAsync では、値プロバイダーを使用して、フォーム本文、クエリ文字列、およびルート データからデータを取得します。 TryUpdateModelAsync
は通常、次のようになります。
- 過剰な投稿を防止するために、Razor Pages と MVC アプリ (コントローラーとビューを使用) で使用されます。
- フォーム データ、クエリ文字列、およびルート データから使用される場合を除き、Web API では使用されません。 ON を使用 JSする Web API エンドポイントでは 、Input フォーマッタ を使用して、要求本文を オブジェクトに逆シリアル化します。
詳細については、「TryUpdateModelAsync」をご覧ください。
[FromServices] 属性
この属性の名前は、データ ソースを指定するモデル バインド属性のパターンに従います。 ただし、それは、値プロバイダーからのデータ バインドを説明するものではありません。 依存関係挿入コンテナーから型のインスタンスが取得されます。 その目的は、特定のメソッドが呼び出された場合にのみサービスを必要するときにコンストラクターの挿入の代替手段を提供することにあります。