記事内に広告が含まれています

ASP.NET Core MVC のチュートリアルを丁寧にやってみた①(プロジェクトの作成からビューの追加まで)

C#

ASP.NET Core MVC を初めて触る人でもスムーズに理解できるように、公式チュートリアルをより丁寧に解説してみました。(全4回)

チュートリアルで少しわかりにくい表現をかみ砕いて説明したり、英語圏向けの表記を日本語向けに直したりしています。また、冗長と思われる部分を省いています。

第1回では、プロジェクトの作成から、コントローラーとビューの基本的な使い方までを見ていきます。(公式チュートリアルのパート1~3に対応)

ASP.NET Core MVC の概要
ASP.NET Core MVC の概要について説明します。

本記事の対象

  • 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 の型に変換して、コントローラーのアクションメソッドに提供する仕組みです。

今回の処理の流れを簡単に表すとこのようになります。

  1. URLでリクエストを受信
  2. アクションメソッドの最初の引数(今回はnameというstring型の変数)を検索
  3. クエリ文字列内のname=Taroを見つける
  4. 次の引数(ageというint型)を検索
  5. クエリ文字列内のage=20を取得
  6. 文字列の”20″を数値の20に変換
  7. Welcomeメソッドが呼ばれて引数に取得した値が渡される
ASP.NET Core でのモデル バインド
ASP.NET Core でのモデル バインドのしくみと、その動作のカスタマイズ方法について説明します。

最後に、先ほど説明していなかったパラメーターの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回で紹介するビューモデルは、コンパイル時に型チェックが行われるため「厳密な型指定」と呼ばれます。

弱い型指定のデータはビューモデルに比べて便利なものの、型チェックができない分エラーが発生しやすいといったデメリットがあるため、多用するのは避けるべきです。

より詳細な使い方等は下記ドキュメントを参照してください。

ASP.NET Core MVC のビュー
ビューがアプリのデータ表示と、ASP.NET Core MVC でのユーザー操作を処理する方法について説明します。

続いて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ではなくビューモデルを使ってデータを渡す方法について見ていきたいと思います。

【おすすめ書籍】

コメント

  1. かと より:

    これはありがたい・・・

    .NET6がリリースされたので、15年ぶりに色試してみようと思ったのですが、いろいろ拡張されまくってまって、どこから手を付けたらいいのか途方に暮れていたところでした。

    まずはひらひらさんのわかりやすいチュートリアルから手をつけてみようと思います。

    感謝してます。
    ありがとうございました!

    • ひらひら より:

      コメントありがとうございます。
      15年前とは相当仕様が変わってしまっていますよね…
      私もまだまだ勉強中の身ですが、そう言っていただけるととても励みになります!
      いずれは.NET6に対応した記事も書いていきたいと思いますので、その際もぜひ参考にしていただけたら嬉しいです。

タイトルとURLをコピーしました