[ASP.NET MVC] 檢視Controller產生的Edit方法及View。
Edit-編輯的方法及View
開啟 Movies Controller,可以看到兩個Edit的Action方法,如下:
// 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
// 若要免於過量張貼攻擊,請啟用想要繫結的特定屬性,如需
// 詳細資訊,請參閱 http://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]屬性,這個屬性表示這個Edit方法只能由POST要求呼叫。
也可以在第一個Edit方法的前面加上[HttpGet]屬性。不過並不建議,因為[HttpGet]是預設的不用特別綁定屬性,綁定屬性也是另一種安全機制。因此,只需要在你想設定為其他屬性時,才加上。
[ValidateAntiForgeryToken]屬性建立一個隱藏的表單,Edit方法的[ValidateAntiForgeryToken]配對View(Views\Movies\Edit.cshtml)裡的@Html.AntiForgeryToken() 。用來防止請求偽造。
HttpGet Edit方法帶有ID參數。在Index View點選Edit時,會將ID參數帶至Controller,依照ID並透過Entity Framework的Find方法(Edit方法中的Movie movie = db.Movies.Find(id);),回傳所選擇的電影到Edit View畫面。
如果找不到電影,會回傳HttpNotFound,如下圖指定ID參數為100時:
當產生Edit view的畫面時,會檢視Movies Class與每個<label>和<input>標籤的Class屬性,建立程式碼。
例如Edit view :
@model MyMVC.Models.Movie
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Movie</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.ID)
<div class="form-group">
@Html.LabelFor(model => model.Title, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Title, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Title, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.ReleaseDate, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.ReleaseDate, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.ReleaseDate, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Genre, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Genre, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Genre, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Price, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Price, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })
</div>
</div>
<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")
}
在View(Views\Movies\Edit.cshtml)的頂端可以看到@model MvcMovie.Models.Movie,指定了這個View的Model為Movie(Model\Movie.cs)。
程式碼中可以看到多種的helper方法:
Html.LabelFor 顯示名稱 ("Title", "ReleaseDate", "Genre", "Price")。
Html.EditorFor 顯示<input>標籤。
Html.ValidationMessageFor 顯示與屬相相關的驗證訊息。
執行應用程式,將網址列引導到/Movies ,按下Edit連結。在網頁中右鍵檢視原始碼,在約47行的位置,可以看到表單元素的HTML如下:
<form action="/Movies/Edit/1" method="post">
<input name="__RequestVerificationToken" type="hidden" value="27cD0FgOkCjmkxU7NcYv1wgAmvggW5R2RCixpQ0P59RBMLHNB3vUGYRxqnlw7Ds1ZHkMpY5H-yqx9dhUGnN1PkYZ6oelzseW8uwGInaIfvs1" />
<div class="form-horizontal">
<h4>Movie</h4>
<hr />
<input data-val="true" data-val-number="欄位 ID 必須是數字。" data-val-required="ID 欄位是必要項。" id="ID" name="ID" type="hidden" value="1" />
<div class="form-group">
<label class="control-label col-md-2" for="Title">Title</label>
<div class="col-md-10">
<input class="form-control text-box single-line" id="Title" name="Title" type="text" value="高年級實習生" />
<span class="field-validation-valid text-danger" data-valmsg-for="Title" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="ReleaseDate">ReleaseDate</label>
<div class="col-md-10">
<input class="form-control text-box single-line" data-val="true" data-val-date="欄位 ReleaseDate 必須是日期。" data-val-required="ReleaseDate 欄位是必要項。" id="ReleaseDate" name="ReleaseDate" type="datetime" value="2015/10/17 上午 12:00:00" />
<span class="field-validation-valid text-danger" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="Genre">Genre</label>
<div class="col-md-10">
<input class="form-control text-box single-line" id="Genre" name="Genre" type="text" value="劇情" />
<span class="field-validation-valid text-danger" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="Price">Price</label>
<div class="col-md-10">
<input class="form-control text-box single-line" data-val="true" data-val-number="欄位 Price 必須是數字。" data-val-required="Price 欄位是必要項。" id="Price" name="Price" type="text" value="100.00" />
<span class="field-validation-valid text-danger" data-valmsg-for="Price" data-valmsg-replace="true"></span>
</div>
</div>
<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>
</form>
在上面原始碼中可以看到<input>標籤於HTML<form>中。
第一行<form>的action="/Movies/Edit/1"定義了POST的URL為/Movies/Edit,點擊Save按鈕時,表單的資料將會POST到Server。
第二行的原始碼中,@Html.AntiForgeryToken()建立了隱藏的RSRF token,防止請求偽造。
處理POST Request
下面顯示Edit action方法的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 屬性驗證在View中的@Html.AntiForgeryToken()產生XSRF token。
ASP.NET MVC model binder 取得POST的FORM表單中的值,並建立一個作為movie參數傳遞movie的Movie物件。
ModelState.IsValid方法檢驗表單中提交的資料是否可用於修改(edit 或 update)Movie物件。
如果資料是有效的,電影的資料會被儲存到DB(MovieDBContext)的Movie集合裡。
新的電影資料通過MovieDBContext裡的SaveChanges方法儲存到資料庫中。
在儲存資料後,程式碼return RedirectToAction("Index"),重新將使用者引向MoviesController的Index方法,在Index View中顯示電影列表,包括了剛剛對電影所做的變更。
在View Edit.cshtml 中,Html.ValidationMessageFor 負責顯示錯誤訊息,當輸入的資料有錯誤時,可以看到欄位下方顯示錯誤訊息。
修改日期欄位的顯示方式
在上面的錯誤訊息中,日期欄位帶出的值 2015/10/17 上午 12:00:00 ,必須去掉後面的時間 上午 12:00:00 ,才能通過資料驗證。
接著,來修改日期欄位,讓日期欄位更好看一些,開啟 Models\Movie.cs
在最上端引用:
using System.ComponentModel.DataAnnotations;
在Movies Class中加入以下程式:
[Display(Name = "日期")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
最後的Movie.cs程式如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
namespace MyMVC.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
[Display(Name = "日期")]
[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 = "日期",指定了要顯示的名稱( "日期" 會取代原本的欄位名稱 "ReleaseDate" )。
DataType屬性指定資料的型態,DataType.Date指定為日期格式,便不會在日期後面帶出時間。如同HTML標籤中的type="date"。
DisplayFormat屬性則是為了防止日期格式顯示不正確的BUG。
ActionLink
執行應用程式,將滑鼠放在Edit連結上方,可以看到左下角的連結URL。
Edit連結是由View Views\Movies\Index.cshtml 的Html.ActionLink方法所產生:
@Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
上面的程式碼中,@Html可以幫忙產生System.Web.Mvc.WebViewPage基本的Class屬性,ActionLink方法可以建立ㄧ個可以連結到Controller的超連結。
ActionLink方法的第一個參數表示超連結要顯示的文字,如同<a>Edit</a>。若修改為"編輯",那麼在Index畫面就可以看到超連結的文字為中文編輯。
第二個參數為Action方法的名稱,"Edit"即為MoviesController的Edit Action方法。
第三個參數為資料的參數,new { id=item.ID },表示進入Edit Action方法時電影的id參數。
在上面的Index畫面可以看到左下角的連結http://localhost:56698/Movies/Edit/1。預設的路由(檢視App_Start\RouteConfig.cs檔)URL為 "{controller}/{action}/{id}"。
因此,http://localhost:56698/Movies/Edit/1 表示對MoviesController中的Edit Action方法發出參數id為1的請求。
檢視 App_Start\RouteConfig.cs 檔,MapRoute方法中url: "{controller}/{action}/{id}"提供Controller、Action 方法及參數給HTTP請求。
除此之外,MapRoute也用於HtmlHelpers的ActionLink設定URL的 Controller、Action 方法及參數。
routes.MapRoute
(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
也可以使用查詢字串(query string)來操作Action方法。例如:http://localhost:56698/Movies/Edit?id=1,傳遞參數id=1到MoviesController的Edit action方法,如下: