パート 3、ASP.NET Core MVC アプリへのビューの追加

Note

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の ASP.NET Core 8.0 バージョンを参照してください。

作成者: Rick Anderson

このセクションでは、Razor ビュー ファイルを使用するよう、HelloWorldController クラスを変更します。 これにより、クライアントへの HTML 応答を生成するプロセスが完全にカプセル化されます。

ビュー テンプレートは Razor を使用して作成されます。 Razor ベースのビュー テンプレートは次のとおりです。

  • .cshtml ファイル拡張子を含めます。
  • C# を使用して HTML 出力を作成する洗練された方法が提供されます。

現在、Index メソッドは、コントローラー クラスにメッセージを含め文字列を返します。 HelloWorldController クラスでは、Index メソッドを次のコードで置き換えます。

public IActionResult Index()
{
    return View();
}

上記のコードでは、次のことが行われます。

  • コントローラーの View メソッドを呼び出します。
  • ビュー テンプレートを使用し HTML 応答を生成します。

コントローラー メソッドは次のとおりです。

  • "アクション メソッド" と呼ばれます。 たとえば、前のコードの Index アクション メソッドです。
  • 通常、string のような型ではなく IActionResult または ActionResult から派生したクラスを返します。

ビューを追加する

Views フォルダーを右クリックし、[追加] > [新しいフォルダー] の順に選択して、フォルダーに HelloWorld という名前を付けます。

Views/HelloWorld フォルダーを右クリックし、[追加] > [新しい項目] の順に選択します。

[新しい項目の追加] ダイアログで、[すべてのテンプレートの表示] を選びます。

[新しい項目の追加 - MvcMovie] ダイアログで次を行います。

  • 右上の検索ボックスに「view」と入力します。
  • [Razor ビュー - 空] を選択します。
  • [名前] ボックスの値、Index.cshtml を維持します。
  • [追加] を選択します。

Add New Item dialog

Views/HelloWorld/Index.cshtmlRazor ビュー ファイルの内容を次に置き換えます。

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>Hello from our View Template!</p>

次のように https://localhost:{PORT}/HelloWorld に移動します。

  • HelloWorldControllerIndex メソッドにより、ビュー テンプレート ファイルを使用して、ブラウザーに応答をレンダリングするようメソッドを指定する、ステートメント return View(); が実行されました。

  • ビュー テンプレートのファイル名が指定されていないため、MVC で既定のビュー ファイルが使われました。 ビュー ファイル名を指定しない場合は、既定のビューが返されます。 この例では、既定のビューの名前は、アクション メソッド Index と同じ名前が付けられています。 ビュー テンプレート /Views/HelloWorld/Index.cshtml が使用されています。

  • 次の画像は、ビューにハードコーディングされた "Hello from our View Template!" という文字列を示しています。

    Browser window

ビューとレイアウト ページを変更する

MvcMovieHomePrivacy の各メニューのリンクを選択します。 各ページには同じメニューのレイアウトが表示されます。 メニューのレイアウトは、Views/Shared/_Layout.cshtml ファイルに実装されています。

Views/Shared/_Layout.cshtml ファイルを開きます。

レイアウト テンプレートでは、次のことが可能です。

  • 1 か所でサイトの HTML コンテナー レイアウトを指定できます。
  • サイト内の複数のページに HTML コンテナー レイアウトを適用できます。

@RenderBody() という行を見つけます。 RenderBody は、作成したビュー固有のページがすべて表示されるプレースホルダーで、レイアウト ページにラップされます。 たとえば、Privacy リンクを選択すると、Views/Home/Privacy.cshtml ビューが RenderBody メソッド内でレンダリングされます。

Views/Shared/_Layout.cshtml ファイルの内容を次のマークアップに置き換えます。 変更が強調表示されています。

<!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.css" />
    <link rel="stylesheet" href="~/css/site.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">
            &copy; 2023 - Movie App - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

上記のマークアップでは、次の変更が加えられています。

  • MvcMovie から Movie App が 3 回発生します。
  • アンカー要素 <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">MvcMovie</a><a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a> になりました。

このアプリでは区分を使用していないため、上記のマークアップでは、asp-area=""アンカー タグ ヘルパー属性と属性値が省略されています。

注: まだ Movies コントローラーは実装されていません。 この時点では、Movie App リンクは機能していません。

変更を保存し、 Privacy リンクを選択します。 ブラウザー タブのタイトルの表示が、Privacy Policy - MvcMovie ではなく、Privacy Policy - Movie App になっていることに注目してください。

Privacy tab

[Home](ログの一元化) リンクを選択します。

タイトルとアンカー テキストに Movie App と表示されていることに着目してください。 レイアウト テンプレートが一度変更され、この新しいリンク テキストと新しいタイトルがサイト上のすべてのページに反映されました。

Views/_ViewStart.cshtml ファイルを調べます。

@{
    Layout = "_Layout";
}

Views/_ViewStart.cshtml ファイルは Views/Shared/_Layout.cshtml ファイルに取り込まれ、各ビューに適用されます。 Layout プロパティを使用すれば、別のレイアウト ビューを設定することも、null に設定してレイアウト ファイルが使用されないようにすることもできます。

Views/HelloWorld/Index.cshtml ビュー ファイルを開きます。

タイトルと <h2> 要素を、以下の強調表示どおりに変更します。

@{
    ViewData["Title"] = "Movie List";
}

<h2>My Movie List</h2>

<p>Hello from our View Template!</p>

タイトルと <h2> 要素は若干違うので、コードのどの部分によって表示が変更されるのかを確認できます。

上のコードの ViewData["Title"] = "Movie List"; では、Title ディクショナリの ViewData プロパティを "Movie List" に設定します。 Title プロパティは、次のように、レイアウト ページの <title> HTML 要素で使用されます。

<title>@ViewData["Title"] - Movie App</title>

変更内容を保存して、https://localhost:{PORT}/HelloWorld に移動します。

次が変更されたことを確認します。

  • ブラウザー タイトル。
  • 1 番目の見出し。
  • 2 番目の見出し。

ブラウザーに変更がない場合は、キャッシュされたコンテンツが表示されている場合があります。 ブラウザーで Ctrl + F5 キーを押して、サーバーからの応答が強制的に読み込まれるようにします。 ブラウザーのタイトルは、Index.cshtml ビュー テンプレートで設定した ViewData["Title"] で作成されます。レイアウト ファイルには "- Movie App" が追加されます。

Index.cshtml ビュー テンプレート内のコンテンツは、Views/Shared/_Layout.cshtml ビュー テンプレートにマージされます。 1 つの HTML 応答がブラウザーに送信されます。 レイアウト テンプレートを使用すれば、アプリのすべてのページに適用される変更を簡単に行うことができます。 詳細については、「Layout」(レイアウト) を参照してください。

Movie List view

ここでは、"データ" の一部 ("Hello from our View Template!" というメッセージ) がハードコーディングされています。 MVC アプリケーションには "V" (ビュー) があり、"C" (コントローラー) もありますが、"M" (モデル) はまだありません。

コントローラーからビューへのデータの受け渡し

コントローラー アクションは、受信 URL 要求への応答として呼び出されます。 コントローラー クラスでは、受信ブラウザー要求を処理するコードが記述されます。 コントローラーはデータ ソースからデータを取得し、ブラウザーに返す応答の種類を決定します。 ビュー テンプレートを使用すれば、コントローラーからブラウザーへの HTML 応答を生成して書式を設定できます。

コントローラーは、ビュー テンプレートで応答をレンダリングするために必要なデータを提供します。

ビュー テンプレートでは、次を行いません

  • ビジネス ロジックの実行
  • データベースとの直接のやりとり

ビュー テンプレートは、コントローラーから提供されるデータのみを処理する必要があります。 この "関心の分離" を維持すれば、コードを次のように保つことができます。

  • クリーン。
  • テスト可能。
  • 保守しやすい。

現時点では、HelloWorldController クラスの Welcome メソッドは nameID パラメーターを受け取ってから、ブラウザーに直接値を出力します。

コントローラーにこの応答を文字列としてレンダリングさせるのではなく、代わりにビュー テンプレートを使用するようにコントローラーを変更します。 このビュー テンプレートは、応答を動的に生成します。つまり、応答の生成には、コントローラーからビューに適切なデータが渡される必要があります。 そのためには、ビュー テンプレートが必要とする動的データ (パラメーター) を、コントローラーに ViewData ディレクトリに配置させます。 これにより、ビュー テンプレートが動的データにアクセスできるようになります。

HelloWorldController.cs 内で、Welcome メソッドを変更して Message および NumTimes 値を ViewData ディクショナリに追加します。

ViewData ディクショナリは、すべての型を使用できる動的なオブジェクトです。 ViewData オブジェクトには、何かが追加されるまで、プロパティは定義されていません。 MVC のモデル バインド システムは、namenumTimes の名前付きパラメーターをクエリ文字列からメソッドのパラメーターに自動的にマップします。 完全な HelloWorldController は次のとおりです。

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers;

public class HelloWorldController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
    public IActionResult Welcome(string name, int numTimes = 1)
    {
        ViewData["Message"] = "Hello " + name;
        ViewData["NumTimes"] = numTimes;
        return View();
    }
}

ViewData ディクショナリ オブジェクトには、ビューに渡されるデータが含まれています。

Views/HelloWorld/Welcome.cshtml という名前の [ようこそ] ビュー テンプレートを作成します。

"Hello" NumTimes を表示する Welcome.cshtml ビュー テンプレートにループを作成します。 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>

変更内容を保存し、次の URL を参照します。

https://localhost:{PORT}/HelloWorld/Welcome?name=Rick&numtimes=4

データは URL から取得され、MVC モデル バインダーを使用してコントローラーに渡されます。 コントローラーはデータを ViewData ディクショナリにパッケージ化し、そのオブジェクトをビューに渡します。 その後、ビューでブラウザーに HTML としてデータがレンダリングされます。

Privacy view showing a Welcome label and the phrase Hello Rick shown four times

上のサンプルでは、ViewData ディクショナリを使用して、コントローラーからビューにデータを渡しました。 チュートリアルの後半では、ビュー モデルを使用して、コントローラーからビューにデータを渡します。 データを渡すには、ViewData ディクショナリを使用する方法より、ビュー モデルを使用する方法が推奨されます。

次のチュートリアルでは、ムービーのデータベースを作成します。

このセクションでは、Razor ビュー ファイルを使用するよう、HelloWorldController クラスを変更します。 これにより、クライアントへの HTML 応答を生成するプロセスが完全にカプセル化されます。

ビュー テンプレートは Razor を使用して作成されます。 Razor ベースのビュー テンプレートは次のとおりです。

  • .cshtml ファイル拡張子を含めます。
  • C# を使用して HTML 出力を作成する洗練された方法が提供されます。

現在、Index メソッドは、コントローラー クラスにメッセージを含め文字列を返します。 HelloWorldController クラスでは、Index メソッドを次のコードで置き換えます。

public IActionResult Index()
{
    return View();
}

上記のコードでは、次のことが行われます。

  • コントローラーの View メソッドを呼び出します。
  • ビュー テンプレートを使用し HTML 応答を生成します。

コントローラー メソッドは次のとおりです。

  • "アクション メソッド" と呼ばれます。 たとえば、前のコードの Index アクション メソッドです。
  • 通常、string のような型ではなく IActionResult または ActionResult から派生したクラスを返します。

ビューを追加する

Views フォルダーを右クリックし、[追加] > [新しいフォルダー] の順に選択して、フォルダーに HelloWorld という名前を付けます。

Views/HelloWorld フォルダーを右クリックし、[追加] > [新しい項目] の順に選択します。

[新しい項目の追加 - MvcMovie] ダイアログで次を行います。

  • 右上の検索ボックスに「view」と入力します。
  • [Razor ビュー - 空] を選択します。
  • [名前] ボックスの値、Index.cshtml を維持します。
  • [追加] を選択します。

Add New Item dialog

Views/HelloWorld/Index.cshtmlRazor ビュー ファイルの内容を次に置き換えます。

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>Hello from our View Template!</p>

次のように https://localhost:{PORT}/HelloWorld に移動します。

  • HelloWorldControllerIndex メソッドにより、ビュー テンプレート ファイルを使用して、ブラウザーに応答をレンダリングするようメソッドを指定する、ステートメント return View(); が実行されました。

  • ビュー テンプレートのファイル名が指定されていないため、MVC で既定のビュー ファイルが使われました。 ビュー ファイル名を指定しない場合は、既定のビューが返されます。 この例では、既定のビューの名前は、アクション メソッド Index と同じ名前が付けられています。 ビュー テンプレート /Views/HelloWorld/Index.cshtml が使用されています。

  • 次の画像は、ビューにハードコーディングされた "Hello from our View Template!" という文字列を示しています。

    Browser window

ビューとレイアウト ページを変更する

MvcMovieHomePrivacy の各メニューのリンクを選択します。 各ページには同じメニューのレイアウトが表示されます。 メニューのレイアウトは、Views/Shared/_Layout.cshtml ファイルに実装されています。

Views/Shared/_Layout.cshtml ファイルを開きます。

レイアウト テンプレートでは、次のことが可能です。

  • 1 か所でサイトの HTML コンテナー レイアウトを指定できます。
  • サイト内の複数のページに HTML コンテナー レイアウトを適用できます。

@RenderBody() という行を見つけます。 RenderBody は、作成したビュー固有のページがすべて表示されるプレースホルダーで、レイアウト ページにラップされます。 たとえば、Privacy リンクを選択すると、Views/Home/Privacy.cshtml ビューが RenderBody メソッド内でレンダリングされます。

Views/Shared/_Layout.cshtml ファイルの内容を次のマークアップに置き換えます。 変更が強調表示されています。

<!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.css" />
    <link rel="stylesheet" href="~/css/site.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">
            &copy; 2022 - Movie App - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

上記のマークアップでは、次の変更が加えられています。

  • MvcMovie から Movie App が 3 回発生します。
  • アンカー要素 <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">MvcMovie</a><a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a> になりました。

このアプリでは区分を使用していないため、上記のマークアップでは、asp-area=""アンカー タグ ヘルパー属性と属性値が省略されています。

注: まだ Movies コントローラーは実装されていません。 この時点では、Movie App リンクは機能していません。

変更を保存し、 Privacy リンクを選択します。 ブラウザー タブのタイトルの表示が、Privacy Policy - MvcMovie ではなく、Privacy Policy - Movie App になっていることに注目してください。

Privacy tab

[Home](ログの一元化) リンクを選択します。

タイトルとアンカー テキストに Movie App と表示されていることに着目してください。 レイアウト テンプレートが一度変更され、この新しいリンク テキストと新しいタイトルがサイト上のすべてのページに反映されました。

Views/_ViewStart.cshtml ファイルを調べます。

@{
    Layout = "_Layout";
}

Views/_ViewStart.cshtml ファイルは Views/Shared/_Layout.cshtml ファイルに取り込まれ、各ビューに適用されます。 Layout プロパティを使用すれば、別のレイアウト ビューを設定することも、null に設定してレイアウト ファイルが使用されないようにすることもできます。

Views/HelloWorld/Index.cshtml ビュー ファイルを開きます。

タイトルと <h2> 要素を、以下の強調表示どおりに変更します。

@{
    ViewData["Title"] = "Movie List";
}

<h2>My Movie List</h2>

<p>Hello from our View Template!</p>

タイトルと <h2> 要素は若干違うので、コードのどの部分によって表示が変更されるのかを確認できます。

上のコードの ViewData["Title"] = "Movie List"; では、Title ディクショナリの ViewData プロパティを "Movie List" に設定します。 Title プロパティは、次のように、レイアウト ページの <title> HTML 要素で使用されます。

<title>@ViewData["Title"] - Movie App</title>

変更内容を保存して、https://localhost:{PORT}/HelloWorld に移動します。

次が変更されたことを確認します。

  • ブラウザー タイトル。
  • 1 番目の見出し。
  • 2 番目の見出し。

ブラウザーに変更がない場合は、キャッシュされたコンテンツが表示されている場合があります。 ブラウザーで Ctrl + F5 キーを押して、サーバーからの応答が強制的に読み込まれるようにします。 ブラウザーのタイトルは、Index.cshtml ビュー テンプレートで設定した ViewData["Title"] で作成されます。レイアウト ファイルには "- Movie App" が追加されます。

Index.cshtml ビュー テンプレート内のコンテンツは、Views/Shared/_Layout.cshtml ビュー テンプレートにマージされます。 1 つの HTML 応答がブラウザーに送信されます。 レイアウト テンプレートを使用すれば、アプリのすべてのページに適用される変更を簡単に行うことができます。 詳細については、「Layout」(レイアウト) を参照してください。

Movie List view

ここでは、"データ" の一部 ("Hello from our View Template!" というメッセージ) がハードコーディングされています。 MVC アプリケーションには "V" (ビュー) があり、"C" (コントローラー) もありますが、"M" (モデル) はまだありません。

コントローラーからビューへのデータの受け渡し

コントローラー アクションは、受信 URL 要求への応答として呼び出されます。 コントローラー クラスでは、受信ブラウザー要求を処理するコードが記述されます。 コントローラーはデータ ソースからデータを取得し、ブラウザーに返す応答の種類を決定します。 ビュー テンプレートを使用すれば、コントローラーからブラウザーへの HTML 応答を生成して書式を設定できます。

コントローラーは、ビュー テンプレートで応答をレンダリングするために必要なデータを提供します。

ビュー テンプレートでは、次を行いません

  • ビジネス ロジックの実行
  • データベースとの直接のやりとり

ビュー テンプレートは、コントローラーから提供されるデータのみを処理する必要があります。 この "関心の分離" を維持すれば、コードを次のように保つことができます。

  • クリーン。
  • テスト可能。
  • 保守しやすい。

現時点では、HelloWorldController クラスの Welcome メソッドは nameID パラメーターを受け取ってから、ブラウザーに直接値を出力します。

コントローラーにこの応答を文字列としてレンダリングさせるのではなく、代わりにビュー テンプレートを使用するようにコントローラーを変更します。 このビュー テンプレートは、応答を動的に生成します。つまり、応答の生成には、コントローラーからビューに適切なデータが渡される必要があります。 そのためには、ビュー テンプレートが必要とする動的データ (パラメーター) を、コントローラーに ViewData ディレクトリに配置させます。 これにより、ビュー テンプレートが動的データにアクセスできるようになります。

HelloWorldController.cs 内で、Welcome メソッドを変更して Message および NumTimes 値を ViewData ディクショナリに追加します。

ViewData ディクショナリは、すべての型を使用できる動的なオブジェクトです。 ViewData オブジェクトには、何かが追加されるまで、プロパティは定義されていません。 MVC のモデル バインド システムは、namenumTimes の名前付きパラメーターをクエリ文字列からメソッドのパラメーターに自動的にマップします。 完全な HelloWorldController は次のとおりです。

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers;

public class HelloWorldController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
    public IActionResult Welcome(string name, int numTimes = 1)
    {
        ViewData["Message"] = "Hello " + name;
        ViewData["NumTimes"] = numTimes;
        return View();
    }
}

ViewData ディクショナリ オブジェクトには、ビューに渡されるデータが含まれています。

Views/HelloWorld/Welcome.cshtml という名前の [ようこそ] ビュー テンプレートを作成します。

"Hello" NumTimes を表示する Welcome.cshtml ビュー テンプレートにループを作成します。 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>

変更内容を保存し、次の URL を参照します。

https://localhost:{PORT}/HelloWorld/Welcome?name=Rick&numtimes=4

データは URL から取得され、MVC モデル バインダーを使用してコントローラーに渡されます。 コントローラーはデータを ViewData ディクショナリにパッケージ化し、そのオブジェクトをビューに渡します。 その後、ビューでブラウザーに HTML としてデータがレンダリングされます。

Privacy view showing a Welcome label and the phrase Hello Rick shown four times

上のサンプルでは、ViewData ディクショナリを使用して、コントローラーからビューにデータを渡しました。 チュートリアルの後半では、ビュー モデルを使用して、コントローラーからビューにデータを渡します。 データを渡すには、ViewData ディクショナリを使用する方法より、ビュー モデルを使用する方法が推奨されます。

次のチュートリアルでは、ムービーのデータベースを作成します。

このセクションでは、Razor ビュー ファイルを使用するよう、HelloWorldController クラスを変更します。 これにより、クライアントへの HTML 応答を生成するプロセスが完全にカプセル化されます。

ビュー テンプレートは Razor を使用して作成されます。 Razor ベースのビュー テンプレートは次のとおりです。

  • .cshtml ファイル拡張子を含めます。
  • C# を使用して HTML 出力を作成する洗練された方法が提供されます。

現在、Index メソッドは、コントローラー クラスにメッセージを含め文字列を返します。 HelloWorldController クラスでは、Index メソッドを次のコードで置き換えます。

public IActionResult Index()
{
    return View();
}

上記のコードでは、次のことが行われます。

  • コントローラーの View メソッドを呼び出します。
  • ビュー テンプレートを使用し HTML 応答を生成します。

コントローラー メソッドは次のとおりです。

  • "アクション メソッド" と呼ばれます。 たとえば、前のコードの Index アクション メソッドです。
  • 通常、string のような型ではなく IActionResult または ActionResult から派生したクラスを返します。

ビューを追加する

Views フォルダーを右クリックし、[追加] > [新しいフォルダー] の順に選択して、フォルダーに HelloWorld という名前を付けます。

Views/HelloWorld フォルダーを右クリックし、[追加] > [新しい項目] の順に選択します。

[新しい項目の追加 - MvcMovie] ダイアログで次を行います。

  • 右上の検索ボックスに「view」と入力します。
  • [Razor ビュー - 空] を選択します。
  • [名前] ボックスの値、Index.cshtml を維持します。
  • [追加] を選択します。

Add New Item dialog

Views/HelloWorld/Index.cshtmlRazor ビュー ファイルの内容を次に置き換えます。

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>Hello from our View Template!</p>

次のように https://localhost:{PORT}/HelloWorld に移動します。

  • HelloWorldControllerIndex メソッドにより、ビュー テンプレート ファイルを使用して、ブラウザーに応答をレンダリングするようメソッドを指定する、ステートメント return View(); が実行されました。

  • ビュー テンプレートのファイル名が指定されていないため、MVC で既定のビュー ファイルが使われました。 ビュー ファイル名を指定しない場合は、既定のビューが返されます。 この例では、既定のビューの名前は、アクション メソッド Index と同じ名前が付けられています。 ビュー テンプレート /Views/HelloWorld/Index.cshtml が使用されています。

  • 次の画像は、ビューにハードコーディングされた "Hello from our View Template!" という文字列を示しています。

    Browser window

ビューとレイアウト ページを変更する

MvcMovieHomePrivacy の各メニューのリンクを選択します。 各ページには同じメニューのレイアウトが表示されます。 メニューのレイアウトは、Views/Shared/_Layout.cshtml ファイルに実装されています。

Views/Shared/_Layout.cshtml ファイルを開きます。

レイアウト テンプレートでは、次のことが可能です。

  • 1 か所でサイトの HTML コンテナー レイアウトを指定できます。
  • サイト内の複数のページに HTML コンテナー レイアウトを適用できます。

@RenderBody() という行を見つけます。 RenderBody は、作成したビュー固有のページがすべて表示されるプレースホルダーで、レイアウト ページにラップされます。 たとえば、Privacy リンクを選択すると、Views/Home/Privacy.cshtml ビューが RenderBody メソッド内でレンダリングされます。

Views/Shared/_Layout.cshtml ファイルの内容を次のマークアップに置き換えます。 変更が強調表示されています。

<!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.css" />
    <link rel="stylesheet" href="~/css/site.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">
            &copy; 2021 - Movie App - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

上記のマークアップでは、次の変更が加えられています。

  • MvcMovie から Movie App が 3 回発生します。
  • アンカー要素 <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">MvcMovie</a><a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a> になりました。

このアプリでは区分を使用していないため、上記のマークアップでは、asp-area=""アンカー タグ ヘルパー属性と属性値が省略されています。

注: まだ Movies コントローラーは実装されていません。 この時点では、Movie App リンクは機能していません。

変更を保存し、 Privacy リンクを選択します。 ブラウザー タブのタイトルの表示が、Privacy Policy - MvcMovie ではなく、Privacy Policy - Movie App になっていることに注目してください。

Privacy tab

[Home](ログの一元化) リンクを選択します。

タイトルとアンカー テキストに Movie App と表示されていることに着目してください。 レイアウト テンプレートが一度変更され、この新しいリンク テキストと新しいタイトルがサイト上のすべてのページに反映されました。

Views/_ViewStart.cshtml ファイルを調べます。

@{
    Layout = "_Layout";
}

Views/_ViewStart.cshtml ファイルは Views/Shared/_Layout.cshtml ファイルに取り込まれ、各ビューに適用されます。 Layout プロパティを使用すれば、別のレイアウト ビューを設定することも、null に設定してレイアウト ファイルが使用されないようにすることもできます。

Views/HelloWorld/Index.cshtml ビュー ファイルを開きます。

タイトルと <h2> 要素を、以下の強調表示どおりに変更します。

@{
    ViewData["Title"] = "Movie List";
}

<h2>My Movie List</h2>

<p>Hello from our View Template!</p>

タイトルと <h2> 要素は若干違うので、コードのどの部分によって表示が変更されるのかを確認できます。

上のコードの ViewData["Title"] = "Movie List"; では、Title ディクショナリの ViewData プロパティを "Movie List" に設定します。 Title プロパティは、次のように、レイアウト ページの <title> HTML 要素で使用されます。

<title>@ViewData["Title"] - Movie App</title>

変更内容を保存して、https://localhost:{PORT}/HelloWorld に移動します。

次が変更されたことを確認します。

  • ブラウザー タイトル。
  • 1 番目の見出し。
  • 2 番目の見出し。

ブラウザーに変更がない場合は、キャッシュされたコンテンツが表示されている場合があります。 ブラウザーで Ctrl + F5 キーを押して、サーバーからの応答が強制的に読み込まれるようにします。 ブラウザーのタイトルは、Index.cshtml ビュー テンプレートで設定した ViewData["Title"] で作成されます。レイアウト ファイルには "- Movie App" が追加されます。

Index.cshtml ビュー テンプレート内のコンテンツは、Views/Shared/_Layout.cshtml ビュー テンプレートにマージされます。 1 つの HTML 応答がブラウザーに送信されます。 レイアウト テンプレートを使用すれば、アプリのすべてのページに適用される変更を簡単に行うことができます。 詳細については、「Layout」(レイアウト) を参照してください。

Movie List view

ここでは、"データ" の一部 ("Hello from our View Template!" というメッセージ) がハードコーディングされています。 MVC アプリケーションには "V" (ビュー) があり、"C" (コントローラー) もありますが、"M" (モデル) はまだありません。

コントローラーからビューへのデータの受け渡し

コントローラー アクションは、受信 URL 要求への応答として呼び出されます。 コントローラー クラスでは、受信ブラウザー要求を処理するコードが記述されます。 コントローラーはデータ ソースからデータを取得し、ブラウザーに返す応答の種類を決定します。 ビュー テンプレートを使用すれば、コントローラーからブラウザーへの HTML 応答を生成して書式を設定できます。

コントローラーは、ビュー テンプレートで応答をレンダリングするために必要なデータを提供します。

ビュー テンプレートでは、次を行いません

  • ビジネス ロジックの実行
  • データベースとの直接のやりとり

ビュー テンプレートは、コントローラーから提供されるデータのみを処理する必要があります。 この "関心の分離" を維持すれば、コードを次のように保つことができます。

  • クリーン。
  • テスト可能。
  • 保守しやすい。

現時点では、HelloWorldController クラスの Welcome メソッドは nameID パラメーターを受け取ってから、ブラウザーに直接値を出力します。

コントローラーにこの応答を文字列としてレンダリングさせるのではなく、代わりにビュー テンプレートを使用するようにコントローラーを変更します。 このビュー テンプレートは、応答を動的に生成します。つまり、応答の生成には、コントローラーからビューに適切なデータが渡される必要があります。 そのためには、ビュー テンプレートが必要とする動的データ (パラメーター) を、コントローラーに ViewData ディレクトリに配置させます。 これにより、ビュー テンプレートが動的データにアクセスできるようになります。

HelloWorldController.cs 内で、Welcome メソッドを変更して Message および NumTimes 値を ViewData ディクショナリに追加します。

ViewData ディクショナリは、すべての型を使用できる動的なオブジェクトです。 ViewData オブジェクトには、何かが追加されるまで、プロパティは定義されていません。 MVC のモデル バインド システムは、namenumTimes の名前付きパラメーターをクエリ文字列からメソッドのパラメーターに自動的にマップします。 完全な HelloWorldController は次のとおりです。

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers
{
    public class HelloWorldController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Welcome(string name, int numTimes = 1)
        {
            ViewData["Message"] = "Hello " + name;
            ViewData["NumTimes"] = numTimes;

            return View();
        }
    }
}

ViewData ディクショナリ オブジェクトには、ビューに渡されるデータが含まれています。

Views/HelloWorld/Welcome.cshtml という名前の [ようこそ] ビュー テンプレートを作成します。

"Hello" NumTimes を表示する Welcome.cshtml ビュー テンプレートにループを作成します。 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>

変更内容を保存し、次の URL を参照します。

https://localhost:{PORT}/HelloWorld/Welcome?name=Rick&numtimes=4

データは URL から取得され、MVC モデル バインダーを使用してコントローラーに渡されます。 コントローラーはデータを ViewData ディクショナリにパッケージ化し、そのオブジェクトをビューに渡します。 その後、ビューでブラウザーに HTML としてデータがレンダリングされます。

Privacy view showing a Welcome label and the phrase Hello Rick shown four times

上のサンプルでは、ViewData ディクショナリを使用して、コントローラーからビューにデータを渡しました。 チュートリアルの後半では、ビュー モデルを使用して、コントローラーからビューにデータを渡します。 データを渡すには、ViewData ディクショナリを使用する方法より、ビュー モデルを使用する方法が推奨されます。

次のチュートリアルでは、ムービーのデータベースを作成します。

このセクションでは、Razor ビュー ファイルを使用するよう、HelloWorldController クラスを変更します。 これにより、クライアントへの HTML 応答を生成するプロセスが完全にカプセル化されます。

ビュー テンプレートは Razor を使用して作成されます。 Razor ベースのビュー テンプレートは次のとおりです。

  • .cshtml ファイル拡張子を含めます。
  • C# を使用して HTML 出力を作成する洗練された方法が提供されます。

現在、Index メソッドは、コントローラー クラスにメッセージを含め文字列を返します。 HelloWorldController クラスでは、Index メソッドを次のコードで置き換えます。

public IActionResult Index()
{
    return View();
}

上記のコードでは、次のことが行われます。

  • コントローラーの View メソッドを呼び出します。
  • ビュー テンプレートを使用し HTML 応答を生成します。

コントローラー メソッドは次のとおりです。

  • "アクション メソッド" と呼ばれます。 たとえば、前のコードの Index アクション メソッドです。
  • 通常、string のような型ではなく IActionResult または ActionResult から派生したクラスを返します。

ビューを追加する

Views フォルダーを右クリックし、[追加] > [新しいフォルダー] の順に選択して、フォルダーに HelloWorld という名前を付けます。

Views/HelloWorld フォルダーを右クリックし、[追加] > [新しい項目] の順に選択します。

[新しい項目の追加 - MvcMovie] ダイアログで次を行います。

  • 右上の検索ボックスに「view」と入力します。
  • [Razor ビュー - 空] を選択します。
  • [名前] ボックスの値、Index.cshtml を維持します。
  • [追加] を選択します。

Add New Item dialog

Views/HelloWorld/Index.cshtmlRazor ビュー ファイルの内容を次に置き換えます。

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>Hello from our View Template!</p>

次のように https://localhost:{PORT}/HelloWorld に移動します。

  • HelloWorldControllerIndex メソッドにより、ビュー テンプレート ファイルを使用して、ブラウザーに応答をレンダリングするようメソッドを指定する、ステートメント return View(); が実行されました。

  • ビュー テンプレートのファイル名が指定されていないため、MVC で既定のビュー ファイルが使われました。 ビュー ファイル名を指定しない場合は、既定のビューが返されます。 この例では、既定のビューの名前は、アクション メソッド Index と同じ名前が付けられています。 ビュー テンプレート /Views/HelloWorld/Index.cshtml が使用されています。

  • 次の画像は、ビューにハードコーディングされた "Hello from our View Template!" という文字列を示しています。

    Browser window

ビューとレイアウト ページを変更する

MvcMovieHomePrivacy の各メニューのリンクを選択します。 各ページには同じメニューのレイアウトが表示されます。 メニューのレイアウトは、Views/Shared/_Layout.cshtml ファイルに実装されています。

Views/Shared/_Layout.cshtml ファイルを開きます。

レイアウト テンプレートでは、次のことが可能です。

  • 1 か所でサイトの HTML コンテナー レイアウトを指定できます。
  • サイト内の複数のページに HTML コンテナー レイアウトを適用できます。

@RenderBody() という行を見つけます。 RenderBody は、作成したビュー固有のページがすべて表示されるプレースホルダーで、レイアウト ページにラップされます。 たとえば、Privacy リンクを選択すると、Views/Home/Privacy.cshtml ビューが RenderBody メソッド内でレンダリングされます。

Views/Shared/_Layout.cshtml ファイルの内容を次のマークアップに置き換えます。 変更が強調表示されています。

<!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.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</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">
                <a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-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">
            &copy; 2020 - Movie App - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

上記のマークアップでは、次の変更が加えられています。

  • MvcMovie から Movie App が 3 回発生します。
  • アンカー要素 <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">MvcMovie</a><a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a> になりました。

このアプリでは区分を使用していないため、上記のマークアップでは、asp-area=""アンカー タグ ヘルパー属性と属性値が省略されています。

注: まだ Movies コントローラーは実装されていません。 この時点では、Movie App リンクは機能していません。

変更を保存し、 Privacy リンクを選択します。 ブラウザー タブのタイトルの表示が、Privacy Policy - MvcMovie ではなく、Privacy Policy - Movie App になっていることに注目してください。

Privacy tab

[Home](ログの一元化) リンクを選択します。

タイトルとアンカー テキストに Movie App と表示されていることに着目してください。 レイアウト テンプレートが一度変更され、この新しいリンク テキストと新しいタイトルがサイト上のすべてのページに反映されました。

Views/_ViewStart.cshtml ファイルを調べます。

@{
    Layout = "_Layout";
}

Views/_ViewStart.cshtml ファイルは Views/Shared/_Layout.cshtml ファイルに取り込まれ、各ビューに適用されます。 Layout プロパティを使用すれば、別のレイアウト ビューを設定することも、null に設定してレイアウト ファイルが使用されないようにすることもできます。

Views/HelloWorld/Index.cshtml ビュー ファイルを開きます。

タイトルと <h2> 要素を、以下の強調表示どおりに変更します。

@{
    ViewData["Title"] = "Movie List";
}

<h2>My Movie List</h2>

<p>Hello from our View Template!</p>

タイトルと <h2> 要素は若干違うので、コードのどの部分によって表示が変更されるのかを確認できます。

上のコードの ViewData["Title"] = "Movie List"; では、Title ディクショナリの ViewData プロパティを "Movie List" に設定します。 Title プロパティは、次のように、レイアウト ページの <title> HTML 要素で使用されます。

<title>@ViewData["Title"] - Movie App</title>

変更内容を保存して、https://localhost:{PORT}/HelloWorld に移動します。

次が変更されたことを確認します。

  • ブラウザー タイトル。
  • 1 番目の見出し。
  • 2 番目の見出し。

ブラウザーに変更がない場合は、キャッシュされたコンテンツが表示されている場合があります。 ブラウザーで Ctrl + F5 キーを押して、サーバーからの応答が強制的に読み込まれるようにします。 ブラウザーのタイトルは、Index.cshtml ビュー テンプレートで設定した ViewData["Title"] で作成されます。レイアウト ファイルには "- Movie App" が追加されます。

Index.cshtml ビュー テンプレート内のコンテンツは、Views/Shared/_Layout.cshtml ビュー テンプレートにマージされます。 1 つの HTML 応答がブラウザーに送信されます。 レイアウト テンプレートを使用すれば、アプリのすべてのページに適用される変更を簡単に行うことができます。 詳細については、「Layout」(レイアウト) を参照してください。

Movie List view

ここでは、"データ" の一部 ("Hello from our View Template!" というメッセージ) がハードコーディングされています。 MVC アプリケーションには "V" (ビュー) があり、"C" (コントローラー) もありますが、"M" (モデル) はまだありません。

コントローラーからビューへのデータの受け渡し

コントローラー アクションは、受信 URL 要求への応答として呼び出されます。 コントローラー クラスでは、受信ブラウザー要求を処理するコードが記述されます。 コントローラーはデータ ソースからデータを取得し、ブラウザーに返す応答の種類を決定します。 ビュー テンプレートを使用すれば、コントローラーからブラウザーへの HTML 応答を生成して書式を設定できます。

コントローラーは、ビュー テンプレートで応答をレンダリングするために必要なデータを提供します。

ビュー テンプレートでは、次を行いません

  • ビジネス ロジックの実行
  • データベースとの直接のやりとり

ビュー テンプレートは、コントローラーから提供されるデータのみを処理する必要があります。 この "関心の分離" を維持すれば、コードを次のように保つことができます。

  • クリーン。
  • テスト可能。
  • 保守しやすい。

現時点では、HelloWorldController クラスの Welcome メソッドは nameID パラメーターを受け取ってから、ブラウザーに直接値を出力します。

コントローラーにこの応答を文字列としてレンダリングさせるのではなく、代わりにビュー テンプレートを使用するようにコントローラーを変更します。 このビュー テンプレートは、応答を動的に生成します。つまり、応答の生成には、コントローラーからビューに適切なデータが渡される必要があります。 そのためには、ビュー テンプレートが必要とする動的データ (パラメーター) を、コントローラーに ViewData ディレクトリに配置させます。 これにより、ビュー テンプレートが動的データにアクセスできるようになります。

HelloWorldController.cs 内で、Welcome メソッドを変更して Message および NumTimes 値を ViewData ディクショナリに追加します。

ViewData ディクショナリは、すべての型を使用できる動的なオブジェクトです。 ViewData オブジェクトには、何かが追加されるまで、プロパティは定義されていません。 MVC のモデル バインド システムは、namenumTimes の名前付きパラメーターをクエリ文字列からメソッドのパラメーターに自動的にマップします。 完全な HelloWorldController は次のとおりです。

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers
{
    public class HelloWorldController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Welcome(string name, int numTimes = 1)
        {
            ViewData["Message"] = "Hello " + name;
            ViewData["NumTimes"] = numTimes;

            return View();
        }
    }
}

ViewData ディクショナリ オブジェクトには、ビューに渡されるデータが含まれています。

Views/HelloWorld/Welcome.cshtml という名前の [ようこそ] ビュー テンプレートを作成します。

"Hello" NumTimes を表示する Welcome.cshtml ビュー テンプレートにループを作成します。 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>

変更内容を保存し、次の URL を参照します。

https://localhost:{PORT}/HelloWorld/Welcome?name=Rick&numtimes=4

データは URL から取得され、MVC モデル バインダーを使用してコントローラーに渡されます。 コントローラーはデータを ViewData ディクショナリにパッケージ化し、そのオブジェクトをビューに渡します。 その後、ビューでブラウザーに HTML としてデータがレンダリングされます。

Privacy view showing a Welcome label and the phrase Hello Rick shown four times

上のサンプルでは、ViewData ディクショナリを使用して、コントローラーからビューにデータを渡しました。 チュートリアルの後半では、ビュー モデルを使用して、コントローラーからビューにデータを渡します。 データを渡すには、ViewData ディクショナリを使用する方法より、ビュー モデルを使用する方法が推奨されます。

次のチュートリアルでは、ムービーのデータベースを作成します。

このセクションでは、Razor ビュー ファイルを使用するよう、HelloWorldController クラスを変更します。 これにより、クライアントへの HTML 応答を生成するプロセスが完全にカプセル化されます。

ビュー テンプレートは Razor を使用して作成されます。 Razor ベースのビュー テンプレートは次のとおりです。

  • .cshtml ファイル拡張子を含めます。
  • C# を使用して HTML 出力を作成する洗練された方法が提供されます。

現在、Index メソッドは、コントローラー クラスにメッセージを含め文字列を返します。 HelloWorldController クラスでは、Index メソッドを次のコードで置き換えます。

public IActionResult Index()
{
    return View();
}

上記のコードでは、次のことが行われます。

  • コントローラーの View メソッドを呼び出します。
  • ビュー テンプレートを使用し HTML 応答を生成します。

コントローラー メソッドは次のとおりです。

  • "アクション メソッド" と呼ばれます。 たとえば、前のコードの Index アクション メソッドです。
  • 通常、string のような型ではなく IActionResult または ActionResult から派生したクラスを返します。

ビューを追加する

Views フォルダーを右クリックし、[追加] > [新しいフォルダー] の順に選択して、フォルダーに HelloWorld という名前を付けます。

Views/HelloWorld フォルダーを右クリックし、[追加] > [新しい項目] の順に選択します。

[新しい項目の追加 - MvcMovie] ダイアログで次を行います。

  • 右上の検索ボックスに「view」と入力します。
  • [Razor ビュー - 空] を選択します。
  • [名前] ボックスの値、Index.cshtml を維持します。
  • [追加] を選択します。

Add New Item dialog

Views/HelloWorld/Index.cshtmlRazor ビュー ファイルの内容を次に置き換えます。

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>Hello from our View Template!</p>

次のように https://localhost:{PORT}/HelloWorld に移動します。

  • HelloWorldControllerIndex メソッドにより、ビュー テンプレート ファイルを使用して、ブラウザーに応答をレンダリングするようメソッドを指定する、ステートメント return View(); が実行されました。

  • ビュー テンプレートのファイル名が指定されていないため、MVC で既定のビュー ファイルが使われました。 ビュー ファイル名を指定しない場合は、既定のビューが返されます。 この例では、既定のビューの名前は、アクション メソッド Index と同じ名前が付けられています。 ビュー テンプレート /Views/HelloWorld/Index.cshtml が使用されています。

  • 次の画像は、ビューにハードコーディングされた "Hello from our View Template!" という文字列を示しています。

    Browser window

ビューとレイアウト ページを変更する

MvcMovieHomePrivacy の各メニューのリンクを選択します。 各ページには同じメニューのレイアウトが表示されます。 メニューのレイアウトは、Views/Shared/_Layout.cshtml ファイルに実装されています。

Views/Shared/_Layout.cshtml ファイルを開きます。

レイアウト テンプレートでは、次のことが可能です。

  • 1 か所でサイトの HTML コンテナー レイアウトを指定できます。
  • サイト内の複数のページに HTML コンテナー レイアウトを適用できます。

@RenderBody() という行を見つけます。 RenderBody は、作成したビュー固有のページがすべて表示されるプレースホルダーで、レイアウト ページにラップされます。 たとえば、Privacy リンクを選択すると、Views/Home/Privacy.cshtml ビューが RenderBody メソッド内でレンダリングされます。

Views/Shared/_Layout.cshtml ファイルの内容を次のマークアップに置き換えます。 変更が強調表示されています。

<!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.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</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">
                <a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-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 flex-sm-row-reverse">
                    <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">
            &copy; 2020 - Movie App - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @RenderSection("Scripts", required: false)
</body>
</html>

上記のマークアップでは、次の変更が加えられています。

  • MvcMovie から Movie App が 3 回発生します。
  • アンカー要素 <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">MvcMovie</a><a class="navbar-brand" asp-controller="Movies" asp-action="Index">Movie App</a> になりました。

このアプリでは区分を使用していないため、上記のマークアップでは、asp-area=""アンカー タグ ヘルパー属性と属性値が省略されています。

注: まだ Movies コントローラーは実装されていません。 この時点では、Movie App リンクは機能していません。

変更を保存し、 Privacy リンクを選択します。 ブラウザー タブのタイトルの表示が、Privacy Policy - MvcMovie ではなく、Privacy Policy - Movie App になっていることに注目してください。

Privacy tab

[Home](ログの一元化) リンクを選択します。

タイトルとアンカー テキストに Movie App と表示されていることに着目してください。 レイアウト テンプレートが一度変更され、この新しいリンク テキストと新しいタイトルがサイト上のすべてのページに反映されました。

Views/_ViewStart.cshtml ファイルを調べます。

@{
    Layout = "_Layout";
}

Views/_ViewStart.cshtml ファイルは Views/Shared/_Layout.cshtml ファイルに取り込まれ、各ビューに適用されます。 Layout プロパティを使用すれば、別のレイアウト ビューを設定することも、null に設定してレイアウト ファイルが使用されないようにすることもできます。

Views/HelloWorld/Index.cshtml ビュー ファイルを開きます。

タイトルと <h2> 要素を、以下の強調表示どおりに変更します。

@{
    ViewData["Title"] = "Movie List";
}

<h2>My Movie List</h2>

<p>Hello from our View Template!</p>

タイトルと <h2> 要素は若干違うので、コードのどの部分によって表示が変更されるのかを確認できます。

上のコードの ViewData["Title"] = "Movie List"; では、Title ディクショナリの ViewData プロパティを "Movie List" に設定します。 Title プロパティは、次のように、レイアウト ページの <title> HTML 要素で使用されます。

<title>@ViewData["Title"] - Movie App</title>

変更内容を保存して、https://localhost:{PORT}/HelloWorld に移動します。

次が変更されたことを確認します。

  • ブラウザー タイトル。
  • 1 番目の見出し。
  • 2 番目の見出し。

ブラウザーに変更がない場合は、キャッシュされたコンテンツが表示されている場合があります。 ブラウザーで Ctrl + F5 キーを押して、サーバーからの応答が強制的に読み込まれるようにします。 ブラウザーのタイトルは、Index.cshtml ビュー テンプレートで設定した ViewData["Title"] で作成されます。レイアウト ファイルには "- Movie App" が追加されます。

Index.cshtml ビュー テンプレート内のコンテンツは、Views/Shared/_Layout.cshtml ビュー テンプレートにマージされます。 1 つの HTML 応答がブラウザーに送信されます。 レイアウト テンプレートを使用すれば、アプリのすべてのページに適用される変更を簡単に行うことができます。 詳細については、「Layout」(レイアウト) を参照してください。

Movie List view

ここでは、"データ" の一部 ("Hello from our View Template!" というメッセージ) がハードコーディングされています。 MVC アプリケーションには "V" (ビュー) があり、"C" (コントローラー) もありますが、"M" (モデル) はまだありません。

コントローラーからビューへのデータの受け渡し

コントローラー アクションは、受信 URL 要求への応答として呼び出されます。 コントローラー クラスでは、受信ブラウザー要求を処理するコードが記述されます。 コントローラーはデータ ソースからデータを取得し、ブラウザーに返す応答の種類を決定します。 ビュー テンプレートを使用すれば、コントローラーからブラウザーへの HTML 応答を生成して書式を設定できます。

コントローラーは、ビュー テンプレートで応答をレンダリングするために必要なデータを提供します。

ビュー テンプレートでは、次を行いません

  • ビジネス ロジックの実行
  • データベースとの直接のやりとり

ビュー テンプレートは、コントローラーから提供されるデータのみを処理する必要があります。 この "関心の分離" を維持すれば、コードを次のように保つことができます。

  • クリーン。
  • テスト可能。
  • 保守しやすい。

現時点では、HelloWorldController クラスの Welcome メソッドは nameID パラメーターを受け取ってから、ブラウザーに直接値を出力します。

コントローラーにこの応答を文字列としてレンダリングさせるのではなく、代わりにビュー テンプレートを使用するようにコントローラーを変更します。 このビュー テンプレートは、応答を動的に生成します。つまり、応答の生成には、コントローラーからビューに適切なデータが渡される必要があります。 そのためには、ビュー テンプレートが必要とする動的データ (パラメーター) を、コントローラーに ViewData ディレクトリに配置させます。 これにより、ビュー テンプレートが動的データにアクセスできるようになります。

HelloWorldController.cs 内で、Welcome メソッドを変更して Message および NumTimes 値を ViewData ディクショナリに追加します。

ViewData ディクショナリは、すべての型を使用できる動的なオブジェクトです。 ViewData オブジェクトには、何かが追加されるまで、プロパティは定義されていません。 MVC のモデル バインド システムは、namenumTimes の名前付きパラメーターをクエリ文字列からメソッドのパラメーターに自動的にマップします。 完全な HelloWorldController は次のとおりです。

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers
{
    public class HelloWorldController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Welcome(string name, int numTimes = 1)
        {
            ViewData["Message"] = "Hello " + name;
            ViewData["NumTimes"] = numTimes;

            return View();
        }
    }
}

ViewData ディクショナリ オブジェクトには、ビューに渡されるデータが含まれています。

Views/HelloWorld/Welcome.cshtml という名前の [ようこそ] ビュー テンプレートを作成します。

"Hello" NumTimes を表示する Welcome.cshtml ビュー テンプレートにループを作成します。 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>

変更内容を保存し、次の URL を参照します。

https://localhost:{PORT}/HelloWorld/Welcome?name=Rick&numtimes=4

データは URL から取得され、MVC モデル バインダーを使用してコントローラーに渡されます。 コントローラーはデータを ViewData ディクショナリにパッケージ化し、そのオブジェクトをビューに渡します。 その後、ビューでブラウザーに HTML としてデータがレンダリングされます。

Privacy view showing a Welcome label and the phrase Hello Rick shown four times

上のサンプルでは、ViewData ディクショナリを使用して、コントローラーからビューにデータを渡しました。 チュートリアルの後半では、ビュー モデルを使用して、コントローラーからビューにデータを渡します。 データを渡すには、ViewData ディクショナリを使用する方法より、ビュー モデルを使用する方法が推奨されます。

次のチュートリアルでは、ムービーのデータベースを作成します。