您的位置:首页 > 其它

第3章 第一个MVC应用程序 — 精通MVC 3 框架

2011-10-29 08:45 381 查看

Your First MVC Application

第一个MVC应用程序

The best way to appreciate a software development framework is to jump right in and use it. In this chapter, you’ll create a simple data-entry application using the ASP.NET MVC Framework. We’ll take things a step at a time so you can see how an ASP.NET MVC
application is constructed. To keep things simple, we’ll skip over some of the technical details for the moment; but don’t worry—if you are new to MVC, you’ll find plenty to keep you interested. Where we use something without explaining it, we provide a reference
to the chapter where you can find all the details.

鉴赏一个软件开发框架最好的办法是投入其中并使用它。本章中,你将用ASP.NET MVC框架生成一个简单的“数据-实体”应用程序。我们将把事情分为一个个步骤,以使你能明白ASP.NET MVC应用程序是如何构造的。为简化起见,这里我们将跳过某些技术细节,但是不必着急 — 如果你是MVC新手,你将会发现大量让你感兴趣的东西。在我们不加解释地使用某些东西的地方,我们会提供参考章节,在那里,你会找到所有细节。

Creating a New ASP.NET MVC Project

生成一个新ASP.NET MVC项目

We are going to start by creating a new MVC project in Visual Studio. Select New Project from the File menu to open the New Project dialog. If you select the Web templates, you’ll see that the MVC 3 installer has created a new item called ASP.NET MVC 3 Web
Application, as shown in Figure 3-1.

我们打算从生成一个新的MVC项目开始。在Visual Studio的“文件”菜单中选择“新建项目”,以打开新项目对话框。如果你选择“Web”模板,你会看到MVC 3安装程序已经生成了一个新条目,叫做“ASP.NET MVC 3 Web应用程序”,如图3-1所示。



Figure 3-1. The Visual Studio MVC 3 project template

n Caution The MVC 3 installer doesn’t remove MVC version 2, so you’ll also see the old templates available alongside the new. When creating a new project, be careful to select the right one.

小心:MVC 3安装程序并未删除MVC 2版本,因此你也会在这个新模板的旁边看到老版本的模板。在生成一个新项目时,要小心选择正确的模板。

Set the name of the new project to PartyInvites and click the OK button to continue. You will see another dialog box, shown in Figure 3-2, which asks you to choose between three different types of MVC project templates.

将这个新项目的名字设置为PartyInvites,并点击OK按钮。你将看到另一个对话框。如图3-2所示,它让你在三个不同的MVC项目模板之间进行选择。



Figure 3-2. Selecting a type of MVC 3 project

The Empty option creates a project with only the minimum files and folders required for an MVC 3 application. The Internet Application option creates a small example application that you can modify and build on. It includes user registration and authentication,
navigation, and a consistent visual style. The Intranet Application option is similar to Internet Application, but is designed for use in environments that authenticate users through a domain/Active Directory infrastructure. For this chapter, we are going
to keep things simple. Select the Empty option, leave the Use HTML5 semantic markup option unchecked, and click OK to create the new project.

“空模板(Empty)”选项只生成一个MVC 3应用程序所需要的最少文件和文件夹。“Internet应用程序”选项生成一个小型的例子应用程序,你可以在它上面进行修改,并建立其它功能。它包括了用户注册与认证、导航,以及一个协调的视觉enable debugging样式。Intranet应用程序选项比Internet应用程序更小些,但它是设计用于通过一个域/活动目录(domain/Active Directory)体系结构进行用户认证的环境。现在选择“空模板”选项,让“使用HTML 5语义标记”复选框为不选,点击OK以生成这个新项目。

n Note Under the template options in Figure 3-2, you can see a drop-down menu that lets you specify the view engine for the project. As we mentioned in Chapter 1, MVC 3 includes a new and improved view engine called Razor, which we’ll be using Razor throughout
this book. We recommend that you do the same. But if you want to use the regular ASP.NET view engine (known as the ASPX engine), this is where you select it.

注:在图3-2的模板选项下,你可以看到一个下拉菜单,它让你指定项目的视图引擎。正如我们在第1章所提到的,MVC包括了一个新的、叫做Razor的改善了的视图引擎,我们在本书中都使用这个Razor。我们建议你也这样做。但如果你想使用老式的ASP.NET视图引擎(称为ASPX引擎),你可以在这里选择。

Once Visual Studio creates the project, you’ll see a number of files and folders displayed in the Solution Explorer window. This is the default structure for an MVC 3 project. You can try to run the application now by selecting Start Debugging from the Debug
menu (if it prompts you to enable debugging, just click the OK button). You can see the result in Figure 3-3. Since we started with the empty project template, the application doesn’t contain anything to run, so we see a 404 Not Found Error.

一旦Visual Studio生成了这个项目,你将在“解决方案浏览器”窗口中看到一些文件和文件夹。这是MVC 3项目默认的结构。现在,通过选择“调试”菜单中的“启动调试”,你可以试着运行一下这个应用程序(如果提示你启用调试,点一下OK按钮)。你会看到图3-3所示的结果。因为我们是从空项目模板开始的,此应用程序尚未含有任何可以运行的东西,因此我们会看到“404未找到错误”的提示屏幕。



Figure 3-3. Trying to run an empty project

When you’re finished, be sure to stop debugging by closing the browser window that shows the error, or by going back to Visual Studio and selecting Stop Debugging from the Debug menu.

做完上述事情之后,要确保停止调试,这可以通过关闭显示错误消息的浏览器窗口,或返回Visual Studio,在调试菜单上选择“停止调试”。

Adding the First Controller

添加第一个控制器

In MVC architecture, incoming requests are handled by controllers. In ASP.NET MVC, controllers are just simple C# classes (usually inheriting from System.Web.Mvc.Controller, the framework’s built-in controller base class). Each public method in a controller
is known as an action method, meaning you can invoke it from the Web via some URL to perform an action. The MVC convention is to put controllers in a folder called Controllers, which Visual Studio created for us when it set up the project. You don’t need to
follow this or most other MVC conventions, but we recommend that you do—not least because it will help you make sense of the examples in this book.

在MVC体系结构中,传入的请求是由控制器处理的。在ASP.NET MVC中,控制器只是简单的C#类(通常继承于System.Web.Mvc.Controller,这是.NET框架内建的基类)。在控制器中的每一个public方法称为一个动作方法,意即,你可以通过某个URL从Web来调用它,以执行一个动作。MVC约定,把控制器放在一个名为Controllers的文件夹中,这是Visual Studio在建立项目时为我们自动生成的。你不一定要遵循这一约定以及其它大多数MVC约定,但我们建议你还是遵循它 — 至少因为它有助于你搞清本书例子的意思。

To add a controller to our project, right-click the Controllers folder in the Visual Studio Solution Explorer window and choose Add and then Controller from the pop-up menus, as shown in Figure 3-4.

要把一个控制器添加到我们的项目,右击Controllers文件夹,并从弹出菜单中选择“添加” — “控制器”,如图3-4所示。



Figure 3-4. Adding a controller to the MVC project

When the Add Controller dialog appears, set the name to HomeController, as shown in Figure 3-5. This is another convention: the names we give to controllers should be descriptive and end with Controller.

当“添加控制器”对话框出现时,将其命名为HomeController,如图3-5所示。这是另一个约定:我们给控制器的命名应该是描述性的,并以Controller结尾。



Figure 3-5. Setting the name for the controller

The Scaffolding options section of the dialog allows us to create a controller using a template with common functions. We aren’t going to use this feature, so ensure that the Empty controller item is selected in the Template menu, as shown in the figure.

这个对话框的辅助选项(Scaffolding Options)部分允许我们用一个带有常规功能的模板生成一个控制器。我们暂不打算使用这一特性,因此,确保在“模板(Template)”菜单中选择了“空(Empty)”条目,如图3-5所示。

n Note If you don’t see the Add Controller dialog as it is shown in Figure 3-5, you have probably forgotten to install the MVC 3 Tools Update. See Chapter 2 for details.

注:如果你没看到如图3-5所示的添加控制器对话框,你可能忘记了安装MVC 3工具更新。详见第2章。

Click the Add button to create the controller. Visual Studio will create a new C# code file in the Controller folder called HomeController.cs and open it for editing. You can see that the class is called HomeController and it is derived from System.Web.Mvc.Controller.
Edit the code in this file so that it matches Listing 3-1.

点击添加按钮以生成这个控制器。Visual Studio将在Controller文件夹中生成一个名为HomeController.cs的新的C#代码文件,并打开它进行编辑。你可以看到这个类的名字为HomeController,而且它派生于System.Web.Mvc.Controller。编辑这个文件中的代码,使其内容如清单 3-1。

Listing 3-1. Modifying the HomeController Class

using System.Web.Mvc;
namespace PartyInvites.Controllers {
public class HomeController : Controller {
public string Index() {
return "Hello, world";
}
}
}


We haven’t created anything exciting, but this is a good way of getting started with MVC. We’ve created an action method called Index, which returns the string “Hello, world”. Run the project again by selecting Start Debugging from the Visual Studio Debug
menu. The browser will display the result of the Index action method, as shown in Figure 3-6.

我们还没有生成任何让人兴奋的东西,但这是MVC的一个良好的开端。我们已经生成了一个名为Index的动作方法,它返回字符串“Hello, world”。再次运行这个项目,浏览器将显示Index动作方法的结果,如图3-6所示。



Figure 3-6. The output form of our controller action method

Understanding Routes

理解路由

As well as models, views, and controllers, MVC applications also use the ASP.NET routing system, which decides how URLs map to particular controllers and actions.

和模型、视图、控制器一样,MVC应用程序也使用ASP.NET的路由系统,它决定如何把URL映射到特定的控制器和动作上。

When Visual Studio creates the MVC project, it adds some default routes to get us started. You can request any of the following URLs, and they will be directed to the Index action on the HomeController:

当Visual Studio生成MVC项目时,它添加了一些默认的路由,以使我们能够开始。你可以请求以下任何一个URL,而它们将被引向HomeController上的Index动作上:

/
/Home
/Home/Index

So, when a browser requests http://yoursite/ or http://yoursite/Home, it gets back the output from HomeController’s Index method. Right now, the output is the string “Hello, world”. This is a good example of benefiting from following the MVC conventions.
In this case, the convention is that we will have a controller called HomeController and that it will be the starting point for our MVC application. The default routes that Visual Studio creates for a new project assume that we will follow this convention.
Since we did follow the convention, we got support for the URLs in the preceding list. If we had not followed the convention, we would need to modify the routes to point to whatever controller we had created instead. For this simple example, the default configuration
is all we need.

因此,当浏览器请求http://yoursite/或http://yoursite/Home时,它将得到HomeController的Index方法的输出反馈。此时,这个输出是字符串“Hello, world”。这是受益于遵循MVC约定的一个很好的例子。在这个例子中,该约定是我们有一个名为HomeController的控制器,而且它将是我们的MVC应用程序的起点。Visual Studio为一个新项目生成的默认路由假设我们遵循了这个约定。因为我们确实遵循了这个约定,所以我们获得了前面所列出的对URLs的支持。如果我们不遵循这种约定,我们就需要修改路由以指向我们所生成的控制器。对于这个简单例子,默认配置就是我们所需要的。

n Tip You can see and edit your routing configuration by opening the Global.asax.cs file. In Chapter 7, you’ll set up custom routing entries, and in Chapter 11 you’ll learn much more about what routing can do.

提示:你可以打开Global.asax.cs文件来查看和编辑路由配置。在第7章中,你将建立自定义路由条目,而在第11章你将学习更多关于路由能做什么的内容。

Rendering Web Pages

渲染Web页面

The output from the previous example wasn’t HTML—it was just the string “Hello, world”. To produce an HTML response to a browser request, we need to create a view.

前面例子的输出并不是HTML — 它只是一个字符串“Hello, world”。为了给浏览器请求产生个HTML响应,我们需要生成一个视图。

Creating and Rendering a View

生成并渲染一个视图

The first thing we need to do is modify our Index action method, as shown in Listing 3-2.

我们需要做的第一件事是修改我们的Index动作方法,如清单3-2所示

Listing 3-2. Modifying the Controller to Render a View

using System.Web.Mvc;
namespace PartyInvites.Controllers {
public class HomeController : Controller {
public ViewResult Index() {
return View();
}
}
}


The changes in Listing 3-2 are shown in bold. When we return a ViewResult object from an action method, we are instructing MVC to render a view. We create the ViewResult by calling the View method with no parameters. This tells MVC to render the default
view for the action.

清单3-2中的改动以黑体显示。当我们从一个动作方法返回一个ViewResult对象时,我们便在指示MVC来渲染一个视图。我们通过调用不带参数的View方法来生成这个ViewResult。这告诉MVC为这个动作渲染一个默认视图。

If you run the application at this point, you can see the MVC Framework trying to find a default view to use, as shown in the error message displayed in Figure 3-7.

如果你此时运行这个应用程序,你可以看到,MVC框架正试图找到一个要用的默认试图,并显示所图3-7所示的错误消息。



Figure 3-7. The MVC Framework trying to find a default view

This error message is more helpful than most. It explains not only that MVC couldn’t find a view for our action method, but it shows where it looked. This is another nice example of an MVC convention: views are associated with action methods by a naming
convention. Our action method is called Index, and you can see from Figure 3-7 that MVC is trying to find different files in the Views folder that have that name.

这个错误消息是很有帮助的。它不仅解释了MVC没找到这个动作方法的视图,而且它还显示查找了哪儿。这是MVC约定的另一个很好的示例:视图是由命名约定与动作方法相关联的。我们的动作方法叫做Index,而你从图3-7可以看到,MVC试图在Views文件夹中查找有这个名字的不同文件。

To create a view, right-click the action method in the HomeController.cs code file (either on the method name or inside the method body) and select Add View from the pop-up menu. This opens the Add View dialog, which is shown in Figure 3-8.

为了生成一个视图,右击HomeController.cs代码文件中的动作方法(在方法名上,或在方法体内),并从弹出菜单中选“添加视图”。这会打开添加视图对话框,如图3-8所示。



Figure 3-8. The Add View dialog

Uncheck Use a layout or master page. We are not using layouts in this example, but we’ll see them in use in Chapter 5. Click the Add button, and Visual Studio will create a new view file for you called Index.cshtml, in the Views/Home folder. If you look
back at the error message in Figure 3-7, you’ll see that the file we just created matches one of the locations that was searched.

去掉“布局或母板页”复选框。本例中我们不用布局,但我们将在第5章用到它。点击“添加”按钮,Visual Studio将在Views/Home文件夹中为你生成一个名字为Index.cshtml的视图文件。如果你返回到刚才图3-7的错误消息,你会看到我们刚生成的文件与它搜索的某个位置相匹配。

n Tip The .cshtml file extension denotes a C# view that will be processed by Razor. Previous versions of MVC relied on the ASPX view engine, for which view files have the .aspx extension.

提示:.cshtml文件扩展名指示是一个由Razor处理的C#视图。MVC之前的版本建立的ASPX视图引擎之上,视图文件为.aspx扩展名。

The Index.cshtml file will open for editing. You’ll see that this file contains mostly HTML. The exception is the part that looks like this:

Index.cshtml文件将打开到编辑状态。你将看到这个文件主要是HTML。不同的是看到像这样的部分:

@{
Layout = null;
}


This is a code block that will be interpreted by the Razor View Engine. This is a pretty simple example. It just tells Razor that we chose not to use a master page. Let’s ignore Razor for the moment. Make the addition to the Index.cshtml file that is shown
in bold in Listing 3-3.

这是一个将由Razor视图引擎来解释的代码块。这是一个相当简单的例子。它只是告诉Razor,我们并未选用布局。让我们暂不考虑Razor。现在把清单3-3所示的黑体内容添加到Index.cshtml文件中。

Listing 3-3. Adding to the View HTML

@{
Layout = null;
}<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div>
Hello, world (from the view)
</div>
</body>
</html>


The addition displays another simple message. Select Start Debugging from the Debug menu to run the application and test our view. You should see something similar to Figure 3-9.

这个添加显示另一条简单消息。选择调试来运行这个应用程序并测试我们的视图。你应当看到类似于图3-9所示的内容。



Figure 3-9. Testing the view

When we first created the Index action method, it returned a string value. This meant that MVC did nothing except relay the string value as is to the browser. Now that the Index method returns a ViewResult, we instruct MVC to render a view and return HTML.
We didn’t tell MVC which view should be used, so it used the naming convention to find one automatically. The convention is that the view has the name of the action method and is contained in a folder named after the controller—~/Views/Home/Index.cshtml.

当我们最初生成Index动作方法时,它返回一个字符串值。这意味着MVC除了把这个字符串传递给浏览器之外未做其它事情。现在,Index方法返回了一个ViewResult,我们指示MVC来渲染一个视图并返回HTML。这个约定是,视图具有动作方法的名字,并位于控制器名的文件夹之后 — ~/Views/Home/Index.cshtml。

We can return other results from action methods besides strings and ViewResult objects. For example, if we return a RedirectResult, we cause the browser to be redirected to another URL. If we return an HttpUnauthorizedResult, we force the user to log in.
These objects are collectively known as action results, and they are all derived from the ActionResult class. The action result system lets us encapsulate and reuse common responses in actions. We’ll tell you more about them and show some complex uses as we
move through the book.

我们可以从动作方法返回除了字符串和ViewResult对象之外的其它结果。例如,如果我们返回一个RedirectResult,可以使浏览器被重定向到另一个URL。如果我们返回一个HttpUnauthorizedResult,会强迫用户进行登录。这些对象统称为动作结果,且它们都从ActionResult类派生而来。动作结果系统使我们能够封装并重用动作中的常用响应。本书中我们将让你了解它们,并演示一些复杂的运用。

Adding Dynamic Output

添加动态输出

Of course, the whole point of a web application platform is to construct and display dynamic output. In MVC, it’s the controller’s job to construct some data, and it’s the view’s job to render it as HTML. The data is passed from the controller to the view.

当然,一个web应用程序平台的全部目标是构造并显示动态输出。在MVC中,控制器的工作是构造某些数据,而视图的工作是把它渲染为HTML。数据是从控制器传递给视图的。

One way to pass data from the controller to the view is by using the ViewBag object. This is a member of the Controller base class. ViewBag is a dynamic object to which you can assign arbitrary properties, making those values available in whatever view is
subsequently rendered. Listing 3-4 demonstrates passing some simple dynamic data in this manner.

把数据从控制器传递给视图的一种方法是通过使用ViewBag(视图包)对象。这是Controller基类的一个成员。ViewBag是一种动态对象,你可以给它指定任意属性,让这些值在随后要渲染的视图中可用。清单3-4演示了以这种方法传递一些简单的动态数据。

Listing 3-4. Setting Some View Data

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();
}
}
}


The statement where we provide data for the view is shown in bold. To display the data in the view, we do something very similar, as shown in Listing 3-5.

我们为视图提供数据的语句以黑体显示。要在视图中显示这个数据,我们做一些变化,如清单3-5所示。

Listing 3-5. Retrieving a ViewBag Data Value

@{
Layout = null;
}<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div>
@ViewBag.Greeting, world (from the view)
</div>
</body>
</html>


The addition to Listing 3-5 is a Razor code block that retrieves the value held in the ViewBag’s Greeting property. There’s nothing special about the property name Greeting; you could replace this with any custom property name and it would work the same.
Of course, you can pass multiple custom data values from your controller to the view in the same way.

清单3-5所添加的内容是一个Razor代码块,它接收ViewBag的Greeting属性所持有的值。对属性名Greeting没有特别的要求,你可以用任何自定义属性名来代替它,它会一样工作。当然,你可以以同样的方法从控制器把多个值传递给视图。

n Tip Notice that we don’t need to terminate a Razor code block. We just start with the @ character, and then add our C# code. This is one of the nice features of Razor. It is more readable, and we don’t need to worry about balancing <% and %> tags.

提示:注意到我们不需要终止一个Razor代码块。我们只以@字符开始,随后加C#代码。这是Razor的一个优良特性。它更可读,不必顾虑<%和%>标签的配对。

If we run the project again, we can see our first dynamic MVC output, as shown in Figure 3-10.

如果我们再次执行这个项目,我们可以看到我们的第一个动态MVC输出,如图3-10所示。



Figure 3-10. A dynamic response from MVC

Creating a Simple Data-Entry Application

生成一个简单的“数据-实体”应用程序

In the rest of this chapter, we’ll explore more of the basic MVC features by building a simple data-entry application. We are going to pick up the pace in this section. Our goal is to demonstrate MVC in action, so we’ll skip over some of the explanations
as to how things work behind the scenes. But don’t worry—we’ll revisit these topics in depth in later chapters.

在本章的其余部分,我们将通过建立一个简单的“数据-实体”应用程序来考察更多MVC的基本特性。本节我们打算分步进行。我们的目标是演示MVC的运行,因此会跳过事情幕后是如何工作的一些解释。不用担心 — 我们将在以后的章节中重新讨论这些论题。

Setting the Scene

准备工作

We are going to imagine that a friend has decided to host a New Year’s Eve party and that she has asked us to create a web site that allows her invitees to electronically RSVP. She has asked for four key features:

我们设想一个朋友决定主办一个“纽约除夕晚会”,于是她请我们生成一个web网站,以允许她的邀请人进行电子回复(RSVP)。她的要求有四个关键特征:

A home page that shows information about the party

一个显示此晚会信息的主页
A form that can be used to RSVP

一个可以用来进行RSVP的表单
Validation for the RSVP form, which will display a thank-you page

RSVP表单验证,它将显示一个“谢谢你”的页面
RSVPs e-mailed to the party host when complete

当完成RSVPs时发送电子邮件给晚会主人

In the following sections, we’ll build up the MVC project we created at the start of the chapter and add these features. We can knock the first item off the list by applying what we covered earlier—we can add some HTML to our existing view that gives details
of the party, as shown in Listing 3-6.

在以下小节中,我们将建立这个本章开始已经生成的MVC项目,并添加这些特征。我们可以利用前面已经涉及到的内容,这样可以省掉上述列表的第一项步骤 — 只把一些HTML添加到现在的视图上,以给出晚会的细节,如清单3-6所示。

Listing 3-6. Displaying Details of the Party

@{
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>


We are on our way. If you run the application, you’ll see the details of the party—well, the placeholder for the details, but you get the idea—as shown in Figure 3-11.

我们的工作已经开始。如果你运行这个应用程序,你将看到晚会的详情 — 用于详情的占位符,但你得到了需要的思想 — 如图3-11所示。



Figure 3-11. Adding to the view HTML

Designing a Data Model

设计一个数据模型

In MVC, the M stands for model, and it is the most important part of the application. The model is the representation of the real-world objects, processes, and rules that define the subject, known as the domain, of our application. The model, often referred
to as a domain model, contains the C# objects (known as domain objects) that make up the universe of our application and the methods that let us manipulate them. The views and controllers then expose the domain to our clients in a consistent manner. A well-designed
MVC application starts with a well-designed model, which is then the focal point as we add controllers and views.

在MVC中,M意为模型,它是应用程序最重要的部分。模型是我们应用程序项目(称为域)所定义的实际的对象、过程以及规则的描述。模型,通常称为域模型,含有形成应用程序世界的C#对象(称为域对象),和让我们能够维护这些对象的方法。视图和控制器然后以一致的方式把这个域展示给客户端。一个设计良好的MVC应用程序必须从设计良好的模型开始,它是随后我们添加控制器和视图焦点。

We don’t need a complex model for the PartyInvites application, but there is one domain class that we’ll use. We’ll call it GuestResponse. This object will be responsible for storing, validating, and confirming an RSVP.

对PartyInvites应用程序,我们不需要一个复杂的模型,但有一个我们将要用到的域类。我们把它叫做GuestResponse。这个对象将负责存储、验证、和配置一个RSVP。

Adding a Model Class

添加一个模型类

The MVC convention is that the classes that make up a model are placed inside the ~/Models folder. Right-click Models in the Solution Explorer window and select Add followed by Class from the pop-up menus. Set the file name to GuestResponse.cs and click
the Add button to create the class. Edit the contents of the class to match Listing 3-7.

MVC规范是把所建立的模型类放在~/Models文件夹内。在解决方案窗口右击Models,并从弹出菜单中选择“添加类”。把文件名设为GuestResponse.cs,并点击“添加”按钮来生成这个类。把这个类的内容编辑为清单3-7。

Listing 3-7. The GuestResponse Domain Class

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; }
}
}


n Tip You may have noticed that the WillAttend property is a nullable bool, which means that it can be true, false, or null. We explain the rationale for this in the “Adding Validation” section later in the chapter.

提示:你可能注意到WillAttend属性是一个可空的(nullable)布尔属性,意即,它可以是true、false、或null。我们将在本章稍后的“添加验证”小节中解释这一原理。

Linking Action Methods

连接动作方法

One of our application goals is to include an RSVP form, so we need to add a link to it from our Index.cshtml view, as shown in Listing 3-8.

我们应用程序的目标之一是要包括一个RSVP表单,因此我们需要在我们的Index.cshtml视图添加一个指向它的链接,如清单3-8所示。

Listing 3-8. Adding a Link to the RSVP Form

@{
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 is an HTML helper method. The MVC Framework comes with a collection of built-in helper methods that are convenient for rendering HTML links, text inputs, checkboxes, selections, and even custom controls. The ActionLink method takes two parameters:
the first is the text to display in the link, and the second is the action to perform when the user clicks the link. We explain the rest of the HTML helper methods in Chapters 15 and 16. You can see the link we’ve added in Figure 3-12.

Html.ActionLink是一个HTML辅助方法。MVC框架随带了一组内建的辅助方法,可以很方便地用来渲染HTML的连接、文本输入框、复选框、选择、甚至自定义控件。ActionLink方法需要两个参数:第一个是这个链接中的显示文本,第二个是当用户点击这个连接要执行的动作。第15章和第16章我们将解释其余的HTML辅助方法。你可以在图3-12中看到我们添加的这个链接。



Figure 3-12. Adding a link to a view

If you roll your mouse over the link in the browser, you’ll see that the link points to http://yourserver/Home/RsvpForm. The Html.ActionLink method has inspected our application’s URL routing configuration and worked out that /Home/RsvpForm is the URL for
an action called RsvpForm on a controller called HomeController. Notice that, unlike traditional ASP.NET applications, MVC URLs don’t correspond to physical files. Each action method has its own URL, and MVC uses the ASP.NET routing system to translate these
URLs into actions.

如果你把鼠标放到这个连接上,你将看到这个连接指向http://yourServer/Home/RsvpForm。Html.ActionLink方法已经检查了我们应用程序的URL路由配置,并得出/Home/RsvpForm是在一个叫做HomeController的控制器上调用RscpForm动作的URL。

Creating the Action Method

生成动作方法

You’ll see a 404 Not Found error if you click the link. That’s because we haven’t created the action method that corresponds to the /Home/RsvpForm URL. We do this by adding a method called RsvpForm to our HomeController class, as shown in Listing 3-9.

如果你点击这个链接,你会看到一个404未找到的错误,这是因为我们还没有生成与这个/Home/RsvpForm地址所对应的方法。我们可以把一个名为RsvpForm的方法添加到HomeController类,如清单3-9所示。

Listing 3-9. Adding a New Action Method to the Controller

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();
}
}
}


Adding a Strongly Typed View

添加一个强类型视图

We are going to add a view for our RsvpForm action method, but we are going to do something slightly different—we are going to create a strongly typed view. A strongly typed view is intended to render a specific domain type, and if we specify the type we
want to work with (GuestResponse in this example), MVC can create some helpful shortcuts to make it easier.

我们打算添加一个用于RsvpForm动作方法的视图,但我们的做法稍有不同 — 我们要生成一个强类型的视图。强类型视图是意在渲染一个特定的域类型,而且如果我们指定了想要与之一起工作的类型(本例是GuestResponse),MVC可以生成使它更容易使用的一些有用捷径。

n Caution Before doing anything else, make sure your MVC project is compiled. If you have created the GuestResponse class but not compiled it, MVC won’t be able to create a strongly typed view for this type. To compile your application, select Build Solution
from the Visual Studio Build menu.

注意:在做其它事情之前,请确保你的MVC项目被编译。如果你已经生成了GuestResponse类,但未编译它,MVC将不能为这个类型生成一个强类型视图。要编译你的应用程序,从Visual Studio的“建立”菜单选择“建立解决方案”。

Right-click inside the RsvpForm action method and choose Add View from the pop-up menu to create the view. In the Add View dialog, check the Create a strongly-typed view option and select GuestResponse from the drop-down menu. Uncheck Use a layout or master
page and ensure that Razor is selected as the view engine and that the Scaffold template option is set to Empty, as shown in Figure 3-13.

在RsvpForm动作方法中右击,并从弹出菜单中选择“添加视图”。在添加视图对话框中,选中“生成强类型视图”选项,并从下拉列表菜单中选择GuestResponse。去掉“布局或母板页”复选框,并确保选择了Razor作为视图引擎,辅助模板选择“空模板(Empty)”,如图3-13所示。



Figure 3-13. Adding a strongly typed view

Click the Add button to create the new view. Visual Studio will open the RvspForm.cshtml file that it created. You will see that it is a skeletal HTML file with a @model Razor block. As you’ll see in a moment, this is the key to a strongly typed view and
the convenience it offers.

点击“添加”按钮来生成这个新视图。Visual Studio将打开它所生成的这个RvspForm.cshtml文件。你将看到这是一个带有Razor的@model块的HTML骨架文件。正如你一会儿将看到的,@model块是强类型视图的关键,并且它提供了方便性。

Building the Form

建立表单

Now that we’ve created the strongly typed view, we can build out the contents of RsvpForm.cshtml to make it into an HTML form for editing GuestResponse objects. Edit the view so that it matches Listing 3-10.

现在,我们已经生成了这个强类型视图,我们可以扩建RsvpForm.cshtml的内容,把它制作成编辑GuestResponse对象的HTM表单。编辑这个视图,使它如清单3-10。

Listing 3-10. Creating a Form View

@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>


For each property of the GuestResponse model class, we use an HTML helper method to render a suitable HTML input control. These methods let you select the property that the input element relates to using a lambda expression, like this:

对GuestResponse模型类的每个属性,我们用一个HTML辅助方法来渲染一个适当的HTML输入控件。这些方法让你选择将输入元素与一个lambda表达式相关联的属性,像这样:

@Html.TextBoxFor(x => x.Phone)


Don’t worry if you aren’t familiar with C# lambda expressions. We provide an overview in Chapter 5.

如果你不熟悉C#的lambda表达式,不用着急。我们在第5章提供了一个概览。

The HTML helper method generates the HTML that creates an input element, sets the type parameter to text, and sets the id and name attributes to Phone, the name of the selected domain class property, as follows:

HTML辅助方法生成HTML输入元素,把type参数设置为text,并把id和name属性设置为Phone,Phone是所选域类属性的名字,如下所示:

<input id="Phone" name="Phone" type="text" value="" />


This handy feature works because our RsvpForm view is strongly typed, and we have told MVC that GuestResponse is the type that we want to render with this view.

这种方便的特性会起作用,是因为我们的RsvpForm视图是强类型的,并且我们已经告诉了MVC,我们想用这个视图来渲染的类型是GuestResponse。

An alternative to using lambda expressions is to refer to name of the model type property as a string, like this:

代替运用lambda表达式的一种方法是,把模型类型的属性名引用为一个字符串,像这样:

@Html.TextBox("Email")


We find that the lambda expression technique prevents us from mistyping the name of the model type property. This is because Visual Studio IntelliSense pops up and lets us pick the property automatically, as shown in Figure 3-14.

我们发现lambda表达式可以防止我们把模型类型的属性输错。这是因为Visaul Studio智能感应会弹出,并让我们自动地选取属性,如图3-14。



Figure 3-14. Visual Studio IntelliSense for lambda expressions in HTML helper methods

Another convenient helper method is Html.BeginForm, which generates an HTML form element configured to postback to the action method. Since we haven’t passed any parameters to the helper method, it assumes we want to postback to the same URL. A neat trick
is to wrap this in a C# using statement, like this:

另一个方便的辅助方法是Html.BeginForm,它生成一个HTML表单元素,它被配置用来回递给动作方法。因为我们还没传递任何参数给这个辅助方法,它假设我们是想回递到同样的URL。一个灵活的窍门是把它包装在一个C#的using语句中,像这样:

@using (Html.BeginForm()) {
...form contents go here...
}


Normally, when applied like this, the using statement ensures that an object is disposed of when it goes out of scope. It is commonly used for database connections, for example, to make sure that they are closed as soon as a query has completed. (This application
of the using keyword is different from the kind that brings classes in a namespace into scope in a class.) Instead of disposing of an object, the HtmlBeginForm helper closes the HTML form element when it goes out of scope. This means that the Html.BeginForm
helper method creates both parts of a form element, like this:

正常情况下,当像这样运用时,using语句在对象超出范围时确保对这个对象作了清理(清理意指收回对象所占用的内存 — 译者)。例如,这通常用于数据库连接,以确保查询完成后尽快关闭连接。(本应用程序的using关键词与在一个类中引用命名空间的那种using不同。)这里不是清理一个对象,而是Html.BeginForm辅助方法在它超出范围时关闭HTML表单。这意味着,Html.BeginForm辅助方法生成form元素的两部分(指<form>元素的开标签和闭标签 — 译者),像这样:

<form action="/Home/RsvpForm" method="post">
...form contents go here...
</form>


Don’t worry if you are not familiar with disposing of C# objects. The point here is to demonstrate how to create a form using the HTML helper method.

如果你不熟悉C#对象的清理,不用着急。这里会演示如何用HTML辅助方法生成一个表单。

n Tip ASP.NET Web Forms supports only one server-side form in a web page, usually expressed as <form runat=“server”>, which is a container for the View State data and postback logic. MVC doesn’t user server-side forms. All forms are expressed using regular
HTML, and you can have as many of them as you like in a single view. There are no View State or other hidden form elements, and the ID values you assign to IDs don’t get mangled.

提示:ASP.NET Web表单仅支持一个web页面中的服务器端表单,通常表达成<form runat=”server”>,它是视图状态数据和回递逻辑的一个容器。MVC并不使用服务器端表单。所有表单都表示成规则的HTML,你在一个视图中可以用任意多个表单。没有视图状态和其它隐藏的表单元素,而且你赋给IDs的值不会被混乱。

You can see the form in the RsvpForm view when you run the application and click the RSVP Now link. Figure 3-15 shows the result.

当运行这个应用程序时,你可以看到RsvpForm视图中的这个表单,点击RSVP连接,图3-15显示了这一结果。



Figure 3-15. The RspvForm view

n Note This isn’t a book about CSS or web design. For the most part, we will be creating examples whose appearance might be described as dated (although we prefer the term classic, which feels less disparaging). MVC views generate very clean and pure HTML,
and you have total control over the layout of elements and the classes they are assigned to, so you’ll have no problems using design tools or off-the-shelf templates to make your MVC project pretty.

注:这不是一本关于CSS或web设计的书。大部分内容,我们将把例子生成流行的样式(虽然我们更喜欢古典这一说法,这样让人感觉少一些蔑视)。MVC视图生成很纯净的HTML,并使你对元素布局和所赋值的类有完全的控制,因此你采用设计工具或它所提供的模板使你的MVC项目很美观是没有问题的。

Handling Forms

处理表单

We haven’t told MVC what we want to do when the form is posted to the server. As things stand, clicking the Submit RSVP button just clears any values you’ve entered into the form. That’s because the form posts back to the RsvpForm action method in the Home
controller, which just tells MVC to render the view again.

我们还没有告诉MVC,当表单被递交给服务器时,我们要做什么。在目前情况下,点击Submit RSVP按钮只是清除你在表单中已经输入的任何值。这是因为回递给Home控制器中的RsvpForm动作方法,它只是告诉MVC再次渲染这个视图。

n Caution You might be surprised that the input data is lost when the view is rendered again. If so, you have probably been developing applications with ASP.NET Web Forms, which automatically preserves data in this situation. We’ll show you how to achieve
the same effect with MVC shortly.

注意:你也许奇怪,视图再次渲染时,输入的数据丢失了。如果是这样,你可能正在用ASP.NET Web表单开发应用程序,它在这种情况下自动地保留数据。我们将很快给你演示如何用MVC取得同样的效果。

To receive and process submitted form data, we’re going to do a clever thing. We will add a second RsvpForm action method in order to create the following:

为了获得并处理递交表单的数据,我们打算做一件聪明的事情。我们将添加第二个RsvpForm动作方法以生成如下作用:

A method that responds to HTTP GET requests: A GET request is what a browser issues normally each time someone clicks a link. This version of the action will be responsible for displaying the initial blank form when someone first visits /Home/RsvpForm.

一个方法用来响应HTTP的 GET请求:GET请求是某人点击一个连接,浏览器发出的请求。这个动作将负责某人最初访问/Home/RsvpForm时显示空白表单。
A method that responds to HTTP POST requests: By default, forms rendered using Html.BeginForm() are submitted by the browser as a POST request. This version of the action will be responsible for receiving submitted data and deciding what to do with it.

一个方法用来响应HTTP的 POST请求:默认地,用Html.BeginForm()渲染的表单是由浏览器作为一个POST请求递交的。这个动作负责获得所递交的数据,并决定用它做什么。

(其实,GET请求和POST请求的区别很简单:GET请求是获取(GET)一个页面,而POST请求是递交(POST)一个页面(当然要包括在表单中输入的数据)。— 译者注)

Handing GET and POST requests in separate C# methods helps to keep our code tidy, since the two methods have different responsibilities. Both action methods are invoked by the same URL, but MVC makes sure that the appropriate method is called, based on whether
we are dealing with a GET or POST request. Listing 3-11 shows the changes we need to apply to the HomeController class.

在不同的C#方法中分别处理GET和POST请求,这有助于保持代码整洁,因为这两种方法有不同的职责。两种方法都是由同样的URL调用的,但MVC确保会根据我们是处理GET还是POST请求来调用合适的方法。清单3-11显示了我们要对HomeController类进行的修改。

Listing 3-11. Adding an Action Method to Support POST Requests

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);
}
}
}


We have added the HttpGet attribute to our existing RsvpForm action method. This tells MVC that this method should be used only for GET requests. We then added an overloaded version of RsvpForm, which takes a GuestResponse parameter and applies the HttpPost
attribute. The attribute tells MVC that the new method will deal with POST requests. Notice that we have imported the PartyInvites.Models namespace. This is so we can refer to the GuestResponse model type without needing to qualify the class name.

我们已经把HttpGet属性加到了现在的RsvpForm动作方法上。这告诉MVC,这个方法应该仅用于GET请求。然后我们添加了一个过载的RsvpForm方法,它带有一个GuestResponse参数,并运用了HttpPost属性。此属性告诉MVC,这个新方法将处理POST请求。注意,这里我们已经引入了PartyInvites.Modes命名空间。这样,我们可以直接使用GuestResponse模型类型而不需要使用这个类的限定名。

Using Model Binding

使用模型绑定

The first overload of the RsvpForm action method renders the same view as before. It generates the form shown in Figure 3-15. The second overload is more interesting because of the parameter, but given that the action method will be invoked in response to
an HTTP POST request, and that the GuestResponse type is a C# class, how are the two connected?

第一个过载的RsvpForm动作方法渲染和前面同样的视图。它生成如图3-15所示的表单。第二个过载方法由于其参数变得更有趣,但已经给定了这个动作方法的调用以响应HTTP POST请求,而GuestResponse类型是一个C#类,这两者是如何连接的呢?

The answer is model binding, an extremely useful MVC feature whereby incoming data is parsed and the key/value pairs are used to populate properties of domain model types. This process is the opposite of using the HTML helper methods; that is, when creating
the form data to send to the client, we generated HTML input elements where the values for the id and name attributes were derived from the model class property names. In contrast, with model binding, the names of the input elements are used to set the values
of the properties in an instance of the model class, which is then passed to our POST-enabled action method.

答案是模型绑定,这是一个非常有用的MVC特性,以解决如何解析数据,以及“键/值”对如何组装域模型类型属性等问题。这一过程与使用HTML辅助方法是反向的,即,当生成表单数据以便把它们发送到客户端时,我们(用HTML辅助方法)生成了HTML输入元素,在这里,id和name属性的值是从模型类的属性名派生而来的。反过来,利用模型绑定,输入元素的名字用来设置模型类实例的属性值,这些值然后由POST动作方法进行传递。

Model binding is a powerful and customizable feature that eliminates the grind and toil of dealing with HTTP requests, letting us work with C# objects rather than dealing with Request.Form[] and Request.QueryString[] values. The GuestResponse object that
is passed as the parameter to our action method is automatically populated with the data from the form fields. We’ll dive into the detail of model binding, including how it can be customized, in Chapter 17.

模型绑定是一个功能强大并可定制的特性,它消除了处理HTTP请求的折磨和辛苦,使我们能够用C#对象进行工作,而不是处理Request.Form[]和Request.QuertyString[]值。作为参数被传递给动作方法的GuestResponse对象自动发布了表单字段的数据。我们将在第17章研究模型绑定,包括如何定制它的细节。

Rendering Other Views

渲染其它视图

The second overload of the RsvpForm action method also demonstrates how we can tell MVC to render a specific view in response to a request. Here is the relevant statement:

第二个过载的RsvpForm动作方法也演示了我们如何才能告诉MVC去渲染一个指定的视图。以下是相关语句:

return View("Thanks", guestResponse);


This call to the View method tells MVC to find and render a view called Thanks and to pass our GuestResponse object to the view. To create the view we’ve specified, right-click inside one of the HomeController methods and select Add View from the pop-up
menu. Set the name of the view to Thanks, as shown in Figure 3-16.

这个对View方法的调用告诉MVC找到并渲染一个名为Thanks的视图,并把我们的GuestResponse对象传递给这个视图。要生成我们指定的这个视图,右击HomeController中的一个方法,并从弹出菜单中选择“添加视图”。将视图名设为Thanks,如图3-16所示。



Figure 3-16. Adding the Thanks view

We are going to create another strongly typed view, so check that box in the Add View dialog. The data class we select for the view must correspond with the class we pass to the view using the View method, so select GuestResponse from the drop-down list.
Ensure that the Select master page option is not checked, that View engine is set to Razor, and the view content is set to Empty. Click Add to create the new view. Since the view is associated with the Home controller, MVC creates the view as ~/Views/Home/Thanks.cshtml.
Edit the new view so that it matches Listing 3-12.

我们打算生成另一个强类型视图,因此选中“生成强类型”复选框。我们为这个视图所选择的数据类,必须与我们用View方法传递给这个视图的类相对应,故从下拉列表中选择GrestResponse。确保“选择母板页”选项未选,视图引擎设置为Razor,视图内容设置为“空模板”。点击“添加”以生成这个新视图。因为这个视图与Home控制器相对应,MVC将此视图生成为~/Views/Home/thanks.cshtml。把这个新视图的内容编辑为如清单3-12所示。

Listing 3-12. The Thanks View

@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>


The Thanks view uses Razor to display content based on the value of the GuestResponse properties that we passed to the View method in the RsvpForm action method. The Razor @model operator specifies the domain model type that the view is strongly typed with.
To access the value of a property in the domain object, we use Model.PropertyName. For example, to get the value of the Name property, we call Model.Name. Don’t worry if the Razor syntax doesn’t make sense—we’ll explain Razor in Chapter 5.

Thanks视图使用Razor基于我们在RsvpForm动作方法中传递给View方法的GuestResponse对象的属性的值来显示内容。Razor的@model操作符指示域模型类型是此视图的强类型。为了访问这个域对象中的属性值,我们使用Model.PropertyName(Model.<属性名>)。例如,要获得Name属性的值,我们调用Model.Name。如果Razor语法没有智能感应,不用着急 — 我们将在第5章解释Razor。

Now that we have created the Thanks view, we have a working example. Start the application in Visual Studio, click the RSVP Now link, add some data to the form, and click the Submit RSVP button. You’ll see the result shown in Figure 3-17 (although it might
differ if your name isn’t Joe and you said you couldn’t attend).

现在,我们已经生成了Thanks视图,我们有了一个工作示例。在Visual Studio中启动这个应用程序,现在点击RSVP连接,在表单上添加一些信息,然后点击递交RSVP按钮。你将看到显示在图3-17中的结果(如果你的名字不是Joe,而你说不参加这个晚会,显示也许会有不同)。



Figure 3-17. The rendered Thanks view

Adding Validation

添加验验

We are now in a position to add validation to our application. If we didn’t do this, our users could enter nonsense data or even submit an empty form.

现在到了我们把验证添加到我们的应用程序的时候了。如果我们不这样做,我们的用户可能输入无意义的数据,甚至递交一个空的表单。

In an MVC application, validation is typically applied in the domain model, rather than in the user interface. This means that we define our validation criteria in one place, and it takes effect in any place the model class is used. ASP.NET MVC supports
declarative validation rules defined with attributes from the System.ComponentModel.DataAnnotations namespace. Listing 3-13 shows how these attributes can be applied to the GuestResponse model class.

在一个MVC应用程序中,验证典型地用于域模型中,而不是在用户界面。这意味着我们在一个地方定义验证条件,它会在模型运用的任何地方生效。ASP.NET MVC支持验证规则声明,验证规则是以System.ComponentModel.DataAnnotations命名空间中的属性进行定义的。清单3-13演示了这些属性如何才能用于GuestResponse模型类。

Listing 3-13. Applying Validation to the GuestResponse Model Class

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; }
}
}


The validations rules are shown in bold. MVC detects the validation attributes and uses them to validate data during the model-binding process. Notice that we have imported the namespace that contains the validations, so we can refer to them without needing
to qualify their names.

验证规则显示为黑体。MVC侦测这些验证属性,并把它们用于验证模型绑定过程中的数据。注意,我们已经引入了含有验证的命名空间,因此我们不需要用限定名来引用它们。

n Tip As noted earlier, we used a nullable bool for the WillAttend property. We did this so we could apply the Required validation attribute. If we used a regular bool, the value we received through model binding could be only true or false, and we wouldn’t
be able to tell if the user had selected a value. A nullable bool has three possible values: true, false, and null. The null value will be used if the user hasn’t selected a value, and this causes the Required attribute to report a validation error.

提示:正如之前说明的,我们对WillAttend属性使用了一个可空的布尔型。因此这样做我们可以运用Required验证属性。如果我们使用一个规则的布尔型,那么我们通过模型绑定能够使用的只能是true或false,则我们就不能说明用户是否已选择了一个值。一个可空的布尔型有三个可能的值:true、false、和null。如果用户尚未选择一个值,将用null来表示,这样可使Required属性来报告一个验证错误。

We can check to see if there has been a validation problem using the ModelState.IsValid property in our controller class. Listing 3-14 shows how to do this in our POST-enabled RsvpForm action method.

我们可以用控件类中的ModelState.IsValid属性来查看是否有验证问题。清单3-14演示了在可以POST的RsvpForm动作方法中如何做这件事。

Listing 3-14. Checking for Form Validation Errors

[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();
}
}


If there are no validation errors, we tell MVC to render the Thanks view as we did previously. If there are validation errors, we rerender the RsvpForm view by calling the View method without any parameters.

如果没有验证错误,就像我们之前做的那样,告诉MVC来渲染Thanks视图。如果有验证错误,我们通过调用不带参数的视图方法来重新渲染这个RsvpForm视图。

We need to display the validation errors to the user, and we can do this by using the Html.ValidationSummary helper method in the RsvpForm view, as shown in Listing 3-15.

我们需要把验证错误显示给用户,而我们可以通过使用RsvpForm视图中的Html.ValidationSummary辅助方法来实现,如清单3-15所示。

Listing 3-15. Using the Html.ValidationSummary Help Method

...
<body>
@using (Html.BeginForm()) {
@Html.ValidationSummary()
<p>Your name: @Html.TextBoxFor(x => x.Name) </p>
<p>Your email: @Html.TextBoxFor(x => x.Email)</p>
...


If there are no errors, the Html.ValidationSummary method creates a hidden list item as a placeholder in the form. MVC makes the placeholder visible and adds the error messages defined by the validation attributes. You can see how this appears in Figure
3-18.

如果没有错误,Html.ValidationSummary方法在表单中生成一个隐藏的列表条目占位符。MVC使这个占位符成为可见的,并添加根据验证属性所定义的错误消息。在图3-18中你可以看到这是如何出现的。



Figure 3-18. The validation summary

The user won’t be shown the Thanks view until all of the validation constraints we applied to the GuestResponse class have been satisfied. Notice that the data we entered into the form was preserved and displayed again when the view was rendered with the
validation summary. This is a benefit we get from model binding.

直到我们用于GuestResponse类的所有验证约束都得到满足,才会显示Thanks视图。注意,我们在表单中输入的数据是保留的,并且当带有验证摘要的视图被重新渲染时被再次显示出来。这是我们通过模型绑定所得到的好处。

(总结:验证规则是在模型中设置的,而验证过程和对验证的处理是在视图中进行的。 — 译者注)

n Note If you’ve worked with ASP.NET Web Forms, you’ll know that Web Forms has a concept of “server controls” that retain state by serializing values into a hidden form field called __VIEWSTATE. ASP.NET MVC model binding is not related to the Web Forms concepts
of server controls, postbacks, or View State. ASP.NET MVC doesn’t inject a hidden __VIEWSTATE field into your rendered HTML pages.

注:如果你曾经用过ASP.NET Web表单,你应该知道Web表单有一个“服务器控件”的概念,服务器控件通过把值序列化到一个叫做__VIESTATE的隐含字段的方法来保持状态。ASP.NET MVC模型绑定与web表单的服务器控件、回递、或视图状态等概念无关。ASP.NET MVC不会把一个隐含字段__VIEWSTATE注入到你渲染的HTML页面之中。

Highlighting Invalid Fields

高亮无效字段

The HTML helper methods that create text boxes, drop-downs, and other elements have a very handy feature that can be used in conjunction with model binding. The same mechanism that preserves the data that a user entered in a form can also be used to highlight
individual fields that failed the validation checks.

生成文本框、下拉列表、以及其它元素的HTML辅助方法有一个十分灵活的特性,可以用来与模型绑定相关联。与保留用户在表单中输入的数据同样的机制也可以用来高亮验证检查失败的字段。

When a model class property has failed validation, the HTML helper methods will generate slightly different HTML. As an example, here is the HTML that a call to Html.TextBoxFor(x => x.Name) generates when there is no validation error:

当一个模型类属性验证失败时,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="" />


And here is the HTML the same call generates when the user doesn’t provide a value (which is a validation error because we applied the Required attribute to the Name property in the GuestResponse model class):

而以下是同样的调用在用户未提供一个值(这是一个验证错误,因为我们对GuestResponse模型类中的Name属性运用了Required属性)时所生成的HTML:

<input class="input-validation-error" data-val="true" data-val-required="Please enter your name" id="Name" name="Name" type="text" value="" />


We have highlighted the difference in bold. This helper method added a CSS class called input-validation-error. Different helper methods apply different CSS classes, but they can all be found in the ~/Content/Site.css style sheet that Visual Studio adds
to all MVC projects. To use this style sheet, we add a new reference to the head section of RsvpForm view, like this:

我们已经以黑体高亮了其差异。这个辅助方法添加了一个名为input-validation-error的CSS类。不同的辅助方法运用不同的CSS类,但它们都可以在Visual Studio给MVC项目添加的~/Content/Site.css样式表中找到。要使用这个样式表,我们可以在RsvpForm视图的头部添加一个新的引用,像这样:

<link rel="Stylesheet" href="@Href("~/Content/Site.css")" type="text/css"/>


n Tip If you’ve used the ASPX view engine, you may be used to specifying paths directly using the tilde (~) (like this: href="~/Content/Site.css") and relying on the view engine to convert this into a URL that the browser can follow (such as ../Content/Site.css).
The Razor View Engine takes a different approach. It requires the Href operator to convert URLs (like this: href="@Href("~/Content/Site.css")"). You can find more details about Razor in Chapter 5.

提示:如果你使用ASPX视图引擎,你可能习惯于用波浪号(~)直接指定路径(如,href=”~/Content/Site.css”)并依靠视图引擎把它转换成一个浏览器可以采用的URL(如,../Content/Site.css)。Razor视图引擎采用一种不同的方式。它需要Href操作符来转换URL(如,href=”@Href(“~/Content/Site.css”)”)。第5章你可以了解Razor的更多细节。

Now when the user submits data that causes a validation error, he will see a more useful indication of the cause of the problems, as shown in Figure 3-19.

现在,当用户递交会引发验证错误的数据时,他会看到引起这个错误的更多有用的提示,如图3-19所示。



Figure 3-19. Automatically highlighted validation errors

Completing the Example

完成示例

The last requirement for our sample application is to e-mail completed RSVPs to our friend, the party organizer. We could do this by adding an action method to create and send an e-mail message using the e-mail classes in the .NET Framework. Instead, we
are going to use the WebMail helper method. This isn’t part of the MVC framework, but it does let us complete this example without getting mired in the details of setting up other forms of sending e-mail.

这个示例应用程序的最后需求是把完成了RSVP的电子邮件发给我们的朋友,该晚会的组织者。我们可以通过添加一个动作方法用.NET框架的e-mail类来生成并发送一份邮件消息的办法来完成这一任务。这里,我们打算使用WebMail辅助方法。这不是MVC框架的一部分,但它确实能让我们完成这个例子而不必陷入要建立另一个发送邮件的表单这样的麻烦。

n Note We used the WebMail helper because it lets us demonstrate sending an e-mail message with a minimum of effort. Typically, however, we would prefer to put this functionality in an action method. We’ll explain why when we describe the MVC architecture
pattern in Chapter 4.

注:我们使用WebMail辅助方法是因为它让我们以最少的努力就可以发送一封邮件。然而,典型地,我们更喜欢把这个功能放在一个动作方法中。我们将在第4章中描述MVC体系结构模式时解释这是为什么。

We want the e-mail message to be sent as we render the Thanks view. Listing 3-16 show the changes that we need to apply.

我们希望,当我们渲染Thanks视图时发送这份邮件消息。清单3-16显示了我们需要进行的修改。

Listing 3-16. Using the WebMail Helper

@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>


We have added a Razor code block that uses the WebMail helper to configure the details of our e-mail server, including the server name, whether the server requires SSL connections, and account details. Once we’ve configured all of the details, we use the
WebMail.Send method to send the e-mail.

我们已经添加了一段Razor代码块,它用WebMail辅助器来配置邮件服务器的细节,包括服务器名、服务器是否需要SSL连接,以及帐号细节。一旦我们已经配置了所有这些细节,我们就可以用WebMail.Send方法来发送这封邮件。

We have enclosed all of the e-mail code in a try...catch block so that we can alert the user if the e-mail isn’t sent. We do this by adding a block of text to the output of the Thanks view. A better approach would be to display a separate error view when
the e-mail message can’t be sent, but we wanted to keep things simple in our first MVC application.

我们把所有邮件代码封闭在一个try…catch块中,这样,如果不能发送邮件,我们可以向用户发出告警。我们把一个文本块添加到thanks视图的输出。一个更好的办法是当不能发送邮件消息时显示一个独立的错误视图,但我们希望在我们的第一个MVC应用程序中保持事物简单。

Summary

摘要

In this chapter, we created a new MVC project and used it to construct a simple MVC data-entry application, giving you a first glimpse of the MVC Framework architecture and approach. We skipped over some key features (including Razor syntax, routing, and
automated testing), but we’ll come back to these topics in depth in later chapters.

本章我们生成了一个新的MVC项目,并用它构造了一个简单的MVC数据-实体应用程序,让你概览了MVC框架的体系结构和方法。我们跳过了一些关键特性(包括Razor语法、路由,以及自动测试),但我们将在之后的章节中深入讨论这些主题。

In the next chapter, we’ll explore the MVC architecture, design patterns, and techniques that we’ll use throughout the book.

下一章我们考察MVC体系结构、设计模式,以及在本书中要运用的技术。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: