第一个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框架,设计模式,和技术。