次の方法で共有


パート 3、ASP.NET Core でスキャフォールディングされた Razor ページ

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

警告

このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、 .NET および .NET Core サポート ポリシーを参照してください。 現在のリリースについては、 この記事の .NET 10 バージョンを参照してください。

作成者: Rick Anderson

このチュートリアルでは、Razorでスキャフォールディングによって作成された ページについて説明します。

[作成]、[削除]、[詳細]、および [編集] ページ

Pages/Movies/Index.cshtml.cs ページ モデルを調べます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Data;
using RazorPagesMovie.Models;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace RazorPagesMovie.Pages.Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IList<Movie> Movie { get;set; } = default!;

        public async Task OnGetAsync()
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor ページは PageModel から派生します。 慣例により、PageModel 派生クラスには、PageNameModel という名前が付けられます。 たとえば、Index ページには、IndexModel という名前が付けられます。

コンストラクターは依存性の注入を使用して、RazorPagesMovieContext をページに追加します。

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Entity Framework での非同期プログラミングの詳細については、「非同期コード」を参照してください。

ページに対して GET 要求が行われると、OnGetAsync メソッドにより Razor Page にムービーのリストが返されます。 Razor ページでは、OnGetAsync または OnGet が呼び出され、ページの状態が初期化されます。 この場合、OnGetAsync でムービーのリストを取得し、表示します。

OnGet によって void が返される場合、または OnGetAsync によって Task が返される場合は、return ステートメントは使用されません。 たとえば、Privacy ページを調べます。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }

}

戻り値の型が IActionResult または Task<IActionResult> の場合は、return ステートメントを指定する必要があります。 たとえば、Pages/Movies/Create.cshtml.cs OnPostAsync メソッドでは、次の操作を行います。

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Pages/Movies/Index.cshtml Razor ページを調べます。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor では、HTML から C# または Razor 固有のマークアップに移行できます。 @ シンボルの後に Razor で予約済みのキーワードが続いている場合は、Razor 固有のマークアップに移行します。それ以外の場合は、C# に移行します。

@page ディレクティブ

@page Razor ディレクティブを使うと、ファイルが MVC アクションになります。つまり、これで要求を処理できます。 @page はページ上で最初の Razor ディレクティブである必要があります。 @page および @model は、Razor 固有のマークアップへの移行の例です。 詳細については、「Razor の構文」を参照してください。

@model ディレクティブ

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@model ディレクティブは、Razor ページに渡されるモデルの型を指定します。 前の例では、@model 行によって、PageModel ページで Razor 派生クラスが使用できるようになります。 モデルは、ページの @Html.DisplayNameFor および @Html.DisplayForHTML ヘルパーで使用されます。

次の HTML ヘルパーで使用されるラムダ式を確認します。

@Html.DisplayNameFor(model => model.Movie[0].Title)

DisplayNameFor HTML ヘルパーは、ラムダ式で参照される Title プロパティを検査し、表示名を判別します。 ラムダ式は評価されるのではなく検査されます。 これは、modelmodel.Movie、または model.Movie[0]null または空である場合、アクセス違反がないことを意味します。 ラムダ式が (@Html.DisplayFor(modelItem => item.Title) などを使用して) 評価される場合は、モデルのプロパティ値が評価されます。

レイアウト ページ

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

Pages/Shared/_Layout.cshtml ファイルを開いて調べます。

レイアウト テンプレートを使用すると、HTML コンテナーのレイアウトを次のようにすることができます。

  • 1 つの場所で指定される。
  • サイトの複数のページに適用される。

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

ViewData とレイアウト

Pages/Movies/Index.cshtml ファイルの次のマークアップを考えてみます。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

上の強調表示されたマークアップは、Razor の C# への移行例です。 { 文字と } 文字で C# コードのブロックを囲みます。

PageModel 基底クラスには ViewData 辞書プロパティが含まれており、これを使用してビューにデータを渡すことができます。 オブジェクトは、"ViewData" のパターンを使用して、 辞書に追加されます。 上のサンプルでは、Title プロパティが ViewData 辞書に追加されます。

Title プロパティは、Pages/Shared/_Layout.cshtml ファイルで使用されます。 次のマークアップは、 _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"] - RazorPagesMovie</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/RazorPagesMovie.styles.css" asp-append-version="true" />

レイアウトの更新

  1. <title> ではなく、Pages/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</title>
    
  2. Pages/Shared/_Layout.cshtml ファイルで次のアンカー要素を見つけます。

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. 上の要素を次のマークアップに置き換えます。

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    上のアンカー要素はタグ ヘルパーです。 この場合は、アンカー タグ ヘルパーです。 asp-page="/Movies/Index" タグ ヘルパーの属性と値によって、/Movies/IndexRazor ページへのリンクが作成されます。 asp-area 属性の値が空なので、リンクではこの区分が使用されていません。 詳細については、区分に関する記事を参照してください。

  4. 変更内容を保存し、RpMovie リンクを選択してアプリをテストします。 問題がある場合は、GitHub の _Layout.cshtml ファイルを参照してください。

  5. HomeRpMovieCreateEditDelete の各リンクをテストします。 各ページで、ブラウザー タブで表示できるタイトルを設定します。ページをブックマークすると、ブックマークでタイトルが使用されます。

Price フィールドに小数点のコンマを入力できない場合があります。 小数点にコンマ (",") を使い、英語 (米国) 以外の日付形式を使う英語以外のロケールの jQuery 検証をサポートするには、アプリをグローバル化する手順を行う必要があります。 小数点のコンマの追加方法については、こちらの GitHub issue 4076 を参照してください。

Layout プロパティは、Pages/_ViewStart.cshtml ファイルに設定されます。

@{
    Layout = "_Layout";
}

上のマークアップを使用すると、Pages/Shared/_Layout.cshtml フォルダーのすべての Razor ファイルについて、レイアウト ファイルを に設定します。 詳細については、「Layout」 (レイアウト) を参照してください。

Create ページのモデル

Pages/Movies/Create.cshtml.cs ページ モデルを確認します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using RazorPagesMovie.Data;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Movie Movie { get; set; } = default!;

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

OnGet メソッドは、ページに必要な状態を初期化します。 [作成] ページには初期化する状態はないため、Page が返されます。 このチュートリアルの後半では、状態を初期化する OnGet の例を示します。 Page メソッドでは、PageResult ページをレンダリングする Create.cshtml オブジェクトが作成されます。

Movie プロパティでは [BindProperty] 属性を使用して、モデル バインドにオプトインします。 [作成] フォームでフォーム値が投稿されると、ASP.NET Core ランタイムが投稿された値を Movie モデルにバインドします。

OnPostAsync メソッドは、ページでフォーム データが投稿されたときに実行されます。

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

モデル エラーがある場合は、投稿されたフォーム データと共にフォームが再表示されます。 ほとんどのモデル エラーは、フォームが投稿される前にクライアント側でキャッチできます。 モデル エラーの例では、日付に変換できない日付フィールドの値が投稿されています。 クライアント側の検証とモデルの検証については、チュートリアルの後半で説明します。

モデル エラーがない場合は、次のようになります。

  • データが保存されます。
  • ブラウザーは Index ページにリダイレクトされます。

Razor の作成ページ

Pages/Movies/Create.cshtml Razor ページ ファイルを調べます。

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

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

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

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

Visual Studio に、タグ ヘルパーで使用される独特な太字のフォントで次のタグが表示されます。

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Create.cshtml ページの VS ビュー

<form method="post"> 要素はフォーム タグ ヘルパーです。 フォーム タグ ヘルパーには自動的に偽造防止トークンが含まれます。

スキャフォールディング エンジンでは、次のような、(ID を除く) モデルの各フィールドの Razor マークアップが作成されます。

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

検証タグ ヘルパー (<div asp-validation-summary<span asp-validation-for) には検証エラーが表示されます。 検証については、後で詳しく説明します。

ラベル タグ ヘルパー (<label asp-for="Movie.Title" class="control-label"></label>) は、[for] プロパティのラベル キャプションと Title 属性を生成します。

入力タグ ヘルパー (<input asp-for="Movie.Title" class="form-control">) は DataAnnotations 属性を使用し、クライアント側で jQuery 検証に必要な HTML 属性を生成します。

<form method="post"> などのタグ ヘルパーについては、「ASP.NET Core のタグ ヘルパー」を参照してください。

次のステップ

[作成]、[削除]、[詳細]、および [編集] ページ

Pages/Movies/Index.cshtml.cs ページ モデルを調べます。

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies;

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

    public IList<Movie> Movie { get;set; }  = default!;

    public async Task OnGetAsync()
    {
        if (_context.Movie != null)
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor ページは PageModel から派生します。 慣例により、PageModel 派生クラスには、PageNameModel という名前が付けられます。 たとえば、Index ページには、IndexModel という名前が付けられます。

コンストラクターは依存性の注入を使用して、RazorPagesMovieContext をページに追加します。

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Entity Framework での非同期プログラミングの詳細については、「非同期コード」を参照してください。

ページに対して GET 要求が行われると、OnGetAsync メソッドにより Razor Page にムービーのリストが返されます。 Razor ページでは、OnGetAsync または OnGet が呼び出され、ページの状態が初期化されます。 この場合、OnGetAsync でムービーのリストを取得し、表示します。

OnGet によって void が返される場合、または OnGetAsync によって Task が返される場合は、return ステートメントは使用されません。 たとえば、Privacy ページを調べます。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

戻り値の型が IActionResult または Task<IActionResult> の場合は、return ステートメントを指定する必要があります。 たとえば、Pages/Movies/Create.cshtml.cs OnPostAsync メソッドでは、次の操作を行います。

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Pages/Movies/Index.cshtml Razor ページを調べます。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor では、HTML から C# または Razor 固有のマークアップに移行できます。 @ シンボルの後に Razor で予約済みのキーワードが続いている場合は、Razor 固有のマークアップに移行します。それ以外の場合は、C# に移行します。

@page ディレクティブ

@page Razor ディレクティブを使うと、ファイルが MVC アクションになります。つまり、これで要求を処理できます。 @page はページ上で最初の Razor ディレクティブである必要があります。 @page および @model は、Razor 固有のマークアップへの移行の例です。 詳細については、「Razor の構文」を参照してください。

@model ディレクティブ

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@model ディレクティブは、Razor ページに渡されるモデルの型を指定します。 前の例では、@model 行によって、PageModel ページで Razor 派生クラスが使用できるようになります。 モデルは、ページの @Html.DisplayNameFor および @Html.DisplayForHTML ヘルパーで使用されます。

次の HTML ヘルパーで使用されるラムダ式を確認します。

@Html.DisplayNameFor(model => model.Movie[0].Title)

DisplayNameFor HTML ヘルパーは、ラムダ式で参照される Title プロパティを検査し、表示名を判別します。 ラムダ式は評価されるのではなく検査されます。 これは、modelmodel.Movie、または model.Movie[0]null または空である場合、アクセス違反がないことを意味します。 ラムダ式が (@Html.DisplayFor(modelItem => item.Title) などを使用して) 評価される場合は、モデルのプロパティ値が評価されます。

レイアウト ページ

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

Pages/Shared/_Layout.cshtml ファイルを開いて調べます。

レイアウト テンプレートを使用すると、HTML コンテナーのレイアウトを次のようにすることができます。

  • 1 つの場所で指定される。
  • サイトの複数のページに適用される。

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

ViewData とレイアウト

Pages/Movies/Index.cshtml ファイルの次のマークアップを考えてみます。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

上の強調表示されたマークアップは、Razor の C# への移行例です。 { 文字と } 文字で C# コードのブロックを囲みます。

PageModel 基底クラスには ViewData 辞書プロパティが含まれており、これを使用してビューにデータを渡すことができます。 オブジェクトは、"ViewData" のパターンを使用して、 辞書に追加されます。 上のサンプルでは、Title プロパティが ViewData 辞書に追加されます。

Title プロパティは、Pages/Shared/_Layout.cshtml ファイルで使用されます。 次のマークアップは、 _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"] - RazorPagesMovie</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="~/RazorPagesMovie.styles.css" asp-append-version="true" />

レイアウトの更新

  1. <title> ではなく、Pages/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</title>
    
  2. Pages/Shared/_Layout.cshtml ファイルで次のアンカー要素を見つけます。

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. 上の要素を次のマークアップに置き換えます。

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    上のアンカー要素はタグ ヘルパーです。 この場合は、アンカー タグ ヘルパーです。 asp-page="/Movies/Index" タグ ヘルパーの属性と値によって、/Movies/IndexRazor ページへのリンクが作成されます。 asp-area 属性の値が空なので、リンクではこの区分が使用されていません。 詳細については、区分に関する記事を参照してください。

  4. 変更内容を保存し、RpMovie リンクを選択してアプリをテストします。 問題がある場合は、GitHub の _Layout.cshtml ファイルを参照してください。

  5. HomeRpMovieCreateEditDelete の各リンクをテストします。 各ページで、ブラウザー タブで表示できるタイトルを設定します。ページをブックマークすると、ブックマークでタイトルが使用されます。

Price フィールドに小数点のコンマを入力できない場合があります。 小数点にコンマ (",") を使い、英語 (米国) 以外の日付形式を使う英語以外のロケールの jQuery 検証をサポートするには、アプリをグローバル化する手順を行う必要があります。 小数点のコンマの追加方法については、こちらの GitHub issue 4076 を参照してください。

Layout プロパティは、Pages/_ViewStart.cshtml ファイルに設定されます。

@{
    Layout = "_Layout";
}

上のマークアップを使用すると、Pages/Shared/_Layout.cshtml フォルダーのすべての Razor ファイルについて、レイアウト ファイルを に設定します。 詳細については、「Layout」 (レイアウト) を参照してください。

Create ページのモデル

Pages/Movies/Create.cshtml.cs ページ モデルを確認します。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Movie Movie { get; set; } = default!;
        

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.Movie == null || Movie == null)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

OnGet メソッドは、ページに必要な状態を初期化します。 [作成] ページには初期化する状態はないため、Page が返されます。 このチュートリアルの後半では、状態を初期化する OnGet の例を示します。 Page メソッドでは、PageResult ページをレンダリングする Create.cshtml オブジェクトが作成されます。

Movie プロパティでは [BindProperty] 属性を使用して、モデル バインドにオプトインします。 [作成] フォームでフォーム値が投稿されると、ASP.NET Core ランタイムが投稿された値を Movie モデルにバインドします。

OnPostAsync メソッドは、ページでフォーム データが投稿されたときに実行されます。

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

モデル エラーがある場合は、投稿されたフォーム データと共にフォームが再表示されます。 ほとんどのモデル エラーは、フォームが投稿される前にクライアント側でキャッチできます。 モデル エラーの例では、日付に変換できない日付フィールドの値が投稿されています。 クライアント側の検証とモデルの検証については、チュートリアルの後半で説明します。

モデル エラーがない場合は、次のようになります。

  • データが保存されます。
  • ブラウザーは Index ページにリダイレクトされます。

Razor の作成ページ

Pages/Movies/Create.cshtml Razor ページ ファイルを調べます。

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

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

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

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

Visual Studio に、タグ ヘルパーで使用される独特な太字のフォントで次のタグが表示されます。

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Create.cshtml ページの VS17 ビュー

<form method="post"> 要素はフォーム タグ ヘルパーです。 フォーム タグ ヘルパーには自動的に偽造防止トークンが含まれます。

スキャフォールディング エンジンでは、次のような、(ID を除く) モデルの各フィールドの Razor マークアップが作成されます。

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

検証タグ ヘルパー (<div asp-validation-summary<span asp-validation-for) には検証エラーが表示されます。 検証については、後で詳しく説明します。

ラベル タグ ヘルパー (<label asp-for="Movie.Title" class="control-label"></label>) は、[for] プロパティのラベル キャプションと Title 属性を生成します。

入力タグ ヘルパー (<input asp-for="Movie.Title" class="form-control">) は DataAnnotations 属性を使用し、クライアント側で jQuery 検証に必要な HTML 属性を生成します。

<form method="post"> などのタグ ヘルパーについては、「ASP.NET Core のタグ ヘルパー」を参照してください。

次のステップ

[作成]、[削除]、[詳細]、および [編集] ページ

Pages/Movies/Index.cshtml.cs ページ モデルを調べます。

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies;

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

    public IList<Movie> Movie { get;set; }  = default!;

    public async Task OnGetAsync()
    {
        if (_context.Movie != null)
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor ページは PageModel から派生します。 慣例により、PageModel 派生クラスには、PageNameModel という名前が付けられます。 たとえば、Index ページには、IndexModel という名前が付けられます。

コンストラクターは依存性の注入を使用して、RazorPagesMovieContext をページに追加します。

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Entity Framework での非同期プログラミングの詳細については、「非同期コード」を参照してください。

ページに対して GET 要求が行われると、OnGetAsync メソッドにより Razor Page にムービーのリストが返されます。 Razor ページでは、OnGetAsync または OnGet が呼び出され、ページの状態が初期化されます。 この場合、OnGetAsync でムービーのリストを取得し、表示します。

OnGet によって void が返される場合、または OnGetAsync によって Task が返される場合は、return ステートメントは使用されません。 たとえば、Privacy ページを調べます。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

戻り値の型が IActionResult または Task<IActionResult> の場合は、return ステートメントを指定する必要があります。 たとえば、Pages/Movies/Create.cshtml.cs OnPostAsync メソッドでは、次の操作を行います。

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Pages/Movies/Index.cshtml Razor ページを調べます。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor では、HTML から C# または Razor 固有のマークアップに移行できます。 @ シンボルの後に Razor で予約済みのキーワードが続いている場合は、Razor 固有のマークアップに移行します。それ以外の場合は、C# に移行します。

@page ディレクティブ

@page Razor ディレクティブを使うと、ファイルが MVC アクションになります。つまり、これで要求を処理できます。 @page はページ上で最初の Razor ディレクティブである必要があります。 @page および @model は、Razor 固有のマークアップへの移行の例です。 詳細については、「Razor の構文」を参照してください。

@model ディレクティブ

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@model ディレクティブは、Razor ページに渡されるモデルの型を指定します。 前の例では、@model 行によって、PageModel ページで Razor 派生クラスが使用できるようになります。 モデルは、ページの @Html.DisplayNameFor および @Html.DisplayForHTML ヘルパーで使用されます。

次の HTML ヘルパーで使用されるラムダ式を確認します。

@Html.DisplayNameFor(model => model.Movie[0].Title)

DisplayNameFor HTML ヘルパーは、ラムダ式で参照される Title プロパティを検査し、表示名を判別します。 ラムダ式は評価されるのではなく検査されます。 これは、modelmodel.Movie、または model.Movie[0]null または空である場合、アクセス違反がないことを意味します。 ラムダ式が (@Html.DisplayFor(modelItem => item.Title) などを使用して) 評価される場合は、モデルのプロパティ値が評価されます。

レイアウト ページ

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

Pages/Shared/_Layout.cshtml ファイルを開いて調べます。

レイアウト テンプレートを使用すると、HTML コンテナーのレイアウトを次のようにすることができます。

  • 1 つの場所で指定される。
  • サイトの複数のページに適用される。

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

ViewData とレイアウト

Pages/Movies/Index.cshtml ファイルの次のマークアップを考えてみます。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

上の強調表示されたマークアップは、Razor の C# への移行例です。 { 文字と } 文字で C# コードのブロックを囲みます。

PageModel 基底クラスには ViewData 辞書プロパティが含まれており、これを使用してビューにデータを渡すことができます。 オブジェクトは、"ViewData" のパターンを使用して、 辞書に追加されます。 上のサンプルでは、Title プロパティが ViewData 辞書に追加されます。

Title プロパティは、Pages/Shared/_Layout.cshtml ファイルで使用されます。 次のマークアップは、 _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"] - RazorPagesMovie</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="~/RazorPagesMovie.styles.css" asp-append-version="true" />

@*Markup removed for brevity.*@ 行は Razor コメントです。 HTML コメント <!-- --> とは異なり、Razor コメントはクライアントには送信されません。 詳細については、「MDN Web ドキュメント: HTML の概要」をご覧ください。

レイアウトの更新

  1. <title> ではなく、Pages/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</title>
    
  2. Pages/Shared/_Layout.cshtml ファイルで次のアンカー要素を見つけます。

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. 上の要素を次のマークアップに置き換えます。

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    上のアンカー要素はタグ ヘルパーです。 この場合は、アンカー タグ ヘルパーです。 asp-page="/Movies/Index" タグ ヘルパーの属性と値によって、/Movies/IndexRazor ページへのリンクが作成されます。 asp-area 属性の値が空なので、リンクではこの区分が使用されていません。 詳細については、区分に関する記事を参照してください。

  4. 変更内容を保存し、RpMovie リンクを選択してアプリをテストします。 問題がある場合は、GitHub の _Layout.cshtml ファイルを参照してください。

  5. HomeRpMovieCreateEditDelete の各リンクをテストします。 各ページで、ブラウザー タブで表示できるタイトルを設定します。ページをブックマークすると、ブックマークでタイトルが使用されます。

Price フィールドに小数点のコンマを入力できない場合があります。 小数点にコンマ (",") を使い、英語 (米国) 以外の日付形式を使う英語以外のロケールの jQuery 検証をサポートするには、アプリをグローバル化する手順を行う必要があります。 小数点のコンマの追加方法については、こちらの GitHub issue 4076 を参照してください。

Layout プロパティは、Pages/_ViewStart.cshtml ファイルに設定されます。

@{
    Layout = "_Layout";
}

上のマークアップを使用すると、Pages/Shared/_Layout.cshtml フォルダーのすべての Razor ファイルについて、レイアウト ファイルを に設定します。 詳細については、「Layout」 (レイアウト) を参照してください。

Create ページのモデル

Pages/Movies/Create.cshtml.cs ページ モデルを確認します。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Movie Movie { get; set; } = default!;
        

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.Movie == null || Movie == null)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

OnGet メソッドは、ページに必要な状態を初期化します。 [作成] ページには初期化する状態はないため、Page が返されます。 このチュートリアルの後半では、状態を初期化する OnGet の例を示します。 Page メソッドでは、PageResult ページをレンダリングする Create.cshtml オブジェクトが作成されます。

Movie プロパティでは [BindProperty] 属性を使用して、モデル バインドにオプトインします。 [作成] フォームでフォーム値が投稿されると、ASP.NET Core ランタイムが投稿された値を Movie モデルにバインドします。

OnPostAsync メソッドは、ページでフォーム データが投稿されたときに実行されます。

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

モデル エラーがある場合は、投稿されたフォーム データと共にフォームが再表示されます。 ほとんどのモデル エラーは、フォームが投稿される前にクライアント側でキャッチできます。 モデル エラーの例では、日付に変換できない日付フィールドの値が投稿されています。 クライアント側の検証とモデルの検証については、チュートリアルの後半で説明します。

モデル エラーがない場合は、次のようになります。

  • データが保存されます。
  • ブラウザーは Index ページにリダイレクトされます。

Razor の作成ページ

Pages/Movies/Create.cshtml Razor ページ ファイルを調べます。

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

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

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

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

Visual Studio に、タグ ヘルパーで使用される独特な太字のフォントで次のタグが表示されます。

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Create.cshtml ページの VS17 ビュー

<form method="post"> 要素はフォーム タグ ヘルパーです。 フォーム タグ ヘルパーには自動的に偽造防止トークンが含まれます。

スキャフォールディング エンジンでは、次のような、(ID を除く) モデルの各フィールドの Razor マークアップが作成されます。

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

検証タグ ヘルパー (<div asp-validation-summary<span asp-validation-for) には検証エラーが表示されます。 検証については、後で詳しく説明します。

ラベル タグ ヘルパー (<label asp-for="Movie.Title" class="control-label"></label>) は、[for] プロパティのラベル キャプションと Title 属性を生成します。

入力タグ ヘルパー (<input asp-for="Movie.Title" class="form-control">) は DataAnnotations 属性を使用し、クライアント側で jQuery 検証に必要な HTML 属性を生成します。

<form method="post"> などのタグ ヘルパーについては、「ASP.NET Core のタグ ヘルパー」を参照してください。

次のステップ

[作成]、[削除]、[詳細]、および [編集] ページ

Pages/Movies/Index.cshtml.cs ページ モデルを調べます。

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IList<Movie> Movie { get;set; } = default!;

        public async Task OnGetAsync()
        {
            if (_context.Movie != null)
            {
                Movie = await _context.Movie.ToListAsync();
            }
        }
    }
}

Razor ページは PageModel から派生します。 慣例により、PageModel 派生クラスには、PageNameModel という名前が付けられます。 たとえば、Index ページには、IndexModel という名前が付けられます。

コンストラクターは依存性の注入を使用して、RazorPagesMovieContext をページに追加します。

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Entity Framework での非同期プログラミングの詳細については、「非同期コード」を参照してください。

ページに対して要求が行われると、OnGetAsync メソッドは Razor ページにムービーのリストを返します。 Razor ページでは、OnGetAsync または OnGet が呼び出され、ページの状態が初期化されます。 この場合、OnGetAsync でムービーのリストを取得し、表示します。

OnGet によって void が返される場合、または OnGetAsync によって Task が返される場合は、return ステートメントは使用されません。 たとえば、Privacy ページを調べます。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

戻り値の型が IActionResult または Task<IActionResult> の場合は、return ステートメントを指定する必要があります。 たとえば、Pages/Movies/Create.cshtml.csOnPostAsync メソッドでは、次の操作を行います。

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid || _context.Movie == null || Movie == null)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Pages/Movies/Index.cshtml Razor ページを調べます。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor では、HTML から C# または Razor 固有のマークアップに移行できます。 @ シンボルの後に Razor で予約済みのキーワードが続いている場合は、Razor 固有のマークアップに移行します。それ以外の場合は、C# に移行します。

@page ディレクティブ

@page Razor ディレクティブを使うと、ファイルが MVC アクションになります。つまり、これで要求を処理できます。 @page はページ上で最初の Razor ディレクティブである必要があります。 @page および @model は、Razor 固有のマークアップへの移行の例です。 詳細については、「Razor の構文」を参照してください。

@model ディレクティブ

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@model ディレクティブは、Razor ページに渡されるモデルの型を指定します。 前の例では、@model 行によって、PageModel ページで Razor 派生クラスが使用できるようになります。 モデルは、ページの @Html.DisplayNameFor および @Html.DisplayForHTML ヘルパーで使用されます。

次の HTML ヘルパーで使用されるラムダ式を確認します。

@Html.DisplayNameFor(model => model.Movie[0].Title)

DisplayNameFor HTML ヘルパーは、ラムダ式で参照される Title プロパティを検査し、表示名を判別します。 ラムダ式は評価されるのではなく検査されます。 これは、modelmodel.Movie、または model.Movie[0]null または空である場合、アクセス違反がないことを意味します。 ラムダ式が (@Html.DisplayFor(modelItem => item.Title) などを使用して) 評価される場合は、モデルのプロパティ値が評価されます。

レイアウト ページ

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

Pages/Shared/_Layout.cshtml ファイルを開いて調べます。

レイアウト テンプレートを使用すると、HTML コンテナーのレイアウトを次のようにすることができます。

  • 1 つの場所で指定される。
  • サイトの複数のページに適用される。

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

ViewData とレイアウト

Pages/Movies/Index.cshtml ファイルの次のマークアップを考えてみます。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

上の強調表示されたマークアップは、Razor の C# への移行例です。 { 文字と } 文字で C# コードのブロックを囲みます。

PageModel 基底クラスには ViewData 辞書プロパティが含まれており、これを使用してビューにデータを渡すことができます。 オブジェクトは、"ViewData" のパターンを使用して、 辞書に追加されます。 上のサンプルでは、Title プロパティが ViewData 辞書に追加されます。

Title プロパティは、Pages/Shared/_Layout.cshtml ファイルで使用されます。 次のマークアップは、 _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"] - RazorPagesMovie</title>

     @*Markup removed for brevity.*@
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />

@*Markup removed for brevity.*@ 行は Razor コメントです。 HTML コメント <!-- --> とは異なり、Razor コメントはクライアントには送信されません。 詳細については、「MDN Web ドキュメント: HTML の概要」をご覧ください。

レイアウトの更新

  1. <title> ではなく、Pages/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</title>
    
  2. Pages/Shared/_Layout.cshtml ファイルで次のアンカー要素を見つけます。

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. 上の要素を次のマークアップに置き換えます。

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    上のアンカー要素はタグ ヘルパーです。 この場合は、アンカー タグ ヘルパーです。 asp-page="/Movies/Index" タグ ヘルパーの属性と値によって、/Movies/IndexRazor ページへのリンクが作成されます。 asp-area 属性の値が空なので、リンクではこの区分が使用されていません。 詳細については、区分に関する記事を参照してください。

  4. 変更内容を保存し、RpMovie リンクを選択してアプリをテストします。 問題がある場合は、GitHub の _Layout.cshtml ファイルを参照してください。

  5. HomeRpMovieCreateEditDelete の各リンクをテストします。 各ページで、ブラウザー タブで表示できるタイトルを設定します。ページをブックマークすると、ブックマークでタイトルが使用されます。

Price フィールドに小数点のコンマを入力できない場合があります。 小数点にコンマ (",") を使い、英語 (米国) 以外の日付形式を使う英語以外のロケールの jQuery 検証をサポートするには、アプリをグローバル化する手順を行う必要があります。 小数点のコンマの追加方法については、こちらの GitHub issue 4076 を参照してください。

Layout プロパティは、Pages/_ViewStart.cshtml ファイルに設定されます。

@{
    Layout = "_Layout";
}

上のマークアップを使用すると、Pages/Shared/_Layout.cshtml フォルダーのすべての Razor ファイルについて、レイアウト ファイルを に設定します。 詳細については、「Layout」 (レイアウト) を参照してください。

Create ページのモデル

Pages/Movies/Create.cshtml.cs ページ モデルを確認します。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Movie Movie { get; set; } = default!;
        

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.Movie == null || Movie == null)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

OnGet メソッドは、ページに必要な状態を初期化します。 [作成] ページには初期化する状態はないため、Page が返されます。 このチュートリアルの後半では、状態を初期化する OnGet の例を示します。 Page メソッドでは、PageResult ページをレンダリングする Create.cshtml オブジェクトが作成されます。

Movie プロパティでは [BindProperty] 属性を使用して、モデル バインドにオプトインします。 [作成] フォームでフォーム値が投稿されると、ASP.NET Core ランタイムが投稿された値を Movie モデルにバインドします。

OnPostAsync メソッドは、ページでフォーム データが投稿されたときに実行されます。

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid || _context.Movie == null || Movie == null)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

モデル エラーがある場合は、投稿されたフォーム データと共にフォームが再表示されます。 ほとんどのモデル エラーは、フォームが投稿される前にクライアント側でキャッチできます。 モデル エラーの例では、日付に変換できない日付フィールドの値が投稿されています。 クライアント側の検証とモデルの検証については、チュートリアルの後半で説明します。

モデル エラーがない場合は、次のようになります。

  • データが保存されます。
  • ブラウザーは Index ページにリダイレクトされます。

Razor の作成ページ

Pages/Movies/Create.cshtml Razor ページ ファイルを調べます。

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

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

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

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

Visual Studio に、タグ ヘルパーで使用される独特な太字のフォントで次のタグが表示されます。

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Create.cshtml ページの VS17 ビュー

<form method="post"> 要素はフォーム タグ ヘルパーです。 フォーム タグ ヘルパーには自動的に偽造防止トークンが含まれます。

スキャフォールディング エンジンでは、次のような、(ID を除く) モデルの各フィールドの Razor マークアップが作成されます。

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

検証タグ ヘルパー (<div asp-validation-summary<span asp-validation-for) には検証エラーが表示されます。 検証については、後で詳しく説明します。

ラベル タグ ヘルパー (<label asp-for="Movie.Title" class="control-label"></label>) は、[for] プロパティのラベル キャプションと Title 属性を生成します。

入力タグ ヘルパー (<input asp-for="Movie.Title" class="form-control">) は DataAnnotations 属性を使用し、クライアント側で jQuery 検証に必要な HTML 属性を生成します。

<form method="post"> などのタグ ヘルパーについては、「ASP.NET Core のタグ ヘルパー」を参照してください。

次のステップ

[作成]、[削除]、[詳細]、および [編集] ページ

Pages/Movies/Index.cshtml.cs ページ モデルを調べます。

// Unused usings removed.
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace RazorPagesMovie.Pages.Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }
        public IList<Movie> Movie { get;set; }

        public async Task OnGetAsync()
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor ページは PageModel から派生します。 慣例により、PageModel 派生クラスは <PageName>Model と呼ばれます。 コンストラクターは依存性の注入を使用して、RazorPagesMovieContext をページに追加します。

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Entity Framework での非同期プログラミングの詳細については、「非同期コード」を参照してください。

ページに対して要求が行われると、OnGetAsync メソッドは Razor ページにムービーのリストを返します。 Razor ページでは、OnGetAsync または OnGet が呼び出され、ページの状態が初期化されます。 この場合、OnGetAsync でムービーのリストを取得し、表示します。

OnGet によって void が返される場合、または OnGetAsync によって Task が返される場合は、return ステートメントは使用されません。 たとえば、Privacy ページは次のようになります。

public class PrivacyModel : PageModel
{
    private readonly ILogger<PrivacyModel> _logger;

    public PrivacyModel(ILogger<PrivacyModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
    }
}

戻り値の型が IActionResult または Task<IActionResult> の場合は、return ステートメントを指定する必要があります。 たとえば、Pages/Movies/Create.cshtml.csOnPostAsync メソッドでは、次の操作を行います。

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Movie.Add(Movie);
        await _context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

Pages/Movies/Index.cshtml Razor ページを調べます。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor では、HTML から C# または Razor 固有のマークアップに移行できます。 @ シンボルの後に Razor で予約済みのキーワードが続いている場合は、Razor 固有のマークアップに移行します。それ以外の場合は、C# に移行します。

@page ディレクティブ

@page Razor ディレクティブを使うと、ファイルが MVC アクションになります。つまり、これで要求を処理できます。 @page はページ上で最初の Razor ディレクティブである必要があります。 @page および @model は、Razor 固有のマークアップへの移行の例です。 詳細については、「Razor の構文」を参照してください。

@model ディレクティブ

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@model ディレクティブは、Razor ページに渡されるモデルの型を指定します。 前の例の @model 行は、PageModel ページで Razor 派生クラスを使用できるようにします。 モデルは、ページの @Html.DisplayNameFor および @Html.DisplayForHTML ヘルパーで使用されます。

次の HTML ヘルパーで使用されるラムダ式を確認します。

@Html.DisplayNameFor(model => model.Movie[0].Title)

DisplayNameFor HTML ヘルパーは、ラムダ式で参照される Title プロパティを検査し、表示名を判別します。 ラムダ式は評価されるのではなく検査されます。 これは、modelmodel.Movie、または model.Movie[0]null または空である場合、アクセス違反がないことを意味します。 ラムダ式が (@Html.DisplayFor(modelItem => item.Title) などを使用して) 評価される場合は、モデルのプロパティ値が評価されます。

レイアウト ページ

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

Pages/Shared/_Layout.cshtml ファイルを開いて調べます。

レイアウト テンプレートを使用すると、HTML コンテナーのレイアウトを次のようにすることができます。

  • 1 つの場所で指定される。
  • サイトの複数のページに適用される。

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

ViewData とレイアウト

Pages/Movies/Index.cshtml ファイルの次のマークアップを考えてみます。

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

上の強調表示されたマークアップは、Razor の C# への移行例です。 { 文字と } 文字で C# コードのブロックを囲みます。

PageModel 基底クラスには ViewData 辞書プロパティが含まれており、これを使用してビューにデータを渡すことができます。 オブジェクトは、"ViewData" のパターンを使用して、 辞書に追加されます。 上のサンプルでは、Title プロパティが ViewData 辞書に追加されます。

Title プロパティは、Pages/Shared/_Layout.cshtml ファイルで使用されます。 次のマークアップは、 _Layout.cshtml ファイルの最初の数行を示しています。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorPagesMovie</title>

    @*Markup removed for brevity.*@

@*Markup removed for brevity.*@ 行は Razor コメントです。 HTML コメント <!-- --> とは異なり、Razor コメントはクライアントには送信されません。 詳細については、「MDN Web ドキュメント: HTML の概要」をご覧ください。

レイアウトの更新

  1. <title> ではなく、Pages/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</title>
    
  2. Pages/Shared/_Layout.cshtml ファイルで次のアンカー要素を見つけます。

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. 上の要素を次のマークアップに置き換えます。

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    上のアンカー要素はタグ ヘルパーです。 この場合は、アンカー タグ ヘルパーです。 asp-page="/Movies/Index" タグ ヘルパーの属性と値によって、/Movies/IndexRazor ページへのリンクが作成されます。 asp-area 属性の値が空なので、リンクではこの区分が使用されていません。 詳細については、区分に関する記事を参照してください。

  4. 変更内容を保存し、RpMovie リンクを選択してアプリをテストします。 問題がある場合は、GitHub の _Layout.cshtml ファイルを参照してください。

  5. HomeRpMovieCreateEditDelete の各リンクをテストします。 各ページで、ブラウザー タブで表示できるタイトルを設定します。ページをブックマークすると、ブックマークでタイトルが使用されます。

Price フィールドに小数点のコンマを入力できない場合があります。 小数点にコンマ (",") を使い、英語 (米国) 以外の日付形式を使う英語以外のロケールの jQuery 検証をサポートするには、アプリをグローバル化する手順を行う必要があります。 小数点のコンマの追加方法については、こちらの GitHub issue 4076 を参照してください。

Layout プロパティは、Pages/_ViewStart.cshtml ファイルに設定されます。

@{
    Layout = "_Layout";
}

上のマークアップを使用すると、Pages/Shared/_Layout.cshtml フォルダーのすべての Razor ファイルについて、レイアウト ファイルを に設定します。 詳細については、「Layout」 (レイアウト) を参照してください。

Create ページのモデル

Pages/Movies/Create.cshtml.cs ページ モデルを確認します。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;
using System;
using System.Threading.Tasks;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Movie Movie { get; set; }

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

OnGet メソッドは、ページに必要な状態を初期化します。 [作成] ページには初期化する状態はないため、Page が返されます。 このチュートリアルの後半では、状態を初期化する OnGet の例を示します。 Page メソッドでは、PageResult ページをレンダリングする Create.cshtml オブジェクトが作成されます。

Movie プロパティでは [BindProperty] 属性を使用して、モデル バインドにオプトインします。 [作成] フォームでフォーム値が投稿されると、ASP.NET Core ランタイムが投稿された値を Movie モデルにバインドします。

OnPostAsync メソッドは、ページでフォーム データが投稿されたときに実行されます。

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

モデル エラーがある場合は、投稿されたフォーム データと共にフォームが再表示されます。 ほとんどのモデル エラーは、フォームが投稿される前にクライアント側でキャッチできます。 モデル エラーの例では、日付に変換できない日付フィールドの値が投稿されています。 クライアント側の検証とモデルの検証については、チュートリアルの後半で説明します。

モデル エラーがない場合は、次のようになります。

  • データが保存されます。
  • ブラウザーは Index ページにリダイレクトされます。

Razor の作成ページ

Pages/Movies/Create.cshtml Razor ページ ファイルを調べます。

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

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

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

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

Visual Studio に、タグ ヘルパーで使用される独特な太字のフォントで次のタグが表示されます。

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Create.cshtml ページの VS17 ビュー

<form method="post"> 要素はフォーム タグ ヘルパーです。 フォーム タグ ヘルパーには自動的に偽造防止トークンが含まれます。

スキャフォールディング エンジンでは、次のような、(ID を除く) モデルの各フィールドの Razor マークアップが作成されます。

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

検証タグ ヘルパー (<div asp-validation-summary<span asp-validation-for) には検証エラーが表示されます。 検証については、後で詳しく説明します。

ラベル タグ ヘルパー (<label asp-for="Movie.Title" class="control-label"></label>) は、[for] プロパティのラベル キャプションと Title 属性を生成します。

入力タグ ヘルパー (<input asp-for="Movie.Title" class="form-control">) は DataAnnotations 属性を使用し、クライアント側で jQuery 検証に必要な HTML 属性を生成します。

<form method="post"> などのタグ ヘルパーについては、「ASP.NET Core のタグ ヘルパー」を参照してください。

次のステップ