7.9 從控制器訪問模型資料
本節,您將建立一個新的MoviesController類並編寫代碼讀取電影資料並用視圖模板在瀏覽器中顯示他們。在繼續前,請先編譯你的應用程式,不然下面找不到模型類。
右鍵Controllers檔案夾建立一個MoviesController控制器。選擇下面的選項:
· Controller name: MoviesController. (預設. )
· Template: Controller with read/write actions and views, using Entity Framework.
· Model class: MOVIE (MvcMovie.DAL)
· Data context class: MovieEntities (MvcMovie.DAL)
· Views: Razor (CSHTML). (預設.)
單擊“Add”。Visual Studio建立了下面的檔案夾和檔案:
1. 在項目的Controller檔案夾下MoviesController.cs檔案
2. 在項目的View檔案夾下Movies檔案夾
3. 在新建立的檔案夾Views\Movies下Create.cshtml, Delete.cshtml,
Details.cshtml, Edit.cshtml, and Index.cshtml。
ASP.NET MVC 3架構機制自動建立CRUD(create, read, update, and delete)回應程式法和視圖。您現在擁有了全部的web應用程式的功能,支援增加、顯示、編輯、刪除電影作品。
運行應用程式並通過在瀏覽器地址欄中URL後追加/Movies來瀏覽Movies控制器。因為應用程式依託預設的路由(在Global.asax檔案中定義),瀏覽請求http://localhost:xxxxx/Movies被路由到Movies控制器的預設方法Index。換句話說,http://localhost:xxxxx/Movies實際上和http://localhost:xxxxx/Movies/Index是一樣的。因為您還沒有添加任何東西,所以電影列表是空的。
7.10 建立電影
選擇“Create New”連結。輸入一個電影的詳細資料然後單擊“Create”按鈕。
單擊“Create”按鈕使頁面回傳到伺服器端(那裡的電影資訊將會儲存在資料庫中)。您將重新導向到/Movies URL,在列表裡您能看到剛被添加的電影資訊。
此時,Visual Studio還自動實現了資料驗證,在的建立頁面,不輸入資訊,然後點擊“Create”會出現如下結果(後面我們會介紹自訂模型驗證):
運行結果:
查看資料庫:
您可以建立一些電影作品,測試全部的功能,編輯、明細、刪除。
7.11 審視代碼
開啟Controllers\MoviesController.cs檔案並審視產生的Index方法代碼。部分控制器Index方法的代碼如下所示:
public class MoviesController : Controller { private MovieEntities db = new MovieEntities(); // // GET: /Movies/ public ViewResult Index() { return View(db.MOVIEs.ToList()); }}
如前面所述,下面的行在MoviesController類中執行個體化了一個電影資料庫的內容。
private MovieEntities db = new MovieEntities();
Movies控制器返回資料庫中的所有電影資料實體並把結果傳遞給Index視圖。
強型別模型和@model關鍵字
在教程的前面部分,您瞭解了如何使用ViewBag對象把資料通過控制器傳遞給視圖。ViewBag是一個動態對象,提供了方便的遲綁定方式將資訊傳遞給視圖。
ASP.NET MVC也支援傳遞強型別資料給視圖模板。這種強型別的方式支援編譯時間檢查代碼和豐富的智能感知。我們將在MoviesController類和Index.cshtml視圖模板中採用這種方式。請注意該代碼建立一個List對象時調用Index方法中View Helper方法。代碼通過控制器傳遞電影列表給視圖:
public ViewResult Index() { return View(db.MOVIEs.ToList()); }
通過在視圖模板檔案的頂部包含@model運算式,您可以指定在視圖中您期望使用的物件類型。當您建立電影控制器的時候,Visual Studio自動在視圖模板檔案Index.cshtml的頂部包含@model運算式:@model IEnumerable<MvcMovie.DAL.MOVIE>
@model指令允許您訪問由控制器使用強型別Model對象傳遞給視圖的電影列表。比如,Index.cshtml模板,代碼通過foreach運算式遍曆了基於強型別的電影資料。
@foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.TITLE) </td> <td> @Html.DisplayFor(modelItem => item.RELEASEDATE) </td> <td> @Html.DisplayFor(modelItem => item.GENRE) </td> <td> @Html.DisplayFor(modelItem => item.PRICE) </td> <td> @Html.ActionLink("Edit", "Edit", new { id=item.ID }) | @Html.ActionLink("Details", "Details", new { id=item.ID }) | @Html.ActionLink("Delete", "Delete", new { id=item.ID }) </td> </tr>}
由於實體物件是強型別(被用作可枚舉對象),迴圈中的每個item對象都是強型別的Movie。其他好處指的是編譯時間檢查代碼和全部的智能感知支援。
您現在擁有了資料庫並通過簡單的列表展現了他們。
審視編輯方法和視圖
在這一節中,您將審視Movies控制器產生的回應程式法和視圖。然後您將添加
一個自訂搜尋網頁面。運行程式並通過在URL追加/Moives瀏覽movie控制器。把滑鼠移至上方在Edit連結上,看看它執行的URL.
Edit的連結由視圖Views\Movies\Index.cshtml 的Html.ActionLink方法產生。
@Html.ActionLink("Edit", "Edit", new { id=item.ID })
Html對象是一個助手,它是WebViewPage基類暴露的屬性。助手的ActionLink方法可以很容易的產生HTML超連結,它指向控制器的回應程式法。ActionLink的第一個參數表示超連結的文本呈現(比如:<a>Edit Me</a>),第二個參數是要調用的回應程式法的名稱,最後一個參數產生路由資料的匿名對象(anonymous object,這裡指ID=4)。
您可以使用查詢字串(query string)傳遞參數給回應程式法。比如URLhttp://localhost:xxxxx/Movies/Edit?ID=4傳遞ID=4給Movies控制器的Edit方法。
開啟Movies控制器。有兩個Edit方法,如下所示:
// GET: /Movies/Edit/5 public ActionResult Edit(decimal id) { MOVIE movie = db.MOVIEs.Single(m => m.ID == id); return View(movie); } // // POST: /Movies/Edit/5 [HttpPost] public ActionResult Edit(MOVIE movie) { if (ModelState.IsValid) { db.MOVIEs.Attach(movie); db.ObjectStateManager.ChangeObjectState(movie, EntityState.Modified); db.SaveChanges(); return RedirectToAction("Index"); } return View(movie); }
注意第二個Edit方法前面是HttpPost屬性。它表明這個重載的Edit方法只能被
POST請求調用。您也可以給第一個Edit方法採用HttpGet屬性,但是這不是必須的,應為方法預設為HttpGet(回應程式法隱含的了HttpGet屬性將被認為是HttpGet方法)。
HttpGet方法將電影的ID作為參數,並使用Entity Framework的Find方法尋找電影,然後返回被找到的電影給視圖模板。當架構體系建立編輯模板時,它檢查Movie類並為每個屬性產生<label><input>代碼去呈現元素。下面的代碼展示了自動產生的Edit視圖模板:
@model MvcMovie.DAL.MOVIE@{ ViewBag.Title = "Edit";}<h2>Edit</h2><script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script><script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>@using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>MOVIE</legend> @Html.HiddenFor(model => model.ID) <div class="editor-label"> @Html.LabelFor(model => model.TITLE) </div> <div class="editor-field"> @Html.EditorFor(model => model.TITLE) @Html.ValidationMessageFor(model => model.TITLE) </div> <div class="editor-label"> @Html.LabelFor(model => model.RELEASEDATE) </div> <div class="editor-field"> @Html.EditorFor(model => model.RELEASEDATE) @Html.ValidationMessageFor(model => model.RELEASEDATE) </div> <div class="editor-label"> @Html.LabelFor(model => model.GENRE) </div> <div class="editor-field"> @Html.EditorFor(model => model.GENRE) @Html.ValidationMessageFor(model => model.GENRE) </div> <div class="editor-label"> @Html.LabelFor(model => model.PRICE) </div> <div class="editor-field"> @Html.EditorFor(model => model.PRICE) @Html.ValidationMessageFor(model => model.PRICE) </div> <p> <input type="submit" value="Save" /> </p> </fieldset>}<div> @Html.ActionLink("Back to List", "Index")</div>
<form>中的<input>元素表示一個提交按鈕,點擊時將提交頁面資料到Controller中的Edit方法。
處理POST請求
由架構體系產生的屬性為HttpGet的Edit方法沒有檢查傳給它的ID的有效性。如果使用者刪除URL的ID欄位,錯誤資訊如下所示:
使用者還可以傳遞一個不存在的ID,比如:
http://localhost:xxxxx/Movies/Edit/1234.您可以給HttpGet Edit方法做兩點修改來限制URL。首先,把ID參數改為預設值為0 (id不是必須傳遞)。
您也可以在回傳電影對象給視圖模板之前,檢查Find方法是否真正的找到了電影資訊。
public ActionResult Edit(decimal id=0) { MOVIE movie = db.MOVIEs.FirstOrDefault(m => m.ID == id); if(movie==null) { return HttpNotFound(); } return View(movie); }
如果沒有找到,HttpNotFound方法被調用。
所有的HttpGet方法都遵循類似的模式。它們擷取一個電影對象(在Index中返回對象列表),然後傳遞模型給視圖。Create方法傳遞一個空電影對象給Create視圖。所有的方法(建立、編輯、刪除)都有一個HttpPost的重載方法。
在HTTP GET方法中修改資料存在安全風險,在部落格ASP.NET MVC Tip #46 – Don’t use Delete Links because they create Security Holes中有描述。在HTTP GET方法中修改資料違反了HTTP的最佳實務和REST架構模式(其中規定GET請求不應改變應用程式狀態)。換句話,執行GET操作應該是一個無副作用的安全操作。