ムービー コントローラーの編集アクション メソッドとビューの確認

作成者 : Rick Anderson

Note

このチュートリアルの更新バージョンは、Visual Studio の最新バージョンを使用してこちらで入手できます。 新しいチュートリアルでは、MVC ASP.NET Core使用します。このチュートリアルでは、このチュートリアルに対して多くの機能強化が提供されます。

このチュートリアルでは、ASP.NET Core MVC のコントローラーとビューについて説明します。 Razor Pages は、ASP.NET Coreの新しい代替手段であり、Web UI の構築を容易かつ生産的にするページ ベースのプログラミング モデルです。 MVC バージョンの前に Razor Pages チュートリアルを試してみることをお勧めします。 この Razor ページのチュートリアルの特徴は次のとおりです。

  • 使いやすい。
  • 多くの機能をカバーしている。
  • 新しいアプリ開発に推奨されるアプローチです。

このセクションでは、ムービー コントローラーに対して生成された Edit アクション メソッドとビューについて説明します。 しかし、最初に、リリース日をより良く見せるために、短い分散を取ります。 Models\Movie.cs ファイルを開き、次に示す強調表示された行を追加します。

using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int ID { get; set; }
        public string Title { get; set; }

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }

    public class MovieDBContext : DbContext
    {
        public DbSet<Movie> Movies { get; set; }
    }
}

日付カルチャを次のように指定することもできます。

[Display(Name = "Release Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

DataAnnotations については、次のチュートリアルで説明します。 Display 属性は、フィールドの名前として表示する内容 (ここでは、"ReleaseDate" ではなく、"Release Date") を指定します。 DataType 属性はデータの種類を指定します。この場合は日付であるため、フィールドに格納されている時刻情報は表示されません。 日付形式が正しくレンダリングされない Chrome ブラウザーのバグには 、DisplayFormat 属性が必要です。

アプリケーションを実行し、コントローラーを Movies 参照します。 [編集] リンクの上にマウス ポインターを置いて、リンク先の URL を表示します。

EditLink_sm

Edit リンクは、Html.ActionLinkViews\Movies\Index.cshtml ビューの メソッドによって生成されました。

@Html.ActionLink("Edit", "Edit", new { id=item.ID })

Html.ActionLink

オブジェクトは HtmlSystem.Web.Mvc.WebViewPage 基本クラスの プロパティを使用して公開されるヘルパーです。 ActionLinkヘルパーの メソッドを使用すると、コントローラー上のアクション メソッドにリンクする HTML ハイパーリンクを簡単に動的に生成できます。 メソッドの最初の ActionLink 引数は、レンダリングするリンク テキストです (例: <a>Edit Me</a>)。 2 番目の引数は、呼び出すアクション メソッドの名前です (この場合は Edit アクション)。 最後の引数は、ルート データ (この場合は ID 4) を生成する 匿名オブジェクト です。

前の画像に示されている生成されたリンクは です http://localhost:1234/Movies/Edit/4。 既定のルート ( App_Start\RouteConfig.cs で確立) は URL パターン {controller}/{action}/{id}を受け取ります。 したがって、ASP.NET は、 パラメーターIDが 4 のコントローラーのMoviesアクション メソッドへの要求Editに変換http://localhost:1234/Movies/Edit/4されます。 App_Start\RouteConfig.cs ファイルから次のコードを調べます。 MapRoute メソッドは、HTTP 要求を正しいコントローラーおよびアクション メソッドにルーティングし、オプションの ID パラメーターを指定するために使用されます。 MapRoute メソッドは、コントローラー、アクション メソッド、ルート データを指定して URL を生成するなどの ActionLinkHtmlHelpers によっても使用されます。

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", 
            id = UrlParameter.Optional }
    );
}

クエリ文字列を使用してアクション メソッドパラメーターを渡すこともできます。 たとえば、URL http://localhost:1234/Movies/Edit?ID=3 は 3 のパラメーターIDもコントローラーのアクション メソッドにEditMovies渡します。

EditQueryString

コントローラーを Movies 開きます。 2 つの Edit アクション メソッドを次に示します。

// GET: /Movies/Edit/5
public ActionResult Edit(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Movie movie = db.Movies.Find(id);
    if (movie == null)
    {
        return HttpNotFound();
    }
    return View(movie);
}

// POST: /Movies/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
// more details see https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include="ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
    if (ModelState.IsValid)
    {
        db.Entry(movie).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(movie);
}

2 番目の Edit アクション メソッドの前に HttpPost 属性が付いていることに注意してください。 この属性は、POST 要求に対してのみ メソッドの Edit オーバーロードを呼び出すことができることを指定します。 属性は最初の HttpGet 編集メソッドに適用できますが、これは既定であるため必要ありません。 (属性を暗黙的にメソッドとしてHttpGet割り当てられるアクション メソッドをHttpGet参照します)。Bind 属性は、ハッカーがデータをモデルに過剰に投稿するのを防ぐもう 1 つの重要なセキュリティ メカニズムです。 変更するバインド属性には、プロパティのみを含める必要があります。 オーバーポストとバインド属性については、オーバー ポストセキュリティノートを参照してください。 このチュートリアルで使用する単純なモデルでは、モデル内のすべてのデータをバインドします。 ValidateAntiForgeryToken 属性は、要求の偽造を防ぐために使用され、編集ビュー ファイル (Views\Movies\Edit.cshtml) で と@Html.AntiForgeryToken()ペアになっています。一部を次に示します。

@model MvcMovie.Models.Movie

@{
    ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()    
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        @Html.ValidationSummary(true)
        @Html.HiddenFor(model => model.ID)

        <div class="form-group">
            @Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Title)
                @Html.ValidationMessageFor(model => model.Title)
            </div>
        </div>

@Html.AntiForgeryToken() は、コントローラーの メソッドで Edit 一致する必要がある隠し形式の偽造防止トークンを Movies 生成します。 クロスサイト リクエスト フォージェリ (XSRF または CSRF とも呼ばれます) の詳細については、 MVC の XSRF/CSRF 防止に関するチュートリアルを参照してください。

メソッドは HttpGetEdit movie ID パラメーターを受け取り、Entity Framework Find メソッドを使用してムービーを検索し、選択したムービーを編集ビューに返します。 ムービーが見つからない場合は、 HttpNotFound が返されます。 スキャフォールディング システムが編集ビューを作成したときは、そのシステムが Movie クラスを調べて、クラスの各プロパティの <label> および <input> 要素をレンダリングするコードを作成しました。 次の例では、Visual Studio のスキャフォールディング システムによって生成された編集ビューを示します。

@model MvcMovie.Models.Movie

@{
    ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()    
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        @Html.ValidationSummary(true)
        @Html.HiddenFor(model => model.ID)

        <div class="form-group">
            @Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Title)
                @Html.ValidationMessageFor(model => model.Title)
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.ReleaseDate, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ReleaseDate)
                @Html.ValidationMessageFor(model => model.ReleaseDate)
            </div>
        </div>
        @*Genre and Price removed for brevity.*@        
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

ビュー テンプレート @model MvcMovie.Models.Movie のファイルの先頭に ステートメントがあることがわかります。これにより、ビューテンプレートのモデルが 型であることがビューで想定されます Movie

スキャフォールディングされたコードでは、いくつかの ヘルパー メソッドを 使用して HTML マークアップを合理化します。 ヘルパーには Html.LabelFor 、フィールドの名前 ("Title"、"ReleaseDate"、"Genre"、または "Price") が表示されます。 ヘルパーは Html.EditorFor HTML <input> 要素をレンダリングします。 ヘルパーには Html.ValidationMessageFor 、そのプロパティに関連付けられている検証メッセージが表示されます。

アプリケーションを実行し、 /Movies URL に移動します。 [編集] リンクをクリックします。 ブラウザーで、ページのソースを表示します。 フォーム要素の HTML を次に示します。

<form action="/movies/Edit/4" method="post">
   <input name="__RequestVerificationToken" type="hidden" value="UxY6bkQyJCXO3Kn5AXg-6TXxOj6yVBi9tghHaQ5Lq_qwKvcojNXEEfcbn-FGh_0vuw4tS_BRk7QQQHlJp8AP4_X4orVNoQnp2cd8kXhykS01" />  <fieldset class="form-horizontal">
      <legend>Movie</legend>

      <input data-val="true" data-val-number="The field ID must be a number." data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="4" />

      <div class="control-group">
         <label class="control-label" for="Title">Title</label>
         <div class="controls">
            <input class="text-box single-line" id="Title" name="Title" type="text" value="GhostBusters" />
            <span class="field-validation-valid help-inline" data-valmsg-for="Title" data-valmsg-replace="true"></span>
         </div>
      </div>

      <div class="control-group">
         <label class="control-label" for="ReleaseDate">Release Date</label>
         <div class="controls">
            <input class="text-box single-line" data-val="true" data-val-date="The field Release Date must be a date." data-val-required="The Release Date field is required." id="ReleaseDate" name="ReleaseDate" type="date" value="1/1/1984" />
            <span class="field-validation-valid help-inline" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
         </div>
      </div>

      <div class="control-group">
         <label class="control-label" for="Genre">Genre</label>
         <div class="controls">
            <input class="text-box single-line" id="Genre" name="Genre" type="text" value="Comedy" />
            <span class="field-validation-valid help-inline" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
         </div>
      </div>

      <div class="control-group">
         <label class="control-label" for="Price">Price</label>
         <div class="controls">
            <input class="text-box single-line" data-val="true" data-val-number="The field Price must be a number." data-val-required="The Price field is required." id="Price" name="Price" type="text" value="7.99" />
            <span class="field-validation-valid help-inline" data-valmsg-for="Price" data-valmsg-replace="true"></span>
         </div>
      </div>

      <div class="form-actions no-color">
         <input type="submit" value="Save" class="btn" />
      </div>
   </fieldset>
</form>

要素は <input> HTML <form> 要素内にあり、その action 属性は /Movies/Edit URL にポストするように設定されています。 [ 保存] ボタンがクリックされると、フォーム データがサーバーにポストされます。 2 行目は、呼び出しによって生成された非表示 の XSRF トークンを @Html.AntiForgeryToken() 示しています。

POST 要求の処理

次のリストでは、Edit アクション メソッドの HttpPost バージョンを示します。

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include="ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
    if (ModelState.IsValid)
    {
        db.Entry(movie).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(movie);
}

ValidateAntiForgeryToken 属性は、ビュー内の呼び出しによって@Html.AntiForgeryToken()生成された XSRF トークンを検証します。

ASP.NET MVC モデル バインダーは、ポストされたフォーム値を受け取り、 パラメーターとして渡されるオブジェクトをmovie作成Movieします。 は ModelState.IsValid 、フォームで送信されたデータを使用して、オブジェクトを変更 (編集または更新) Movie できることを確認します。 データが有効な場合、ムービー データは (MovieDBContext インスタンス) のMoviesコレクションにdb保存されます。 新しいムービー データは、 の メソッドMovieDBContextを呼び出してデータベースにSaveChanges保存されます。 データを保存した後、コードはユーザーを MoviesController クラスの Index アクション メソッドにリダイレクトします。そこでは、行われたばかりの変更を含むムービー コレクションが表示されます。

クライアント側の検証でフィールドの値が無効であると判断されるとすぐに、エラー メッセージが表示されます。 JavaScript が無効になっている場合、クライアント側の検証は無効になります。 ただし、サーバーは、ポストされた値が無効であることを検出し、フォーム値がエラー メッセージと共に再表示されます。

検証については、チュートリアルの後半で詳しく説明します。

Edit.cshtml ビュー テンプレートのヘルパーはHtml.ValidationMessageFor、適切なエラー メッセージを表示します。

abcNotValid

すべてのメソッドは HttpGet 同様のパターンに従います。 ムービー オブジェクト (または の場合 Indexはオブジェクトの一覧) を取得し、モデルをビューに渡します。 メソッドは Create 、空のムービー オブジェクトを Create ビューに渡します。 データの作成、編集、削除、またはそれ以外の変更を行うすべてのメソッドは、メソッドの HttpPost のオーバーロードでそれを行います。 HTTP GET メソッド内のデータの変更は、MVC ヒント #46 のブログ投稿エントリ ASP.NET 説明されているように、セキュリティ 上のリスクです 。セキュリティ ホールが作成されるため、リンクの削除は使用しないでください。 GET メソッドでデータを変更すると、HTTP のベスト プラクティスとアーキテクチャ REST パターンにも違反します。これは、GET 要求がアプリケーションの状態を変更しないことを指定します。 つまり、GET 操作の実行は、副作用がなく、永続化されたデータを変更しない、安全な操作である必要があります。

英語以外のロケールの jQuery 検証

US-English コンピューターを使用している場合は、このセクションをスキップして、次のチュートリアルに進むことができます。 このチュートリアルの Globalize バージョン は、こちらからダウンロードできます。 国際化に関する優れた 2 部構成のチュートリアルについては、 Nadeem の ASP.NET MVC 5 国際化に関するページを参照してください。

Note

小数点にコンマ (",") を使用する英語以外のロケールの jQuery 検証と、US-English以外の日付形式をサポートするには、 を使用Globalize.parseFloatするには、globalize.jsおよび特定のカルチャ/globalize.cultures.js ファイル (from https://github.com/jquery/globalize ) と JavaScript を含める必要があります。 NuGet から jQuery の英語以外の検証を取得できます。 (英語ロケールを使用している場合は、Globalize をインストールしないでください)。

  1. [ツール] メニューの [NuGet パッケージ マネージャー] をクリックし、[ソリューションの NuGet パッケージの管理] をクリックします。

    英語以外のロケールの jQuery 検証を開始するための [ツール] メニューのスクリーンショット。

  2. 左側のウィンドウで[参照]*を選択 します。*(下の画像を参照してください。)

  3. 入力ボックスに「 Globalize*」と入力します。

    Globalize と入力する入力ボックスのスクリーンショット。

    を選択jQuery.Validation.Globalizeし、[インストール] を選択MvcMovieしてクリックします。 Scripts\jquery.globalize\globalize.js ファイルがプロジェクトに追加されます。 *Scripts\jquery.globalize\culture* フォルダーには、多くのカルチャ JavaScript ファイルが含まれます。 このパッケージのインストールには 5 分かかる場合があることに注意してください。

    次のコードは、Views\Movies\Edit.cshtml ファイルの変更を示しています。

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")

<script src="~/Scripts/globalize/globalize.js"></script>
<script src="~/Scripts/globalize/cultures/globalize.culture.@(System.Threading.Thread.CurrentThread.CurrentCulture.Name).js"></script>
<script>
    $.validator.methods.number = function (value, element) {
        return this.optional(element) ||
            !isNaN(Globalize.parseFloat(value));
    }
    $(document).ready(function () {
        Globalize.culture('@(System.Threading.Thread.CurrentThread.CurrentCulture.Name)');
    });
</script>
<script>
    jQuery.extend(jQuery.validator.methods, {
        range: function (value, element, param) {
            //Use the Globalization plugin to parse the value
            var val = Globalize.parseFloat(value);
            return this.optional(element) || (
                val >= param[0] && val <= param[1]);
        }
    });
    $.validator.methods.date = function (value, element) {
        return this.optional(element) ||
            Globalize.parseDate(value) ||
            Globalize.parseDate(value, "yyyy-MM-dd");
    }
</script>
}

すべての編集ビューでこのコードが繰り返されないようにするには、レイアウト ファイルに移動します。 スクリプトのダウンロードを最適化するには、チュートリアル 「バンドルと縮小」を参照してください。

詳細については、「 MVC 3 国際化 ASP.NET 」および 「MVC 3 国際化 - パート 2 (NerdDinner)」ASP.NET 参照してください。

一時的な修正として、ロケールで検証が機能しない場合は、コンピューターで米国英語を強制的に使用するか、ブラウザーで JavaScript を無効にすることができます。 コンピューターで英語 (US) を強制的に使用するには、グローバリゼーション要素をプロジェクト ルート web.config ファイルに追加します。 次のコードは、カルチャが英語米国に設定されたグローバリゼーション要素を示しています。

<system.web>
    <globalization culture ="en-US" />
    <!--elements removed for clarity-->
  </system.web>

次のチュートリアルでは、検索機能を実装します。