ムービー コントローラーの Edit アクション メソッドとビューを調べる
作成者: Rick Anderson
Note
Visual Studio の最新バージョンを使用した、このチュートリアルの更新バージョンはこちらにあります。 新しいチュートリアル (このチュートリアルよりも多くの改善がされています) では、ASP.NET Core MVC を使用しています。
このチュートリアルでは、ASP.NET Core MVC のコントローラーとビューについて説明します。 Razor Pages は ASP.NET Core での新しい代替手段であり、Web UI の構築をより簡単かつ生産的にする、ページベースのプログラミング モデルです。 MVC のバージョンの前に、Razor ページのチュートリアルを試すことをお勧めします。 この 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 属性はデータ型を指定します。この場合は日付なので、フィールドに格納されている時間情報は表示されません。 DisplayFormat 属性は、日付形式が正しくレンダリングされない Chrome ブラウザーのバグに対処するために必要です。
アプリケーションを実行し、Movies
コントローラーを参照します。 Edit リンクの上にマウス ポインターを置くと、リンク先の URL が表示されます。
Edit リンクは、Views\Movies\Index.cshtml ビューで Html.ActionLink
メソッドによって生成されたものです。
@Html.ActionLink("Edit", "Edit", new { id=item.ID })
Html
オブジェクトは、System.Web.Mvc.WebViewPage 基底クラスのプロパティを使用して公開されるヘルパーです。 このヘルパーの ActionLink
メソッドを使用すると、コントローラー上のアクション メソッドにリンクする HTML ハイパーリンクを簡単かつ動的に生成できるようになります。 ActionLink
メソッドの第 1 引数は、リンクとして表示するテキストです (例: <a>Edit Me</a>
)。 第 2 引数は、呼び出すアクション メソッドの名前です (この場合は、Edit
アクション)。 最後の引数は、ルート データを生成する匿名オブジェクトです (この場合は ID 4)。
上記のイメージでは、http://localhost:1234/Movies/Edit/4
というリンクが生成されていることが示されています。 既定値のルート (App_Start\RouteConfig.cs で確立される) は、URL パターン {controller}/{action}/{id}
を受け取ります。 したがって、ASP.NET は、http://localhost:1234/Movies/Edit/4
を、Movies
コントローラーの Edit
アクション メソッドへの要求に変換し、パラメーター ID
は 4 になります。 App_Start\RouteConfig.cs ファイル内の以下のコードを調べます。 MapRoute メソッドは、HTTP 要求を正しいコントローラーとアクション メソッドにルーティングし、オプションの ID パラメーターを指定するために使用されます。 MapRoute メソッドは、コントローラー、アクション メソッド、ルート データを指定して URL を生成するために、ActionLink
などの HtmlHelpers でも使用されます。
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
も Movies
コントローラーの Edit
アクション メソッドにパラメーター ID
3 を渡します。
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
属性が付いていることに注意してください。 この属性は、Edit
メソッドのオーバーロードは POST 要求の場合にのみ呼び出し可能であることを指定するものです。 第 1 の Edit メソッドには HttpGet
属性を適用できますが、そちらは既定で適用済みの扱いになるため必須ではありません (HttpGet
属性が暗黙的に割り当てられているアクション メソッドを HttpGet
メソッドと呼びます)。Bind 属性は、ハッカーがデータをモデルに過剰に投稿するのを防ぐもう 1 つの重要なセキュリティ メカニズムです。 変更する bind 属性にだけプロパティを含める必要があります。 過剰ポスティングと bind 属性については、過剰ポスティングのセキュリティに関するメモで読むことができます。 このチュートリアルで使用する単純なモデルでは、モデル内のすべてのデータをバインドします。 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()
は、Movies
コントローラーの Edit
メソッドで一致する必要がある、非表示のフォーム偽造防止トークンを生成します。 クロスサイト リクエスト フォージェリ (XSRF または CSRF とも呼ばれます) の詳細については、MVC の XSRF/CSRF 防止に関するチュートリアルを参照してください。
HttpGet
Edit
メソッドは 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 に移動します。 [編集] リンクをクリックします。 ブラウザーで、ページのソースを表示します。 form 要素に関する 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>
要素は、/Movies/Edit URL に送信するように action
属性が設定された HTML の <form>
要素に含まれます。 [保存] ボタンがクリックされると、フォームのデータがサーバーに送信されます。 2 行目には、@Html.AntiForgeryToken()
呼び出しによって生成された非表示の XSRF トークンが表示されます。
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
オブジェクトを変更 (編集または更新) できることを検証します。 データが有効な場合、ムービー データは db
(MovieDBContext
インスタンス) の Movies
コレクションに保存されます。 新しいムービー データは、MovieDBContext
の SaveChanges
メソッドを呼び出すことによってデータベースに保存されます。 データを保存した後、コードはユーザーを MoviesController
クラスの Index
アクション メソッドにリダイレクトします。そこでは、行われたばかりの変更を含むムービー コレクションが表示されます。
クライアント側の検証でフィールドの値が無効であると判断されると、すぐにエラー メッセージが表示されます。 JavaScript が無効になっている場合、クライアント側の検証は無効になります。 ただし、ポストされた値が有効でないことがサーバーによって検出され、そのフォームの値がエラー メッセージとともに再表示されます。
検証については、チュートリアルの後半で詳しく説明します。
Edit.cshtml ビュー テンプレートの Html.ValidationMessageFor
ヘルパーは、該当するエラー メッセージの表示を処理します。
HttpGet
のメソッドは、すべて同様のパターンに従います。 映画 オブジェクト (Index
の場合はオブジェクトのリスト) を取得し、モデルをビューに渡します。 Create
メソッドは、空の映画オブジェクトを Create ビューに渡します。 データの作成、編集、削除、またはそれ以外の変更を行うすべてのメソッドは、メソッドの HttpPost
のオーバーロードでそれを行います。 ブログ記事エントリ「ASP.NET MVC Tip #46 – Don't use Delete Links because they create Security Holes」で説明されているように、HTTP GET メソッドでデータを変更することはセキュリティ上のリスクになります。 GET メソッドでデータを変更することは、HTTP のベスト プラクティスや、GET 要求ではアプリケーションの状態を変更してはならないというアーキテクチャの REST パターンにも違反しています。 つまり、GET 操作の実行は、副作用がなく、永続化されたデータを変更しない、安全な操作である必要があります。
英語以外のロケールの jQuery 検証
英語 (米国) のコンピューターを使用している場合は、このセクションをスキップして、次のチュートリアルに進むことができます。 このチュートリアル の Globalize バージョンは、こちらからダウンロードできます。 国際化に関する優れた 2 部構成のチュートリアルについては、Nadeem の ASP.NET MVC 5 の国際化を参照してください。
Note
小数点にコンマ (「,」) を使用する英語以外のロケールや、英語 (米国) 以外の日付形式の jQuery 検証をサポートするには、globalize.js と特定の cultures/globalize.cultures.js ファイル (https://github.com/jquery/globalize から)、および Globalize.parseFloat
を使用する JavaScript を含める必要があります。 jQuery の英語以外の検証は NuGet から取得できます (英語のロケールを使用している場合は、Globalize をインストールしないでください)。
[ツール] メニューで、[NuGet パッケージ マネージャー] をクリックし、次に [ソリューションの NuGet パッケージの管理] をクリックします。
左側のペインで、[参照] を選択します (下の画像を参照)。
入力ボックスに「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>
}
すべての編集ビューでこのコードが繰り返されないようにするために、このコードをレイアウト ファイルに移動できます。 スクリプトのダウンロードを最適化するには、バンドルと縮小のチュートリアルを参照してください。
詳細については、「ASP.NET MVC 3 の国際化」と「MVC 3 の国際化の ASP.NET - パート 2 (NerdDinner)」を参照してください。
ロケールで検証が機能しない場合は、一時的な修正として、コンピューターで英語 (米国) を使用するように強制するか、ブラウザーで JavaScript を無効にすることができます。 コンピューターで英語 (米国) を使用するように強制するには、グローバリゼーション要素をプロジェクトのルート web.config ファイルに追加します。 以下のコードは、カルチャが米国英語に設定された globalization 要素を示しています。
<system.web>
<globalization culture ="en-US" />
<!--elements removed for clarity-->
</system.web>