情報応用演習Ⅰ(2024)

【T4a】モデルとビューの連携(前編)(5/7)

プロジェクトタイプASP.NET Core Web アプリ(Model-View-Controller)
プロジェクト名T4a
ソリューション名PIT4
ターゲットフレームワーク.NET 8.0(長期的なサポート)
最上位レベルのステートメントを使用しない使用する(チェックオフ)
注意
  • 本ページの作業内容は 前のページまでの続き になっていることに注意せよ.
    • 先に前のページまでをすべて読み,指示されている作業を済ませてから本ページを読むこと.
    • プロジェクトの作成作業については準備作業を参照せよ.

4a-5. モデルバインディングを使ったフォーム処理

チュートリアル【T1c】では,フォームからの送信を受け付けるには以下のようにしていた(_)

  1. HTML側のフォームで入力項目ごとに input 要素を書く.
  2. コントローラー側では↑で定義した項目を引数にもつメソッドを定義する.
ASP.NET Core MVCにおけるフォームからの送信処理

この方法は分かりやすいが煩雑でもある.各入力欄( input 要素)で入力されることを期待されるデータ型と, アクションメソッドの引数の名前や型を一致させる必要があるし,そもそも例えば「顧客」(姓,名,住所...)といった 意味のあるひとかたまりのデータとして受け取ることができない. このような,フォームの入力値とC#プログラム内に現れるデータとのすり合わせは非常に面倒な作業であり,エラーの温床となりがちである. ASP.NET Core にはこのような作業を自動化するためにモデルバインディングと呼ばれる仕組みが備わっている. 今回はこのモデルバインディングを使ってチュートリアル【T1c】と同じ動きをするものを より洗練されたかたちで再実装してみよう.

前述したように,C#プログラム内で最も扱いやすいデータの単位はクラスである.例えばチュートリアル【T1c】で 扱ったような「身長と体重」というデータであれば_のようなクラスにしておくと扱いやすいだろう.

「身長と体重」クラス
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class HealthInfo
{
    // 体重(kg)
    public double Weight { get; set; }

    // 身長(cm)
    public double Height { get; set; }

    // BMIの計算をする読み取り専用プロパティ
    public double BMI => Weight / Math.Pow(Height / 100, 2);
}

ASP.NET Coreではこのようなクラスを,フォームを持つページに関連付けることができる.実際にやってみよう. まずプロジェクト内の Models フォルダを右クリックし,「追加」→「クラス」をクリックする(_). 作成するクラス名を訊かれるのでHealthInfo(.csは省略可能)と入力して「追加」ボタンをクリックする(_).

クラスの追加

すると Models フォルダ以下に HealthInfo.cs ファイルが作られる.このファイル内に定義されているHealthInfoクラスは内容が空なので,_の内容を書き込もう. 次に入力用のページを作成しよう.今回はデフォルトで作られているHomeコントローラーのIndexアクションに フォームを設置することにする.このアクション用のビュー(.cshtmlファイル)もあらかじめ Views/Home/Index.cshtml に作られているのでこれを開き, _のように書き換えよう.

Views/Home/Index.cshtmlの内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@model HealthInfo

@{
    ViewData["Title"] = "Home Page";

    if (Model is null) throw new ArgumentNullException(nameof(Model)); // 後述
}

<form asp-action="Index">
    @* 体重(Weight)のための入力欄 *@
    体重(kg): <input asp-for="@Model.Weight" />
    <br />
    
    @* 身長(Height)のための入力欄 *@
    身長(cm): <input asp-for="@Model.Height" />
    <br />
    
    <input type="submit" value="送信" />
</form>

1行目の@modelディレクティブはそのページで入出力を行う モデルクラス の型名を指定するものである. 平たく言えば,@model HealthInfoという指定は「 このページはHealthInfoクラスのインスタンスを表示または編集するのに使いますよ 」 ということをフレームワークに通知するものである.このようにするとこのビュー内では Modelという変数名1で, 指定したモデルクラスのインスタンスにアクセスできるようになる . なお,この例では@modelディレクティブは .cshtml ファイルの先頭に書いているが実際はファイル内のどこに現れてもよい.

10~16行目では,関連付けたモデルクラスの各プロパティ用の入力欄を作成している.asp-for第3回で 言及したタグヘルパーの 一種である.このタグヘルパーにはモデルクラスのプロパティ名を指定する.つまり_の11行目,15行目に見られるように, <input asp-for="@Model.プロパティ名">のような使い方となる.これは<input asp-for="プロパティ名">のように省略することも可能である.ここで重要なのは, input 要素にはこれ以外を指定する必要は基本的にない,ということであるリスト4a-4-1を見ると type 属性(入力内容のデータ型を決める)や min , step 属性(数値入力の最小値や増減のステップ), required 属性(入力必須であることを指定する)などを指定していたが, ASP.NET Core MVC ではこのような入力値に関する制約は,後述するように基本的にモデルクラスの定義から自動的に推定される.

ただしこのままだと入力内容に誤りがあっても,そのことが通知されないので_のように追記しよう.

Views/Home/Index.cshtmlへの追記内容1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@model HealthInfo

@{
    ViewData["Title"] = "Home Page";

    if (Model is null) throw new ArgumentNullException(nameof(Model)); // 後述
}

<form asp-action="Index">
    @* 体重(Weight)のための入力欄 *@
    体重(kg): <input asp-for="@Model.Weight" />
    <span asp-validation-for="@Model.Weight"></span>
    <br />
    
    @* 身長(Height)のための入力欄 *@
    身長(cm): <input asp-for="@Model.Height" />
    <span asp-validation-for="@Model.Height"></span>
    <br />
    
    <input type="submit" value="送信" />
</form>

@section Scripts {
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

12行目,17行目の asp-validation-for 属性も ASP.NET Core であらかじめ定義されているタグヘルパーの一つである. これは指定したモデルクラスのプロパティに対する検証メッセージを表示する要素に付加する属性である. たいていは_のように span 要素を使用する.したがって <span asp-validation-for="@Model.プロパティ"></span>のような使い方となる. そのプロパティへの入力内容に誤りがある場合は,指定した要素にエラーメッセージが表示される.

23~26行目はクライアントサイドでの,つまりブラウザ上での入力内容の検証を有効化するための記述である. 後述するが,入力値の検証はクライアント側(≒ウェブブラウザ―側)とサーバー側の両方で行うのが一般的である.


  1. 厳密にはプロパティ名 ↩︎

Last updated on 2024-04-24
Published on 2024-04-24

Powered by Hugo. Theme by TechDoc. Designed by Thingsym.