第 6 部分,ASP.NET Core 中的控制器方法和檢視
注意
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 如需目前的版本,請參閱 本文的 .NET 9 版本。
此電影應用程式有個不錯的開始,但呈現效果不盡理想,例如 ReleaseDate 應該是兩個字。
如下所示,開啟 Models/Movie.cs
檔案並新增醒目提示的行:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MvcMovie.Models;
public class Movie
{
public int Id { get; set; }
public string? Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string? Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
在下一個教學課程中會說明 DataAnnotations
。 Display 屬性指定要顯示的欄位名稱 (在本例中為 "Release Date",而不是 "ReleaseDate")。 DataType 屬性指定資料的類型 (Date),因此不會顯示儲存在欄位中的時間資訊。
[Column(TypeName = "decimal(18, 2)")]
資料註解為必要項,因此 Entity Framework Core 可將 Price
正確對應到資料庫中的貨幣。 如需詳細資訊,請參閱資料類型。
瀏覽至 Movies
控制器,並將滑鼠指標停留在 Edit 連結,以查看目標 URL。
Views/Movies/Index.cshtml
檔案中的 Core MVC 錨點標籤協助程式會產生 [編輯]、[詳細資料] 和 [刪除] 連結。
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
標記協助程式可啟用伺服器端程式碼,以參與建立和轉譯 Razor 檔案中的 HTML 元素。 在上述程式碼中,AnchorTagHelper
會從控制器動作方法和路由識別碼動態產生 HTML href
屬性值。從您最喜愛的瀏覽器中使用 [檢視原始檔] 或使用開發工具來檢查產生的標記。 產生的 HTML 部分如下所示:
<td>
<a href="/Movies/Edit/4"> Edit </a> |
<a href="/Movies/Details/4"> Details </a> |
<a href="/Movies/Delete/4"> Delete </a>
</td>
回想 Program.cs
檔案中路由的設定格式:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
ASP.NET Core 會將 https://localhost:5001/Movies/Edit/4
轉譯成對 Movies
控制器的 Edit
動作方法的要求,其參數 Id
為 4 (控制器方法也稱為動作方法)。
標記協助程式是 ASP.NET Core 的其中一種最受歡迎的新功能。 如需詳細資訊,請參閱其他資源。
開啟 Movies
控制器,並檢查兩個 Edit
動作方法。 下列程式碼示範 HTTP GET Edit
方法,這個方法會擷取電影,並填入 Edit.cshtml
Razor 檔案所產生的編輯表單。
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FindAsync(id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
下列程式碼示範 HTTP POST Edit
方法,這個方法會處理已發佈的電影值:
// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (id != movie.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
[Bind]
屬性是一種防止過度發佈的方式。 您應該只在想要變更的 [Bind]
屬性 (attribute) 中包含屬性 ( property)。 如需詳細資訊,請參閱保護控制器避免過度發佈。 ViewModels 提供防止過度發佈的替代方法。
請注意,第二個 Edit
動作方法的前面是 [HttpPost]
屬性。
// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (id != movie.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
HttpPost
屬性指定「只」能為 POST
要求叫用這個 Edit
方法。 您可以將 [HttpGet]
屬性套用至第一個編輯方法,但不需要執行此動作,因為 [HttpGet]
是預設值。
ValidateAntiForgeryToken
屬性是用來 防範偽造要求 ,並與編輯檢視表檔案 (Views/Movies/Edit.cshtml
) 所產生的防偽權杖成對。 編輯檢視表檔案使用 表單標記協助程式產生防偽權杖。
<form asp-action="Edit">
表單標記協助程式會產生隱藏的防偽權杖,其必須符合電影控制器的 Edit
方法中 [ValidateAntiForgeryToken]
產生的防偽權杖。 如需詳細資訊,請參閱防止 ASP.NET Core 中的跨網站要求偽造 (XSRF/CSRF) 攻擊。
HttpGet Edit
方法會採用電影 ID
參數,使用 Entity Framework FindAsync
方法查詢電影,並將選取的電影傳回 Edit 檢視。 如果找不到電影,會傳回 NotFound
(HTTP 404)。
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FindAsync(id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
當 Scaffolding 系統建立 Edit 檢視時,它會檢查 Movie
類別,並建立程式碼為類別的每個屬性轉譯 <label>
和 <input>
元素。 下列範例會顯示 Visual Studio Scaffolding 系統所產生的 Edit 檢視:
@model MvcMovie.Models.Movie
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Movie</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Genre" class="control-label"></label>
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
請注意檢視範本在檔案最上方指定 @model MvcMovie.Models.Movie
陳述式的方式。 @model MvcMovie.Models.Movie
指定檢視預期檢視範本的模型必須是 Movie
型別。
包含 Scaffold 的程式碼會使用數個標記協助程式方法來簡化 HTML 標記。 標籤標記協助程式顯示欄位的名稱 ("Title"、"ReleaseDate"、"Genre" 或 "Price")。 輸入標記協助程式轉譯 HTML <input>
元素。 驗證標記協助程式則顯示與該屬性相關聯的任何驗證訊息。
執行應用程式,並巡覽至 /Movies
URL。 按一下 Edit 連結。 在瀏覽器中,檢視頁面的原始檔。 <form>
元素產生的 HTML 如下所示。
<form action="/Movies/Edit/7" method="post">
<div class="form-horizontal">
<h4>Movie</h4>
<hr />
<div class="text-danger" />
<input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="7" />
<div class="form-group">
<label class="control-label col-md-2" for="Genre" />
<div class="col-md-10">
<input class="form-control" type="text" id="Genre" name="Genre" value="Western" />
<span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="Price" />
<div class="col-md-10">
<input class="form-control" type="text" 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" value="3.99" />
<span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
</div>
</div>
<!-- Markup 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>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8Inyxgp63fRFqUePGvuI5jGZsloJu1L7X9le1gy7NCIlSduCRx9jDQClrV9pOTTmqUyXnJBXhmrjcUVDJyDUMm7-MF_9rK8aAZdRdlOri7FmKVkRe_2v5LIHGKFcTjPrWPYnc9AdSbomkiOSaTEg7RU" />
</form>
<input>
元素位於 HTML <form>
元素中,而後者的 action
屬性設定為發佈到 /Movies/Edit/id
URL。 按一下 Save
按鈕時,表單資料將發佈至伺服器。 在結尾 </form>
元素之前的最後一行會顯示表單標記協助程式所產生的隱藏 XSRF 語彙基元。
處理 POST 要求
下列清單顯示 [HttpPost]
版本的 Edit
動作方法。
// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (id != movie.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
[ValidateAntiForgeryToken]
屬性驗證 表單標記協助程式中防偽權杖產生器所產生的隱藏 XSRF 權杖
模型繫結系統採用已發佈的表單值,並建立以 movie
參數傳遞的 Movie
物件。 ModelState.IsValid
屬性會驗證表單中提交的資料可用於修改 (編輯或更新) Movie
物件。 如果資料有效,則會進行儲存。 藉由呼叫資料庫內容的 SaveChangesAsync
方法,更新 (編輯) 的電影資料會儲存到資料庫。 儲存資料之後,程式碼將使用者重新導向至 MoviesController
類別的 Index
動作方法,此方法會顯示電影集合,包括剛剛所進行的變更。
在表單發佈至伺服器之前,用戶端驗證會對欄位檢查任何驗證規則。 如果出現任何驗證錯誤,即會顯示錯誤訊息,且不會發佈該表單。 如果已停用 JavaScript,就不會進行用戶端驗證,但伺服器偵測到無效的發佈值,因此會重新顯示表單值並顯示錯誤訊息。 稍後在本教學課程中,我們會更詳細檢查模型驗證。 Views/Movies/Edit.cshtml
檢視範本中的驗證標記協助程式負責顯示適當的錯誤訊息。
電影控制器中的所有 HttpGet
方法都遵循類似的模式。 他們會取得電影物件 (如果是 Index
則為物件清單),並將此物件 (模型) 傳遞至檢視。 Create
方法會將空白電影物件傳遞至 Create
檢視。 建立、編輯、刪除或以其他方式修改資料的所有方法都會在方法的 [HttpPost]
多載中執行這個動作。 修改 HTTP GET
方法中的資料會造成安全性風險。 修改 HTTP GET
方法中的資料,也違反 HTTP 最佳做法及架構式 REST 模式,此模式指定 GET 要求不應該變更應用程式的狀態。 也就是說,執行 GET 作業應該是安全的作業,沒有任何副作用,而且不會修改您的保存資料。
其他資源
此電影應用程式有個不錯的開始,但呈現效果不盡理想,例如 ReleaseDate 應該是兩個字。
如下所示,開啟 Models/Movie.cs
檔案並新增醒目提示的行:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MvcMovie.Models;
public class Movie
{
public int Id { get; set; }
public string? Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string? Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
在下一個教學課程中會說明 DataAnnotations
。 Display 屬性指定要顯示的欄位名稱 (在本例中為 "Release Date",而不是 "ReleaseDate")。 DataType 屬性指定資料的類型 (Date),因此不會顯示儲存在欄位中的時間資訊。
[Column(TypeName = "decimal(18, 2)")]
資料註解為必要項,因此 Entity Framework Core 可將 Price
正確對應到資料庫中的貨幣。 如需詳細資訊,請參閱資料類型。
瀏覽至 Movies
控制器,並將滑鼠指標停留在 Edit 連結,以查看目標 URL。
Views/Movies/Index.cshtml
檔案中的 Core MVC 錨點標籤協助程式會產生 [編輯]、[詳細資料] 和 [刪除] 連結。
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
標記協助程式可啟用伺服器端程式碼,以參與建立和轉譯 Razor 檔案中的 HTML 元素。 在上述程式碼中,AnchorTagHelper
會從控制器動作方法和路由識別碼動態產生 HTML href
屬性值。從您最喜愛的瀏覽器中使用 [檢視原始檔] 或使用開發工具來檢查產生的標記。 產生的 HTML 部分如下所示:
<td>
<a href="/Movies/Edit/4"> Edit </a> |
<a href="/Movies/Details/4"> Details </a> |
<a href="/Movies/Delete/4"> Delete </a>
</td>
回想 Program.cs
檔案中路由的設定格式:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
ASP.NET Core 會將 https://localhost:5001/Movies/Edit/4
轉譯成對 Movies
控制器的 Edit
動作方法的要求,其參數 Id
為 4 (控制器方法也稱為動作方法)。
標記協助程式是 ASP.NET Core 的其中一種最受歡迎的新功能。 如需詳細資訊,請參閱其他資源。
開啟 Movies
控制器,並檢查兩個 Edit
動作方法。 下列程式碼示範 HTTP GET Edit
方法,這個方法會擷取電影,並填入 Edit.cshtml
Razor 檔案所產生的編輯表單。
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FindAsync(id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
下列程式碼示範 HTTP POST Edit
方法,這個方法會處理已發佈的電影值:
// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (id != movie.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
[Bind]
屬性是一種防止過度發佈的方式。 您應該只在想要變更的 [Bind]
屬性 (attribute) 中包含屬性 ( property)。 如需詳細資訊,請參閱保護控制器避免過度發佈。 ViewModels 提供防止過度發佈的替代方法。
請注意,第二個 Edit
動作方法的前面是 [HttpPost]
屬性。
// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (id != movie.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
HttpPost
屬性指定「只」能為 POST
要求叫用這個 Edit
方法。 您可以將 [HttpGet]
屬性套用至第一個編輯方法,但不需要執行此動作,因為 [HttpGet]
是預設值。
ValidateAntiForgeryToken
屬性是用來 防範偽造要求 ,並與編輯檢視表檔案 (Views/Movies/Edit.cshtml
) 所產生的防偽權杖成對。 編輯檢視表檔案使用 表單標記協助程式產生防偽權杖。
<form asp-action="Edit">
表單標記協助程式會產生隱藏的防偽權杖,其必須符合電影控制器的 Edit
方法中 [ValidateAntiForgeryToken]
產生的防偽權杖。 如需詳細資訊,請參閱防止 ASP.NET Core 中的跨網站要求偽造 (XSRF/CSRF) 攻擊。
HttpGet Edit
方法會採用電影 ID
參數,使用 Entity Framework FindAsync
方法查詢電影,並將選取的電影傳回 Edit 檢視。 如果找不到電影,會傳回 NotFound
(HTTP 404)。
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FindAsync(id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
當 Scaffolding 系統建立 Edit 檢視時,它會檢查 Movie
類別,並建立程式碼為類別的每個屬性轉譯 <label>
和 <input>
元素。 下列範例會顯示 Visual Studio Scaffolding 系統所產生的 Edit 檢視:
@model MvcMovie.Models.Movie
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Movie</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Genre" class="control-label"></label>
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
請注意檢視範本在檔案最上方指定 @model MvcMovie.Models.Movie
陳述式的方式。 @model MvcMovie.Models.Movie
指定檢視預期檢視範本的模型必須是 Movie
型別。
包含 Scaffold 的程式碼會使用數個標記協助程式方法來簡化 HTML 標記。 標籤標記協助程式顯示欄位的名稱 ("Title"、"ReleaseDate"、"Genre" 或 "Price")。 輸入標記協助程式轉譯 HTML <input>
元素。 驗證標記協助程式則顯示與該屬性相關聯的任何驗證訊息。
執行應用程式,並巡覽至 /Movies
URL。 按一下 Edit 連結。 在瀏覽器中,檢視頁面的原始檔。 <form>
元素產生的 HTML 如下所示。
<form action="/Movies/Edit/7" method="post">
<div class="form-horizontal">
<h4>Movie</h4>
<hr />
<div class="text-danger" />
<input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="7" />
<div class="form-group">
<label class="control-label col-md-2" for="Genre" />
<div class="col-md-10">
<input class="form-control" type="text" id="Genre" name="Genre" value="Western" />
<span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="Price" />
<div class="col-md-10">
<input class="form-control" type="text" 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" value="3.99" />
<span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
</div>
</div>
<!-- Markup 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>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8Inyxgp63fRFqUePGvuI5jGZsloJu1L7X9le1gy7NCIlSduCRx9jDQClrV9pOTTmqUyXnJBXhmrjcUVDJyDUMm7-MF_9rK8aAZdRdlOri7FmKVkRe_2v5LIHGKFcTjPrWPYnc9AdSbomkiOSaTEg7RU" />
</form>
<input>
元素位於 HTML <form>
元素中,而後者的 action
屬性設定為發佈到 /Movies/Edit/id
URL。 按一下 Save
按鈕時,表單資料將發佈至伺服器。 在結尾 </form>
元素之前的最後一行會顯示表單標記協助程式所產生的隱藏 XSRF 語彙基元。
處理 POST 要求
下列清單顯示 [HttpPost]
版本的 Edit
動作方法。
// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (id != movie.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
[ValidateAntiForgeryToken]
屬性驗證 表單標記協助程式中防偽權杖產生器所產生的隱藏 XSRF 權杖
模型繫結系統採用已發佈的表單值,並建立以 movie
參數傳遞的 Movie
物件。 ModelState.IsValid
屬性會驗證表單中提交的資料可用於修改 (編輯或更新) Movie
物件。 如果資料有效,則會進行儲存。 藉由呼叫資料庫內容的 SaveChangesAsync
方法,更新 (編輯) 的電影資料會儲存到資料庫。 儲存資料之後,程式碼將使用者重新導向至 MoviesController
類別的 Index
動作方法,此方法會顯示電影集合,包括剛剛所進行的變更。
在表單發佈至伺服器之前,用戶端驗證會對欄位檢查任何驗證規則。 如果出現任何驗證錯誤,即會顯示錯誤訊息,且不會發佈該表單。 如果已停用 JavaScript,就不會進行用戶端驗證,但伺服器偵測到無效的發佈值,因此會重新顯示表單值並顯示錯誤訊息。 稍後在本教學課程中,我們會更詳細檢查模型驗證。 Views/Movies/Edit.cshtml
檢視範本中的驗證標記協助程式負責顯示適當的錯誤訊息。
電影控制器中的所有 HttpGet
方法都遵循類似的模式。 他們會取得電影物件 (如果是 Index
則為物件清單),並將此物件 (模型) 傳遞至檢視。 Create
方法會將空白電影物件傳遞至 Create
檢視。 建立、編輯、刪除或以其他方式修改資料的所有方法都會在方法的 [HttpPost]
多載中執行這個動作。 修改 HTTP GET
方法中的資料會造成安全性風險。 修改 HTTP GET
方法中的資料,也違反 HTTP 最佳做法及架構式 REST 模式,此模式指定 GET 要求不應該變更應用程式的狀態。 也就是說,執行 GET 作業應該是安全的作業,沒有任何副作用,而且不會修改您的保存資料。
其他資源
此電影應用程式有個不錯的開始,但呈現效果不盡理想,例如 ReleaseDate 應該是兩個字。
如下所示,開啟 Models/Movie.cs
檔案並新增醒目提示的行:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MvcMovie.Models;
public class Movie
{
public int Id { get; set; }
public string? Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string? Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
在下一個教學課程中會說明 DataAnnotations
。 Display 屬性指定要顯示的欄位名稱 (在本例中為 "Release Date",而不是 "ReleaseDate")。 DataType 屬性指定資料的類型 (Date),因此不會顯示儲存在欄位中的時間資訊。
[Column(TypeName = "decimal(18, 2)")]
資料註解為必要項,因此 Entity Framework Core 可將 Price
正確對應到資料庫中的貨幣。 如需詳細資訊,請參閱資料類型。
瀏覽至 Movies
控制器,並將滑鼠指標停留在 Edit 連結,以查看目標 URL。
Views/Movies/Index.cshtml
檔案中的 Core MVC 錨點標籤協助程式會產生 [編輯]、[詳細資料] 和 [刪除] 連結。
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
標記協助程式可啟用伺服器端程式碼,以參與建立和轉譯 Razor 檔案中的 HTML 元素。 在上述程式碼中,AnchorTagHelper
會從控制器動作方法和路由識別碼動態產生 HTML href
屬性值。從您最喜愛的瀏覽器中使用 [檢視原始檔] 或使用開發工具來檢查產生的標記。 產生的 HTML 部分如下所示:
<td>
<a href="/Movies/Edit/4"> Edit </a> |
<a href="/Movies/Details/4"> Details </a> |
<a href="/Movies/Delete/4"> Delete </a>
</td>
回想 Program.cs
檔案中路由的設定格式:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
ASP.NET Core 會將 https://localhost:5001/Movies/Edit/4
轉譯成對 Movies
控制器的 Edit
動作方法的要求,其參數 Id
為 4 (控制器方法也稱為動作方法)。
標記協助程式是 ASP.NET Core 的其中一種最受歡迎的新功能。 如需詳細資訊,請參閱其他資源。
開啟 Movies
控制器,並檢查兩個 Edit
動作方法。 下列程式碼示範 HTTP GET Edit
方法,這個方法會擷取電影,並填入 Edit.cshtml
Razor 檔案所產生的編輯表單。
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FindAsync(id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
下列程式碼示範 HTTP POST Edit
方法,這個方法會處理已發佈的電影值:
// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (id != movie.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
[Bind]
屬性是一種防止過度發佈的方式。 您應該只在想要變更的 [Bind]
屬性 (attribute) 中包含屬性 ( property)。 如需詳細資訊,請參閱保護控制器避免過度發佈。 ViewModels 提供防止過度發佈的替代方法。
請注意,第二個 Edit
動作方法的前面是 [HttpPost]
屬性。
// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (id != movie.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
HttpPost
屬性指定「只」能為 POST
要求叫用這個 Edit
方法。 您可以將 [HttpGet]
屬性套用至第一個編輯方法,但不需要執行此動作,因為 [HttpGet]
是預設值。
ValidateAntiForgeryToken
屬性是用來 防範偽造要求 ,並與編輯檢視表檔案 (Views/Movies/Edit.cshtml
) 所產生的防偽權杖成對。 編輯檢視表檔案使用 表單標記協助程式產生防偽權杖。
<form asp-action="Edit">
表單標記協助程式會產生隱藏的防偽權杖,其必須符合電影控制器的 Edit
方法中 [ValidateAntiForgeryToken]
產生的防偽權杖。 如需詳細資訊,請參閱防止 ASP.NET Core 中的跨網站要求偽造 (XSRF/CSRF) 攻擊。
HttpGet Edit
方法會採用電影 ID
參數,使用 Entity Framework FindAsync
方法查詢電影,並將選取的電影傳回 Edit 檢視。 如果找不到電影,會傳回 NotFound
(HTTP 404)。
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FindAsync(id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
當 Scaffolding 系統建立 Edit 檢視時,它會檢查 Movie
類別,並建立程式碼為類別的每個屬性轉譯 <label>
和 <input>
元素。 下列範例會顯示 Visual Studio Scaffolding 系統所產生的 Edit 檢視:
@model MvcMovie.Models.Movie
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Movie</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Genre" class="control-label"></label>
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
請注意檢視範本在檔案最上方指定 @model MvcMovie.Models.Movie
陳述式的方式。 @model MvcMovie.Models.Movie
指定檢視預期檢視範本的模型必須是 Movie
型別。
包含 Scaffold 的程式碼會使用數個標記協助程式方法來簡化 HTML 標記。 標籤標記協助程式顯示欄位的名稱 ("Title"、"ReleaseDate"、"Genre" 或 "Price")。 輸入標記協助程式轉譯 HTML <input>
元素。 驗證標記協助程式則顯示與該屬性相關聯的任何驗證訊息。
執行應用程式,並巡覽至 /Movies
URL。 按一下 Edit 連結。 在瀏覽器中,檢視頁面的原始檔。 <form>
元素產生的 HTML 如下所示。
<form action="/Movies/Edit/7" method="post">
<div class="form-horizontal">
<h4>Movie</h4>
<hr />
<div class="text-danger" />
<input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="7" />
<div class="form-group">
<label class="control-label col-md-2" for="Genre" />
<div class="col-md-10">
<input class="form-control" type="text" id="Genre" name="Genre" value="Western" />
<span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="Price" />
<div class="col-md-10">
<input class="form-control" type="text" 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" value="3.99" />
<span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
</div>
</div>
<!-- Markup 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>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8Inyxgp63fRFqUePGvuI5jGZsloJu1L7X9le1gy7NCIlSduCRx9jDQClrV9pOTTmqUyXnJBXhmrjcUVDJyDUMm7-MF_9rK8aAZdRdlOri7FmKVkRe_2v5LIHGKFcTjPrWPYnc9AdSbomkiOSaTEg7RU" />
</form>
<input>
元素位於 HTML <form>
元素中,而後者的 action
屬性設定為發佈到 /Movies/Edit/id
URL。 按一下 Save
按鈕時,表單資料將發佈至伺服器。 在結尾 </form>
元素之前的最後一行會顯示表單標記協助程式所產生的隱藏 XSRF 語彙基元。
處理 POST 要求
下列清單顯示 [HttpPost]
版本的 Edit
動作方法。
// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (id != movie.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
[ValidateAntiForgeryToken]
屬性驗證 表單標記協助程式中防偽權杖產生器所產生的隱藏 XSRF 權杖
模型繫結系統採用已發佈的表單值,並建立以 movie
參數傳遞的 Movie
物件。 ModelState.IsValid
屬性會驗證表單中提交的資料可用於修改 (編輯或更新) Movie
物件。 如果資料有效,則會進行儲存。 藉由呼叫資料庫內容的 SaveChangesAsync
方法,更新 (編輯) 的電影資料會儲存到資料庫。 儲存資料之後,程式碼將使用者重新導向至 MoviesController
類別的 Index
動作方法,此方法會顯示電影集合,包括剛剛所進行的變更。
在表單發佈至伺服器之前,用戶端驗證會對欄位檢查任何驗證規則。 如果出現任何驗證錯誤,即會顯示錯誤訊息,且不會發佈該表單。 如果已停用 JavaScript,就不會進行用戶端驗證,但伺服器偵測到無效的發佈值,因此會重新顯示表單值並顯示錯誤訊息。 稍後在本教學課程中,我們會更詳細檢查模型驗證。 Views/Movies/Edit.cshtml
檢視範本中的驗證標記協助程式負責顯示適當的錯誤訊息。
電影控制器中的所有 HttpGet
方法都遵循類似的模式。 他們會取得電影物件 (如果是 Index
則為物件清單),並將此物件 (模型) 傳遞至檢視。 Create
方法會將空白電影物件傳遞至 Create
檢視。 建立、編輯、刪除或以其他方式修改資料的所有方法都會在方法的 [HttpPost]
多載中執行這個動作。 修改 HTTP GET
方法中的資料會造成安全性風險。 修改 HTTP GET
方法中的資料,也違反 HTTP 最佳做法及架構式 REST 模式,此模式指定 GET 要求不應該變更應用程式的狀態。 也就是說,執行 GET 作業應該是安全的作業,沒有任何副作用,而且不會修改您的保存資料。
其他資源
此電影應用程式有個不錯的開始,但呈現效果不盡理想,例如 ReleaseDate 應該是兩個字。
如下所示,開啟 Models/Movie.cs
檔案並新增醒目提示的行:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MvcMovie.Models
{
public class Movie
{
public int Id { get; set; }
public string? Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string? Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
}
在下一個教學課程中會說明 DataAnnotations
。 Display 屬性指定要顯示的欄位名稱 (在本例中為 "Release Date",而不是 "ReleaseDate")。 DataType 屬性指定資料的類型 (Date),因此不會顯示儲存在欄位中的時間資訊。
[Column(TypeName = "decimal(18, 2)")]
資料註解為必要項,因此 Entity Framework Core 可將 Price
正確對應到資料庫中的貨幣。 如需詳細資訊,請參閱資料類型。
瀏覽至 Movies
控制器,並將滑鼠指標停留在 Edit 連結,以查看目標 URL。
Views/Movies/Index.cshtml
檔案中的 Core MVC 錨點標籤協助程式會產生 [編輯]、[詳細資料] 和 [刪除] 連結。
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
標記協助程式可啟用伺服器端程式碼,以參與建立和轉譯 Razor 檔案中的 HTML 元素。 在上述程式碼中,AnchorTagHelper
會從控制器動作方法和路由識別碼動態產生 HTML href
屬性值。從您最喜愛的瀏覽器中使用 [檢視原始檔] 或使用開發工具來檢查產生的標記。 產生的 HTML 部分如下所示:
<td>
<a href="/Movies/Edit/4"> Edit </a> |
<a href="/Movies/Details/4"> Details </a> |
<a href="/Movies/Delete/4"> Delete </a>
</td>
回想 Program.cs
檔案中路由的設定格式:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
ASP.NET Core 會將 https://localhost:5001/Movies/Edit/4
轉譯成對 Movies
控制器的 Edit
動作方法的要求,其參數 Id
為 4 (控制器方法也稱為動作方法)。
標籤協助程式是 ASP.NET Core 中的熱門功能。 如需詳細資訊,請參閱其他資源。
開啟 Movies
控制器,並檢查兩個 Edit
動作方法。 下列程式碼示範 HTTP GET Edit
方法,這個方法會擷取電影,並填入 Edit.cshtml
Razor 檔案所產生的編輯表單。
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FindAsync(id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
下列程式碼示範 HTTP POST Edit
方法,這個方法會處理已發佈的電影值:
// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (id != movie.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
[Bind]
屬性是一種防止過度發佈的方式。 您應該只在想要變更的 [Bind]
屬性 (attribute) 中包含屬性 ( property)。 如需詳細資訊,請參閱保護控制器避免過度發佈。 ViewModels 提供防止過度發佈的替代方法。
請注意,第二個 Edit
動作方法的前面是 [HttpPost]
屬性。
// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (id != movie.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
HttpPost
屬性指定「只」能為 POST
要求叫用這個 Edit
方法。 您可以將 [HttpGet]
屬性套用至第一個編輯方法,但不需要執行此動作,因為 [HttpGet]
是預設值。
ValidateAntiForgeryToken
屬性是用來 防範偽造要求 ,並與編輯檢視表檔案 (Views/Movies/Edit.cshtml
) 所產生的防偽權杖成對。 編輯檢視表檔案使用 表單標記協助程式產生防偽權杖。
<form asp-action="Edit">
表單標記協助程式會產生隱藏的防偽權杖,其必須符合電影控制器的 Edit
方法中 [ValidateAntiForgeryToken]
產生的防偽權杖。 如需詳細資訊,請參閱防止 ASP.NET Core 中的跨網站要求偽造 (XSRF/CSRF) 攻擊。
HttpGet Edit
方法會採用電影 ID
參數,使用 Entity Framework FindAsync
方法查詢電影,並將選取的電影傳回 Edit 檢視。 如果找不到電影,會傳回 NotFound
(HTTP 404)。
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FindAsync(id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
當 Scaffolding 系統建立 Edit 檢視時,它會檢查 Movie
類別,並建立程式碼為類別的每個屬性轉譯 <label>
和 <input>
元素。 下列範例會顯示 Visual Studio Scaffolding 系統所產生的 Edit 檢視:
@model MvcMovie.Models.Movie
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Movie</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Genre" class="control-label"></label>
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
請注意檢視範本在檔案最上方指定 @model MvcMovie.Models.Movie
陳述式的方式。 @model MvcMovie.Models.Movie
指定檢視預期檢視範本的模型必須是 Movie
型別。
包含 Scaffold 的程式碼會使用數個標記協助程式方法來簡化 HTML 標記。 標籤標記協助程式顯示欄位的名稱 ("Title"、"ReleaseDate"、"Genre" 或 "Price")。 輸入標記協助程式轉譯 HTML <input>
元素。 驗證標記協助程式則顯示與該屬性相關聯的任何驗證訊息。
執行應用程式,並巡覽至 /Movies
URL。 按一下 Edit 連結。 在瀏覽器中,檢視頁面的原始檔。 <form>
元素產生的 HTML 如下所示。
<form action="/Movies/Edit/7" method="post">
<div class="form-horizontal">
<h4>Movie</h4>
<hr />
<div class="text-danger" />
<input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="7" />
<div class="form-group">
<label class="control-label col-md-2" for="Genre" />
<div class="col-md-10">
<input class="form-control" type="text" id="Genre" name="Genre" value="Western" />
<span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="Price" />
<div class="col-md-10">
<input class="form-control" type="text" 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" value="3.99" />
<span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
</div>
</div>
<!-- Markup 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>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8Inyxgp63fRFqUePGvuI5jGZsloJu1L7X9le1gy7NCIlSduCRx9jDQClrV9pOTTmqUyXnJBXhmrjcUVDJyDUMm7-MF_9rK8aAZdRdlOri7FmKVkRe_2v5LIHGKFcTjPrWPYnc9AdSbomkiOSaTEg7RU" />
</form>
<input>
元素位於 HTML <form>
元素中,而後者的 action
屬性設定為發佈到 /Movies/Edit/id
URL。 按一下 Save
按鈕時,表單資料將發佈至伺服器。 在結尾 </form>
元素之前的最後一行會顯示表單標記協助程式所產生的隱藏 XSRF 語彙基元。
處理 POST 要求
下列清單顯示 [HttpPost]
版本的 Edit
動作方法。
// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (id != movie.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
[ValidateAntiForgeryToken]
屬性驗證 表單標記協助程式中防偽權杖產生器所產生的隱藏 XSRF 權杖
模型繫結系統採用已發佈的表單值,並建立以 movie
參數傳遞的 Movie
物件。 ModelState.IsValid
屬性會驗證表單中提交的資料可用於修改 (編輯或更新) Movie
物件。 如果資料有效,則會進行儲存。 藉由呼叫資料庫內容的 SaveChangesAsync
方法,更新 (編輯) 的電影資料會儲存到資料庫。 儲存資料之後,程式碼將使用者重新導向至 MoviesController
類別的 Index
動作方法,此方法會顯示電影集合,包括剛剛所進行的變更。
在表單發佈至伺服器之前,用戶端驗證會對欄位檢查任何驗證規則。 如果出現任何驗證錯誤,即會顯示錯誤訊息,且不會發佈該表單。 如果已停用 JavaScript,就不會進行用戶端驗證,但伺服器偵測到無效的發佈值,因此會重新顯示表單值並顯示錯誤訊息。 稍後在本教學課程中,我們會更詳細檢查模型驗證。 Views/Movies/Edit.cshtml
檢視範本中的驗證標記協助程式負責顯示適當的錯誤訊息。
電影控制器中的所有 HttpGet
方法都遵循類似的模式。 他們會取得電影物件 (如果是 Index
則為物件清單),並將此物件 (模型) 傳遞至檢視。 Create
方法會將空白電影物件傳遞至 Create
檢視。 建立、編輯、刪除或以其他方式修改資料的所有方法都會在方法的 [HttpPost]
多載中執行這個動作。 修改 HTTP GET
方法中的資料會造成安全性風險。 修改 HTTP GET
方法中的資料,也違反 HTTP 最佳做法及架構式 REST 模式,此模式指定 GET 要求不應該變更應用程式的狀態。 也就是說,執行 GET 作業應該是安全的作業,沒有任何副作用,而且不會修改您的保存資料。
其他資源
此電影應用程式有個不錯的開始,但呈現效果不盡理想,例如 ReleaseDate 應該是兩個字。
如下所示,開啟 Models/Movie.cs
檔案並新增醒目提示的行:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MvcMovie.Models
{
public class Movie
{
public int Id { get; set; }
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
}
接下來的教學課程會涵蓋 DataAnnotations。 Display 屬性指定要顯示的欄位名稱 (在本例中為 "Release Date",而不是 "ReleaseDate")。 DataType 屬性指定資料的類型 (Date),因此不會顯示儲存在欄位中的時間資訊。
[Column(TypeName = "decimal(18, 2)")]
資料註解為必要項,因此 Entity Framework Core 可將 Price
正確對應到資料庫中的貨幣。 如需詳細資訊,請參閱資料類型。
瀏覽至 Movies
控制器,並將滑鼠指標停留在 Edit 連結,以查看目標 URL。
Views/Movies/Index.cshtml
檔案中的 Core MVC 錨點標籤協助程式會產生 [編輯]、[詳細資料] 和 [刪除] 連結。
<a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-action="Details" asp-route-id="@item.ID">Details</a> |
<a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
標記協助程式可啟用伺服器端程式碼,以參與建立和轉譯 Razor 檔案中的 HTML 元素。 在上述程式碼中,AnchorTagHelper
會從控制器動作方法和路由識別碼動態產生 HTML href
屬性值。從您最喜愛的瀏覽器中使用 [檢視原始檔] 或使用開發工具來檢查產生的標記。 產生的 HTML 部分如下所示:
<td>
<a href="/Movies/Edit/4"> Edit </a> |
<a href="/Movies/Details/4"> Details </a> |
<a href="/Movies/Delete/4"> Delete </a>
</td>
回想 Startup.cs
檔案中路由的設定格式:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
ASP.NET Core 會將 https://localhost:5001/Movies/Edit/4
轉譯成對 Movies
控制器的 Edit
動作方法的要求,其參數 Id
為 4 (控制器方法也稱為動作方法)。
開啟 Movies
控制器,並檢查兩個 Edit
動作方法。 下列程式碼示範 HTTP GET Edit
方法,這個方法會擷取電影,並填入 Edit.cshtml
Razor 檔案所產生的編輯表單。
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FindAsync(id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
下列程式碼示範 HTTP POST Edit
方法,這個方法會處理已發佈的電影值:
// POST: Movies/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
if (id != movie.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("Index");
}
return View(movie);
}
[Bind]
屬性是一種防止過度發佈的方式。 您應該只在想要變更的 [Bind]
屬性 (attribute) 中包含屬性 ( property)。 如需詳細資訊,請參閱保護控制器避免過度發佈。 ViewModels 提供防止過度發佈的替代方法。
請注意,第二個 Edit
動作方法的前面是 [HttpPost]
屬性。
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
if (id != movie.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
HttpPost
屬性指定「只」能為 POST
要求叫用這個 Edit
方法。 您可以將 [HttpGet]
屬性套用至第一個編輯方法,但不需要執行此動作,因為 [HttpGet]
是預設值。
ValidateAntiForgeryToken
屬性是用來 防範偽造要求 ,並與編輯檢視表檔案 (Views/Movies/Edit.cshtml
) 所產生的防偽權杖成對。 編輯檢視表檔案使用 表單標記協助程式產生防偽權杖。
<form asp-action="Edit">
表單標記協助程式會產生隱藏的防偽權杖,其必須符合電影控制器的 Edit
方法中 [ValidateAntiForgeryToken]
產生的防偽權杖。 如需詳細資訊,請參閱防止 ASP.NET Core 中的跨網站要求偽造 (XSRF/CSRF) 攻擊。
HttpGet Edit
方法會採用電影 ID
參數,使用 Entity Framework FindAsync
方法查詢電影,並將選取的電影傳回 Edit 檢視。 如果找不到電影,會傳回 NotFound
(HTTP 404)。
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FindAsync(id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
當 Scaffolding 系統建立 Edit 檢視時,它會檢查 Movie
類別,並建立程式碼為類別的每個屬性轉譯 <label>
和 <input>
元素。 下列範例會顯示 Visual Studio Scaffolding 系統所產生的 Edit 檢視:
@model MvcMovie.Models.Movie
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Movie</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Genre" class="control-label"></label>
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
請注意檢視範本在檔案最上方指定 @model MvcMovie.Models.Movie
陳述式的方式。 @model MvcMovie.Models.Movie
指定檢視預期檢視範本的模型必須是 Movie
型別。
包含 Scaffold 的程式碼會使用數個標記協助程式方法來簡化 HTML 標記。 標籤標記協助程式顯示欄位的名稱 ("Title"、"ReleaseDate"、"Genre" 或 "Price")。 輸入標記協助程式轉譯 HTML <input>
元素。 驗證標記協助程式則顯示與該屬性相關聯的任何驗證訊息。
執行應用程式,並巡覽至 /Movies
URL。 按一下 Edit 連結。 在瀏覽器中,檢視頁面的原始檔。 <form>
元素產生的 HTML 如下所示。
<form action="/Movies/Edit/7" method="post">
<div class="form-horizontal">
<h4>Movie</h4>
<hr />
<div class="text-danger" />
<input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="7" />
<div class="form-group">
<label class="control-label col-md-2" for="Genre" />
<div class="col-md-10">
<input class="form-control" type="text" id="Genre" name="Genre" value="Western" />
<span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="Price" />
<div class="col-md-10">
<input class="form-control" type="text" 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" value="3.99" />
<span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
</div>
</div>
<!-- Markup 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>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8Inyxgp63fRFqUePGvuI5jGZsloJu1L7X9le1gy7NCIlSduCRx9jDQClrV9pOTTmqUyXnJBXhmrjcUVDJyDUMm7-MF_9rK8aAZdRdlOri7FmKVkRe_2v5LIHGKFcTjPrWPYnc9AdSbomkiOSaTEg7RU" />
</form>
<input>
元素位於 HTML <form>
元素中,而後者的 action
屬性設定為發佈到 /Movies/Edit/id
URL。 按一下 Save
按鈕時,表單資料將發佈至伺服器。 在結尾 </form>
元素之前的最後一行會顯示表單標記協助程式所產生的隱藏 XSRF 語彙基元。
處理 POST 要求
下列清單顯示 [HttpPost]
版本的 Edit
動作方法。
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
if (id != movie.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
[ValidateAntiForgeryToken]
屬性驗證 表單標記協助程式中防偽權杖產生器所產生的隱藏 XSRF 權杖
模型繫結系統採用已發佈的表單值,並建立以 movie
參數傳遞的 Movie
物件。 ModelState.IsValid
屬性會驗證表單中提交的資料可用於修改 (編輯或更新) Movie
物件。 如果資料有效,則會進行儲存。 藉由呼叫資料庫內容的 SaveChangesAsync
方法,更新 (編輯) 的電影資料會儲存到資料庫。 儲存資料之後,程式碼將使用者重新導向至 MoviesController
類別的 Index
動作方法,此方法會顯示電影集合,包括剛剛所進行的變更。
在表單發佈至伺服器之前,用戶端驗證會對欄位檢查任何驗證規則。 如果出現任何驗證錯誤,即會顯示錯誤訊息,且不會發佈該表單。 如果已停用 JavaScript,就不會進行用戶端驗證,但伺服器偵測到無效的發佈值,因此會重新顯示表單值並顯示錯誤訊息。 稍後在本教學課程中,我們會更詳細檢查模型驗證。 Views/Movies/Edit.cshtml
檢視範本中的驗證標記協助程式負責顯示適當的錯誤訊息。
電影控制器中的所有 HttpGet
方法都遵循類似的模式。 他們會取得電影物件 (如果是 Index
則為物件清單),並將此物件 (模型) 傳遞至檢視。 Create
方法會將空白電影物件傳遞至 Create
檢視。 建立、編輯、刪除或以其他方式修改資料的所有方法都會在方法的 [HttpPost]
多載中執行這個動作。 修改 HTTP GET
方法中的資料會造成安全性風險。 修改 HTTP GET
方法中的資料,也違反 HTTP 最佳做法及架構式 REST 模式,此模式指定 GET 要求不應該變更應用程式的狀態。 也就是說,執行 GET 作業應該是安全的作業,沒有任何副作用,而且不會修改您的保存資料。