檢查電影控制器的編輯動作方法和檢視

作者: 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; }

接下來的教學課程中將涵蓋 DataAnnotationsDisplay 屬性指定要顯示的欄位名稱 (在本例中為 "Release Date",而不是 "ReleaseDate")。 DataType屬性會指定資料類型,在此案例中為日期,因此不會顯示儲存在欄位中的時間資訊。 Chrome 瀏覽器中的 Bug 需要 DisplayFormat 屬性,該錯誤會轉譯日期格式不正確。

執行應用程式並流覽至 Movies 控制器。 將滑鼠指標停留在 [編輯] 連結上方,以查看其所連結的 URL。

EditLink_sm

Edit連結是由 Html.ActionLinkViews\Movies\Index.cshtml檢視中的 方法所產生:

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

Html.ActionLink

物件 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 方法 MoviesEdit 其參數 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 動作方法。

EditQueryString

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) 。

方法 HttpGetEdit 會採用電影識別碼參數、使用 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 集合) 。 新的電影資料會藉由呼叫 SaveChangesMovieDBContext 方法來儲存至資料庫。 儲存資料之後,程式碼將使用者重新導向至 MoviesController 類別的 Index 動作方法,此方法會顯示電影集合,包括剛剛所進行的變更。

一旦用戶端驗證判斷欄位的值無效,就會顯示錯誤訊息。 如果 JavaScript 已停用,則會停用用戶端驗證。 不過,伺服器偵測到張貼的值無效,而且表單值會以錯誤訊息重新顯示。

稍後在本教學課程中會更詳細地檢查驗證。

Html.ValidationMessageForEdit.cshtml檢視範本中的協助程式會負責顯示適當的錯誤訊息。

abcNotValid

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

  1. 從 [ 工具] 功能表中,按一下 [NuGet 套件管理員],然後按一下 [ 管理方案的 NuGet 套件]。

    [工具] 功能表的螢幕擷取畫面,以開始非英文地區設定的 jQuery 驗證。

  2. 在左窗格中,選取 [流覽]*。* (請參閱下圖。)

  3. 在輸入方塊中,輸入 Globalize*。

    輸入方塊要輸入 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>

在下一個教學課程中,我們將實作搜尋功能。