ASP.NET Core MVC を初めて触る人でもスムーズに理解できるように、公式チュートリアルをより丁寧に解説してみました。(全4回)
チュートリアルで少しわかりにくい表現をかみ砕いて説明したり、英語圏向けの表記を日本語向けに直したりしています。また、冗長と思われる部分を省いています。
第1回では、プロジェクトの作成から、コントローラーとビューの基本的な使い方までを見ていきます。(公式チュートリアルのパート1~3に対応)
本記事の対象
- ASP.NET Core MVC の基本的な使い方を理解したい人
- C#の基本文法がある程度わかる人
環境
- Windows 11
- Visual Studio 2022
- .NET 8
準備と新規プロジェクトの作成
(必要な場合のみ)Visual Studio Installerのワークロードに「ASP.NETとWeb開発」が追加されていない場合はインストールしておいてください。
それではプロジェクトを作成します。
Visual Studio 2022 の新規プロジェクトの作成画面を開き、「ASP.NET Core Web アプリ (Model-View-Controller) 」を選択してください。
プロジェクト名は公式チュートリアルと同様に「MvcMovie」にします。
フレームワークには「.NET 8.0 (長期的なサポート)」を選択し、「作成」をクリックします。
プロジェクトが作成できたら、上部の「MvcMovie」をクリックしてみてください。
プログラムが実行され、ブラウザが起動します。
以下のようなページが表示されれば成功です。
※もし「証明書をインストールしますか?」という確認が表示された場合は「はい」を選択してください。)
※ブラウザが自動で起動しない場合は、コンソールの一番上に表示されているアドレス(https://localhost:<ポート番号>
)をブラウザのアドレスバーに貼り付けて移動してください。
アドレスの「localhost」はアプリが動作してるコンピュータ自身を表す特殊なホスト名で、ここで実行されたアプリケーションは自分のPCでしか見ることができません。
その後ろのポート番号は実行環境によって異なります。
(参考)起動するブラウザを変更したい場合は以下から変更できます。
コントローラーの追加
アプリの実装を開始する前に、MVCアーキテクチャについて簡単に説明します。
MVCアーキテクチャとは
ソフトウェアの設計思想の1つで、機能をM:モデル、V:ビュー、C:コントローラーの3つに分けて実装するものです。
3つの機能の主な役割は次のようになります。
- モデル:アプリのデータ処理部分を担当。DBのデータ取得や更新などを行う。
- ビュー:画面表示(UI)部分を担当。モデルのデータを画面に表示する。
- コントローラー:アプリの全体的な制御を担当。クライアントからリクエストを受け付け、モデルに連携してデータを取得し、レスポンスとしてビューを返すといった一連の処理を行う。
このように異なる機能のロジックを分離することで、アプリの構造がわかりやすくなり、メンテナンスがしやすくなるといったメリットがあります。
コントローラーの追加とルーティングの説明
それではまずコントローラーを作成して、実際の挙動などを確認していきます。
プロジェクト作成時に「HomeController.cs」が自動的に作成されていますが、今回は新しいコントローラーを追加してみましょう。
ソリューションエクスプローラーの「Controllers」フォルダを右クリックして、「追加」→「コントローラー」をクリックします。
「MVCコントローラー – 空」選択します。
名前を「HelloWorldController」にして追加します。
※ASP.NET Core MVCではコントローラー名を必ず「○○Controller」とする必要があります。
作成したHelloWorldController.csに、次の2つのpublicメソッドを追加します。
using Microsoft.AspNetCore.Mvc;
namespace MvcMovie.Controllers
{
public class HelloWorldController : Controller
{
// /HelloWorld にアクセスした時の処理
public string Index()
{
return "Hello World!";
}
// /HelloWorld/Welcome にアクセスした時の処理
public string Welcome()
{
return "Welcomeページです。";
}
}
}
このようにコントローラークラス内で定義されたpublicメソッドをアクションメソッドといいます。
F5でデバッグ実行し、/helloworldにアクセスすると「Hello World!」と表示されます。
/helloworld/welcomeに移動すると、Welcomeメソッドで設定したメッセージが表示されます。
このように、指定したURLに応じてコントローラーのアクションメソッドが実行されます。
このルーティングのルールは{コントローラー名}/{アクションメソッド名}/{パラメーター(省略可能)}となっており、Program.csのMapControllerRouteメソッド内で決められています。
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
既定のルールでは、アプリを起動するとまずHomeControllerのIndexメソッドの内容が表示されます。
試しにコントローラー名をHelloWorld、アクションメソッド名をWelcomeに変えて実行してみると、初期表示はHelloWorldControllerのIndexメソッドの内容になります。
※確認したら、元に設定に戻しておいてください。
app.MapControllerRoute(
name: "default",
pattern: "{controller=HelloWorld}/{action=Welcome}/{id?}");
では次にHelloWorldControllerのWelcomeメソッドを次のように変更してみます。
public string Welcome(string name, int age = 1)
{
return $"名前:{name}さん, 年齢:{age}歳";
}
第1引数に文字列型、第2引数に数値型の値(デフォルト値は1)を取るメソッドに変更しました。
これで /HelloWorld/welcome?name=Taro&age=20
のようなアドレスを指定すると、指定した値が画面に表示されます。
このようにnameおよびageパラメーターはクエリ文字列※で渡すことができます。
これはASP․NET Core MVC に備わっているモデルバインドシステム※によって実現されています。
※クエリ文字列とは?
クエリ文字列(URLパラメーター)とは、サーバーに情報を送るためにURLの末尾につけ足す文字列(変数)のこと。
URLの末尾に「?」をつけ、その後ろに「パラメーター=値」の形で指定します。複数のパラメーターをつけたい場合は「&」で繋ぎます。
※モデルバインドシステムとは?
フォーム等から送信された値やクエリ文字列を自動で.NET の型に変換して、コントローラーのアクションメソッドに提供する仕組みです。
今回の処理の流れを簡単に表すとこのようになります。
- URLでリクエストを受信
- アクションメソッドの最初の引数(今回はnameというstring型の変数)を検索
- クエリ文字列内のname=Taroを見つける
- 次の引数(ageというint型)を検索
- クエリ文字列内のage=20を取得
- 文字列の”20″を数値の20に変換
- Welcomeメソッドが呼ばれて引数に取得した値が渡される
最後に、先ほど説明していなかったパラメーターのidを指定した場合の挙動について見てみましょう。
Welcomeメソッドを以下のように書き換えます。
public string Welcome(string name, int Id = 1)
{
return $"名前:{name}さん, ID:{Id}";
}
そのうえで、/HelloWorld/welcome/3?name=Taro
にアクセスしてみると以下のように表示されます。
今まではidを省略していましたが、今回はidを3と指定したことで、Program.csのURLパターンのidパラメーターと一致し、Welcomeメソッドに値が渡されました。
ビューの追加
今まではコントローラーで直接文字列を返していましたが、今後はビューテンプレートを返すように変更します。
Razorビューテンプレートの呼び出し
Razorとは、HTMLにC#の構文を埋め込んだテンプレートを生成することができる仕組みです。
テンプレートの拡張子は.cshtmlです。
まずHelloWorldControllerクラスのIndexメソッドを次のように書き換えます。
public IActionResult Index()
{
return View();
}
今までは単純な文字列を返すだけだったので戻り値にstring型を指定していましたが、今回はIActionResult型を指定しました。
IActionResultを実装すると、RazorテンプレートやJSON形式の文字列をレスポンスとして返したり、他のURLへリダイレクト処理ができたりと様々なパターンに対応できます。
今回のようにViewメソッドを引数なしで呼び出すと、「/Views/コントローラー名/アクション名.cshtml」が返されます。
つまり今回の場合は /Views/HelloWorld/Index.cshtml
が返されます。
テンプレートの追加
Indexメソッドで返すためのテンプレートを作成しましょう。
まずViewsフォルダの直下に「HelloWorld」という名前のフォルダを作成します。
(Viewsフォルダを右クリックして、「追加」→「新しいフォルダー」)
次に、作成したHelloWorldフォルダを右クリックして「追加」→「ビュー」で「新規スキャフォールディング アイテムの追加」ウィンドウを開きます。
「Razorビュー – 空」を選択後、ファイル名を「Index.cshtml」として追加してください。
追加できたら、index.cshtmlの中身を書き換えます。
ViewData[“Title”]には各ページのページタイトルを設定します。
@{
ViewData["Title"] = "Movie List";
}
<h2>My Movie List</h2>
F5で実行し、/HelloWorld/ にアクセスしてみましょう。このように表示されれば成功です。
共通レイアウト
ヘッダーやフッターなど各ページに共通するレイアウトは、Views/Shared/_Layout.cshtml
に書かれています。
6行目のViewData[“Title”]には、先ほどのように各ページで指定したタイトルが代入されます。
34行目の@RenderBody()の部分では、各ページ固有のコンテンツを表示しています。
少し表記を変更してみましょう。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Movie App</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/MvcMovie.styles.css" asp-append-version="true" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Movies" asp-action="Index">Movie App</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2024 - Movie App - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
アプリの名称を「Movie App」に変更しました。
また、ヘッダーのアプリ名をクリックするとMovies/Indexに飛ぶようにしました。
※Moviesコントローラーは次回作成します。
画面を更新し、ヘッダーとフッターの文言が変更されていることを確認してみましょう。
※もし共通レイアウトを変更しても反映されない場合は、Ctrl + F5 キーを押して強制リロードしてみてください。
ちなみに、Views/_ViewStart.cshtml
の中身はこのようになっており
@{
Layout = "_Layout";
}
ビューの処理にあたってまずこの_ViewStart.cshtmlが呼び出され、_Layout.cshtmlが各ページに適用されるという流れになります。
もし共通レイアウトを各ファイルに適用したくない場合はnullを指定すればOKです。
コントローラーからビューへデータを渡す
Welcomeメソッドでもビューテンプレートを返すように変更してみましょう。
既に登場したViewDataを使って、コントローラーからビューテンプレートにデータを渡します。
using Microsoft.AspNetCore.Mvc;
namespace MvcMovie.Controllers
{
public class HelloWorldController : Controller
{
// /HelloWorld
public IActionResult Index()
{
return View();
}
// /HelloWorld/Welcome
public IActionResult Welcome(string name, int numTimes = 1)
{
ViewData["Message"] = $"こんにちは!{name}さん";
ViewData["NumTimes"] = numTimes;
return View();
}
}
}
nameとnumTimes変数をそれぞれViewDataに格納することで、ビューに渡すことができます。
ViewDataとは
ViewDataはすべての型を格納できる動的な辞書型オブジェクトで、実行時に型が決まるため「弱い型指定」と呼ばれます。
一方で、第2回で紹介するビューモデルは、コンパイル時に型チェックが行われるため「厳密な型指定」と呼ばれます。
弱い型指定のデータはビューモデルに比べて便利なものの、型チェックができない分エラーが発生しやすいといったデメリットがあるため、多用するのは避けるべきです。
より詳細な使い方等は下記ドキュメントを参照してください。
続いてViews/HelloWorldフォルダ内にWelcome.cshtmlを新規追加し、次のように書き換えます。
@{
ViewData["Title"] = "Welcome";
}
<h2>Welcome</h2>
<ul>
@for (int i = 0; i < (int)ViewData["NumTimes"]; i++)
{
<li>@ViewData["Message"]</li>
}
</ul>
コントローラーから渡されたViewDataの中身をループで取り出し、リストで表示しています。
実行後に、/HelloWorld/Welcome?name=Taro&numtimes=4
というアドレスにアクセスしてみると、ViewData[“Message”]の内容がnumTimes分だけ繰り返されて表示されます。
以上、プロジェクトの作成からビューの追加までを行いました。
第2回では、ViewDataではなくビューモデルを使ってデータを渡す方法について見ていきたいと思います。
【おすすめ書籍】