天天看點

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

英文原文位址:http://weblogs.asp.net/scottgu/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx

翻譯原文位址:http://blog.joycode.com/scottgu/archive/2007/12/10/112465.aspx

過去的幾個星期内,我一直在寫着讨論我們正在開發的新ASP.NET MVC架構的系列貼子。ASP.NET MVC架構是個你可以用來結構化你的ASP.NET web應用,使之擁有清晰的關注分離,友善你單元測試代碼和支援TDD流程的可選方法。

為示範如何在ASP.NET MVC架構中處理表單輸入和送出場景的一些基本原則,我們将建造一個簡單的産品清單,産品生成,和産品編輯場景。它将擁有三個核心的使用者體驗:

按類列出的産品清單

通過導航到/Products/Category/[CategoryID] 這樣的URL,使用者将能看到在某個特定産品分類内的所有産品的清單:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

添加新産品

使用者将能通過點選上面的“添加新産品”的連結往商店裡添加一個新産品。點選之後,會轉到/Products/New URL,在這裡,系統将提示使用者輸入要添加的新産品的細節:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

在點選Save(儲存)之後,産品就會添加到資料庫中,然後就會轉向傳回到産品清單網頁。

編輯産品

在産品清單網頁上,使用者可以點選每個産品旁邊的“Edit”(編輯)連結。這會轉到/Products/Edit/[ProductID] URL,在這裡,使用者可以改動産品的細節,然後點選Save按鈕,往資料庫裡更新:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

我們将使用SQL Server Northwind樣品資料庫來存儲我們的資料。然後我們将使用.NET 3.5内置的LINQ to SQL對象關系映射器(ORM)來對Product, Category, 和 Supplier對象進行模組化,這些對象代表了我們的資料庫資料表中的記錄行。

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

然後我們将在項目中建立一個NorthwindDataContext部分類(partial class),向裡面添加一些輔助方法。我們定義這些輔助方法有2個原因: 1)避免在我們的Controller類中直接嵌入我們的LINQ查詢,2) 将允許我們在将來更容易地改變我們的控制器以使用dependency injection(依賴注入)。

我們将添加的NorthwindDataContext輔助方法是象下面這樣的:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

我們将使用單一控制器類來實作這三個核心使用者浏覽體驗,我們将稱這個控制器類為“ProductsController”(在Controllers子目錄上右擊,選擇“添加新項” -> “MVC 控制器”來建立這個類:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

我們的 ProductsController 類将通過實作"Category", "New", 和"Edit" 等action方法來處理象/Products/Category/3, /Products/New, 和/Products/Edit/5這樣的URL:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

我們控制器的Action方法将使用三個視圖網頁,用以顯示輸出。"List.aspx", "New.aspx", 和 "Edit.aspx" 網頁将居于 \Views\Products 子目錄下,這些網頁将基于\Views\Shared目錄中的Site.Master母版頁上。

我們要實作的網站的第一部分将是産品清單URL (/Products/Category/[CategoryId]) :

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

我們将使用我們的ProductsController類上的"Category" action方法來實作這個功能。我們将使用LINQ to SQL DataContext類,和我們往其中添加的GetCategoryById輔助方法,來擷取一個Category對象,該對象代表了由URL (譬如, /Products/Category/3) 指定的某個特定分類。然後我們将該Category對象傳給"List"視圖來從中生成回複:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景
[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

然後我們将象下面這樣實作List.aspx:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

上面的視圖在頁面上方顯示了分類名稱,然後顯示了分類内的所有産品的項目清單。

當我們通路 /Products/Category/1 URL時,在浏覽器中檢視源碼的話,你會注意到我們的ASP.NET MVC應用輸出了非常幹淨的HTML和URL辨別:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

現在讓我們來實作網站的“添加新産品”表單送出功能,最終我們想要使用者在通路/Products/New URL時看到象下面這樣的顯示:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

在ASP.NET MVC架構中,表單輸入和編輯場景一般是通過在Controller類上呈示2個Action方法來處理的。第一個Controller Action方法負責發送含有要顯示的初始表單的HTML。第二個Controller Action方法則負責處理從浏覽器發回的任何表單送出。

例如,對上面的“添加産品”螢幕,我們會選擇在ProductsController上的2個不同action中來實作:一個叫"New",另一個叫"Create"。/Products/New URL負責顯示一個帶有HTML文本框和下拉框控件的空白表單,讓使用者輸入新産品的細節。然後,這個網頁上的HTML <form>元素将其action屬性設定為 /Products/Create URL。這意味着當使用者點選表單送出按鈕時,表單的輸入将被發送到"Create" action方法上來處理和更新資料庫。

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

下面是我們可以用來實作ProductsController的一個初始實作。

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

注意上面,在涉及産品生成過程中,我們有2個action方法, - "New" 和 "Create"。 "New" action方法隻是簡單地向使用者顯示一個空白表單。"Create" action方法則處理從表單送出過來的值,根據這些值在資料庫中生成一個新産品,然後将客戶轉向到産品的分類清單網頁。

發送到用戶端的HTML表單,是在由"New" action方法調用的"New.aspx"視圖裡實作的。這個視圖的一個初始實作(每個輸入都用了文本框)看上去象下面這樣:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

注意上面,我們在網頁上使用了标準的 HTML <form> 元素,而不是form runat=server。表單的"action"屬性被設定為ProductsController上的"Create" action方法。在頁面底部的<input type="submit">元素被點選時,送出就會發生,之後,ASP.NET MVC架構就會自動将ProductName, CategoryID, SupplierID 和 UnitPrice值映射為方法參數,傳給ProductsController上的 "Create" action方法:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

至此,我們運作網站時,就有了最基本的産品輸入功能:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

我們在前面一節裡建立的産品輸入螢幕是可行的,但不是很友好。具體來說,它要求使用者知道正輸入的産品的原始CategoryID和SupplierID成員。我們需要通過顯示内含可讀名稱的HTML下拉框來修正這個問題。

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

然後我們将更新 "New" action 方法來填充這些集合,然後将它們作為ViewData傳給 "New" 視圖:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

然後在我們的視圖裡,我們可以使用這些集合來生成 HTML <select> 下拉框。

ASP.NET MVC HTML 輔助方法

我們可以用來生成下拉框的一個方法是在HTML裡手工生成内含 if/else 語句的 <% %> for-循環。這會給與我們對HTML的完全控制,但HTML會很亂。

一個你可以使用的幹淨得多的方法是利用ViewPage基類上的"Html"輔助屬性。這是個友善對象,呈示了一套HTML輔助界面方法,用于自動化HTML界面的生成。例如,在本文章的前面,我們使用了 Html.ActionLink輔助方法來生成 <a href=""> 元素:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

在ASP.NET MVC架構将來的預覽版中,我們将提供幾十個内置的HTML和AJAX輔助方法。在第一個預覽版中,隻有"ActionLink"方法是内置于System.Web.Extensions(目前實作核心ASP.NET MVC架構的程式集)中的。但我們還将有一個單獨的 "MVCToolkit" 下載下傳,你可以加到你的項目中,來得到你可以在第一個預覽版中使用的的幾十個輔助方法。

要安裝MVCToolkit HTML輔助方法的話,隻要将MVCToolkit.dll程式集添加為你的項目的引用即可:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

重新編譯你的項目,然後下一次你鍵入 <%= Html. %> 的話,你将看到許許多多你可以使用的額外界面輔助方法:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

為生成HTML <select>下拉框,我們可以使用Html.Select()方法。每個方法都有重載的版本,在視圖裡有完整的intellisense:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

我們可以更新我們的"New"視圖,用下面的代碼,使用Html.Select選項來顯示使用CategoryID/SupplierID屬性作為值,CategoryName/SupplierName作為顯示文字的下拉框:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

這會在運作時為我們生成适當的<select> HTML辨別:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

在/Products/New螢幕上給使用者一個友善的方式來選擇産品分類和供應商:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

注: 因為我們還是在向伺服器送出CategoryID和SupplierID值,是以我們根本不用更新ProductsController的Create Action方法來支援這個新的下拉框界面,這個方法還是工作的。

我們的ProductsController的"Create" Action方法負責處理我們的“添加産品”場景的表單送出。目前它是以action方法參數的方式來處理進來的表單參數的:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

這個方法是可行的,但對于涉及大量值的表單,Action方法的簽名就會開始變得有點難讀。而且,上面将所有進來的參數值設定到新的Product對象上的代碼有點長,而且單調。

如果你引用了MVCToolkit程式集,你可以利用在System.Web.Mvc.BindingHelpers命名空間下實作的一個有用的擴充方法,來對此代碼作些清理。這個擴充方法叫做“UpdateFrom”,可以用在任何 .NET 對象上。它接受一個字典作為參數,然後,它會對任何比對該對象的公開屬性的鍵,自動對本身進行屬性指派。

例如,我們可以重寫我們上面的Create action方法,來使用UpdateFrom方法,象這樣:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

注: 如果你因為安全的原因,想要更明确些,隻允許某些屬性可以更新的話,你還可以向UpdateFrom方法傳入一個可以更新的屬性名稱的字元串數組:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

現在讓我們來實作網站“編輯産品”的功能。我們最終想要使用者在通路/Products/Edit/[ProductID] URL時看到象下面這樣的螢幕:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

跟上面的“添加新産品”表單送出例子一樣,我們将使用2個ProductsController Action方法來實作這個表單編輯互動,我們将稱這2個方法為"Edit"和"Update":

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

"Edit" 會顯示産品表單,"Update"會被用來處理表單的送出行動。

我們将通過實作ProductController的Edit action方法來開始啟用我們應用的編輯功能。當我們在本貼子的開頭建立産品清單網頁的時候,我們是這麼建造的,Edit action将接受一個作為URL一部分的id參數(譬如,/Products/Edit/5):

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

我們想要Edit Action方法從資料庫中擷取适當的産品對象,以及現有的産品供應商和分類集合(這樣,我們可以在我們的編輯視圖裡實作這些東西對應的下拉框)。我們将使用下面的ProductsEditViewData對象來定義一個強類型的視圖對象來代表所有這些資料:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

然後,我們可以實作我們的Edit action方法來填充這個viewdata對象,在"Edit" 視圖中顯示:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

我們可以使用下述方法來實作Edit.aspx視圖網頁:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

注意我們是如何同時使用上面例子中的Html.TextBox和Html.Select輔助方法來的。這2個方法都是來自MVCToolkit.dll程式集中的擴充方法。

注意Html.Select輔助方法有個重載版本,允許你指定下拉框中的標明值是什麼。在下面的代碼片斷中,我表示我要Category下拉框根據編輯産品目前的CategoryID值自動選擇某一項:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

最後,注意我們是如何使用Url.Action()輔助方法來設定<form>元素的action屬性的:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

最後一步是實作ProductController類上的"Update" action方法:

[轉自Scott]ASP.NET MVC架構(第四部分): 處理表單編輯和送出場景

跟前面的"Create" action方法一樣,我們将利用"UpdateFrom"擴充方法來從請求中自動填充我們的産品對象。但注意,填充的不是一個空對象,我們使用了一個模式,先從資料庫中擷取老的值,然後對它應用使用者做的改動,然後更新到資料庫中。

編譯完畢之後,我們重新定向到産品清單網頁,自動設定 /Products/Category/[CategoryID],以比對我們正在操作的産品的儲存的狀态。

希望本文章提供了在ASP.NET MVC架構中如何處理表單輸入和送出場景的一些細節,還提供了你可以如何處理和結構化常見資料輸入和編輯場景的一些背景。

在将來的文章裡,我将讨論如何處理表單輸入和編輯場景中資料驗證和錯誤複原的情形。我将讨論一些促進快速應用開發的内置的資料和安全支架(scaffolding)。我将讨論你如何在MVC架構中使用ASP.NET AJAX進行啟用AJAX的編輯。我還将對如何單元測試控制器和向控制器添加依賴注入做深入的探讨。

希望本文對你有所幫助,

Scott