学习一个软件开发框架的最有效的方式就是了解并使用它。在本章,你将会创建一个简单基于ASP.NET MVC Framework的数据-实体应用程序。我们会该程序划分成若干小块,每次介绍一个部分,以便你能了解到ASP.NET MVC是如何创建的。在本涨,我们会跳过一些技术细节,以使你能快速对整个框架有一个大致的了解。如果你对这些技术细节干兴趣,那么你也不用担心,我们会在后续的章节陆续介绍她们。
1 准备工作
创建ASP.NET MVC4程序的唯一准备工作就是你需要安装Visual Studio 2012,它包含了所有你需要的东西,比如内置的运行和调试MVC应用程序的服务器,开发数据驱动程序的SQL server,单元测试工具,以及必不可少的代码编辑器,编译器和调适器。
Visual Studio 2012有很多版本,本书将使用visual studio express 2012 for web版本,该版本是免费使用的。此版本可以通过微软网站下载,其地址为http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products. 请注意该页面列举了很多版本,请注意选择支持MVC应用程序的版本。
当你安装好Visual Studio后,你就可以开始了创建MVC程序了。
2 创建一个新的ASP.NET MVC项目
启动Visual Studio,点击创建MVC项目,VS将启动创建项目对话框,然后选择C#下面的Web模板,你会看到ASP.NET MVC 4 Web Application.选择此项目类型
设置项目的名字为PartyInvites,然后点击确认按钮继续。会出现另外一个对话框,列举了项目的各种模板以供选择。不同的项目模板所对应程序将会拥有不同的特性,比如授权,导航或者样式。在本章,为了简化,我们选择空模板,这将创建一个包含基本文件结构的项目,而且没有包含任何创MVC程序的文件。在本章,我们将逐步添加我们需要的文件,并解释我们每步到底在做什么。最后点击确认按钮创建新项目。
当Visual Studio创建了项目之后,你会看到许多文件和文件夹显示在Solution Explorer窗口中。这就是MVC4项目的默认结构。如果你从Debug菜单选择开始调适按钮,你将会得到如下的结果:
由于我们创建的是空项目,因此程序不包含任何可以执行的事物,所以我们得到了404 Not Found错误。
添加第一个控制器
在MVC架构中,用户发送的请求由conrollers处理。在ASP.NET MVC中,controllers就是c#类(继承自System.Web.Mvc.Controller,架构中内建的控制器基类)。控制器中每个public方法都是一个行为方法,其意味着你可以在Web中通过执行某个行为的URL去调用控制器里相应的方法。MVC规范,控制器应当放在Controller文件夹下,该文件夹由Visual Studio在创建MVC项目时自动创建。你当然也可以不遵守在规范,但是我们强烈建议你应该遵守,因为至少在本书中这么做更有意义。
添加一个控制器到项目,你需要在Visual Studio解决方案浏览器窗口中的Controller文件夹上点击右键,然后选择添加,再选择控制器
当添加控制器窗口出现后,设置其名字为HomeController。这里又有一个规范,控制器的名字必须以Controller结束。
对话框中的Scaffolding options部分允许我们创建控制器的时候使用一些常见功能的模板。在目前我们并不适用这些特性,因为只需要选择Empty MVC Controller即可。点击确认按钮创建控制器。Visual Studio将在Controller文件夹下创建一个C#代码文件,其名字为HomeController.cs并在编辑模式下打开该文件。下面我们显示该控制器的所有代码,从代码中我们可以看到HomeController继承了System.Web.Mvc.Controller类。
namespace PartyInvites.Controllers { publicclassHomeController : Controller { // // GET: /Home/ publicActionResult Index() { return View(); } } } |
接下来,我们对该Controller做一点简单的修改
publicclassHomeController : Controller { publicStringIndex() { return"Hello world"; } } |
此刻,我们并没有创建任何令人激动的事情,但是这已经是一个不错的展示结果.我们对Index方法做了一点小修改。更改其返回类型为String,然后返回Hello world。再次点击开始调适以运行该项目,在浏览器中我们可以得到下面的结果
理解路由
与模型,视图和控制器一样,MVC程序也使用ASP.NET路由系统。路由系统用来确定URL如何映射到某个特定的控制器和行为。当Visual Studio创建MVC程序,会自动添加默认的路由。你可以访问下面的URLs,它们都会指向HomeController控制器的Index行为。
- /
- /Home
- /Home/Index
因此,当浏览器请求http://yoursite或http://yoursite/Home,浏览器都会从HomeController的Index方法获取结果。你可以自己尝试在浏览器中更改URL以验证结果是否一样。在本例中,你访问 http://localhost:10003/, http://localhost:10003/Home 或 http://localhost:10003/Home/Index 都会得到同样的结果。
这是一个很好的例子用以说明MVC规范的好处。在上面的例子中,MVC规范就是我们有一个名为HomeController的控制器,该控制器是MVC程序的起点。Visual Studio创建的默认的路由假设我们遵循了MVC规范。由于我们确实遵守了该规范,我们获取了来自预定义列表的支持。如果我们不遵守MVC规范,那么我们就需要修改路由以指向我们创建的控制器。为了简化,默认的配置完全可以满足我们的需求。
3 展示Web页面
前面的例子的输出结果并不是HTML,它仅仅就是一个Hello world字符串。为了向浏览器输出HTML,我们需要创建一个视图。
首先我们需要修改HomeController的Index方法
publicclassHomeController : Controller { publicViewResult Index() { returnView(); } } |
我们通过调用不带参数的View方法,返回一个ViewResult对象,那么MVC将输出默认的视图。
如果你此时运行程序,那么你会看到MVC框架视图找到默认的视图
上面的错误消息相当有用。它不仅解释了MVC不能找到action对应的视图,而且还指出MVC从那里寻找视图。这又是另外一个MVC规范:视图与行为方法通过命名规范联系在一起。当前,我们的行为是Index,控制器是Home,因此MVC试图在Views文件夹下寻找各个不同的文件。
为了创建一个视图,我们首先停止调适,然后在HomtController.cs上点击右键(也可以在方法的名字或者方法内部),然后选择添加视图,将打开添加视图对话框
取消use a layout or master page.在本例中,我们不适用布局文件(在第七章你可以了解到布局文件的详细内容)。然后点击确认按钮,Visual Studio将创建一个命为Index.cshtml的文件,该文件位于Views/Home/文件夹下。如果你再去看上面的错误文件,你会发现我们创建的新文件包含在MVC视图寻找的文件中。
Visual Studio会自动打开该View文件,并处于编辑模式。你将会看到该文件包含了常见的HTML,仅仅是多了下面这句话而已
@{ Layout = null; } |
这是一个将由Razor视图引擎解释的表达式。这是一个相当简单的例子。它仅仅告诉Razor引擎我们没有使用master页。在此刻,我们忽略Razor,在后面再来介绍它。完整的Index.cshtml文件如下
@{ Layout = null; } <!DOCTYPEhtml> <html> <head> <metaname="viewport"content="width=device-width"/> <title>Index</title> </head> <body> <div> Hello world (from the view) </div> </body> </html> |
Ok,我们再次运行程序,将会得到如下的结果:
我们在第一次编辑Index行为方法时,仅仅返回一个字符串。这就意味着MVC仅仅把该字符串的值原封不动地传递给浏览器。而现在Index方法返回一个ViewResult,我们指示MVC呈现一个视图并返回HTML。我们并没有告诉MVC应当使用哪个视图,因为MVC使用命名规范自动寻找视图。该规范就是视图的名字就是行为方法的名字,并且位于对应控制器名字命名的视图文件夹下-~/Views/Home/Index.cshtml。
除了返回字符串和ViewResult对象之外,我们还可以返回其他的结果。比如如果我们返回RedirectResult,那么浏览器将重新指向到另外一个URL。如果我们返回HttpUnauthorizedResult,那么我们强制用户登录。这些对象都是行为结果,并且都继承ActionResult类。行为结果系统允许我们封装和重用常见的行为响应。在本书的后续章节,我们会陆续介绍各种ActionResult。
添加动态输出
Web程序平台的关键点就是构建和显示动态输出。在MVC中,控制器的工作就是构建一些数据并将这些数据传送给视图,然后视图再把这些数据呈现成HTML。
控制器想视图传送数据的一种方式就是使用ViewBag对象,该对象是Controller基类的一个属性。ViewBag是一个动态对象,你可以设置其为任意的值,这些值会通过各种视图呈现给用户。下面的例子展示了在HomeController.cs中传递一些简单的动态数据
publicclassHomeController : Controller { publicViewResult Index() { int hour = DateTime.Now.Hour; ViewBag.Greeting = hour < 12 ? "Good morning" : "Good afternoon"; return View(); } } |
我们设置ViewBag.Greeting属性一个值,然后把这个数据传递给视图。ViewBag就是一个动态对象的示例,直到我们给Greeting属性赋值之前,Greeting属性并不存在。这就允许我们从控制器想视图以一种自由的方式传递数据,并不需要预先定义类。
在视图中,我们通过引用ViewBag.Greeting属性以获取数据的值
@{ Layout = null; } <!DOCTYPEhtml> <html> <head> <metaname="viewport"content="width=device-width"/> <title>Index</title> </head> <body> <div> @ViewBag.Greeting world (from the view) </div> </body> </html> |
@ViewBag.Greeting是一个Razor表达式。当我们调用HomeController中Index方法中的View方法时,MVC框架找到Index.cshtml视图文件,然后请求Razor视图引擎解析Index.cshtml文件的内容。Razor寻找类似我们在文件中添加的@ViewBag.Greeting这样的表达式并处理它们。
对与属性Greeting并没有需要特别注意的地方,你可以使用其他的任何名字替代它,替代后的属性都会以相同的方式自动工作。此外,你还可以通过设置多个属性及其值,从而通过控制器向视图传递多个数据。再次运行程序,你可以看到动态MVC输出结果
创建一个简单的数据-实体程序
在本章后续的内容中,我们通过创建一个简单的数据-模型程序为你展示更多的MVC的基本特性。本小节的重点是演示MVC,所以我们会忽略一些细节,这样有利于我们加快进度。
示例程序背景
- 假设一个朋友将举办一个信念晚会party,他邀请我们创建一个Web站点,以方便她邀请的参加者通过电子地方式回复是否参加。她提出了四个基本的需求:
一个主页用于显示party的信息 - 一个form用户收集回复
- 验证回复form,并显示一个感谢页面
- 当回复form完成后向part举办者发送邮件
接下来,我们基于之前创建MVC程序,添加上述特性以完成用户需求。
首先,我们更新index.cshtml
@{ Layout = null; }
<!DOCTYPEhtml>
<html> <head> <metaname="viewport"content="width=device-width"/> <title>Index</title> </head> <body> <div> @ViewBag.Greeting world (from the view) <p> We're going to have an exciting pary. <br/> (To do: sell it better. Add pictures or something.) </p> </div> </body> </html> |
是的,我们已经开始了。你已经可以通过浏览器看到party的细节内容。虽然我们使用了占位内容,但是他已经是party的内容了。
设计数据模型
在MVC中,M代表模型,它是程序最重要的部分。模型就是现实世界中的对象,过程和规则;它通常也称之为程序的域。模型,经常也叫做域模型,它包含了组成程序的C#对象(域对象),以及允许我们操作这些对象的方法。供视图和控制器使用的域模型,与供客户端使用的域模型是一致的;此外一个设计良好的MVC应用程序从设计良好的域模型开始,因此我们现在需要把焦点从创建控制器和视图转移到创建域模型。
在PartyInvites程序中,我们不需要复合模型,但是我们需要创建一个名为GuestResponse的类。该对象用于存储,验证和确认被邀请者的回复。
向域模型添加一个类
MVC规范要求构成域模型的类必须放在Models文件夹下。因此我们在解决方法窗口的Models文件夹上点击右键,然后选择添加类。设置该文件的名字为GuestResponse.cs,然后点击确认按钮以创建该类。然后编辑其内容
namespace PartyInvites.Models { publicclassGuestResponse { publicstring Name { get; set; } publicstring Email { get; set; } publicstring Phone { get; set; } publicbool? WillAttend { get; set; } } } |
链接到行为方法
我们程序的一个目标是创建一个回复form,因为我们需要在Index.cshtml中添加一个链接
<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.ActionLink是一个HTML帮助方法。MVC框架内建了许多帮助方法,使用这些方法可以非常便利地呈现HTML链接,text输入,复选框,section,以及自定义控件。ActionLink方法接收两个参数:第一个参数是该链接显示在浏览器中的文本内容,第二个参数是当用户点击连接时将执行的行为。在第19~21章,我们将介绍HTML helper方法。现在运行程序,你可以看到如下结果:
如果你移动鼠标到该链接,你会发现其链接到http://localhost:10003/Home/RsvpForm。Html.ActionLink方法检查程序URL路由配置,并确定/Home/RsvpForm就是HomeController中的ResvpForm行为。请注意,与传统的ASP.NET程序不一样,MVC URLs并不对指向一个物理文件。每个行为方法都拥有一个对应的URL,MVC使用ASP.NET路由系统把URL翻译成控制器的行为。
创建行为方法
如果你直接点击上面生成的链接,那么你会得到404错误。这是因为我们还没有为/Home/RsvpForm创建对应的行为方法。下面我们就来创建该方法
publicclassHomeController : Controller { publicViewResult Index() { int hour = DateTime.Now.Hour; ViewBag.Greeting = hour < 12 ? "Good morning" : "Good afternoon"; return View(); }
publicViewResult RsvpForm() { return View(); } } |
添加强类型视图
接下来,我们为RsvpForm添加一个视图,但是这次将和我们之前添加视图的方式有微小的差别,我们将添加一个强类型的视图。强类型视图用于呈现一个指定的域类型,如果我们指定了一个我们想使用的类型,MVC可以创建一些有用的快捷以使创建视图更简单。
我们在RsvpForm上点击右键,然后选择添加视图。在弹出的添加视图窗口中,我们选中创建一个强类型视图,然后在模型类下拉列表中选择GuestResponse。
点击确认按钮,Visual Studio将会自动创建一个名为RvspForm.cshtml的新文件。在该文件的头部,你会发现多了一行Razor表达式@model PartyInvites.Models.GuestResponse
@model PartyInvites.Models.GuestResponse
@{ Layout = null; }
<!DOCTYPEhtml>
<html> <head> <metaname="viewport"content="width=device-width"/> <title>RsvpForm</title> </head> <body> <div>
</div> </body> </html> |
构建回复Form
现在,我们已经创建好强类型视图,现在我们可以在该视图中添加具体的内容
<body> <div> @using (Html.BeginForm()) { <p>Your name: @Html.DisplayTextFor(x=>x.Name)</p> <p>Your email: @Html.DisplayTextFor(x=>x.Email)</p> <p>Your phone: @Html.DisplayTextFor(x=>x.Phone)</p> <p> Will you attend? @Html.DropDownListFor(x=>x.WillAttend, new[]{ newSelectListItem(){Text = "Yes, I'll be there", Value= bool.TrueString}, newSelectListItem(){Text = "No, I can't come", Value= bool.FalseString}, }, "Choose an option" ) </p>
<inputtype="submit"value="Submit RSVP"/> } </div> </body> |
GuestResponse的每个属性,我们都使用了一个HTML帮助方法用于呈现合适的HTML input控件。这些方法允许你通过一个lamda表达式来选择input元素的属性,比如
@Html.TextBoxFor(x=>x.Phone)
HTML的TextBoxFor帮助方法将生成HTML input元素,并设置其类型为text,设置name属性为phone,id属性为phone,该名字就是域类的phone属性的名字,其生成的结果如下
<input id="Phone" type="text" value="" name="Phone"> |
该特性可以工作是因为RsvpView视图是强类型,我们已经告诉MVC GuestResponse就是该视图将呈现的类型,因为HTML帮助方法可以自动推断哪一个数据类型是我们试图从@model表达式中想读取的属性。
如果你对Lamda表达式不熟悉也没有关系,在第四章我们将会详细介绍。此外,还有一种替代方法就是使用属性的名字:@Html.TextBox("Phone")
Lamda表达式是为了防止我们错误地拼写模型属性的名字,因为Visual Studio的智能感知可以让我们自动地选择属性
这里,我们使用到一个帮助方法的规范Html.BeginForm,它将生成一个HTML form元素,并配置好postback到对应的行为。因为我们没有为该方法设置任何参数,因为我们假设postback使用同样的URL。一般地,当我们使用这种方式时,using语句确保了对象的使用范围。该语句特别适合于数据库连接对象,以确保当查询结束后,关闭数据库连接。除了销毁独享,HtmlBeginForm帮助方法还在离开范围之后关闭html form元素。这就意味着Html.BeginForm帮助方法会创建form的两个部分,比如
<form action="/Home/RsvpForm" method="post"> |
如果你不了解C#对象的销毁也没关系。我们目前的任务主要是演示如何使用HTML帮助方法创建一个form。现在你可以看到RsvpForm视图的结果
处理Form
到目前为止,我们还没有告诉MVC接收到提交的form时我们希望它做些什么。按照一般的标准,当点击Submit RSVP按钮后会情况你在form中输入的所有数据。这是因为form提交到HomeController控制器的RsvpForm行为方法,该方法通知MVC再次呈现视图。
为了可以接收和处理提交的form中的数据,我们需要添加第二个RsvpForm行为方法。请注意,两个同名的RsvpForm方法必须满足下面的条件:
- 一个方法响应HTTP get请求:一个get请求就是每次当用户点击一个链接后引发的结果。这种行为负责当用户首次访问/Home/RsvpForm时初始化一个空白的form。
- 另一个方法响应HTTP post请求:默认情况下,通过使用Html.BeginForm呈现的form通过浏览器提交就是一个post请求。这种行为负责接收提交的数据并决定对这些数据做哪些处理。
在C#中把处理get和post请求分开可以保持代码的整洁,因此两个方法有着不同的职责。这两个行为方法都被同一个URL调用,但是MVC确保了基于get或者post请求调用对应的方法。
publicclassHomeController : Controller { publicViewResult Index() { int hour = DateTime.Now.Hour; ViewBag.Greeting = hour < 12 ? "Good morning" : "Good afternoon"; return View(); }
[HttpGet] publicViewResult RsvpForm() { return View(); } [HttpPost] publicViewResult RsvpForm(GuestResponse guestResponse) { return View("Thanks", guestResponse); } } |
我们添加HttpGet特性到已有的RsvpForn行为方法上。这就告诉了MVC该方法仅仅用户处理GET请求。然后,我们添加另外一个重载方法RsvpForm,该方法接收GuestResponse参数并且应用了HttpPost特性。该特性告诉MVC次方法用于处理POST请求。此外,我们应用了PartiInvites.Models命名控件,这样我们就可以引用GuestResponse模型类型。接下来,我们将逐步解释如何完成需求列表上的其他任务。
使用模型绑定
我们已经知道第二个RsvpForm接收一个GuestResponse参数,调用该方法将处理HTTP post请求,而且GuestResponse类型是C#类,那么两者(submit中的数据和GuestResponse)是如何联系到一起的?
答案就是模型绑定,一个非常有用的MVC特性,使用该特性可以将提交的数据进行转化,在HTTP请求中的key/value将赋值给域模型类的属性。这个过程正好与HTML帮助方法相反。这也就是说,当创建form向客户端发送数据时,我们生成HTML input元素,而元素的id和name特性来自与模型类属性的名字。与之相反,使用模型绑定,通过input元素的name和value,提交到post的行为方法,然后在行为方法中,把input的值赋值给实例对应的属性。
模型绑定是一个非常强大并且可自定义的特性,它减去了直接处理HTTP请求的繁杂任务,并且允许我们向使用C#对象那样处理数据,而非通过Request.Form[]或Request.QueryString[]。GuetResponse对象作为行为方法的参数,会自动填充来自提交form的数据。在第22章,我们将会深入了解模型绑定,以及如何自定义模型绑定。
呈现其他视图
处理post的行为方法RsvpForm还演示了告诉MVC响应一个请求时,如何呈现一个特定的视图,而非默认的视图。下面就是有关的语句
return View("Thanks", guestResponse); |
上面的语句告诉MVC找到并呈现名为Thanks的视图,并且把GuestReponse对象传递给该视图。为了创建指定的视图,我们在HomeController上电价右键,然后选择添加新视图,并将其命名为Thanks
点击确认按钮后,我们创建了第二个强类型视图。然后我们开始编辑该视图
<body> <div> <h1>Thank you, @Model.Name</h1>
@if (Model.WillAttend == true) { @:It's great that you're come. 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> |
GuestResponse是通过RsvpForm行为方法传递到Thanks视图后, Thanks视图使用Razor显示基于GuestResponse值的内容。到目前为值,我们已经创建了Thanks视图,我们已经有了一个基本可以工作的处理form的MVC程序。现在重新启动项目,在form中添加一些数据,然后点击Submit RSVP按钮,你将得到如下的结果:
添加验证
现在我们需要为程序添加验证,如果我们不添加验证,那么我们的用户可能数图一些没有意义的数据,甚至提交一个完全空的表单。
在MVC程序中,验证一般都应用在域模型上,而不是用户接口处。这就意味这我们在一个地方定义验证规则,那么在其他任何使用该模型类的地方都可以使用验证规则。ASP.NET MVC支持在模型类上使用来自System.ComponentModel.DataAnnotations命名控件的声明式验证规则。下面的代码展示了应用验证规则后的GuestResponse模型类
using System.ComponentModel.DataAnnotations;
namespace PartyInvites.Models { publicclassGuestResponse { [Required(ErrorMessage="Please enter your name")] publicstring Name { get; set; }
[Required(ErrorMessage = "Please enter your email address")] [RegularExpression(".+\@.+\..+", ErrorMessage="Please enter a valid email address")] publicstring Email { get; set; }
[Required(ErrorMessage = "Please enter your phone number")] publicstring Phone { get; set; }
[Required(ErrorMessage = "Please specify whether you'll attend")] publicbool? WillAttend { get; set; } } } |
现在我们可以在RsvpForm方法中,通过ModelState.IsVliad属性检查域对象是否通过验证
[HttpPost] publicViewResult RsvpForm(GuestResponse guestResponse) { if (ModelState.IsValid) { return View("Thanks", guestResponse); } else { return View(); } } |
如果没有验证错误,我们告诉MVC呈现Thanks视图。如果有验证错误,那么我们再次重新程序RsvpForm视图,铜鼓调用不带参数的View方法。但是当有错误时,仅仅显示表单并没有任何实际意义,我们需要为用户提供额外的信息以显示到底是什么问题,以及为何我们不能接收用户提交的表单。在RsvpForm视图中使用Html.ValidationSummary帮助方法可以解决这些问题。
<body> <div> @using (Html.BeginForm()) { @Html.ValidationSummary() <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[]{ newSelectListItem(){Text = "Yes, I'll be there", Value= bool.TrueString}, newSelectListItem(){Text = "No, I can't come", Value= bool.FalseString}, }, "Choose an option" ) </p>
<inputtype="submit"value="Submit RSVP"/> } </div> </body> |
如果没有错误,那么Html.ValidateionSummary方法创建一个隐藏列表。反之,如果有验证错误,那么MVC将使其可见,并显示由验证特性定义的错误消息。
用户将不会进入到Thanks视图,除非GuestResponse类上所应用的验证限制都得以通过。请注意如果验证没有通过时,用户输入的数据得以保留并显示在视图中。这也是另外一个视图使用绑定的好处。
高亮未通过验证的字段
HTML帮助方法创建文本框,下拉框,以及其他元素,它们都可以方便的与模型绑定联系在一起。与之有相同工作机制的还有在保留用户输入数据的同时验证用户输入的数据是否负责验证规则,并且高亮未通过验证的input元素。当一个模型类没有通过验证,HTML帮助方法将生成与初始视图时稍微不同的HTML。举例来说,如果使用Html.TextBoxFor(x=>x.Name)生成input,如果没有验证错误时,得到如下的HTML:
<input id="Name" type="text" value="" name="Name" data-val-required="Please enter your name" data-val="true"> |
如果有验证错误,那么得到的HTML如下
<input id="Name" class="input-validation-error" type="text" value="" name="Name" data-val-required="Please enter your name" data-val="true"> |
我们可以看到,当验证没有通过时,HTML帮助方法为 input元素添加了一个样式input-validation-error。我们可以利用该特性创建对应的样式表以高亮为通过验证的字段。
在MVC规范中,静态的内容,比如CSS样式表,放在Content文件夹下。因此,我们首先的PartyInvites项目下创建Content文件夹,然后在Content文件夹上点击右键创建一个新的文件,然后选择样式表,并命名为Site.css,点击确认后创建该文件,并添加如下内容:
.field-validation-error {color: #f00;} .field-validation-valid { display: none;} .input-validation-error { border: 1pxsolid#f00; background-color: #fee; } .validation-summary-errors { font-weight: bold; color: #f00;} .validation-summary-valid { display: none;} |
然后,我们在RsvpForm中引用该CSS文件。编译PartyInvites项目,然后打开RsvpForm,不输入任何数据,直接点击Submit Rsvp按钮,那么你将会得到如下的结果
完成整个例子
现在我们还剩下最后一个需求,向完成RSVPs的朋友发送邮件。我们可以添加一个行为方法,通过使用.NET Framework自带的email类创建并发送邮件。但实际上,我们使用WebMail帮助方法。它不是MVC框架的一部分,但是它可以让我们完成整个示例程序而不用深入发送邮件的细节。
我们希望在呈现Thanks视图时发送邮件,因为我们需要对Thanks视图做下面的更改
<body> <div> @{ try{ WebMail.SmtpServer = "smtp.sohu.com"; WebMail.UserName = "***@sohu.com"; WebMail.Password = "***"; WebMail.From = "***@sohu.com"; WebMail.Send(Model.Email, "RSVP Notification", Model.Name + " is" + ((Model.WillAttend ?? false) ? " " : " not ") + "attending"); } catch(Exception ex) { @:<b>Sorry - we couldn't send the email to confirm your RSVP.</b> } }
<h1>Thank you, @Model.Name</h1> @if (Model.WillAttend == true) { @:It's great that you're come. 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> |
我们添加了一个Razor表达式,它使用WebMail帮助方法配置邮件服务器的详细信息,包括服务器名字,是否要求SSL链接,以及账户信息。配置完成后,就是用WebMail.Send方法发送邮件。我们把发送邮件的代码放在try..catch结构中,这样可以告诉用户是否成功发送了邮件。此外,我们依然保留了Thanks视图之前的文本信息。
小结
在本章,我们创建了一个MVC项目,并在该项目中构建了MVC数据实体应用,为你演示了MVC框架结构。我们跳过了一些细节,比如Razor语法,路由系统,以及自动测试,但是在后续的章节我们会陆续介绍它们。
在下一章,我们将介绍MVC体系结构,设计模式,以及我们在本书中会使用到的技术。