檢查電影控制器的編輯動作方法和檢視
作者: Rick Anderson
注意
本教學課程的更新版本 可在這裡 使用最新版本的 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 屬性指定要顯示的欄位名稱 (在本例中為 "Release Date",而不是 "ReleaseDate")。 DataType屬性會指定資料類型,在此案例中為日期,因此不會顯示儲存在欄位中的時間資訊。 Chrome 瀏覽器中的 Bug 需要 DisplayFormat 屬性,該錯誤會轉譯日期格式不正確。
執行應用程式並流覽至 Movies
控制器。 將滑鼠指標停留在 [編輯] 連結上方,以查看其所連結的 URL。
Edit連結是由 Html.ActionLink
Views\Movies\Index.cshtml檢視中的 方法所產生:
@Html.ActionLink("Edit", "Edit", new { id=item.ID })
物件 Html
是使用 System.Web.Mvc.WebViewPage 基類上的屬性公開的協助程式。 ActionLink
協助程式的 方法可讓您輕鬆地動態產生 HTML 超連結,以連結至控制器上的動作方法。 方法的第一個引數 ActionLink
是要轉譯 (的連結文字,例如) <a>Edit Me</a>
。 第二個引數是叫用 (動作方法的名稱。在此情況下, Edit
動作) 。 最後一個引數是 匿名物件 ,在此案例中會產生路由資料 (識別碼 4) 。
上圖中顯示的所產生連結為 http://localhost:1234/Movies/Edit/4
。 在 App_Start\RouteConfig.cs 中建立的預設路由 () 會採用 URL 模式 {controller}/{action}/{id}
。 因此,ASP.NET 會轉譯 http://localhost:1234/Movies/Edit/4
為控制器的 action 方法 Movies
, Edit
其參數 ID
等於 4。 從 App_Start\RouteConfig.cs 檔案檢查下列程式碼。 MapRoute方法可用來將 HTTP 要求路由傳送至正確的控制器和動作方法,並提供選擇性的 ID 參數。 HtmlHelpers也會使用MapRoute方法,例如 ActionLink
,根據控制器、動作方法和任何路由資料來產生 URL。
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
也會將 ID
3 的參數傳遞至 Edit
控制器的 Movies
動作方法。
Movies
開啟控制器。 這兩 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);
}
請注意,第二個 Edit
動作方法的前面是 HttpPost
屬性。 此屬性指定只能針對 POST 要求叫用 方法的多 Edit
載。 您可以將 屬性套用 HttpGet
至第一個編輯方法,但這並非必要,因為它是預設值。 (我們將參考隱含指派 HttpGet
屬性為 HttpGet
methods 的動作方法。) 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()
會產生在控制器的 方法中 Edit
必須符合的 Movies
隱藏形式防偽造權杖。 您可以在 MVC 的 XSRF/CSRF 預防教學課程中深入瞭解跨網站偽造要求 (也稱為 XSRF 或 CSRF) 。
方法 HttpGet
Edit
會採用電影識別碼參數、使用 Entity Framework Find
方法查閱電影,並將選取的電影傳回至 [編輯] 檢視。 如果找不到電影,則會傳回 HttpNotFound 。 當 Scaffolding 系統建立 Edit 檢視時,它會檢查 Movie
類別,並建立程式碼為類別的每個屬性轉譯 <label>
和 <input>
元素。 下列範例會顯示 Visual Studio Scaffolding 系統所產生的 Edit 檢視:
@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
。
Scaffold 程式碼會使用數個 協助程式方法來 簡化 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。 按一下 [ 儲存 ] 按鈕時,表單資料將會張貼到伺服器。 第二行會顯示呼叫所產生的 @Html.AntiForgeryToken()
隱藏XSRF權杖。
處理 POST 要求
下列清單顯示 HttpPost
版本的 Edit
動作方法。
[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
。 如果資料有效,電影資料會儲存至 Movies
(MovieDBContext
實例的 db
集合) 。 新的電影資料會藉由呼叫 SaveChanges
的 MovieDBContext
方法來儲存至資料庫。 儲存資料之後,程式碼將使用者重新導向至 MoviesController
類別的 Index
動作方法,此方法會顯示電影集合,包括剛剛所進行的變更。
一旦用戶端驗證判斷欄位的值無效,就會顯示錯誤訊息。 如果 JavaScript 已停用,則會停用用戶端驗證。 不過,伺服器偵測到張貼的值無效,而且表單值會以錯誤訊息重新顯示。
稍後在本教學課程中會更詳細地檢查驗證。
Html.ValidationMessageFor
Edit.cshtml檢視範本中的協助程式會負責顯示適當的錯誤訊息。
HttpGet
所有方法都會遵循類似的模式。 他們會取得影片物件 (或物件清單,以防 Index
) ,並將模型傳遞至檢視。 方法會將 Create
空的電影物件傳遞至 [建立] 檢視。 建立、編輯、刪除或以其他方式修改資料的所有方法都會在方法的 HttpPost
多載中執行這個動作。 修改 HTTP GET 方法中的資料是安全性風險,如部落格文章 文章 ASP.NET MVC 提示 #46 - 請勿使用刪除連結,因為它們會建立安全性漏洞。 修改 GET 方法中的資料也會違反 HTTP 最佳做法和架構 REST 模式,指定 GET 要求不應該變更應用程式的狀態。 也就是說,執行 GET 作業應該是安全的作業,沒有任何副作用,而且不會修改您的保存資料。
非英文地區設定的 jQuery 驗證
如果您使用US-English電腦,您可以略過本節並移至下一個教學課程。 您可以 在這裡下載本教學課程的全域版本。 如需國際化的絕佳兩部分教學課程,請參閱 Nadeem 的 ASP.NET MVC 5 國際化。
注意
若要支援非英文地區設定的 jQuery 驗證,這些地區設定使用逗號 (「,」) 小數點和非US-English日期格式,您必須包含 globalize.js 和特定 文化特性/globalize.cultures.js 檔案 (, https://github.com/jquery/globalize 才能使用 Globalize.parseFloat
) 和 JavaScript。 您可以從 NuGet 取得 jQuery 非英文驗證。 (如果您使用英文地區設定.) ,請勿安裝 Globalize
從 [ 工具] 功能表中,按一下 [NuGet 套件管理員],然後按一下 [ 管理方案的 NuGet 套件]。
在左窗格中,選取 [流覽]*。* (請參閱下圖。)
在輸入方塊中,輸入 Globalize*。
選擇
jQuery.Validation.Globalize
,選擇MvcMovie
並按一下 [安裝]。 Scripts\jquery.globalize\globalize.js檔案將會新增至您的專案。 *Scripts\jquery.globalize\cultures* 資料夾將包含許多文化特性 JavaScript 檔案。 請注意,安裝此套件可能需要五分鐘的時間。下列程式碼顯示 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 國際化 和 ASP.NET MVC 3 國際化 - 第 2 部分 (NerdDinner) 。
暫時修正時,如果您無法在地區設定中取得驗證工作,您可以強制電腦使用美式英文,或在瀏覽器中停用 JavaScript。 若要強制您的電腦使用美國英文,您可以將全球化元素新增至專案根 目錄web.config 檔案。 下列程式碼顯示全球化元素,其文化特性設定為美國英文。
<system.web>
<globalization culture ="en-US" />
<!--elements removed for clarity-->
</system.web>
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應