本节书摘来自异步社区《精通 ASP.NET MVC 4》一书中的第2章,第2.4节,作者: 【美】Adam Freeman ,译者: 李萍 , 徐燕萍 , 林逸 , 更多章节内容可以访问云栖社区“异步社区”公众号查看。
2.4 创建一个简单的数据录入应用程序
精通 ASP.NET MVC 4
本章的其余部分将通过建立一个简单的数据录入应用程序,来考察MVC的更多基本特性。本节将分步进行,目的是演示MVC的运转,因此会跳过对幕后工作原理的一些解释。不用担心——在后面的章节中还会重新深入地讨论这些论题。
2.4.1 设置场景
设想一个朋友要主办一个“新年除夕晚会”,需要创建一个Web网站,以便让被邀请人进行RSVP(电子回复)。这个网站需要以下四个关键特性:
一个显示此晚会信息的主页;
一个可以用来进行RSVP(电子回复)的表单;
对RSVP表单的验证,它将显示一个“谢谢你”的页面;
当完成RSVP时,给晚会的主人发送一份电子邮件。
以下小节将增强本章开始已经创建的MVC项目,并实现上述这些特性。读者可以利用前面已经涉及的内容来完成上述列表中的第一项——将一些HTML添加到现在的视图上,以给出晚会的细节。清单2-7显示了对Views/Home/Index.cshtml文件所添加的内容。
清单2-7 显示晚会的细节
![f1377f26db7af60100d7b5d81c6215924248f6ea](https://yqfile.alicdn.com/f1377f26db7af60100d7b5d81c6215924248f6ea.png?x-oss-process=image/resize,w_1400/format,webp)
开发已经开始了。如果运行该应用程序,就会看到晚会的详情——没错,这只是一个详情占位符,但可以从中获得思路(指这个页面只是一些简单的消息,在实际应用中,读者可以用丰富的内容来替换这里的消息,故将其说成是占位符——译者注),如图2-12所示。
![42f29c6a52d0804048ceb101de2af1773136252f](https://yqfile.alicdn.com/42f29c6a52d0804048ceb101de2af1773136252f.png?x-oss-process=image/resize,w_1400/format,webp)
2.4.2 设计一个数据模型
在MVC中,M代表模型(Model)是应用程序最重要的部分。模型是应用程序主体(称为域,Domain)所定义的现实对象、过程以及规则的表示。模型,通常称为域模型(Domain Model),含有应用程序域中要建立的C#对象,称为域对象(Domain Object)。这些域对象构成了整个应用程序的体系,以及操纵这些对象的方法。视图和控制器以一致的方式把这个域暴露给客户端。一个设计良好的MVC应用程序,必须从设计良好的模型开始,它是随后添加控制器和视图的焦点(现在国内流行将这里的“域”称为“领域”,译者认为这种术语不妥,但如果读者不习惯这里的“域”术语,可以将本书的“域”理解为“领域”——译者注)。
对于这个PartyInvites(晚会邀请)应用程序,不需要复杂的模型,因此将创建一个域类,叫作GuestResponse。这种对象将负责存储、验证并确认RSVP。
添加模型类
MVC的约定是把建立模型的类放在“Models”文件夹内。在“Solution Explorer(解决方案资源管理器)”窗口中右击“Models”,从弹出菜单中选择“Add(添加)”→“Class(类)”,将文件名设置为“GuestResponse.cs”,点击“Add(添加)”按钮,创建这个类。编辑该类的内容,使其与清单2-8吻合。
提示:如果没有“Class(类)”菜单项,那么可能仍在运行Visual Studio的调试器。调试器正在运行应用程序期间,VisualStudio会限制用户对项目进行修改。
清单2-8 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属性是一个nullable(可空的)的bool型,这意味着它可以是true、false或null。本章的2.4.6节将解释这一原理。
2.4.3 链接动作方法
该应用程序的目标之一是要包括一个RSVP的表单,因此需要在Index.cshtml视图中添加一个指向它的链接,如清单2-9所示。
清单2-9**添加一个指向RSVP表单的链接
![0d36c1428af0d699f6e069de4d78bd3e5afc7ec5](https://yqfile.alicdn.com/0d36c1428af0d699f6e069de4d78bd3e5afc7ec5.png?x-oss-process=image/resize,w_1400/format,webp)
Html.ActionLink是一个HTML辅助器方法(Helper Method),其作用是渲染一个超链接的HTML标记。MVC框架随带了一组内建的辅助器方法,它们可以方便地用来渲染HTML的链接、文本输入框、单选框、复选框,甚至自定义控件等。这个ActionLink方法需要两个参数:第一个是该链接的显示文本,第二个是当用户点击该链接时将要执行的动作。第19~21章将解释其余的HTML辅助器方法。读者可以在图2-13中看到添加的这个链接。
![faa3e476eb8f486e8a486dc361e2767bd5d04edf](https://yqfile.alicdn.com/faa3e476eb8f486e8a486dc361e2767bd5d04edf.png?x-oss-process=image/resize,w_1400/format,webp)
如果在浏览器中将鼠标指针移到这个链接上,可以看到这个链接指向http://<你的服务器>/Home/RsvpForm。Html.ActionLink方法已经检测了应用程序的URL路由配置,并得出/Home/RsvpForm是在一个叫作HomeController的控制器上调用RscpForm动作的URL。注意,与传统的ASP.NET应用程序不同,MVC的URL并不对应于物理文件。每个动作方法有它自己的URL,而MVC使用ASP.NET的路由系统把这些URL转换成动作。
创建动作方法
如果点击这个链接,就会看到一个“404—未找到”的错误,这是因为还没有创建与这个/Home/RsvpForm地址所对应的方法。可以把一个名为“RsvpForm”的方法添加到HomeController类,如清单2-10所示。
清单2-10 给控制器添加一个新的动作方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
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();
}
public ViewResult RsvpForm() {
return View();
}
}
}
添加一个强类型视图
本书打算为这个RsvpForm动作方法添加一个视图,但做法稍有不同——创建一个强类型视图(Strongly Typed View)。强类型视图意在渲染一个特定的域类型,而且,如果指定了想使用的类型(本例是GuestResponse),MVC将能够创建便于使用这个类型的便捷手段(指MVC提供了一些辅助器方法,在视图中可以方便是使用这个类型对象——译者注)。
注意:在做后面的事情之前,请确保已编译了MVC项目。如果已经创建了GuestResponse类,但未编译它,MVC将不能为这个类型创建强类型视图。要编译应用程序,可以从Visual Studio的“Build(生成)”菜单中选择“Build Solution(生成解决方案)”(或简单地按快捷键F6——译者注)。
在RsvpForm动作方法中单击鼠标右键,从弹出菜单中选择“Add View(添加视图)”,创建这个视图。在“Add View(添加视图)”对话框中选中“Create a strongly-typed view(创建强类型视图)”选项,并从下拉列表中选择GuestResponse,取消选中“Use a layout or master page(使用布局或母版页)”复选框,并确保选择了Razor作为视图引擎,在“Scaffold template(支架模板)”中选择“Empty(空模板)”,如图2-14所示。
点击“Add(添加)”按钮,Visual Studio将创建一个名为RvspForm.cshtml的新视图文件,并打开它以便编辑。清单2-11是它的最初内容。读者可能会注意到,这是另一种骨架形式的HTML文件,它含有Razor表达式@model。稍后会看到,这个@model是强类型视图的关键,也是为视图提供方便的关键。
![e24ac7e87a19fb8d93bbca4f8bd739546444260e](https://yqfile.alicdn.com/e24ac7e87a19fb8d93bbca4f8bd739546444260e.png?x-oss-process=image/resize,w_1400/format,webp)
清单2-11 RsvpForm.cshtml文件的最初内容
![016edb57f4643b57ae8a3c36c656e9cf310aca8a](https://yqfile.alicdn.com/016edb57f4643b57ae8a3c36c656e9cf310aca8a.png?x-oss-process=image/resize,w_1400/format,webp)
2.4.4 建立表单
现在,强类型视图已经创建了。可以扩建RsvpForm.cshtml的内容,将其制作成编辑GuestResponse对象的HTML表单。编辑该视图,使它如清单2-12所示。
清单2-12 创建一个表单视图
![8654b31c9b2afba9d8a5c1cfbb5b61dea8348ce4](https://yqfile.alicdn.com/8654b31c9b2afba9d8a5c1cfbb5b61dea8348ce4.png?x-oss-process=image/resize,w_1400/format,webp)
对于GuestResponse模型类的每一个属性,可使用一个HTML辅助器方法来渲染一个适当的HTML的input控件。这些方法让用户能够用lambda(读音同希腊字母λ的发音(拉姆达)——译者注)表达式来选择与input元素有关的属性,如下所示。
@Html.TextBoxFor(x => x.Phone)
这个HTML的TextBoxFor辅助器方法生成HTML的input元素,将其type参数设置为text,并把id和name标签属性设置为Phone,Phone是所选域类的属性名,如下所示(于是为模型属性Phone生成了一个文本框,该文本框是一个HTML的input元素——译者注)。
<input id="Phone" name="Phone" type="text" value="" />
这种灵活的特性是能够起作用的,因为这个RsvpForm视图是强类型的,而且已经告诉了MVC——GuestResponse是希望该视图所渲染的类型。因此,HTML辅助器方法能够通过@model表达式推断出用户想根据哪种数据类型来读取属性。
如果不熟悉C#的lambda表达式,不用着急,本书将在第4章提供一个概览。不过,替代运用lambda表达式的另一种办法是,将模型类型的属性名指示为一个字符串,如下所示。
@Html.TextBox("Email")
可以发现,lambda表达式技术可以防止人们输错模型类型的属性名,因为Visaul Studio会弹出智能感应,并让人们自动地选取属性,如图2-15所示。
另一个方便的辅助器方法是Html.BeginForm,它生成一个回递给动作方法的HTML表单元素(
元素)。由于没有给这个辅助器方法传递任何参数,于是它假设用户是想回递给同样的URL。一个整洁的技巧是把它封装在一个C#的using语句中,如下所示。@using (Html.BeginForm()) {
..._表单内容放在这儿_...
}
正常情况下,像这样运用using语句,会在对象超出范围时,确保对这个对象进行了清理(清理对象意指收回对象所占用的资源——译者注)。例如,这通常用于数据库连接,以确保查询完成后尽快关闭这个连接(此处的using关键词与在一个类中引用命名空间的那种using不同)。
这里并不是清理对象(上一段文字是指“正常情况下”——译者注),而是Html.BeginForm辅助器在它超出范围时关闭HTML的form元素。这意味着,Html.BeginForm辅助器方法会创建form元素的两部分(指
元素的开标签和闭标签——译者注),如下所示。<form action="/Home/RsvpForm" method="post">
..._表单内容放在这儿_...
</form>
即使不熟悉C#的对象清理,也不必担心。这里的关键是演示如何用HTML辅助器方法创建一个表单。当运行这个应用程序并点击“RSVP Now(现在回复)”链接时,便可以看到RsvpForm视图中的这个表单,如图2-16所示。
![f247199f8d4859e71c66a8f004573a590cc71dc2](https://yqfile.alicdn.com/f247199f8d4859e71c66a8f004573a590cc71dc2.png?x-oss-process=image/resize,w_1400/format,webp)
注意:由于这不是一本关于CSS或Web设计的书,本书的大部分内容将把示例演示成流行的样式(虽然本书更喜欢把它说成“经典样式”,但感觉不太对)。MVC视图生成十分整洁且纯净的HTML,而且用户可以完全控制元素的布局,以及用表单进行赋值的类,因此,使用设计工具或现成的模板,让MVC项目美观是没有问题的。
2.4.5 处理表单
此时还没有告诉MVC,将表单递交给服务器时要做什么。此刻,点击“Submit RSVP”按钮只是清除了表单中已经输入的值。这是因为,该表单会回递给Home控制器中的RsvpForm动作方法,它只是告诉MVC再次渲染这个视图。
注意:读者可能会感到奇怪,视图被再次渲染时,输入的数据消失了。如果有这种感觉,可能是因为一直在使用ASP.NET Web Form开发应用程序,Web Form在这种情况下,数据是自动保留的。下面的内容将很快演示如何用MVC取得同样的效果。
为了接收并处理表单所递交的数据,本书打算做一件聪明的事情。添加第二个RsvpForm动作方法,以形成如下作用。
一个方法用于响应HTTP的GET请求:GET请求是某人点击一个链接时浏览器正常发出的请求。当有人第一次访问/Home/RsvpForm时,这个动作版本负责显示最初的空白表单。
一个方法用于响应HTTP的POST请求:默认情况下,用Html.BeginForm()渲染的表单是由浏览器作为一个POST请求递交的。这个动作版本负责接收所递交的数据,并决定用它做什么。
以独立的C#方法分别处理GET和POST请求,有助于保持代码整洁,因为这两种方法有不同的职责。两种方法都由同样的URL进行调用,但MVC确保会根据用户处理的是GET请求,还是POST请求,来调用合适的方法。清单2-13显示了需要对HomeController类所进行的修改。
清单2-13 添加一个支持POST请求的动作方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
**using PartyInvites.Models;**
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();
}
[HttpGet]
public ViewResult RsvpForm() {
return View();
}
[HttpPost]
public ViewResult RsvpForm(GuestResponse guestResponse) {
// TODO: 对晚会的组织者发送Email响应
return View("Thanks", guestResponse);
}
}
}
此时,已经把HttpGet注解属性加到了现在的RsvpForm动作方法上。这告诉MVC,这个方法应该仅用于GET请求。然后添加了一个重载的RsvpForm方法,它带有一个GuestResponse参数,并运用了HttpPost注解属性。该注解属性告诉MVC,这个新方法将处理POST请求。注意,这里已经引入了PartyInvites.Models命名空间。这样,可以直接使用GuestResponse模型类型,而不需要使用这个类的限定名。在以下几节中,本书将解释对该清单所做的添加是如何工作的。
注:本书涉及一些不同含义的“属性”,这些属性在英文中有些称为Property,有些则称为Attribute,但中文均应当把它们称为属性。为了对这些属性加以区别,本书在翻译文字中对这些属性采用了相应的专门术语:① 把类或对象的Property称为属性;② 把[HttpGet]这样的Attribute称为注解属性(Annotation Attribute),注解属性有很多种,除这里的[HttpGet]和[HttpPost]外,本章稍后介绍的验证规则也是一种注解属性;③ 把HTML元素的Attributes称为标签属性(Attributes of HTML Tags),如<input id= "Phone" name="Phone" type="text" value="" />
,其中id、name、type、value都称为标签属性——译者注
1.使用模型绑定
RsvpForm动作方法的第一个重载渲染和前面同样的视图,它生成图3-16所示的表单。第二个重载由于其参数变得更为有趣,但已给定该动作方法是响应HTTP的POST请求而被调用的,也已给定GuestResponse类型是一个C#类,那么,这两者是如何连接的呢?
答案是模型绑定(Model Binding),这是一个非常有用的MVC特性,凭借它可以解析输入数据,并用“键/值”对填充域模型类型的属性。这一过程与使用HTML辅助器方法是相反的。即,当创建发送给客户端的表单数据时,(HTML辅助器方法)生成了HTML的input元素,其中id和name标签属性的值来自于模型类的属性名。
与此相反,通过模型绑定,会用input元素名来设置模型类实例中属性的值(其实这句话说得不完全正确,应当是用input的元素名和值来设置模型类实例中的属性值,以便通过用户在表单的各个input元素中输入的值来构造一个模型类实例——译者注),然后该实例被传递给处理POST的动作方法(简单地说,辅助器的作用是用模型类的数据来创建HTML元素,而模型绑定是用表单中输入的数据创建模型对象。所以说,模型绑定与辅助器方法的作用是相反的——译者注)。
模型绑定是一个功能强大且可定制的特性,它消除了处理HTTP请求的烦琐,使开发者能够用C#对象进行工作,而不是处理Request.Form[]和Request.QuertyString[]的值。作为参数被传递给动作方法的GuestResponse对象自动地被填充了表单字段的数据。第22章将深入研究模型绑定,包括如何对它进行定制的细节。
2.渲染其他视图
RsvpForm动作方法的第二个重载也演示了如何才能告诉MVC去渲染一个对请求进行响应的特定视图。以下是相关语句。
return View("Thanks", guestResponse);
这个对View方法的调用告诉MVC,查找并渲染一个名为“Thanks”的视图,并把GuestResponse对象传递给这个视图。为了创建所指定的这个视图,右击HomeController中的一个方法,从弹出菜单中选择“添加视图”,将视图名设置为Thanks,如图2-17所示。
![a7b82e14fe9b8b0cf84f9d1516f73bc905ddf94a](https://yqfile.alicdn.com/a7b82e14fe9b8b0cf84f9d1516f73bc905ddf94a.png?x-oss-process=image/resize,w_1400/format,webp)
由于打算创建另一个强类型视图,因此,在“Add View(添加视图)”对话框中选中“Create a strongly-typed view(创建强类型视图)”复选框。为这个视图所选择的数据类必须与用View方法传递给这个视图的类相对应,故从下拉列表中选择GuestResponse。确保“Use a layout or master page(使用布局或母版页)”复选框未选中,“View engine(视图引擎)”设置为Razor。
点击“Add(添加)”按钮,创建这个新视图。因为该视图是与Home控制器相关联的,故MVC会将此视图创建为~/Views/Home/thanks.cshtml。编辑此新视图,使之与清单2-14匹配——本书已高亮了需要添加的标记。
清单2-14 Thanks视图
![96b8d27e42c4e3ebeb04d99f34fa43dbdbc6c561](https://yqfile.alicdn.com/96b8d27e42c4e3ebeb04d99f34fa43dbdbc6c561.png?x-oss-process=image/resize,w_1400/format,webp)
该Thanks视图使用Razor并根据GuestResponse属性的值来显示内容,GuestResponse是在RsvpForm动作方法中传递给View方法的。Razor的@model操作符指示了这个强类型视图的域模型类型。为了访问这个域对象中某个属性的值,要使用Model.PropertyName(即Model.<属性名>——译者注)。例如,要获得Name属性的值,调用Model.Name。如果对Razor语法尚不了解,不用担心——本书将在第5章解释Razor。
既然已经创建了Thanks视图,便有了一个以MVC处理表单的简单运行示例。
在Visual Studio中启动该应用程序,点击“RSVP Now(现在回复)”链接,对表单添加一些数据,然后点击“Submit RSVP(递交回复)”按钮。读者将看到图2-18所示的结果(如果姓名不是Joe,并且表明不出席,显示会有所不同)。
![0fad7fe1a8b873c48f4c03b67ed702515da72e55](https://yqfile.alicdn.com/0fad7fe1a8b873c48f4c03b67ed702515da72e55.png?x-oss-process=image/resize,w_1400/format,webp)
2.4.6 添加验证
现在,到了对应用程序添加验证的时候了。如果不做此事,用户可能会输入无意义的数据,甚至递交一个空白表单。
在MVC应用程序中,验证典型地运用于域模型而不是用户界面。这意味着,在一个地方定义验证条件,会在运用模型类的任何地方生效。ASP.NET MVC支持验证规则声明,验证规则是以System.ComponentModel.DataAnnotations命名空间中的注解属性进行定义的。清单2-15演示了如何把这些注解属性运用于GuestResponse模型类。
清单2-15 对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属性使用了一个可空的(nullable)bool型。这样做是为了可以运用Required验证注解属性。如果使用一个常规的bool型,那么,通过模型绑定所接收的值只能是true或false,但不能判断用户是否已选择了一个值。一个可空的bool型有三个可能的值:true、false和null。如果用户尚未选择,系统会默认用null来表示,这会让Required注解属性能够报告一个验证错误。
读者可以在控制器类中用ModelState.IsValid属性来检查是否有验证问题。清单2-16演示了在处理POST的RsvpForm动作方法中,如何运用ModelState.IsValid。
清单2-16 检查表单验证错误
...
[HttpPost]
public ViewResult RsvpForm(GuestResponse guestResponse) {
if (ModelState.IsValid) {
// TODO: 给晚会的组织者发送电子邮件
return View("Thanks", guestResponse);
} else {
// 有验证错误
return View();
}
}
...
如果没有验证错误,便告诉MVC渲染Thanks视图,这就像先前所做的那样。如果有验证错误,则通过调用不带参数的View方法,来重新渲染RsvpForm视图。
在有错误时仅显示表单并不十分有用——需要给用户提供一些指示,告诉他们有什么问题以及为什么不能接受表单递交。通过在RsvpForm视图中使用Html.ValidationSummary(验证摘要)辅助器方法可以完成这种工作,如清单2-17所示。
清单2-17 使用Html.ValidationSummary辅助器方法
![7fe8a5385b8671ba6e7dcfc6a46490ba9f312c06](https://yqfile.alicdn.com/7fe8a5385b8671ba6e7dcfc6a46490ba9f312c06.png?x-oss-process=image/resize,w_1400/format,webp)
如果没有错误,这个Html.ValidationSummary方法会在表单中创建一个隐藏的列表条目占位符;否则,MVC会使这个占位符成为可见,并添加由验证注解属性所定义的错误消息。读者可以在图2-19中看到这是如何显示的。
![ff1e6b9f2020eb5e4c03d5b1e0b3141d12fa6302](https://yqfile.alicdn.com/ff1e6b9f2020eb5e4c03d5b1e0b3141d12fa6302.png?x-oss-process=image/resize,w_1400/format,webp)
直到运用于GuestResponse类的所有验证约束都得到满足,用户才会看到Thanks视图。注意,在表单中输入的数据是被保留的,并且,当带有验证摘要的视图被重新渲染时,这些数据会再次显示出来。这是通过模型绑定所得到的另一个好处。
注:曾经用过ASP.NET Web Form的读者,应该知道Web Form有一种“服务器控件”的概念。服务器控件通过把值序列化到一个叫作“__VIESTATE”的隐藏字段来保持状态。ASP.NET MVC模型绑定与Web Form的服务器控件、回递或视图状态等概念无关。ASP.NET MVC不会把隐含字段__VIEWSTATE注入到渲染的HTML页面中。
高亮无效字段
创建文本框、下拉列表,以及其他元素的HTML辅助器方法有一个十分灵活的特性,可以用来与模型绑定相关联。保留用户在表单中的输入数据的同样机制,也可以用来高亮验证检查失败的个别字段。
当模型类属性验证失败时,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="" />
而当用户未提供一个值时(这是一个验证错误,因为对GuestResponse模型类中的Name属性运用了Required注解属性),以下是同样的调用所生成的HTML。
<input class="input-validation-error"** data-val="true" data-val-required="Please enter yourname" id="Name" name="Name" type="text" value="" />
上述代码已经以黑体高亮了其差异。辅助器方法添加了一个值为“input-validation-error”的class标签属性。通过创建样式表可以利用这一特性,该样式表可以为这个class以及其他HTML辅助器方法所生成的class设置一些不同的CSS样式(这样就可以对不同的class值设置一些不同的显示效果——译者注)。
MVC项目的约定是,静态内容(如CSS样式表等)要放入名为Content的文件夹中。在“Solution explorer(解决方案资源管理器)”中右击“PartyInvites”项目,从弹出菜单中选择“Add(添加)”→“New Folder(新文件夹)”,便可以创建Content文件夹。右击“Content”,选择“Add(添加)”→“New Item(新项)”,在“Add New Item(添加新项)”对话框中选取“Style Sheet(样式表)”,便可创建样式表。将样式表取名为Site.css,这是使用一个MVC模板但不是Empty创建项目时,Visual Studio会自动创建的一个网站样式表文件。读者可以在清单2-18中看到Content/Site.css文件的内容(该清单的内容需要读者输入或粘贴进去,并非是自动生成的——译者注)。
清单2-18 Content/Site.css文件的内容
.field-validation-error {color: #f00;}
.field-validation-valid {display: none;}
.input-validation-error {border: 1px solid #f00; background-color: #fee;}
.validation-summary-errors {font-weight: bold; color: #f00;}
.validation-summary-valid {display: none;}
为了使用该样式表,可以在RsvpForm视图的头部(
元素中——译者注)添加一个新的引用,如清单2-19所示。给视图添加link元素就像常规的静态HTML文件。清单2-19 对RsvpForm视图添加链接元素
![4eb3d6c6217e8d3d69d9e9358205a69bce73f7bb](https://yqfile.alicdn.com/4eb3d6c6217e8d3d69d9e9358205a69bce73f7bb.png?x-oss-process=image/resize,w_1400/format,webp)
提示:曾用过MVC 3的读者可能会料想到,本书会将href标签属性指定为@Href("~/Content/Site.css"),或@Url.Content("~/Content/Site.css"),使CSS文件添加到视图。但对于MVC 4,Razor自动地检测以~/开始的标签属性,并自动插入@Href或者@Url调用。
现在,当递交会引发验证错误的数据时,会显示更明显的验证错误,如图2-20所示。
![59d47b5a2535381fdda4099ed96733e3886defc5](https://yqfile.alicdn.com/59d47b5a2535381fdda4099ed96733e3886defc5.png?x-oss-process=image/resize,w_1400/format,webp)
2.4.7 完成示例
这个示例应用程序的最后需求是,将完成了的RSVP用电子邮件发给邀请的宾客和晚会的组织者。可以添加一个动作方法,使用.NET框架的E-mail类来创建并发送一份邮件消息,以完成这一任务。但在这里,本书打算使用WebMail辅助器方法。这不是MVC框架的一部分,但它确实能完成这个例子,而不必建立另一个发送邮件的表单。
注:这里使用WebMail辅助器方法,是因为它可以轻松地演示发送一份邮件消息。然而,人们通常更喜欢把这个功能放在一个动作方法中。第3章将在描述MVC体系结构模式时,对此进行解释。
此处希望在渲染Thanks视图时发送这份邮件消息。清单2-20显示了需要进行的修改。
清单2-20 使用WebMail辅助器
![1964f4cb9c8cd517f5a67ef0af48f833fbd20ab7](https://yqfile.alicdn.com/1964f4cb9c8cd517f5a67ef0af48f833fbd20ab7.png?x-oss-process=image/resize,w_1400/format,webp)
清单2-16添加了一段Razor代码块,它用WebMail辅助器来配置邮件服务器的细节,包括服务器名、服务器是否需要SSL连接,以及账号细节。一旦配置了所有这些细节,就可以用WebMail.Send方法来发送这封邮件了。
将所有关于e-mail的代码都封装在一个try...catch块中,于是,如果不能发送邮件,则可以向用户发出警告。做这件事的办法是,把一个文本块添加到Thanks视图的输出。一个更好的办法是,当不能发送邮件消息时,显示一个独立的错误视图,但这里希望这第一个MVC应用程序尽量简单。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。