Skip to content

第26章 ASP.NET Core MVC 入門:占いアプリ

この章から、もう一つの大きなアプリ形式 ── Web アプリ(ASP.NET Core MVC) に入ります。

第 18〜25 章では、PC 上で起動する Windows フォームアプリ を作りました。 これから扱う Web アプリは、ブラウザ からアクセスして使うアプリです。

Windows フォーム: PC 上でアプリを直接起動 → フォーム画面が出る
Web アプリ: ブラウザで URL を開く → HTML 画面が出る

この章では、ちょっと遊び心のある 「占いアプリ」 を題材にして、MVC の基本を体験します。データベースはまだ使いません。Model・View・Controller の役割分担 と、ブラウザとサーバーのやり取り を理解することが目的です。

この研修での Web アプリ編の進め方

Web アプリは本格的に作ろうとすると、必要な要素がたくさんあります。 本研修では時間に余裕がないため、「動作の仕組みを理解する」ことに重点 を置きます。 Visual Studio が用意してくれる MVC テンプレート をそのまま使い、小さな改造で動かしながら、次の 3 つを理解することを目指します。

  1. リクエストとレスポンス ── ブラウザから情報が送られ、HTML が返ってくる
  2. GET と POST の違い ── 画面を見るときと、入力を送るときで方法が違う
  3. MVC の役割分担 ── Controller・Model・View の 3 つに役割を分けている

自分でゼロから複雑な Web アプリを組めるレベルは目指しません。 後で必要になったときに、「あ、これは Controller だな」と読み解ける状態を目指します。


この章でできるようになること

Section titled “この章でできるようになること”

この章を終えると、次のことができるようになります。

  • ASP.NET Core MVC プロジェクトを Visual Studio で作成できる
  • Controller・Model・View の役割を自分の言葉で説明できる
  • GET と POST の違いを説明できる
  • URL とコントローラー・アクションの対応を読める
  • フォーム送信したデータが Controller に届く仕組み(Model Binding)の概要を説明できる
  • View で Model のプロパティを表示する書き方が分かる
  • 入力画面と結果画面の 2 画面構成 を組み立てられる
  • Windows フォームアプリとの違いを説明できる

項目内容
開発環境Visual Studio 2022
プロジェクト種類ASP.NET Core Web アプリ(Model-View-Controller)
対象フレームワーク.NET 8
ソリューション名Chapter26
プロジェクト名Ch26_MvcFortuneApp
認証の種類なし
HTTPS 用の構成既定のまま

csproj の Nullable は disable に変更してください

プロジェクト作成後、Ch26_MvcFortuneApp.csproj を開いて <Nullable>disable</Nullable> に変更してください(第 1 章「1-1」参照)。 Web アプリでも本研修では Nullable disable で統一します。


  • 第 25 章を Git に提出済みである
  • Visual Studio で「ASP.NET Core Web アプリ(Model-View-Controller)」テンプレートが選べる
  • ブラウザで https://localhost:xxxx/ を開ける(Visual Studio が自動で起動するブラウザ)

Web アプリは、ブラウザとサーバーのやり取り で動きます。

[ブラウザ] [サーバー = あなたが作る Web アプリ]
URL を入力 ──→ リクエスト ──→ Controller が受け取る
(例: /Fortune/Index) ↓
画面を組み立てる(View)
HTML を画面に表示 ←── レスポンス ←── HTML を返す

このやり取りを リクエストレスポンス といいます。

用語内容
リクエストブラウザがサーバーに「この URL を見せて」とお願いする
レスポンスサーバーがブラウザに「これが画面の HTML です」と返す

Web アプリのプログラムを書くということは、「リクエストを受けて、どんなレスポンスを返すか」を決めるコードを書く ということです。


MVC は、Web アプリの中身を 3 つの役割に分ける 考え方です。

役割担当
Model画面で扱うデータの形
View画面に表示する HTML
Controllerリクエストを受け取り、処理の流れを決める
MVCこの章の占いアプリでの例
Model入力された名前・生年月日・気分と、占い結果を持つ FortuneViewModel
View入力フォーム Index.cshtml、占い結果 Result.cshtml
Controller入力を受け取って結果を作る FortuneController

3 役のやり取りを図にすると、次のような流れになります。

ブラウザが最初に話しかけるのは 必ず Controller です。 Controller が司令塔となって Model にデータを頼み、View に表示を任せ、最終的に View が組み立てた HTML がブラウザに返ります。

役割が分かれていると:

  • Controller を読めば「どんな URL でどんな処理が動くか」が分かる
  • Model を読めば「画面で何のデータを扱うか」が分かる
  • View を読めば「画面の見た目」が分かる

役割を意識してコードを読むことが、Web アプリ理解の第一歩です。


26-3 Visual Studio で MVC プロジェクトを作成する

Section titled “26-3 Visual Studio で MVC プロジェクトを作成する”

新しいプロジェクトを作ります。

項目
テンプレートASP.NET Core Web アプリ(Model-View-Controller)
ソリューション名Chapter26
プロジェクト名Ch26_MvcFortuneApp
フレームワーク.NET 8.0
認証の種類なし
HTTPS 用の構成既定のままチェックを入れる
Docker の有効化チェックを外す

作成したら、Ch26_MvcFortuneApp.csproj を開いて <Nullable>disable</Nullable> に変更します。

そして 何も改造せずに F5 で実行 してみてください。 Visual Studio が自動でブラウザを起動し、テンプレートの初期画面が表示されるはずです。


26-4 Web アプリの動かし方(初回の準備)

Section titled “26-4 Web アプリの動かし方(初回の準備)”

Web アプリは Windows フォームと違って、「Web サーバー にアクセスして使う」ものです。 本研修では、その Web サーバーも Visual Studio が自動で用意・起動 してくれます。 初回だけ少し癖があるので、ここでまとめて確認しておきます。

別の Web サーバーをインストールする必要はない

Section titled “別の Web サーバーをインストールする必要はない”

「Web アプリ」と聞くと Apache や IIS のような Web サーバーを別途インストールするイメージがあるかもしれませんが、本研修では不要 です。

.NET 8 には Kestrel(ケストレル) という小さな Web サーバーが組み込まれており、Visual Studio が F5 を押したタイミングで起動してくれます。

F5 を押す
Visual Studio がプロジェクトをビルド
Kestrel(.NET 内蔵 Web サーバー)を起動
ブラウザが自動で開く
プロジェクトの起動 URL が表示される

実務でアプリをサーバーに公開するときは、IIS や Linux 上の Nginx と組み合わせるパターンがありますが、研修中は意識しなくて OK です。


初回起動時:HTTPS 開発証明書の信頼

Section titled “初回起動時:HTTPS 開発証明書の信頼”

ASP.NET Core MVC は、起動時に https://localhost:xxxx/ で動こうとします(xxxx は数字)。 HTTPS を使うためには、ローカル PC に 開発用の SSL 証明書 が必要です。

初回 F5 のときに、次のような確認ダイアログが順に出ることがあります。

ダイアログ出る場所選ぶ答え
ASP.NET Core が開発用 SSL 証明書を作成しました。信頼しますか?Visual Studioはい
この証明書をインストールしますか?(Windows のセキュリティ警告)Windowsはい

この操作は、研修環境で 1 回だけ やればその後は出てきません。

ブラウザによっては、起動後にも「セキュリティ警告」のような画面が出ることがあります。 そのときは「詳細設定」 → 「このまま続行」のような選択で進めて OK です(開発用のため)。


Visual Studio がブラウザで開く URL は次のような形です。

https://localhost:7XXX/
http://localhost:5XXX/

7XXX5XXX の数字部分は ポート番号 といい、プロジェクト作成時に Visual Studio が決めて、Properties/launchSettings.json に書き込みます。 プロジェクトごとに違うので、別のプロジェクトを起動したら URL も変わります。

Ch26_MvcFortuneApp → https://localhost:7123/
別のプロジェクト → https://localhost:7456/

URL のポート番号は 研修中は深く意識しなくて OK です。「ブラウザが自動で開く URL を使う」とだけ覚えておけば足ります。

補足:localhost とは

localhost は「自分自身の PC」を意味する特別な名前です。 ブラウザは「自分の PC で動いている Kestrel」にアクセスして、その応答を画面に表示しています。 他人の PC からはアクセスできません。


ブラウザのタブを閉じても、Web サーバー(Kestrel)は裏で動いたまま になります(ブラウザは「画面を見る道具」にすぎないため)。 止めるには、Visual Studio 側で操作します。

やりたいこと操作
デバッグ実行を止めるVisual Studio の「停止」ボタン(赤い四角)、または Shift + F5
ブラウザだけ閉じるブラウザのタブを × で閉じる(サーバーは止まらない)

止め忘れると、Visual Studio が「すでに実行中なので F5 が押せない」状態になります。 そのときは 「停止」ボタンを押してから F5 で再起動します。


変更箇所必要な操作
View(.cshtml)保存して ブラウザでリロード(F5 または Ctrl + R) だけで反映
Controller(.cs)Visual Studio で ホットリロード ボタン、または デバッグ停止 → F5 で再実行
Model(.cs)同上(Controller と同じ)

View はテキストファイルに近い扱いなので、ブラウザのリロードだけで変更が見えます。 Controller・Model は C# のコードなので、再ビルドが必要です(ホットリロードが効くこともあります)。

ホットリロードとは

Visual Studio 2022 には、デバッグ実行中にコードを変更しても ビルドし直さずに反映する 機能があります(炎のアイコン)。 効くケースと効かないケースがあるので、迷ったら「停止 → F5」で問題ありません。


ファイアウォール警告が出たら

Section titled “ファイアウォール警告が出たら”

初回 F5 のときに、Windows ファイアウォールから「このアプリのネットワーク通信を許可しますか?」というダイアログが出ることがあります。

設定項目チェック
プライベートネットワーク✅(チェックを入れる)
パブリックネットワーク⬜(チェックを外す)

その後「アクセスを許可する」を選んでください。 研修環境では自分の PC 内でしか使わないため、これで十分です。


ASP.NET Core MVC アプリの実行
Visual Studio で F5 を押すだけ
裏側で Kestrel(Web サーバー)が起動
ブラウザが自動で開く
止めるときは Visual Studio の停止ボタンか Shift+F5

Web アプリは別のサーバーを立てる必要がある」というイメージを持っていた人もいるかもしれませんが、研修中の体験範囲では Visual Studio だけで完結 します。

実務でサーバーに公開する手順はもっと複雑ですが、本研修の範囲外です。 動かす仕組みが分かっていれば、後で公開手順を学んだときも理解が早くなります。


26-5 プロジェクト構成を確認する

Section titled “26-5 プロジェクト構成を確認する”

プロジェクトを作ると、研修の他の章と同じ 「ソリューションフォルダの中にプロジェクトフォルダ」 という階層になります(主要なものだけ抜粋)。

Chapter26/ ← ソリューションフォルダ
├─ Chapter26.sln
└─ Ch26_MvcFortuneApp/ ← プロジェクトフォルダ
├─ Controllers/
│ └─ HomeController.cs
├─ Models/
├─ Views/
│ ├─ Home/
│ │ ├─ Index.cshtml
│ │ └─ Privacy.cshtml
│ └─ Shared/
│ └─ _Layout.cshtml
├─ wwwroot/ ← CSS・JavaScript・画像
├─ Properties/
│ └─ launchSettings.json ← 起動 URL・ポート番号などの設定
├─ Program.cs ← アプリ起動の入口
├─ appsettings.json ← 設定情報
└─ Ch26_MvcFortuneApp.csproj ← プロジェクトファイル(Nullable 設定もここ)
フォルダ・ファイル役割
ControllersController クラスを置く
ModelsModel クラスを置く
Views画面表示の .cshtml を置く(Controller 名のフォルダで整理)
wwwrootCSS・JS・画像など、ブラウザに直接送る静的ファイル
Properties/launchSettings.jsonF5 実行時に開く URL・ポート番号(本研修ではほぼ触らない)
Program.csアプリ起動時の設定(本研修ではほぼ触らない)
appsettings.json接続文字列などの設定値(第 27 章以降で使う)
Ch26_MvcFortuneApp.csprojプロジェクト設定。Nullable disable はここで変更

この章では、ControllersModelsViews の 3 つだけを意識すれば OK です。 ソリューションフォルダ階層は第 1 章以降の他の章(コンソールアプリ・Windows フォーム)と同じなので、混乱しないよう注意してください。


26-6 最初から動いている部分を読み解く

Section titled “26-6 最初から動いている部分を読み解く”

Controllers/HomeController.cs は、テンプレートが最初から用意している Controller です。 中を覗くと、次のような Index メソッドがあります。

public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
// ... (他のメソッド)
}

これだけのコードで、https://localhost:xxxx/ を開いたときに Views/Home/Index.cshtml が表示されています。

処理の流れ:

ブラウザで / にアクセス
ASP.NET Core が URL を見て「Home コントローラーの Index アクション」と判断
HomeController.Index() が呼ばれる
return View() で対応する View を返す
Views/Home/Index.cshtml の内容が HTML として送られる
ブラウザに画面が表示される

URL と Controller・Action・View の対応:

URLControllerActionView
/HomeControllerIndex()Views/Home/Index.cshtml
/Home/IndexHomeControllerIndex()Views/Home/Index.cshtml
/Home/PrivacyHomeControllerPrivacy()Views/Home/Privacy.cshtml

URL の /Xxx/Yyy が、XxxControllerYyy() メソッドに対応する」 ── これが ASP.NET Core MVC の基本ルールです。

.cshtml はブラウザに届かない ── サーバーで HTML に変わる

Section titled “.cshtml はブラウザに届かない ── サーバーで HTML に変わる”

ここで、Web アプリを理解するうえで いちばん大事なポイント を押さえます。

.cshtml ファイルは、書いたまま そのままブラウザに送られるわけではありません。次の順で動きます。

.cshtml(HTML + @… + asp-…)
↓ サーバー(ASP.NET Core)が処理する
@… や asp-… を実行して、ふつうの HTML に組み立てる
できあがった「素の HTML」だけがブラウザに届く

つまり、@asp- といった記号はサーバーの中で消化され、ブラウザには一切届きません。ブラウザが受け取るのは、<h1><input> だけでできた、ごくふつうの HTML です。

自分の目で確かめる:ページのソースを表示

F5 で動かして画面を開き、ブラウザで 右クリック → 「ページのソースを表示」(または Ctrl + U)してみてください。.cshtml に書いたはずの @modelasp-forどこにもなく、素の HTML(<input name="..."> など)になっているはずです。「自分が書いたファイル」と「ブラウザが受け取る HTML」は別物だ、と体感できます。

この「サーバーで HTML を組み立ててから送る」という仕組みが分かると、

  • なぜ HTML の中に C#(@)を書けるのか(→ 送る前にサーバーで実行されるから)
  • なぜ asp-for のような独自の属性が使えるのか(→ サーバーが素の HTML に変換してくれるから)

がすっきり理解できます。asp-for が実際にどんな HTML に変わるかは、入力フォームを作る 26-9 で具体的に見ます。


26-7 Home 画面の View を改造してみる

Section titled “26-7 Home 画面の View を改造してみる”

まず、テンプレートが用意した View を 少し書き換えて みます。 View を変えると画面表示が変わることを体感するのが目的です。

Views/Home/Index.cshtml を開いて、全体を次のように書き換えてください。

@{
ViewData["Title"] = "占いアプリ トップ";
}
<h1>占いアプリへようこそ</h1>
<p>この画面は、HomeController の Index アクションが返している View です。</p>
<p>これから、入力フォームを持つ占い画面を作ります。</p>
<p><a asp-controller="Fortune" asp-action="Index" class="btn btn-primary">占いを始める</a></p>

<a asp-controller="Fortune" asp-action="Index"> は、まだ存在しない FortuneControllerIndex アクションへのリンクです。今はクリックするとエラーになりますが、後で FortuneController を作るとここから飛べるようになります。

F5 で実行(または変更を保存してブラウザをリロード)し、画面が変わることを確認してください。

ここで分かること:

  • View を編集すると、画面表示がそのまま変わる(C# コードのビルドは不要なことが多い)
  • View は HTML がベース(<h1><p> などのタグがそのまま使える)
  • @{ ... } の中には C# コードが書ける(Razor 構文)

ここから、占いアプリを作っていきます。 これが「Model・View・Controller の 3 役そろい踏み」を体験する小さなアプリになります。

アプリの動作:

1. ブラウザで /Fortune/Index を開く
→ 名前・生年月日・今日の気分の入力フォームが表示される
2. 入力して「占う」ボタンを押す
→ /Fortune/Result に POST 送信される
→ ラッキーカラー・ラッキーナンバー・今日の一言が表示される
3. 結果画面の「もう一度占う」リンクで /Fortune/Index に戻る

作るもの(4 つ):

種類ファイル役割
ModelModels/FortuneViewModel.cs入力された値と占い結果を持つ
ControllerControllers/FortuneController.csGET で入力画面、POST で結果画面
View(入力)Views/Fortune/Index.cshtml入力フォーム
View(結果)Views/Fortune/Result.cshtml占い結果

この 4 つを、3 つのステップに分けて、各ステップでブラウザで動かして確かめながら 作っていきます。いきなり全部を書くのではなく、動くものを少しずつ育てる のがコツです。

ステップ作る/足すもの動かして確かめること
Step1(26-9)Model + Controller の Index(GET)+ 入力 View入力フォームが表示される
Step2(26-10)Controller の Result(POST)+ 結果 View入力した値が結果画面に渡る(Model Binding)
Step3(26-11)Result に占いロジック(ランダム)を足すラッキーカラー等が表示される

入力画面と結果画面が分かれているのがポイント

第 19〜25 章の Windows フォームでは「1 つのフォームの上で値が変わる」スタイルでした。 Web では 画面ごとに別の URL・別の View にするのが自然です。 このやり方に慣れると、画面遷移の多い業務アプリも組みやすくなります。


まず「ブラウザで入力フォームが表示される」ところまで作ります。必要なのは データの入れ物(Model)・入口の Index(GET)・入力 View の 3 つです。占うボタンの処理(POST)は Step2 で足すので、ここでは作りません。

Models フォルダの上で 右クリック → 追加 → クラス で、新しいクラスを追加します。 名前は FortuneViewModel.cs にしてください。

FortuneViewModel.cs
namespace Ch26_MvcFortuneApp.Models;
public class FortuneViewModel
{
// 入力(入力画面で使う)
public string UserName { get; set; } = "";
public DateTime? BirthDate { get; set; }
public string Mood { get; set; } = "";
// 結果(結果画面で使う)
public string LuckyColor { get; set; } = "";
public int LuckyNumber { get; set; }
public string Comment { get; set; } = "";
}

これは、第 7・10 章で学んだ普通の C# クラスです。 入力 3 項目と結果 3 項目をまとめて持つ、シンプルな データの入れ物 です。

プロパティ意味
UserName入力された名前
BirthDate入力された生年月日(未入力なら null)
Mood選んだ気分(「元気」「普通」「疲れ気味」のいずれか)
LuckyColor占い結果のラッキーカラー
LuckyNumber占い結果のラッキーナンバー
Comment占い結果の今日の一言

「ViewModel」という名前

名前の末尾に ViewModel が付いていますが、これは 「View で使うことを意図した Model」、言いかえると 画面に必要な値をまとめて持つ入れ物 という意味の慣習名です。中身は普通の C# クラスなので、特別な仕掛けはありません。

この章の FortuneViewModel は、入力 3 項目(UserNameBirthDateMood)と結果 3 項目(LuckyColorLuckyNumberComment)を 1 つにまとめています。第 29 章 では、検索条件・部署の一覧・検索結果をまとめた EmployeeSearchViewModel が出てきます。「View に必要な値を 1 つにまとめておくと、画面側から 型付きで(@Model.プロパティ で)アクセスでき、名前のタイプミスも減らせる」── この感覚をここでつかんでおくと、後の章が読みやすくなります。

DateTime??

第 11 章で学んだ Nullable<T> の省略形です。DateTime?Nullable<DateTime> と同じ意味で、「日付の値、または未設定(null)」を表せます。 入力画面の生年月日欄を空のまま送信されたときに null として受け取れるように、DateTime? にしています。


Controller を作る(まずは Index だけ)

Section titled “Controller を作る(まずは Index だけ)”

Controllers フォルダの上で 右クリック → 追加 → コントローラー → 「MVC コントローラー - 空」 を選び、名前を FortuneController.cs にします。 (慣れていれば「右クリック → 追加 → クラス」で空のクラスを作って、: Controller を自分で書いても構いません。)

Step1 では、入力画面を返す Index(GET)だけを書きます。占う処理は Step2 で足します。

FortuneController.cs
using Microsoft.AspNetCore.Mvc;
using Ch26_MvcFortuneApp.Models;
namespace Ch26_MvcFortuneApp.Controllers;
public class FortuneController : Controller
{
[HttpGet]
public IActionResult Index()
{
FortuneViewModel model = new FortuneViewModel();
return View(model);
}
}

ここでのポイント:

  • : Controller(継承):Controller 親クラスを 継承(第 13 章)することで、Web の機能(View() メソッドなど)が使えます。
  • [HttpGet] Index():ブラウザで /Fortune/Index を開いたときに呼ばれ、空の FortuneViewModel を作って入力 View に渡します。
  • Result(POST)と占いロジックは Step2・Step3 で足します。「画面を見るときは GET、送るときは POST」という分け方は Step2 で詳しく扱います。

入力 View を作る:Views/Fortune/Index.cshtml

Section titled “入力 View を作る:Views/Fortune/Index.cshtml”

View を置くには、まず Views フォルダの中に Fortune フォルダ を作ります(Controller 名と同じ名前)。

Views/
├─ Home/
├─ Shared/
└─ Fortune/ ← 新規作成
├─ Index.cshtml ← この後作る(入力画面)
└─ Result.cshtml ← その次に作る(結果画面)

その中に、右クリック → 追加 → 新しい項目 → Razor ビュー - 空 を選んで、Index.cshtml を作ります。

Index.cshtml
@model Ch26_MvcFortuneApp.Models.FortuneViewModel
@{
ViewData["Title"] = "今日の占い";
}
<h1>今日の占い</h1>
<p>名前と生年月日、今日の気分を入力して「占う」を押してください。</p>
<form asp-controller="Fortune" asp-action="Result" method="post">
<div class="mb-3">
<label for="UserName" class="form-label">名前</label>
<input asp-for="UserName" class="form-control" />
</div>
<div class="mb-3">
<label for="BirthDate" class="form-label">生年月日</label>
<input asp-for="BirthDate" type="date" class="form-control" />
</div>
<div class="mb-3">
<label for="Mood" class="form-label">今日の気分</label>
<select asp-for="Mood" class="form-control">
<option value="">選んでください</option>
<option value="元気">元気</option>
<option value="普通">普通</option>
<option value="疲れ気味">疲れ気味</option>
</select>
</div>
<button type="submit" class="btn btn-primary">占う</button>
</form>

各部分の意味を見ていきます。

@model Ch26_MvcFortuneApp.Models.FortuneViewModel

この View が どの型の Model を受け取るか を宣言します。 これにより、ファイル内の Model という変数が FortuneViewModel として扱われます。

<form asp-controller="Fortune" asp-action="Result" method="post">

Section titled “<form asp-controller="Fortune" asp-action="Result" method="post">”

入力フォームのタグです。 属性で「送信先は Fortune コントローラーの Result アクション、メソッドは POST」と指定しています。

属性意味
asp-controller="Fortune"送信先の Controller
asp-action="Result"送信先のアクション
method="post"POST で送る(GET だと URL に値が付く)

<input asp-for="UserName" /> などの入力部品

Section titled “<input asp-for="UserName" /> などの入力部品”

asp-for="UserName" は、この入力欄を Model の UserName プロパティに対応 させる指定です。 これがあるおかげで、フォーム送信時に model.UserName に値が自動で入ります(Model Binding)。

入力タグ対応プロパティ入力タイプ
<input asp-for="UserName" />model.UserName文字列
<input asp-for="BirthDate" type="date" />model.BirthDate日付ピッカー
<select asp-for="Mood">model.Moodドロップダウン

<select><option value="元気">元気</option> のように、value 属性で送信される値を指定できます。

asp-for は何に変わる?(サーバーが生成する HTML)

Section titled “asp-for は何に変わる?(サーバーが生成する HTML)”

asp-for="UserName" は、サーバーで処理されると次のような 素の HTML に変わってからブラウザに届きます(26-6 で見た「.cshtml はそのまま送られない」の具体例です)。

<!-- View に書くと(.cshtml) -->
<input asp-for="UserName" class="form-control" />
<!-- ブラウザが受け取る HTML(サーバーが生成) -->
<input class="form-control" type="text" id="UserName" name="UserName" />

ポイントは、asp-for="UserName"name="UserName" を自動で付けてくれる ことです。 この name="UserName" こそが、送信されたときに model.UserName へ値を届けるための名札 になります(次の 26-10 Model Binding)。

asp-for="UserName" → name="UserName" → model.UserName に届く
(View に書く) (ブラウザに届く HTML) (Controller で受け取る)

asp-for は、Model のプロパティ名から入力欄の name を作ってくれる部品」と捉えると、入力 → Model Binding までが 1 本の線でつながります。

HTML タグの読み方(事前学習の復習)

<form> は入力欄のまとまり、<input> は 1 つの入力欄、<select> はドロップダウン、<label> は項目名のラベルです。事前に学習した HTML の復習として、ここで思い出しておきましょう。

class="..." は Bootstrap の見た目

form-control / btn btn-primary / mb-3 などの classBootstrap(見た目を整える仕組み)のクラスです。付けると入力欄やボタンが整って見える だけで、動作には影響しません。中身の仕組みは気にしなくて OK です(一覧は 26-12 の早見表参照)。

ここで動かす(1 回目・Step1 完了)

F5 で実行し、/Fortune/Index を開いてください。入力フォームが表示されれば Step1 は成功 です。 ※この時点で「占う」を押すと、まだ Result を作っていないのでエラーになります(Step2 で作ります)。26-7 で作ったトップ画面の「占いを始める」リンクからも、この入力画面に来られるようになりました。


26-10 Step2:入力した値が結果画面に渡るのを確かめる(Model Binding)

Section titled “26-10 Step2:入力した値が結果画面に渡るのを確かめる(Model Binding)”

次に「占うボタンで送った入力が、結果画面に表示される」ところまで作ります。ここでは 占いのランダムはまだ足しません。まず「入力がサーバーに届く」ことだけを確かめます。

FortuneControllerIndex メソッドの下に、Result メソッドを足します。

[HttpPost]
public IActionResult Result(FortuneViewModel model)
{
// まずは入力をそのまま結果画面に渡すだけ(占いは Step3 で足す)
return View(model);
}
メソッドいつ呼ばれる?返す View
[HttpGet] Index()ブラウザで /Fortune/Index を直接開いたときViews/Fortune/Index.cshtml
[HttpPost] Result(...)入力フォームで「占う」を押したときViews/Fortune/Result.cshtml

「画面を見るときは Index(GET)」「フォーム送信して結果を出すときは Result(POST)」と役割を分けています。

return View()return View(model) の違い

Controller の最後でよく書く return View(...) には、2 つの形があります。

  • return View() … 対応する View を表示する。Model は渡さない(View 側で @Model を使わない画面向き。26-6 の HomeController.Index() がこの形)。
  • return View(model) … 対応する View を表示し、同時に Model を渡す。View 側では @Model.プロパティ で受け取れる(この章の IndexResult はこちら)。

どちらも「自分のアクション名と同じ名前の View」を探します(Index() なら Views/Fortune/Index.cshtml)。別の View を表示したいときは return View("Result", model) のように View 名を明示 することもできますが、本研修では基本の 2 形だけ覚えれば十分です。

なお、「View を返す」以外に、別のアクションへ送り直す return RedirectToAction("Index") という返し方もあります。登録・更新・削除のあとに一覧画面へ戻すときに使い、第 30 章 で扱います(この章では使いません)。

Model Binding:入力が引数に届くしくみ

フォームに入力された値は、自動的に 同じ名前の引数プロパティ(model.UserName など)に詰められて Result(model) に届きます。この自動マッピングを Model Binding と呼びます。橋渡しのカギは、入力欄の名前(asp-for)と引数プロパティの名前が一致していること です。

第 10 章で学んだメソッドの引数が、ここでは「フォームから来た値の受け取り口」になっています。名前さえ合っていれば、値は自動で運ばれてきます。

実際に「占う」を押してから値が届くまでの 時系列の流れ(ブラウザ → Model Binding → Controller)は、26-13「処理の流れを 2 つに分けて理解する」 の POST シーケンス図で確認できます。

補足:Model Binding と Model

ここで「Model Binding」と「Model」の関係を整理しておきます。

  • Model Binding は、ブラウザから送られてきた値を、Controller のアクションの引数に自動で入れる仕組み です。
  • アクションの引数は、stringint のような 単純な型 でも受け取れます(Result(string userName) と書けば userName に値が入ります)。
  • 一方、FortuneViewModel のような クラス型 で受け取ると、フォームの入力値が そのクラスの各プロパティ(UserNameBirthDateMood)に自動で入ります。入力欄が多いときは、引数を 1 つずつ並べるより、まとめて受け取れるクラス型のほうが便利です。
  • そしてこの章の FortuneViewModel は、入力値を受け取る役割(POST で Result(model) に届く)と、結果画面へ表示する値を渡す役割(return View(model) で View に渡す)の 両方 を 1 つで担っています。だから入力 3 項目と結果 3 項目を 1 つのクラスにまとめてあるのです(26-9 の定義を見返してみてください)。

結果 View を作る(まず入力の確認だけ):Views/Fortune/Result.cshtml

Section titled “結果 View を作る(まず入力の確認だけ):Views/Fortune/Result.cshtml”

Views/Fortune フォルダの中に、Result.cshtmlRazor ビュー - 空 で追加します。Step2 ではまず 入力された値だけ を表示します(占い結果は Step3 で足します)。

Result.cshtml
@model Ch26_MvcFortuneApp.Models.FortuneViewModel
@{
ViewData["Title"] = "占い結果";
}
<h1>占い結果</h1>
<div class="alert alert-info">
<p><strong>@Model.UserName さんの今日の運勢</strong></p>
@if (Model.BirthDate.HasValue)
{
<p>生年月日: @Model.BirthDate.Value.ToString("yyyy年M月d日")</p>
}
<p>今の気分: @Model.Mood</p>
</div>
<p>
<a asp-controller="Fortune" asp-action="Index" class="btn btn-secondary">もう一度占う</a>
</p>

ポイント:

  • @Model.UserName のように書くと、Controller から渡った Model の値が HTML に埋め込まれます。
  • @if (Model.BirthDate.HasValue) は Razor の条件分岐で、「生年月日が入力されていれば表示」しています。
  • <a asp-controller="Fortune" asp-action="Index"> は入力画面へ戻る GET リンクです。

ここで動かす(2 回目・Step2 完了)

F5/Fortune/Index で入力 → 「占う」。結果画面に名前・気分・(入れていれば)生年月日が出れば成功 です。 これで「入力した値がサーバーの Controller に届き(Model Binding)、結果画面まで渡っている」ことが確認できました。まだラッキー結果は出ません(Step3 で足します)。


26-11 Step3:占いの結果(ランダム)を足す

Section titled “26-11 Step3:占いの結果(ランダム)を足す”

最後に、ランダムな占い結果(ラッキーカラー・ナンバー・一言)を足して完成させます。

FortuneController に色の候補 LuckyColorsMakeComment を足し、Result を次のように書き換えます。

FortuneController.cs
using Microsoft.AspNetCore.Mvc;
using Ch26_MvcFortuneApp.Models;
namespace Ch26_MvcFortuneApp.Controllers;
public class FortuneController : Controller
{
private static readonly string[] LuckyColors =
{ "", "", "", "", "", "", "", "ピンク", "オレンジ" };
[HttpGet]
public IActionResult Index()
{
FortuneViewModel model = new FortuneViewModel();
return View(model);
}
[HttpPost]
public IActionResult Result(FortuneViewModel model)
{
Random random = Random.Shared;
model.LuckyColor = LuckyColors[random.Next(LuckyColors.Length)];
model.LuckyNumber = random.Next(1, 100);
model.Comment = MakeComment(model.Mood, random);
return View(model);
}
private string MakeComment(string mood, Random random)
{
string[] candidates;
switch (mood)
{
case "元気":
candidates = new[]
{
"今日はあなたのエネルギーが周りに伝わる日です。",
"新しいことに挑戦すると、思った以上にうまくいきそうです。"
};
break;
case "普通":
candidates = new[]
{
"焦らず一歩ずつ進めば、思わぬ発見がある一日に。",
"落ち着いた行動が、良い結果につながります。"
};
break;
case "疲れ気味":
candidates = new[]
{
"今日は無理せず、自分を大切にする一日に。",
"ゆっくり休むことが、明日への力になります。"
};
break;
default:
candidates = new[] { "今日も一日、自分らしく。" };
break;
}
return candidates[random.Next(candidates.Length)];
}
}

static readonlyRandom.Shared

色の候補は変わらない固定値なので static readonly で 1 つだけ用意し、全リクエストで共有します(第 8 章)。Randomnew Random() を毎回作らず、.NET 共有の Random.Shared を使います。new Random() を短時間に連続生成すると内部のシード(乱数の元)が重複して同じ結果が続くことがありますが、Random.Shared ならその心配がありません。

Result.cshtml<div class="alert alert-info"> の中、「今の気分」の段落の下に、次を足します。

<hr />
<ul>
<li>ラッキーカラー: <strong>@Model.LuckyColor</strong></li>
<li>ラッキーナンバー: <strong>@Model.LuckyNumber</strong></li>
<li>今日の一言: @Model.Comment</li>
</ul>

ここで動かす(3 回目・完成)

F5/Fortune/Index で入力 → 「占う」。ラッキーカラー・ナンバー・一言が表示されれば占いアプリ完成 です。同じ入力で何度か占うと、結果が毎回変わります(Random)。 「もう一度占う」で入力画面(GET)に戻ります。アドレスバーに /Fortune/Result を直接入れると エラー(POST 限定) になることも確かめてみましょう。


26-12 MVC の「おまじない」早見表

Section titled “26-12 MVC の「おまじない」早見表”

ここまでで @modelasp-forclass="..." など、「こう書くと動く決まり」 がいくつも出てきました。これらは MVC やフレームワークの お約束(おまじない) で、いまは 「何をするものか(役割)」だけ分かれば十分 です。仕組みの中身は、後で必要になったときに深掘りすれば大丈夫。

迷ったらこの表に戻ってきてください(第 27 章以降でも参照します)。

おまじない何をするもの(役割)
@model 型名(View 先頭)この View が受け取るデータの型を宣言する
@Model.プロパティController から渡された値を画面に表示する
@{ ... } / @if (...) { ... }View(HTML)の中に C# を書く(Razor 構文)
asp-controller="Fortune"リンク / フォームの送信先の Controller を指定
asp-action="Result"送信先のアクションを指定
asp-for="UserName"入力欄を Model の UserName と結びつける(Model Binding の橋渡し)
[HttpGet] / [HttpPost]そのアクションを GET / POST のどちらで呼ぶか
: Controller(継承)Web 用の機能(View() など)を使えるようにする
class="btn btn-primary"Bootstrap の見た目クラス。付けると整って見える(中身は気にしなくてよい)
builder.Services.AddControllersWithViews()(Program.cs)「MVC を使う」という起動時のお決まり
app.MapControllerRoute(...)(Program.cs)URL と Controller / Action の対応づけの規定(/Xxx/YyyXxxController.Yyy())

「おまじない」と「ブラックボックス」は違う

めざすのは「役割は一言で言えるが、内部実装は今は受け入れる」状態です。完全に無視するのではなく、「これは〇〇する決まり」と言えるようにしておきましょう。後の章のコメント課題でも、おまじないは「何をする決まりか」を一言だけ書きます。


26-13 処理の流れを 2 つに分けて理解する

Section titled “26-13 処理の流れを 2 つに分けて理解する”

ここがこの章で 最も大切な部分 です。

URL と Controller / View の対応(占いアプリ)

Section titled “URL と Controller / View の対応(占いアプリ)”

まず、占いアプリの 2 つの URL が、どのメソッド・どの View につながるかを 1 本の線で追ってみます(各メソッドが「いつ呼ばれるか」は 26-10 の表も参照)。

/Fortune/Index
→ FortuneController の Index() が呼ばれる
→ return View(model)
→ Views/Fortune/Index.cshtml が使われる
/Fortune/Result
→ FortuneController の Result(model) が呼ばれる
→ return View(model)
→ Views/Fortune/Result.cshtml が使われる

View ファイルに直接アクセスしているわけではありません

ブラウザがリクエストを送る先は、あくまで Controller の Action です。 Index.cshtmlResult.cshtml を URL で直接開いているのではなく、Controller が「どの View を使うか」を選んで、組み立てた HTML を返しています。 だから /Fortune/Index.cshtml のような URL は存在しません。URL に書くのは /コントローラー名/アクション名 だけです。

この「URL → Controller のアクション → View」という流れを、次に GET と POST の 2 パターンで詳しく見ます。

GET の流れ(入力画面を表示するとき)

Section titled “GET の流れ(入力画面を表示するとき)”
  • ブラウザは URL を GET で開く(アドレスバー入力やリンククリック)
  • Controller は [HttpGet]Index() が呼ばれる
  • 空の FortuneViewModel を作って、Views/Fortune/Index.cshtml に渡す
  • View が入力フォームの HTML を組み立ててブラウザに返す

POST の流れ(占い結果を表示するとき)

Section titled “POST の流れ(占い結果を表示するとき)”
  • ブラウザは「占う」ボタンで POST 送信する(フォームに入力された値が一緒に送られる)
  • ASP.NET Core が Model Binding で値を FortuneViewModel に詰める
  • Controller は [HttpPost]Result(model) が呼ばれる
  • Random で結果 3 項目を model に追加し、Views/Fortune/Result.cshtml に渡す
  • View が占い結果の HTML を組み立ててブラウザに返す

入力画面と結果画面は別の URL・別の View で、ボタン押下のタイミングで POST 送信されて切り替わる ── この流れを覚えてください。

GET も POST も、ブラウザが話しかける先は同じ FortuneController です

2 つの図で「呼ばれる相手」が違うように見えないように注意してください。どちらも、裏では ASP.NET Core がルーティング(と POST のときは Model Binding)を済ませてから、FortuneController のアクションを呼びます。 あなたが実装するのは、どちらの場合も Controller のアクション です。 GET と POST で変わるのは、HTTP メソッド(GET / POST) と、それによって呼ばれる アクション(Index / Result) だけで、「呼び先のクラスを変える」必要はありません([HttpGet] / [HttpPost] の属性で振り分けます)。


観点GETPOST
主な用途画面を 見る(取得する)データを 送る(更新する)
値の渡し方URL に付く(?name=山田二郎)リクエスト本体に含まれる
ブックマークできるできない(URL に値がない)
何度繰り返しても OK?OK(同じ画面が返るだけ)注意(送信が複数回起こる可能性)
画面表示、検索結果ページの URLフォーム送信、ログイン、占い結果生成

見るだけは GET、送るときは POST」と覚えれば、ほぼ大丈夫です。

ブラウザのアドレスバーは GET

ブラウザのアドレスバーに URL を入れて Enter を押すと、必ず GET リクエストになります。 フォームの送信ボタンを押したときだけ、HTML の <form method="post"> 指定によって POST になります。 このため、POST だけ呼ばれる処理は、アドレスバーから直接は実行できません。

/Fortune/Result をアドレスバーに直接入れたらどうなる?

エラー(HTTP 405 Method Not Allowed)になります。 Result[HttpPost] 限定のアクションなので、GET ではアクセスできないためです。 「結果は必ずフォーム送信から作る」というルールが、属性で表現されています。

よく出る HTTP ステータス(最小限)

Section titled “よく出る HTTP ステータス(最小限)”

Web アプリでは、うまくいかないときに HTTP ステータス という番号でエラーの種類が返ります。次の 3 つだけ知っておくと、第 27 章以降のトラブル対応がぐっと楽になります。

状態意味よくある原因
404 Not FoundURL に対応する処理がないController 名・Action 名・URL の打ち間違い
405 Method Not AllowedURL はあるが GET / POST が違う[HttpPost] のアクションに GET でアクセスした(上の /Fortune/Result の直打ちがこれ)
500 Internal Server Errorサーバー側で例外が起きたC# コードの例外、View のエラー

番号を覚えておくと、画面がエラーページになったときに「これは URL の間違い(404)か」「メソッド違い(405)か」「コード側の例外(500)か」と 当たりを付けられます。第 27 章以降で DB に接続すると 500 に出会いやすくなるので、ここで顔だけ覚えておきましょう。


26-15 Windows フォームアプリとの比較

Section titled “26-15 Windows フォームアプリとの比較”

第 19 章の SimpleCalc(Forms)と、この章の占いアプリの構造を並べてみます。

観点Windows フォーム(SimpleCalc)Web(占いアプリ)
入力部品TextBoxComboBox<input><select>
実行のきっかけボタン Click イベントフォーム POST 送信
処理を書く場所Button_Click メソッド[HttpPost] Result(model)
入力値の取得nameTextBox.Textmodel.UserName(Model Binding)
結果の表示先別ラベルの Text プロパティModel.LuckyColor を View に渡す
画面遷移同じフォームで値が変わる入力画面と結果画面で URL が変わる

入力 → 処理 → 出力」という流れは同じです。 ただし、処理が走る場所 が違います。

種類処理が走る場所
Windows フォーム利用者の PC 上(クライアントのみ)
Web アプリサーバー上(あなたが用意するサーバー)+ ブラウザ

Web ではブラウザに HTML を「送り直す」ことで画面が切り替わります。 Windows フォームのように「同じ画面の表示だけ更新する」とは仕組みが違うことを意識してください。

Web は「1 回ごとに作り直す」── 前の状態は覚えていない

Windows フォームは、アプリが起動している間ずっと TextBox やフィールドに値が 残り続けます。 一方 Web は 1 リクエストごとに独立 しています。サーバーは前のリクエストのことを基本的に覚えておらず、リクエストが来るたびに Controller が動いて HTML を 一から組み立てて返し、返したら忘れます。 だから「入力した値を次の画面でも使いたい」ときは、フォームで もう一度サーバーに送る(POST)必要があります。占いアプリで入力画面と結果画面が分かれていて、結果が POST 送信で作られるのも、この「毎回送って作り直す」仕組みに沿っているからです。


つまずき原因対応
「ASP.NET Core Web アプリ」テンプレートが見つからない必要なワークロードが入っていないVisual Studio Installer で「ASP.NET と Web 開発」をインストール
実行してもブラウザが起動しないVisual Studio の起動設定「IIS Express」または「プロジェクト名」で起動するように選ぶ
/Fortune/Index にアクセスすると 404Controller の名前または View の場所が違うFortuneController + Views/Fortune/Index.cshtml を確認
入力した値が model.UserName に入らないasp-for="UserName" の名前が違うプロパティ名と完全一致させる(大文字小文字も)
<select> で選んだ気分が model.Mood に入らない<option value="..."> の値が指定されていない<option value="元気">元気</option> のように value を明示
占い結果画面で @Modelnull エラー結果画面に直接 GET でアクセスしている必ず入力画面から「占う」ボタンで POST 送信して開く
結果が毎回同じnew Random() を短時間に複数回作るとシードが重複することがある本文どおり Random.Shared を使う(共有インスタンスなのでシード重複の心配がない)
@Model.BirthDate.Value で例外BirthDatenull のままModel.BirthDate.HasValue で先にチェック
「アクセス権がない」と HTTPS 警告開発用証明書Visual Studio が自動で扱うので、警告を許可

  • ブラウザとサーバーがリクエスト/レスポンスでやり取りすることを説明できる
  • Visual Studio の F5 で Web サーバー(Kestrel)が自動起動することを説明できる
  • 別途 Web サーバーをインストール不要なことを説明できる
  • Web サーバーを止める方法(VS の停止ボタン)を覚えている
  • MVC の Model・View・Controller それぞれの役割を説明できる
  • URL とコントローラー・アクションの対応(/Xxx/YyyXxxController.Yyy())を説明できる
  • ブラウザは View ファイルを直接開くのではなく、Controller のアクションにリクエストし Controller が View を選ぶ、と説明できる
  • return View()return View(model) の違いを説明できる
  • GET と POST の違いを 2 つ以上挙げられる
  • [HttpGet][HttpPost] でアクションを使い分けられることを説明できる
  • Model Binding の概要(フォーム入力 → Controller の引数)を説明できる
  • 入力画面と結果画面を別の View に分けるメリットを説明できる
  • View の @model@Model.プロパティ を読み解ける
  • .cshtml がサーバーで HTML に変換されてからブラウザに届くこと(@/asp- は届かない)を説明できる
  • asp-for="UserName"name="UserName" を生成し、それが Model Binding の名札になることを説明できる
  • アクション引数は単純型(string/int)でもクラス型でも受け取れ、FortuneViewModel が入力と結果の両方の役割を持つことを説明できる
  • asp-forasp-controllerasp-action の役割を 1 つずつ説明できる
  • 404 / 405 / 500 のおおまかな意味を説明できる
  • Windows フォームとの違い(処理が走る場所、画面遷移)を説明できる

研修の進め方によっては、隣の人またはチーム内で説明確認を行います。

次の内容を、自分の言葉で説明してください。

  1. リクエストとレスポンスとは何ですか。
  2. MVC の Controller・Model・View はそれぞれ何を担当しますか。
  3. URL /Fortune/Index は、どのファイルのどのメソッドに対応しますか。
  4. ブラウザで URL を直接開いたときと、フォームを送信したときで、何が違いますか。
  5. なぜ Index(GET)と Result(POST)で別のメソッドにしているのですか。
  6. フォームに入れた値が model.UserName などに届く仕組みは何と呼ばれますか。
  7. View の @model 行は何のために書きますか。
  8. Windows フォームと Web アプリで、画面遷移の仕組みはどう違いますか。

この章の演習課題に取り組みます。

本章では、第 4〜15 章のコンソール課題と同じ タイマー方式 で進めます。次の 3 段階で取り組んでください。

段階時間内容
① 準備10 分(目安)上の「ペア確認」と、必須課題(仕様)の読み込み。ペアや講師に質問してよい
② ソロ作業30 分(タイマーで計測)一人で必須課題に取り組む。タイマーが鳴ったら、完成・未完成にかかわらず作業を止めて提出する
③ チーム時間講師が指定する発表開始時刻までチーム内で 教え合い(分かった人が詰まった人に説明)とコードレビューを行い、発表者を決める。発展課題や実装の続行も可。時間配分はチームで管理する

評価対象はタイマー時点で提出されたコードです(タイマー後に書き足した分は評価には含まれません)。 発表開始時刻は厳守 です。チーム時間中も、その時刻が来たら全員手を止めて発表に移ります。

必須課題の土台は先に作っておきます

必須課題は「本文 26-8〜26-11 で完成させた占いアプリに 年齢の表示を追加する」課題です。 占いアプリ本体(Model・Controller・View 2 つ)は本文どおり コピーで作って構いません(Web 1 日目で初出が多いため、ここは評価の主眼ではありません)。 ソロ作業のタイマーで測るのは、そこに自力で足す年齢計算 です。本文には完成コードを載せていないので、方針を見ながら自分で組み立てます。

演習の進め方の詳細は、付録A「演習の進め方」 を参照してください。


課題はソリューション Kadai26 の中に作成してください。課題ごとに別のプロジェクトを作成 し、指定されたプロジェクト名を使います。

課題必須/発展プロジェクト名内容
課題26-1必須Kd26_01_MvcFortuneApp占いアプリ + 年齢の計算・表示
課題26-2発展Kd26_02_MvcFortuneCustom占いの改造(文言・数字・時間帯の挨拶)
課題26-3発展(プロジェクト不要)処理の流れを自分の言葉でまとめる

提出ルール(タイマー方式)

30 分のタイマーが鳴ったら手を止め、git addcommitpush を行います。評価対象はタイマー時点のコミットのみです(タイマー後の追加は評価外)。Git が使えないときは付録 C のコピー提出に従い、提出メモ.txt に「どこまで完成 / 詰まったポイント」を書きます。

コミットメッセージの形式:

Chapter26 タイマー提出: <どこまで完成> / <詰まったポイント>

例:Chapter26 タイマー提出: 年齢計算まで完成 / 誕生日前後の境目で詰まった

タイマー後のチーム時間

発表開始時刻まで、教え合い(分かった人が詰まった人に説明)/ コードレビュー / 発表者選出 / 発展課題や実装続行(任意・評価外)。時間配分はチームの判断、発表開始時刻は厳守


課題 26-1 占い結果に「年齢」を追加する

Section titled “課題 26-1 占い結果に「年齢」を追加する”

本文 26-8 〜 26-11(Step1〜3)にそって占いアプリを完成させたら、結果画面に 年齢 を表示できるようにします。

生年月日(Model.BirthDate)と今日の日付(DateTime.Today)から年齢を計算して出します。ここがソロ作業(タイマー)の中心 です。本文に完成コードはないので、下の仕様と方針を手がかりに 自力で 実装してください。

前提(タイマー前・コピーOK)

  • 本文どおり占いアプリが動いている(/Fortune/Index → 入力 → 「占う」で結果が出る)

ソロ作業でやること(仕様)

  • 結果画面に @Model.UserName さんの年齢: ○○歳 のように年齢を表示する
  • 生年月日が null(未入力)のときは年齢を表示しない
  • 月日まで考慮した正確な年齢にする(今年まだ誕生日が来ていなければ 1 引く)

実装方針(おすすめは ①)

方針場所特徴
① ViewModel に Age プロパティを追加FortuneViewModel.cs + Controller計算は Controller、表示は View(MVC の役割分担に沿う)
② View で計算Result.cshtml の Razor 部分Controller を変えなくて済む

考え方のヒント(コードは自分で組み立てる)

基本は「今年の年 − 生まれた年」です。そこから、今年まだ誕生日が来ていなければ 1 を引く という補正を入れます。「誕生日が来たか」は、今日の月・日と生年月日の月・日を比べて判断します(DateTime.Today.Year / .Month / .Day が使えます)。 生年月日が null のときは計算せずに表示も出さないよう、先に Model.BirthDate.HasValue で分岐してください(.Value で中身の DateTime が取れます)。

確認すること

  • 占いアプリが本文どおり動く(入力 → 占う → 結果)
  • 生年月日を入れて占うと、年齢が正しく表示される(誕生日前後の境目も含む)
  • 生年月日を空のままだと年齢欄が出ない
  • Controller と View のどちらに計算ロジックを置いたか、自分で説明できる
  • タイマー時点で commit(または 提出メモ.txt)を済ませた

発展課題はチーム時間に取り組みます(評価対象外・任意)。改造系は Kd26_02_MvcFortuneCustom として、必須課題のプロジェクトをコピーして実験すると安全です。

占いアプリを、下から 1 つ以上 選んで改造してみましょう。

選択肢内容ヒント
A:文言バリエーション追加各気分の comments 配列を 4 つ以上に増やすMakeCommentcase を編集
B:ラッキー数字を 100 〜 999 の範囲に3 桁の数字を出すrandom.Next(100, 1000)
C:挨拶を時間帯で変える結果画面の冒頭を「おはよう/こんにちは/こんばんは」と切り替えるDateTime.Now.Hour で分岐。Controller で ViewBag.Greeting = "..." のように代入し、View で @ViewBag.Greeting と書くと表示できる(下記補足参照)

補足:ViewBag とは

ViewBag は、Controller から View に 「Model 以外の値をちょっと渡したい」 ときに使う仕組みです。本文 26-7 で使った ViewData["Title"] と中身はほぼ同じで、ViewBag.TitleViewData["Title"] はどちらでも同じ値を指します。

// Controller 側
ViewBag.Greeting = "こんにちは";
@* View 側 *@
<p>@ViewBag.Greeting</p>

Model にプロパティを増やすほどでもない小さな値(タイトル、メッセージ、フラグなど)を渡すときに便利です。 ただし「型がチェックされない」ため、業務コードでは Model に入れる方が安全で、ViewBag は補助的に使う程度にとどめます。

確認すること

  • 選んだ改造が正しく動く
  • 改造を入れた場所を自分で指せる

課題 26-3 処理の流れを自分の言葉でまとめる

Section titled “課題 26-3 処理の流れを自分の言葉でまとめる”

ここまでで作ったアプリを題材に、自分の言葉で処理の流れをまとめて ください。

提出形式

  • 課題 26-1 のプロジェクト Kd26_01_MvcFortuneAppプロジェクト直下提出メモ.md(または 提出メモ.txt)を作成
  • 下記「まとめる観点」を見出しまたは番号付きで網羅
  • 紙のノートに書いたものを写してもよい、ペアで口頭発表した内容を文字起こししてもよい

まとめる観点

  1. ブラウザで /Fortune/Index を開いたとき、何が起きて画面が表示されるか
  2. 入力して「占う」を押したとき、サーバー側で何が起きるか
  3. Controller・Model・View が どのタイミングで何をしているか
  4. GET と POST はどう使い分けられているか
  5. Windows フォーム版(第 19 章 SimpleCalc)との違い

説明テンプレート(参考)

「/Fortune/Index を開くと、まずブラウザが ___ という GET リクエストを送ります。
サーバーは ___ を見て、FortuneController の ___ メソッドを呼びます。
そこで ___ が作られ、View に渡されて、___ が HTML として返ります。
...」

この課題はコードを書きません。 「コピーして動かしたコード」を「読めて、話せる」状態にする ことが目的です。 書いた内容は次回のペア確認・発表で使えるよう、自分の言葉で残しておくのがおすすめです。


  • ソリューション名が Chapter26
  • プロジェクト名が Ch26_MvcFortuneApp
  • csproj が <Nullable>disable</Nullable>
  • Models/FortuneViewModel.cs が作成されている
  • Controllers/FortuneController.cs に GET Index と POST Result がある
  • Views/Fortune/Index.cshtml(入力画面)が作成されている
  • Views/Fortune/Result.cshtml(結果画面)が作成されている
  • /Fortune/Index にアクセスして入力フォームが表示される
  • 入力して「占う」ボタンで結果画面に遷移する
  • 「もう一度占う」で入力画面に戻る
  • 課題 26-1 で年齢が正しく表示される(未入力なら年齢欄なし)
  • タイマー時点で commit(または 提出メモ.txt)を済ませた
  • (発展 26-2 を実施した場合)選んだ改造が動く
  • binobj.vs フォルダが Git 管理に入っていない
  • 開発用 HTTPS 証明書ファイルなど機密物がコミットされていない

Terminal window
git status
git add .
git commit -m "Chapter26 タイマー提出: <どこまで完成> / <詰まったポイント>"
git push origin main

Git の詳しい操作は、付録 C「Git のインストールと提出ルール」 を参照してください。


  • Web アプリは、ブラウザとサーバーの リクエスト/レスポンス で動く
  • F5 を押すと Visual Studio が内蔵 Web サーバー(Kestrel)を自動起動 する(別途 Web サーバー不要)
  • 初回は HTTPS 開発証明書 を信頼する必要がある(以降は不要)
  • 止めるには Visual Studio の停止ボタン(または Shift + F5)
  • MVC は、Web アプリの中身を Model・View・Controller の 3 役に分ける考え方
  • ASP.NET Core MVC の URL ルール:/Xxx/YyyXxxController.Yyy() に対応。ブラウザは View ファイルを直接開くのではなく、Controller のアクションにリクエストし、Controller が View を選ぶ
  • 画面ごとに 別のアクション・別の View を用意するのが Web アプリの基本
  • return View() は Model を渡さず、return View(model) は Model を渡して @Model.XXX で表示できる
  • うまくいかないときは 404(URL 違い)・405(GET/POST 違い)・500(コード側の例外) で当たりを付ける
  • フォーム入力は Model Binding で自動的に Controller の引数に届く
  • View は HTML + Razor 構文(@model@Model.XXX@if など)
  • .cshtmlサーバーで素の HTML に変換されてから ブラウザに届く(@asp- はブラウザには行かない)。asp-for="UserName"name="UserName" を生成し、それが Model Binding の名札になる
  • asp-forasp-controllerasp-action で、Model と画面の対応を宣言できる
  • [HttpGet][HttpPost] で、同じ URL でも別のメソッドを呼べる(/Fortune/Index と /Fortune/Result のように URL を変えてもよい)
  • Windows フォーム編との違いは「処理が走る場所」(PC かサーバーか)と「画面遷移」(同じ画面が更新されるか別画面が返るか)
  • 本章はゼロから組むことより、動かして観察し、説明できる ことを優先

次章では、もう一歩 MVC に踏み込み、MVC アプリから SQLServer のデータベースを扱う 方法を学びます。

占いアプリではデータベースを使わず、Controller の中で結果を作っていました。 第 27 章からは、第 16 章で作った TrainingDBemployees テーブル に Web アプリから接続し、Web 版の社員管理アプリを作っていきます。

第 23〜25 章の Windows フォーム社員管理アプリを そのまま Web 上で動かす イメージです。 画面の作り方や認証方式(Web では SQL 認証)は変わりますが、「画面 → Controller → DB → 画面」 という流れは Windows フォームと共通の発想で読み解けます。