在上一文章中由Entity Framework(實體框架)去實現了對數據庫的CURD操做。在本篇文章中,主要是調試修改自動生成的動做方法和視圖,以及調試編輯功能與編輯功能的Book控制器。html
首先,在Visual Studio中運行一下上次的應用程序,經過瀏覽器訪問http://localhost:36878/Book。將鼠標指針移到瀏覽器中的一個「Edit」連接上,就能夠看到這個「Edit」指向的URL。以下圖紅框所示。jquery
「Edit」 連接經過Html.ActionLink方法在瀏覽中生成指向Views\Book\Edit.cshtml 視圖的連接。代碼以下。git
@Html.ActionLink("Edit", "Edit", new { id=item.BookID })
如上圖中所示,這個「HTML」對象是經過System.Web.Mvc.WebViewPage基類的屬性暴露出來的。這個ActionLink方法能夠很容易地動態生成一個連接,這個連接指向控制器的一個動做方法。ActionLink方法的第一個參數是顯示連接的文本信息(例如,「Edit」)。第二個參數是指動做方法要調用控制器中的方法名稱。最後一個參數是生成的路由數據(如示例中的ID=8)的一個匿名對象。 github
在上圖中所示的生成的「Edit」連接是http://localhost:36878/Book/Edit/8。默認路由URL模板是{controller}/{action}/{id}(模板代碼位於App_Start\ RouteConfig.cs)。所以,瀏覽器會發出http://localhost:36878/Book/Edit/8 這個URL請求,同時傳遞參數ID=8給Asp.net MVC的Book控制器的「Edit」方法。下面就是App_Start\ RouteConfig.cs裏面的路由模板設置。web
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 } ); }
您還可使用action方法後面帶上查詢參數。例如,URL http://localhost:36878/Book/Edit?ID=8,經過ID=8,把參數ID的數據(8)傳遞給了Book控制器的「Edit」方法。以上兩種方式的執行結果以下圖。數據庫
其次,咱們打開BookController.cs文件,來仔細看看裏面的兩個「Edit」方法。代碼以下所示。瀏覽器
// // GET: /Book/Edit/8 public ActionResult Edit(int id = 0) { Book book = db.Books.Find(id); if (book == null) { return HttpNotFound(); } return View(book); } // // POST: /Book/Edit/8 [HttpPost] public ActionResult Edit(Book book)
{ if (ModelState.IsValid) { db.Entry(book).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } return View(book); }
請注意,第二個「Edit」方法的上面有一個「HttpPost」屬性。這個屬性指定了此方法只能經過POST請求來調用。也能夠給第一個方法上面加上「HTTPGET」屬性,但這不是必要的,由於HTTPGET屬性是默認的。 (Visual Studio將會給全部沒有指明的方法,默認且隱性的分配HTTPGET屬性,這種隱性分配了「HTTPGET」屬性的方法稱爲HTTPGET操做方法。)
第一個「HTTPGET」的「 Edit」方法是將書籍的ID作爲參數,使用Entity Framework查找方法找到指定ID的書籍,並返回找到的書籍數據給編輯視圖。 此方法中的ID參數被指定了默認值,若是當調用時不帶參數,則ID會使用默認值0。若是書籍記錄沒有找到,將會返回HttpNotFound。由基架系統建立的編輯視圖經過<label>與<input>來呈現Book類中的每一個屬性。示例代碼以下。安全
@model MvcApplication1.Models.Book @{ ViewBag.Title = "Edit"; } <h2>Edit</h2> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>Book</legend> @Html.HiddenFor(model => model.BookID) <div class="editor-label"> @Html.LabelFor(model => model.Category) </div> <div class="editor-field"> @Html.EditorFor(model => model.Category) @Html.ValidationMessageFor(model => model.Category) </div> <div class="editor-label"> @Html.LabelFor(model => model.Name) </div> <div class="editor-field"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) </div> <div class="editor-label"> @Html.LabelFor(model => model.Numberofcopies) </div> <div class="editor-field"> @Html.EditorFor(model => model.Numberofcopies) @Html.ValidationMessageFor(model => model.Numberofcopies) </div> <div class="editor-label"> @Html.LabelFor(model => model.AuthorID) </div> <div class="editor-field"> @Html.EditorFor(model => model.AuthorID) @Html.ValidationMessageFor(model => model.AuthorID) </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> <div class="editor-label"> @Html.LabelFor(model => model.PublishDate) </div> <div class="editor-field"> @Html.EditorFor(model => model.PublishDate) @Html.ValidationMessageFor(model => model.PublishDate) </div> <p> <input type="submit" value="Save" /> </p> </fieldset> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
請注意,這個視圖模板文件的頂部中有一句@model MvcApplication1.Models.Book,這一句代碼的意思是在文件中聲明瞭對象的類型定義 ——即指定了本視圖中的對象類型是Book。
由VS自動生成的基架代碼中使用了幾個輔助方法,這幾個輔助方法用來減小輸入HTML標記。架構
第一個輔助方法是:Html.LabelFor輔助方法用於顯示字段的名稱(例如:「Name」,「PublishDate」,「Price」)。框架
第二個輔助方法是:Html.EditorFor輔助方法用於自動生成一個HTML<input>元素。
第三個輔助方法是:Html.ValidationMessageFor輔助方法用於顯示與該屬性關聯的驗證消息。
再次,按F5運行應用程序,並在瀏覽器中瀏覽/Book網址,在頁面中,使用鼠標左鍵單擊「Edit」連接。瀏覽器會自動導航到編輯頁面,在頁面中,單擊鼠標右鍵,在彈出菜單中選擇「查看源代碼」,你會看到已經生成的HTML表單元素。以下。
<form action="/book/Edit/8" method="post"> <fieldset> <legend>Book</legend> <input data-val="true" data-val-number="字段 BookID 必須是一個數字。" data-val-required="BookID 字段是必需的。" id="BookID"
name="BookID" type="hidden" value="8" /> <div class="editor-label"> <label for="Category">Category</label> </div> <div class="editor-field"> <input class="text-box single-line" id="Category" name="Category" type="text" value="SAP" /> <span class="field-validation-valid" data-valmsg-for="Category" data-valmsg-replace="true"></span> </div> <div class="editor-label"> <label for="Name">Name</label> </div> <div class="editor-field"> <input class="text-box single-line" id="Name" name="Name" type="text" value="SAP" /> <span class="field-validation-valid" data-valmsg-for="Name" data-valmsg-replace="true"></span> </div> <div class="editor-label"> <label for="Numberofcopies">Numberofcopies</label> </div> <div class="editor-field"> <input class="text-box single-line" data-val="true" data-val-number="字段 Numberofcopies 必須是一個數字。"
data-val-required="Numberofcopies 字段是必需的。" id="Numberofcopies" name="Numberofcopies" type="number" value="12" /> <span class="field-validation-valid" data-valmsg-for="Numberofcopies" data-valmsg-replace="true"></span> </div> <div class="editor-label"> <label for="AuthorID">AuthorID</label> </div> <div class="editor-field"> <input class="text-box single-line" data-val="true" data-val-number="字段 AuthorID 必須是一個數字。"
data-val-required="AuthorID 字段是必需的。" id="AuthorID" name="AuthorID" type="number" value="12" /> <span class="field-validation-valid" data-valmsg-for="AuthorID" data-valmsg-replace="true"></span> </div> <div class="editor-label"> <label for="Price">Price</label> </div> <div class="editor-field"> <input class="text-box single-line" data-val="true" data-val-number="字段 Price 必須是一個數字。"
data-val-required="Price 字段是必需的。" id="Price" name="Price" type="text" value="1000000.00" /> <span class="field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span> </div> <div class="editor-label"> <label for="PublishDate">PublishDate</label> </div> <div class="editor-field"> <input class="text-box single-line" data-val="true" data-val-date="字段 PublishDate 必須是日期。"
data-val-required="PublishDate 字段是必需的。" id="PublishDate" name="PublishDate" type="datetime" value="2013/1/1 0:00:00" /> <span class="field-validation-valid" data-valmsg-for="PublishDate" data-valmsg-replace="true"></span> </div> <p> <input type="submit" value="Save" /> </p> </fieldset> </form>
其中最後一個<input>元素設置爲Submit,用於提交HTML <form>中的表格數據到/Book/Edit URL。
處理POST請求
下面的代碼示例,顯示了Edit操做方法的HttpPost版本。
[HttpPost] public ActionResult Edit(Book book) { if (ModelState.IsValid) { db.Entry(book).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } return View(book); }
ASP.NET MVC Model Binder簡單的說就是控制器中的Action方法須要參數數據;而這些參數數據包含在HTTP請求中,包括表單上的Value和URL中的參數等。經過Model Binder的功能將表單上的Value和URL中的參數換成Book對象,而後將Book對象綁定到Action操做方法中的參數上面。其中ModelState.IsValid方法驗證用戶提交的表單中的數據是否能夠被用於修改(編輯或更新)一個Book對象。若是數據是有效的,Book數據將被保存到數據庫的Book集合中(BookDBContext實例)。新的Book數據是經過調用BookDBContext的SaveChanges方法保存到數據庫中。保存數據後,代碼將用戶重定向到BookController類的Index操做,同時在瀏覽器中顯示最新的Book列表。
若是提交過來的表單中的值是無效的,這些值將從新顯示在瀏覽器的頁面中。在Edit.cshtml視圖模板的Html.ValidationMessageFor助手方法顯示相應的錯誤消息。以下圖。
請注意,ASP.NET MVC 4.0 已經支持jQuery的驗證方法,在非英語語言環境中使用逗號(「,」)代替小數點,你必須包括globalize.js和你本身所須要的特定語言的globalize.cultures.js文件(從https://github.com/ jQuery/ globalize 下載)和JavaScript中使用Globalize.parseFloat。下面的代碼顯示了修改Views\Book\ Edit.cshtml文件中顯示法語(「fr-FR」):
@section Scripts { @Scripts.Render("~/bundles/jqueryval") <script src="~/Scripts/globalize.js"></script> <script src="~/Scripts/globalize.culture.fr-FR.js"></script> <script> $.validator.methods.number = function (value, element) { return this.optional(element) || !isNaN(Globalize.parseFloat(value)); } $(document).ready(function () { Globalize.culture('fr-FR'); }); </script> <script> jQuery.extend(jQuery.validator.methods, { range: function (value, element, param) { //Use the Globalization plugin to parse the value var val = $.global.parseFloat(value); return this.optional(element) || ( val >= param[0] && val <= param[1]); } }); </script> }
小數字段可能須要使用一個逗號,不是小數點。做爲一個臨時的解決辦法,你能夠在全球化的元素添加到項目的根目錄下的Web.config文件中。下面的代碼顯示了全球化設置爲」en-US」。
<system.web> <globalization culture ="en-US" /> <!--elements removed for clarity--> </system.web>
說明:全部的建立,編輯,刪除或以其餘方式修改數據的方法都是經過HttpPost將數據傳給相應控制器的相應方法。
HTTPGET方法遵循相似的模式。他們獲取一個書籍(Book)對象(或是對象列表,在Index方法狀況下) ,同時將對象數據傳遞給視圖,由視圖進行呈現。 Create方法傳遞一個空的書籍對象給Create視圖。經過HTTP GET方式修改數據存在一個安全隱患。使用GET方法修改數據也違反了HTTP的最佳實踐和REST的架構模式,它指定了GET請求不該該改變你的應用程序的狀態。換句話說,執行GET操做應該是一個安全的操做,沒有任何反作用,不會修改你的持久化的數據。