第一個MVC應用程序
了解一個軟件最好的方法就是走進它並使用它。在這一章,將使用ASP.NET MVC框架建立一個簡單的數據錄入程序。我們將一步一個腳印以至於你能看懂ASP.NET MVC應用程序是如何搭建的。為了讓事情變得簡答,這一刻我們將忽略一些技術細節;但是不要緊張-----如果你是MVC的新手,你將發現大量的東西令你感興趣。如果我們沒有解釋它使用的東西,我們
提供一個參考的章節,在這里你可以找到所有的細節。
建立一個新的ASP.NET MVC項目
我們通過在Visual Studio中建立一個新的MVC項目來開始。從文件菜單中找到新建項目打開“新建項目”對話框。如果你選擇Web板塊,你將看到MVC 3安裝程序已經建立了一個叫做ASP.NET MVC 3應用程序的新項目,如同圖3-1所示。

圖3-1 Visual Studio MVC 3項目模板
注意: MVC 3安裝程序沒有移除MVC 2版本,因此你同樣能看到舊的模板在新模板的旁邊。當建立一個新項目,要小心選擇正確的那個。
設置新項目需要填寫的名稱,並單擊OK按鈕繼續。你將看到另外對話框,如圖3-2所示,告訴你選擇三種不同類型的MVC 項目模板。

圖3-2 選擇一種MVC 3項目類型
空選項建立一個只有最小文件和文件夾要求的MVC 3應用程序。Internet應用程序選項建立一個小實例應用程序你能修改和以此為基礎。它包括用戶注冊和認證,導航,和一致的視覺風格。Intranet應用程序選項是類似Internet應用,但被設計為使用驗證用戶通過域/ Active Directory基礎結構的環境。在這一章中,我們將保持一切簡單化。選擇空選項,不選中使用HTML5的語義標記選項,單擊“確定”以創建新的項目。
注意 在圖3-2中的選項中,你能看到一個下拉類表讓你明確項目中的View engine。正如第一章中提及的,MVC 3包含了一個新的和改良的View engine叫做Rezor,在這里我們選擇使用Rezor。我們建議你同我們一樣使用Rezor。但是如果你想使用常規的ASP.NET view engine
(即ASPX引擎),你也可以選擇它。
自從Visual Studio建立了項目,你將看到許多的文件和文件夾顯示在Solution Explorer(解決方案)窗口中。這是MVC 3項目中的一個默認架構。你現在能試着運行應用程序通過選擇“調試”目錄中的“啟動調試”(如果提示您啟用調試,只要按一下“確定”按鈕)。你將看到如圖3-3中的結果。自從我們開始選擇了空項目模板,應用程序不包含任何運行,因此出現了404未找到錯誤。

圖3-3 試着運行一個空項目
當你完成后,請務必通過關閉瀏覽器窗口來停止調試錯誤顯示或回到Visual Studio中,從Debug菜單中選擇停止調試。
添加第一個控制器
在MVC架構中,由控制器控制進入請求。在ASP.NET MVC中,控制器只是簡單的C#類(通常從System.Web.Mvc.Controller繼承 ,內置框架的繼承控制器基類)。在控制器中的每一個公共方法就如同一個行為方法,意味着你能通過網站URL來調用它完成一個行為。MVC約定把控制器放在一個叫做Controllers的文件夾中,Controllers是當項目建立時VS給用戶建立的,你不需要跟隨這些或者其他的約定,但是我們推薦你不要這樣做,因為它將幫助你去體會本書中的例子。
右擊Visual Solution 解決方案窗口中的Controller文件夾然后選擇“添加”接着選擇彈出菜單的“控制器”,在項目中添加一個控制器。如圖3-4所示。

圖3-4 正MVC項目中正在添加一個控制器
添加控制器“對話框出現時,設置名稱的HomeController,如圖3-5所示。這是另一個約定:我們給到控制器的名稱應該是描述並且要以Controller結束。

圖3-5 設置控制器的名字
“Scaffolding options”(基架選擇)對話框允許我們使用模板創建一個控制器常見的功能。我們不打算使用這個特征,因此必須確保在模板菜單中選擇”空控制器“,如圖中所示。
注意 如果你看到的添加控制器的對話框如圖3-5所示,你很可能忘了安裝MVC 3更新工具了。詳細請看第二章。
點擊“添加”按鈕建立控制器。VS將在Controller文件夾創建一個新的C#代碼文件稱為HomeController.cs並在編輯框中打開。你能看到類名為HomeController並且它繼承於System.Web.Mvc.Controller。編輯此文件中的代碼以至於它符合3-1列表清單。
清單3-1 修改HomeController類
using System.Web.Mvc;
namespace PartyInvites.Controllers {
public class HomeController : Controller {
public string Index() {
return "Hello, world";
}
}
}
我們還沒有創建任何令人興奮的,但是這是一個好的方法入門MVC。我們創建了一個叫做Index的行為方法,它返回了一個字符串“Hello,world”。通過選擇VS“調試”菜單中的“啟動調試”運行該項目。瀏覽器將顯示Index行為方法中的結果,如圖3-6所示。

圖3-6 控制器行為方法輸出
了解路由
即使有模型,視圖和控制器,MVC應用程序同樣可以使用ASP.NET路由系統,這決定如何網址映射到特定的控制器和動作。
當Visual Studio創建MVC項目時,它增加了一些默認路由讓我們開始。你能請求以下的任何URL並且它們將被導航到HomeController中的Index行為上:
/
/Home
/Home/Index
因此,當一個瀏覽器請求http://yoursite/或者http://yoursite/Home,它會返回來自於HomeController中的Index模型的輸出。現在,輸出字符串“Hello,world”。這是一個好例子得益於MVC的規定。在這種情形下,我們將有一個稱為HomeController的控制器中並且它將是我們的MVC應用程序的起點。Visual Studio中創建一個新項目的默認路由,假設我們將遵循這一規定。自從我們遵循了一個規定,我們得到了支持上述列表中的網址。如果我們沒有遵循該規定,我們需要修改路由指向我們在里面建立任何控制器。通過這個簡單的例子,
默認配置是我們需要的。
注意: 你能通過選擇Global.asax.cs文件看到和編輯你的路由配置。在第七章,你將自定義路由配置並且在第十一章你將學習更多關於路由如何工作的知識。
渲染網頁
前面的例子輸出的不是HTML,他只是字符串“Hello world“。為了產生一個HTML回應瀏覽器請求,我們需要建立一個視圖。
建立和渲染視圖
首先我們必須修改我們的Index行為方法,如列表3-2所示。
列表清單3-2 修改控制器呈現視圖
using System.Web.Mvc;
namespace PartyInvites.Controllers {
public class HomeController : Controller {
public ViewResult Index() {
return View();
}
}
}
黑色字體呈現的就是列表3-2修改的。當返回一個來自於一個行為方法的ViewResult對象,將指示MVC呈現一個視圖。我們通過調用不帶參數的View方法來創建ViewResult。告訴MVC來呈現對行動的默認視圖。
如果你在這一點上運行應用程序,你能看到MVC框架試着尋找可使用的默認視圖,如圖3-7顯示的錯誤信息。

圖3-7 MVC框架試圖尋找默認視圖
這個錯誤信息是如此的有用。它不僅闡述了MVC沒有為我們的行為方法找到視圖,而且展現了它是在哪里尋找視圖。這是MVC規定的另一個好例子:視圖是通過命名規則同行為方法相聯的。我們的行為方法叫做Index,你能在圖3-7看到MVC 試圖在視圖文件夾尋找命名為改名的文件。
為了創建一個視圖,右鍵單擊HomeController.cs的代碼文件中的操作方法(無論是在
方法名稱或內部方法體),並從彈出菜單中選擇“添加視圖”。然后打開“添加視圖”對話框,如圖3-8所示。

圖3-8 添加視圖對話框
不選擇“使用布局和母版頁”。我們在這個例子中沒有用到布局,但我們將在第五章使用。點擊“添加”按鈕,VS將在Views/Home文件夾建立一個新的視圖文件Index.cshtml。如果你看回圖3-7的錯誤信息,你會看到與我們剛剛創建的文件相匹配的,就是被搜查的地點之一。
注意 .cshtml擴展名文件通過Rezor表示為一個C#視圖。以前的MVC版本依靠ASPX視圖引擎,視圖文件擴展名為.aspx。
Index.cshtml文件可以打開來編輯。你將看到這個文件主要包含HTML。例外的是部分看起來像這樣:
@{
Layout = null;
}
這個代碼塊將通過Razor視圖引擎來解析。這是一個簡單的例子。它只說了我們沒有選擇的母版頁Razor。讓我們先忽略Razor。如列表3-3黑色字體所示在Index.cshtml文件中做一些添加。
列表3-3 添加HTML視圖
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div>
Hello, world (from the view)
</div>
</body>
</html>
添加代碼顯示另一種簡單的消息。選擇從“調試”菜單中的“啟動調試“運行應用程序和測試我們的視圖。你將看到類似的東西如圖3-9所示。

圖3-9 測試視圖
當我們第一次創建Index行為方法,它返回一個字符串值。這意味着MVC可以在瀏覽器傳遞字符串值。當Index方法返回一個ViewResult時,我們指示MVC提供一個視圖並返回HTML。我們不能告訴MVC我們將使用那個視圖,因此使用命名規則來自動尋找。命名規則為視圖名字同行為方法名相同並且包含在一個命名為同控制器名一樣的文件夾中----~/Views/Home/Index.cshtml。
我們從行為方法中能返回其他結果除了字符串和ViewResult對象。例如,如果你返回一個RedirectoryResult,導致瀏覽器被重定位到另外的URL。如果返回一個HttpUnauthorizedResult,我們要求必須用戶注冊。這些對象統稱為行為結果並且它們都通過ActionResult類驅動。行為結果系統讓我們封裝和重用共同行為。當你繼續看下去后,我們會告訴你更多和展現復雜的使用關於它們。
增加動態輸出
當然,整個網站應用程序平台就是為了構建和展現動態輸出。在MVC中,控制器的工作是構建一些數據並且視圖的工作是呈現HTML。數據從控制器傳輸到視圖。
一種在控制器和視圖之間傳輸數據的方式是使用ViewBag對象。ViewBag是一個動態的對象,你可以指定任意屬性,這些值可以在任何視圖呈現。清單3-4演示了在這種方式傳遞一些簡單的動態數據。
清單3-4 設置一些視圖數據
using System;
using System.Web.Mvc;
namespace PartyInvites.Controllers {
public class HomeController : Controller {
public ViewResult Index() {
int hour = DateTime.Now.Hour;
ViewBag.Greeting = hour < 12 ? "Good morning" : "Good afternoon";
return View();
}
}
}
為視圖提供數據的說明展現在黑色字體中。為了在視圖展現數據,我們做了一些相似的操作,如清單3-5所示。
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div>
@ViewBag.Greeting, world (from the view)
</div>
</body>
</html>
清單3-5中附加的是一個Razor程序塊用來檢索ViewBag的Greeting屬性。Greeting屬性名稱沒有任何特殊性;你能使用任何自定義的屬性名稱來代替並且它將同樣的運行。當然,你能通過相同的方式從控制器傳遞復雜的自定義數據到視圖中。
忠告 注意我們不需要終止Razor代碼塊。我們只需要從@字符開始增加C#代碼。這是Razor中不錯的功能。這樣更具可讀性,而且我們不用擔心<%和%>標簽的平衡。
如果我們再次運行項目,你能看到第一個動態MVC輸出,如圖3-10所示。

圖3-10 MVC動態響應
創建一個簡單的數據輸入應用程序
在這一章,我們將通過創建一個簡單的數據輸入程序來研究更多的MVC基礎特征。在這一章我們將加快步伐。我們的目標是在實際中演示MVC,因此我們將跳過一些如程序的幕后運行等理論的解釋。當不用擔心——我們將重回這些話題在后面的章節。
設置場景
我們想象一個朋友決定主辦一個新年派對並且邀請我們建立一個邀請者能通過電子RSVP(資源預留協議 譯為電子答復)進行答復的網站。他提出了四項主要的特征:
主頁展現派對的信息
能使用RSVP的表單
為RSVP提供校驗,並能顯示感謝頁面
當完整時電郵RSVP到派對主辦人
在接下來的部分,我們將在本章開始時建立的MVC項目基礎上增加這些功能。我們可以通過申請前面介紹的來敲定類表中的第一項,我們能增加一些HTML在現有的視圖中並給出派對的細節,如清單3-6所示。
清單3-6 顯示派對的細節
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div>
@ViewBag.Greeting, world (from the view)
<p>We're going to have an exciting party.<br />
(To do: sell it better. Add pictures or something.)
</p>
</div>
</body>
</html>
我們使用這種方式。如果你運行這個應用程序,你將看到派對的細節,占位符的詳細信息,但你的想法如圖3-11所示。

圖3-11 增加HTML視圖
設計一個數據模型
在MVC中,M代表模型,並且它是應用程序中非常重要的部分。模型描述了真實世界中的對象,過程和項目中定義的規則,在我們的程序中被稱為域。模型通常指的是域模型,包含了構成我們項目中的所有的C#對象(稱為域對象)和讓我們操控的方法。視圖和控制器以慣有的方式暴露域給客戶。一個好的MVC程序開始於一個好的設計模式,這就是我們增加控制器和視圖的焦點所在。
在PartyInvites中,我們不需要一個復雜的模型,但是我們將使用一個域類。我們稱為GuestResponse。這個對象將負責存儲,驗證,確認RSVP。
添加一個模型類
MVC規定建立的模型類放置在~/Models文件夾中。右擊解決方案中的“Models”選擇,鼠標置於“添加”后選擇下級菜單中的“類”選項(如附圖)。設置文件名為GuestResponse.cs並且點擊“添加”按鈕添加類。編輯類中的內容如清單3-7。

附圖
清單3-7 GuestResponse類域
namespace PartyInvites.Models {
public class GuestResponse {
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public bool? WillAttend { get; set; }
}
}
忠告 你可能注意到WillAttend屬性是可空的,這意味着它可以是true,false或者null。我們在本章稍后的”添加驗證”章節解釋它的原理。
連接操作方法
應用程序中的一個目標就是包含一個RSVP表單,因此我們必須添加一個來自於Index.cshtml視圖的連接,如清單3-8所示。
清單3-8 添加一個RSVP表單連接
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div>
@ViewBag.Greeting, world (from the view)
<p>We're going to have an exciting party.<br />
(To do: sell it better. Add pictures or something.)
</p>
@Html.ActionLink("RSVP Now", "RsvpForm")
</div>
</body>
</html>
Html.ActionLink是一個HTML輔助方法。MVC框架帶有一個內建的集合輔助方法,渲染HTML鏈接,文本輸入,復選框,選擇框,搜索框,甚至自定義控件。ActionLink方法帶有兩個參數:一個為連接顯示的文本,一個為用戶點擊時行為的執行。我們將在15和16章解釋其余的HTML輔助方法。在圖3-12中你能看到我們增加的連接。

圖3-12 添加一個視圖連接
如果在瀏覽器中滑動鼠標通過連接,你將看到連接指向http://yourserver/Home/RsvpForm。
Html.ActionLink方法檢查程序中的URL路由配置和制定了/Home/RsvpForm為控制器HomlController中稱為RsvpForm方法的路徑。注意,不像傳統的ASP.NET應用程序,MVC URL無法對應到物理文件。每一個行為方法都有自己的URL,並且MVC使用ASP.NET路由系統將網站轉為行為。
建立行為方法
如果你點擊連接將看到一個404 Not Found錯誤。這是應為我們沒有建立行為方法對應於/Home/RsvpForm網址。我們在HomeController類中添加一個稱為RsvpForm的方法,如清單3-9所示。
清單3-9 為控制器添加一個新的行為方法
using System;
using System.Web.Mvc;
namespace PartyInvites.Controllers {
public class HomeController : Controller {
public ViewResult Index() {
int hour = DateTime.Now.Hour;
ViewData["greeting"] = hour < 12 ? "Good morning" : "Good afternoon";
return View();
}
public ViewResult RsvpForm() {
return View();
}
}
}
添加一個強類型視圖
我們要為RsvpForm行為方法添加一個視圖,但是我們要做一些輕微地不同----我們將創建一個強類型視圖。一個強類型的視圖提供了特定的域類型,如果我們指定需要使用的類型(在GuestResponse這個例子中),MVC可以創建一些有用的捷徑使其更容易。
警告 在做任何事這前,確保你的MVC項目時可編譯的。如果你建立了GuestResponse類卻不能編譯,MVC是無法創建一個強類型視圖的。編譯你的應用程序,在VS創建菜單中找到“生成解決方案”。
在RsvpForm行為方法中右鍵並選擇彈出菜單中的“添加視圖”來創建視圖。在添加視圖對話框中,選擇“創建強類型視圖”選項並且在下拉列表中選擇GuestResponse。“使用布局和模板頁”不打勾並且確保視圖引擎下為Razor而且“支架模板”選項為Empty,如圖3-13所示。

圖3-13 添加一個強類型是視圖
點擊“添加”按鈕建立新視圖。當建立完成時VS將打開RvspForm.cshtml文件。你將看到帶着@model Razor塊地HTML文件支架。正如你看到的,這就是強類型視圖的主要部分
和它提供的方便。
建立表單
當我們建立了強類型視圖時,我們能擴建RsvpForm.cshtml內容使它成為HTML表單用於編輯GuestResponse對象。編輯視圖以至於匹配清單3-10。
清單3-10 建立表單視圖
@model PartyInvites.Models.GuestResponse
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>RsvpForm</title>
</head>
<body>
@using (Html.BeginForm()) {
<p>Your name: @Html.TextBoxFor(x => x.Name) </p>
<p>Your email: @Html.TextBoxFor(x => x.Email)</p>
<p>Your phone: @Html.TextBoxFor(x => x.Phone)</p>
<p>
Will you attend?
@Html.DropDownListFor(x => x.WillAttend, new[] {
new SelectListItem() {Text = "Yes, I'll be there", Value = bool.TrueString},
new SelectListItem() {Text = "No, I can't come", Value = bool.FalseString}
}, "Choose an option")
</p>
<input type="submit" value="Submit RSVP" />
}
</body>
</html>
對於GuestResponse模型類地每一個屬性,我們使用HTML輔助方法來呈現合適的HTML輸入控制。這些方法讓你選擇輸入元素涉及到使用lambda表達式,如下:
@Html.TextBoxFor(x => x.Phone)
如果你不熟悉C#的lambda表達式也不用着急。我們將在第五章提供概述。
HTML輔助方法為生成的HTML建立了一個輸入元素,設置了text參數為“type”,id和name都為“phone”,作為選中域類型的屬性,如下:
<input id="Phone" name="Phone" type="text" value="" />
因為我們的RsvpForm視圖是強類型,為了方便工作我們只需要告訴MVC 我們想在視圖上呈現的GuestResponse類型的視圖。
另一種使用lambda表達式是以字符串的方式提交模型類型屬性的名字,如下:
@Html.TextBox("Email")
我們發現, lambda表達式技術防止了誤輸入模型類型屬性的名稱。這是因為vs中的IntelliSense彈出列表讓我們自動選擇屬性,如圖3-14所示。

圖3-14 HTML輔助方法中lambda表達式的VS IntelliSense
另一個方便的輔助方法為HTML.BeginForm,它能生成一個HTML來自於回傳行為方法的元素配置。因為我們還沒有傳遞任何參數給助手方法,它假定我們要回發到相同的URL。
一個簡單的技巧就是包含在C#的using聲明中,如下:
@using (Html.BeginForm()) {
...form contents go here...
}
通常,當這樣應用時,Using聲明確保對象超出范圍的處理。它通常被用於數據庫連接,例如,以確保他們關閉盡快查詢已完成。(這種using關鍵字的申請不同於在一個類命名空間范圍內攜帶類的類)。為了處理一個對象,當它超出范圍時HtmlBefinForm助手關閉HTNL表單元素。這意味着Html.BeginForm輔助方法在一個表單元素中建立了兩部分,入下:
<form action="/Home/RsvpForm" method="post">
...form contents go here...
</form>
不要擔心,如果你不熟悉C#對象的處理。這里的關鍵是要證明如何創建使用HTML輔助方法的形式。
注意:ASP.NET Web表單在一個網頁中只支持一個服務器端,通常表現為<form runat=”server”>,這是一個容器視圖狀態數據和回傳邏輯。MVC不使用服務器端表單。所有的表單表現普通的HTML形式,你能根據自己的意願來使用單一視圖。沒有視圖狀態或其他隱藏的表單元素,並且也沒有ID值錯位現象。
當你運行程序點擊RSVP連接時你能看到RsvpForm視圖表單。如圖3-15展現的結果。

圖3-15 RspvForm視圖
注意:這不是一本關於css和網頁設計的書。極大部分下,我們將建立實例外觀可能都過時了(雖然我們喜歡讓人感覺輕蔑的經典)。MVC視圖生成的HTML非常清晰和簡潔並且你能完全控制元素的布局和類的分配,因此使用設計將沒有難題或者舍棄現有模板是你的MVC項目更加漂亮。
處理表單
當我表單發送到服務器時我們沒有告訴MVC要做什么。隨着事態的發展,單擊提交RSVP 按鈕只是清除任何你輸入到表單中的值。這是因為表單在Home控制器中回發RsvpForm行為方法,這只是告訴MVC再次呈現視圖。
警告:你可能會驚訝再次呈現視圖時輸入框的數據丟失了。如果是這樣,你有可能制定了ASP.NET Web窗體應用程序,它會自動保存在這個數據的應用情況。我們會在稍后告訴你如何在MVC中實現同樣的效果。
為了接收和處理提交的表單數據,我們打算做一間事。我們將增加第二個RsvpForm行為方法為了建立一下功能:
一個回應HTTP GET請求的方法。GET請求通常是每次點擊連接產生的瀏覽器問題。此版本的行為將是負責顯示初始的空白表格,當有人第一次訪問/Home/ RsvpForm時。
一個回應HTTP POSE請求的方法。默認情況下,表單通過瀏覽器呈現使用的Html.BeginForm ( )來提交POST請求。這個版本的行為將承擔提交數據和決定如何使用它。
處理GET和POST請求分開在C#的兩個方法助手中有利於代碼的整潔性,因此連個方法有各自的不同責任。兩種行為方法待用相同的URL,但是MVC能確保請求正確的方法,根據是否我們處理的是GET或者POST請求。清單3-11展現了我們需要請求HomeController類的改變。
清單3-11 添加一個行為方法支持POST請求
using System;
using System.Web.Mvc;
using PartyInvites.Models;
namespace PartyInvites.Controllers {
public class HomeController : Controller {
public ViewResult Index() {
int hour = DateTime.Now.Hour;
ViewData["greeting"] = hour < 12 ? "Good morning" : "Good afternoon";
return View();
}
[HttpGet]
public ViewResult RsvpForm() {
return View();
}
[HttpPost]
public ViewResult RsvpForm(GuestResponse guestResponse) {
// TODO: Email guestResponse to the part organizer
return View("Thanks", guestResponse);
}
}
}
我們在現有的RsvpForm行為方法中添加HttpGet屬性。這是為了告訴MVC這個方法只能被GET請求使用。然后增加一個RsvpForm重載函數,GuestResponse作為參數並且聲明為HttpPost屬性。屬性告訴MVC新方法將用於處理POST請求。請注意,我們已經引入了
PartyInvites.Models命名空間。所以我們可以使用GuestResponse模型類型,而不需要限定類名。
使用模型綁定
第一個重載的RsvpForm行為方法呈現的視圖同以前的相同的。它生成的表單如圖3-15所示。第二個重載由於他傳入參數使得更有趣,但鑒於在響應一個HTTP POST請求的操作方法將被調用並且GuestResponse類型是一個C#類,兩者之間是如何連接的?
答案就是模型綁定,一個非常有用的MVC功能,憑借輸入的數據和使用的鍵/值對來填充域模型類型的屬性。這個過程是相對的使用HTML輔助方法;也就是說,當創建的表單數據發送到客戶端時,我們生成的HTML輸入元素的id和name屬性的值均來自模型類的屬性名。與此相反,模型綁定中輸入元素的名稱是用於設置在模型類的實例的屬性值,然后傳遞給POST啟用行為方法。
模型綁定的一個強大和可定制的功能消除了處理HTTP請求的麻煩,讓我們使用C#對象來代替了處理Request.From[]和Request.QueryString[]值。GuestResponse對象作為參數傳遞到行動方法中自動填充表單域的數據。在第12章,我們將深入到綁定模型的細節和如何定制它。
渲染其他視圖
第二種重載的RsvpForm行為方法同樣顯示了如何告訴NVC渲染一個詳細的視圖回應請求。這里是有關的聲明:
return View(“Thanks”,guestResponse);
這個調用視圖的方法告訴MVC去尋找一個稱為“感謝”的視圖,並傳遞一個GuestResponse對象給視圖。要創建我們已經指定的視圖,右鍵單擊
HomeController方法,並選擇彈出菜單中的“添加視圖”。設置視圖的名字為Thanks,如圖3-16所示。

圖3-16 添加Thanks視圖
我們建立另一個強類型視圖,因此點擊“添加視圖”對話框。我們選擇的數據類型必須符合我們傳遞給視圖中使用的視圖模型,因此在下拉列表中選擇GuestResponse。確保“選擇模板頁選項”不被選中,“視圖引擎”為Razor並且視圖內容設置為Empty。點擊“添加”創建新視圖。由於視圖和Home控制器相關聯。MVC建立的視圖為~/Views/Home/Thanks.cshtml。編輯新視圖如清單3-12
清單3-12 Thanks清單
@model PartyInvites.Models.GuestResponse
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Thanks</title>
</head>
<body>
<div>
<h1>Thank you, @Model.Name!</h1>
@if (Model.WillAttend == true) {
@:It's great that you're coming. The drinks are already in the fridge!
} else {
@:Sorry to hear that you can't make it, but thanks for letting us know.
}
</div>
</body>
</html>
Thanks視圖使用Razor來顯示內容是基於在RscpForm行為方法中傳遞視圖模型給GuestResponse的屬性值。Razor@model運營商指定領域模型是強類型的視圖。為了訪問域對象中的屬性值。我們使用Model.PropetyName。例如,為了獲取Name屬性的值,我們調用Model.Name。不用擔心不懂Razor語法,我們將在第五章講解。
現在我們建立了Thanks視圖,我們有了工作例子。在VS中運行應用程序,點擊RSVP Now連接,並且輸入表單數據,點擊Submit RSVP按鈕。你將看到如圖3-17的顯示(雖然它可能會有所不同,如果你的名字是不是喬或者你選的的是不出席)

圖3-17 Thanks視圖呈現
添加驗證
現在我們需要在程序中添加驗證。如果你不這樣做,我們的用戶將輸入無意義的數據或者提交空表單。
驗證在MVC應用程序中,通常是在應用在域模型中,而不是在用戶接口中。着意味着我們在同一個地方定義的驗證准則能在任何的類模型中使用。ASP.NET MVC 支持的驗證規則屬性來自於System.CompontModel.DataAnnotation命名空間。清單3-13展現了這些屬性如何能使用於GuestResponse類模型中。
清單3-13 GuestResponse類模型申請驗證
using System.ComponentModel.DataAnnotations;
namespace PartyInvites.Models {
public class GuestResponse {
[Required(ErrorMessage="Please enter your name")]
public string Name { get; set; }
[Required(ErrorMessage="Please enter your email address")]
[RegularExpression(".+\\@.+\\..+",
ErrorMessage="Please enter a valid email address")]
public string Email { get; set; }
[Required(ErrorMessage="Please enter your phone number")]
public string Phone { get; set; }
[Required(ErrorMessage="Please specify whether you'll attend")]
public bool? WillAttend { get; set; }
}
}
以粗體顯示的驗證規則。 MVC檢測驗證屬性,並用它們來驗證模型結合過程的數據。注意
我們意見引入了關聯驗證的命名空間,因此我們能指出它們在沒有前綴名下。
忠告:如前所述,我們使用一個可空布爾為WillAttend屬性。我們這樣做,所以我們可以申請必要的驗證屬性。如果你使用規定的布爾值,我們通過模型建立返回的值就只有真或假,並且我們將無法確保用戶是否選擇了值。一個可空的布爾值有三個可能的值:true,false和null。Null是用戶沒有選擇值是應用的,並且這將引起必須值驗證錯誤。
我們可以查看是否有驗證問題通過使用在控制器類中的ModelState.IsValid屬性。清單3-14展現了如何在POST的RsvpForm行為方法中使用。
清單3-14 檢查表單驗證錯誤
[HttpPost]
public ViewResult RsvpForm(GuestResponse guestResponse) {
if (ModelState.IsValid) {
// TODO: Email guestResponse to the part organizer
return View("Thanks", guestResponse);
} else {
// there is a validation error - redisplay the form
return View();
}
}
如果沒有驗證錯誤,我們告訴MVC呈現以前寫好的Thanks視圖。如果驗證錯誤,我們將通過調用沒有任何參數的視圖方法來呈現RsvpForm視圖。
我們需要顯示驗證錯誤給用戶,並且我們能在RsvpForm中通過Html.ValidationSummary輔助方法做到。如清單3-15所示。
清單3-15 使用Html.ValidationSummary輔助方法
...
<body>
@using (Html.BeginForm()) {
@Html.ValidationSummary()
<p>Your name: @Html.TextBoxFor(x => x.Name) </p>
<p>Your email: @Html.TextBoxFor(x => x.Email)</p>
...
如果沒有錯誤,Html.ValidationSummary方法建立一個隱藏列表項如表單的占位符一樣。MVC通過驗證屬性使占位符顯示和添加已定義的錯誤信息。在圖3-18你能看到這是如何呈現的的。

圖3-18 驗證摘要
只有所有的驗證約束都符合GuestResponse類才會顯示Thanks視圖。注意輸入到表單中的數據被保留和顯示出來當視圖完成驗證摘要中的驗證。這有利於從模型綁定中獲取數據。
注意 如果你使用的是ASP.NET Web窗體,Web窗體有“服務器控件”這個概念,它通過隱藏在稱為_VIEWSTATE表單文件中序列值來保留狀態。ASP.NET MVC的模型綁定不涉及到Web窗體服務器控件,回發,或視圖狀態的概念。ASP.NET MVC 沒有引入_VIEWSTATE隱藏文件在HTML呈現頁面。
高亮提示域
HTML協助方法建立的text boxes,drop-downs和其他的元素有一個方便的特征就是能被用於連接模型綁定。相同的機制保證用戶輸入到表單中的數據也能被用於突出失敗驗證的單獨領域。
當模型類驗證失敗,HTML輔助方法將生成細微不同的HTML。作為例子,當沒有驗證錯誤就請求Html.TextBoxFor(x=>x.Name)生成HTML:
<input data-val="true" data-val-required="Please enter your name" id="Name" name="Name"
type="text" value="" />
當用戶不提供值時也是相同的請求生成的HTML(這是一個驗證錯誤因為我們請求所需的Name屬性在GuestResponse模型類中):
<input class="input-validation-error" data-val="true" data-val-required="Please enter your
name" id="Name" name="Name" type="text" value="" />
我們已經用黑色高亮標出。這個輔助方法添加了一個稱為“input-validation-error”的CSS類。不同的輔助方法支持不同的CSS類,但是這些都可以在VS在MVC項目中添加的~/Content/Site.css樣式表中找到。為了使用樣式表,我們在RevpForm視圖中添加一個新的頭結點,如下:
<link rel="Stylesheet" href="@Href("~/Content/Site.css")" type="text/css"/>
注意:如果你用的是ASPX視圖引擎,你可以直接使用符號(~)指定路徑(如 href="~/Content/Site.css")並且依靠視圖引擎連接URL瀏覽器就能遵循(如 ../Content/Site.css)。Razor視圖引擎使用的是不同的方式。它請求Href操作來連接URLs(如 href="@Href("~/Content/Site.css")")。在第5章你能找到更多的細節。
當用戶提交的數據驗證錯誤,他將看到更多有用的關於錯誤原因的提示,如圖3-19所示。

圖3-19 自動高亮顯示驗證錯誤
完整的例子
我們的示例應用程序的最后一個要求是組織者通過RSVPs回復電子郵件給我們的朋友。我們能通過增加一個行為方法模型來建立和發送電子郵件信息使用.NET框架中的電子郵件類。在此,我們使用WebMail協助方法。它不是MVC框架中的一部分,但是它讓我們完成這個例子,而不會陷入設立其他形式發送電子郵件的細節中。
注意:我們使用WebMail輔助方法是因為它能讓我們以最小的努力發送電子郵件。典型地,我們更喜歡把這個函數放在一個行為方法中。當在第4章描述MVC架構模式時我們將解釋為什么。
我們想讓電子郵件信息發送如Thanks視圖顯示那樣。清單3-16展現了我們需要的改變。
清單3-16 使用WebMail助手
@model PartyInvites.Models.GuestResponse
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Thanks</title>
</head>
<body>
@{
try {
WebMail.SmtpServer = "smtp.example.com";
WebMail.SmtpPort = 587;
WebMail.EnableSsl = true;
WebMail.UserName = "mySmtpUsername";
WebMail.Password = "mySmtpPassword";
WebMail.From = "rsvps@example.com";
WebMail.Send("party-host@example.com", "RSVP Notification",
Model.Name + " is " + ((Model.WillAttend ?? false) ? "" : "not")
+ "attending");
} catch (Exception) {
@:<b>Sorry - we couldn't send the email to confirm your RSVP.</b>
}
}
<div>
<h1>Thank you, @Model.Name!</h1>
@if (Model.WillAttend == true) {
@:It's great that you're coming. The drinks are already in the fridge!
} else {
@:Sorry to hear that you can't make it, but thanks for letting us know.
}
</div>
</body>
</html>
我們添加了一個Razor代碼塊使用WebMail助手來配置電子郵件服務,包括服務器名稱,不管是服務器SSL請求連接和客戶詳細信息。一旦我們配置了所有的細節,我們就可以使用WebMail.Send方法發送電子郵件。
我們用try…catch包含了電子郵件代碼以至於在電子郵件沒有發出去時發出警告。我們通過添加一個文字塊來輸出Thanks視圖。一個好的方式是當電子郵件不能發送時能顯示單獨的視圖錯誤,但是為了保證第一個MVC應用程序簡單化我們沒有那樣做。
總結
在這一章,我們建立了一個新的MVC項目並且使用它構建了一個簡單的數據錄入程序,第一次目睹了MVC框架的架構和方式。我們跳過了一些關鍵的特征(包括Razor語法,路由和自動測試),但是我們將回到這些主題在接下來的章節。
在后面的章節,我們將研究將在這本書中使用的MVC框架,設計模式,和技術。
