【译】《Pro ASP.NET MVC4 4th Edition》第二章(四)

  本文地址http://www.cnblogs.com/outtamyhead/archive/2013/03/29/2988632.html,转载请保留本地址

说在前面:

1、由于是头次翻译整本书籍,所以错误难免,希望大家都提出来,翻译的不好还望大家少拍砖多鼓励。

2、该系列没有按照原文直译,而是加入了我的一些言语在里面(在没有改变原意的情况下),所以大家在看的时候希望有所对照。

3、该系列每周出一或二篇博客,因为我最近很忙,一直在加班,很累的说。

4、该系列不提供原版文字,希望看原版的可以自行下载Pdf。

5、该系列省去了前面的废话,单刀直入,讲主体内容。

  第二章:第一个ASP.NET MVC4程序(下二)

  处理表单

  当表单被传到服务器之后,我们并没有告诉MVC我们想做什么。从目前来看,点击【Submit RSVP】按钮只是清除了我们在表单里留下的内容。这是因为表单回递到Home控制器的RsvpForm动作方法后,只是告诉了MVC要重新渲染这个视图。

   提示:你也许会好奇为什么当视图重新渲染之后输入的数据会丢失。假如是这样的话,你大概会开发一个ASP.NET Web Froms,在上面这种情况下它会自动保存数据。我们会在一会告诉你如何达到这同样的效果。

   为了接收和处理提交的表单数据,我们打算做一件聪明的事情。为了创建下面的内容,我们会添加第二个RsvpForm动作方法。

  一个响应HTTP GET请求的方法:GET请求就是某人点击一个链接浏览器发出的请求。这样的动作会使你在初次访问/Home/RsvpForm时显示一个初始空白的表单。

  一个响应HTTP POST请求的方法:默认情况下,通过Html.BeginForm()渲染的表单会被浏览器以POST请求的方式提交。这种动作将负责接收提交的数据,并决定该怎么做

   在不同的C#方法中处理GETPOST请求,会使我们的代码保持整洁,因为这两种方法有不同的职责。这两个动作方法都有相同的URL调用,但是MVC会确保调用到合适的方法,基于我们正在处理的一个GETPOST请求。清单2-14列出了我们在HomeController添加的内容

清单:2-14
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 response to the party organizer
return View("Thanks", guestResponse);
}
}
}

  我们在已经存在的RsvpForm动作方法上添加了HttpGet特性。这就告诉MVC这个方法只有在GET请求时被用到。我们再添加一个RsvpForm的重载方法,这个方法需要一个GuestResponse参数并加上HttpPost特性。这个特性会告诉MVC这个新方法将会处理POST请求。注意我们也引入了PartyInvites.Models命名空间--这样我们就可以直接使用GuestResponse模型类型而不需要限定类名。我们会在后面解释我们添加的这些是如何工作的。

  使用模型绑定

  第一个RsvpForm动作方法的重载方法会像以前那样渲染相同的视图。它产生的表单如图2-16所示。第二个重载方法比较有趣,因为有参数,但是在响应HTTP POST请求时会给这个动作方法赋值,而这个GuestResponse类型是一个C#类,这两者如何联系呢?

   答案就是模型绑定,这是在解析输入数据以及用键/值对来发布主域模型类型的属性等方面的一个非常有用的MVC特性。在使用HTML辅助方法时这个过程却是相反的;那就是,当创建表单的数据发送到客户端,我们生成HTML input元素,它的idname特性的值对应模型类属性名称。相比之下,使用模型绑定,input元素的名称通常会被设置为模型类中一个实例的属性的值,随后传递给我们能够使用POST的动作方法。

  模型绑定是一种强大的并且可自定义的功能,利用HTTP请求直接消除了研磨和辛劳的处理。让我们通过C#对象去工作而不是通过Request.Form[]Request.QueryString[]值。作为参数传递到我们动作方法中的GuestResponse对象自动发布了表单字段的数据。在第22章,我们将深入的了解包括模型绑定以及它的自定义的细节。

  渲染其他视图

  第二个RsvpForm动作方法的重载方法也说明了我们如何告诉MVC在响应请求时去渲染一个特定的视图,而不是默认的视图。这是相关的代码段:

  return View("Thanks", guestResponse);

  这个调用的View方法告诉MVC去找到并且渲染一个叫做Thanks的视图然后把GuestResponse对象传递到视图上。要创建我们指定的视图,就在HomeController的一个方法里右键选择【Add View】。把视图名字设置为Thanks。如图2-17

  

  我们打算创建另外一个强类型的视图,所以在【Add View】对话框中选中那一项。我们为视图选择的数据类必须与我们通过View方法传递到视图上的类相一致,所以要确保GuestResponse被选中。确保【Use a layout or master page】项未被选中,【View engine】设置为Razor,【Scaffod template】设置为Empty

  点击【Add】按钮来创建新视图。因为这个视图和Home控制器有关,所以MVC创建的这个视图的位置是~/Views/Home/Thanks.cshtml。编辑这个视图使它和清单2-15一样--我们高亮显示了你需要添加的代码。

清单2-15
@model PartyInvites.Models.GuestResponse
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<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视图基于我们在RsvpForm动作方法中通过View方法传递的GuestResponse的属性值来展现内容。@model运算符指定主域模型类型。要访问主域对象中一个属性的值,我们要使用Model.PropertyName。例如,要获得Name属性的值,我们就调用Model.Name。不用担心如果Razor语法没有表达出它的含义--我们会在第5章详细解释。

  现在我们已经创建了Thanks视图,我们有了一个通过MVC处理表单的基本的工作示例。

  运行程序,点击【RSVP Now】链接,在表单中添加一些数据,然后点击【Submit RSVP】按钮。你会看到如图2-18所示的结果(如果你输入的名字不是Joe或者你说你不能参加,这会有些不同)。

  

  添加验证

  我们现在要给我们的应用程序添加验证。如果我们不做这步操作,我们的用户会输入一些没有意义的数据或者甚至提交一个空表单。

  在一个MVC应用程序中,在主域模型上添加验证是很典型的一种方式,而不是在用户端。这就意味着在一个地方定义我们验证标准,它就会在用到的模型类的任何地方发挥作用。ASP.NET MVC 支持从System.ComponentModel.DataAnnotations命名空间声明的属性定义的验证规则。清单2-16说明了这些特性如何添加到GuestResponse模型类。

清单2-16
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属性添加了一个可为空的布尔类型。因此我们可以添加Required验证特性。如果我们使用正规的布尔类型,我们通过模型绑定收到的值就只有truefalse,并且我们不能知道用户是否选择了值。一个可为空的布尔包括三个可能值:truefalsenull。如果用户没有选择一个值,null就会被用到,这就引起了Required特性去产生一个错误验证。

  我们可以通过在控制器类中的ModelState.IsValid属性来检查如果有验证问题。清单2-17展示了在POST方式的RsvpForm动作方法如何做这件事。

清单2-17
...
[HttpPost]
public ViewResult RsvpForm(GuestResponse guestResponse) {
if (ModelState.IsValid) {
// TODO: Email response to the party organizer
return View("Thanks", guestResponse);
} else {
// there is a validation error
return View();
}
}
…

  如果没有验证错误,我们就告诉MVC去渲染Thanks视图。如果存在验证错误,我们重新渲染RsvpForm视图通过不带参数的View方法。

  当出现错误时只显示表单是没有用的--我们需要给用户提供一些问题的描述和我们为什么不能接受提交的表单数据。我们在RsvpForm视图中通过Html.ValidationSummary辅助方法来实现这一点,如清单2-18所示

清单2-18
@model PartyInvites.Models.GuestResponse
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>RsvpForm</title>
</head>
<body>
@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[] {
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>

  如果没有错误,Html.ValidationSummary方法会在表单中创建一个隐式列表项占位符。MVC使占位符可见并添加错误信息通过验证特性。在图2-19中说明了它是怎么出现的。

  

  Thanks视图不会在用户面前展现直到我们添加在GuestResponse类上的验证限制得到满足。需要注意的是当视图通过验证集合重新渲染的时候我们添加到表单的数据是被保存的并被再次展现在视图上。这是模型绑定的另一个优势。

  提示:如果你用ASP.NET Web Forms工作,你会了解Web表单有一个“服务端控件”概念,它的持久状态是通过把序列化的值保存到一个叫做_VIEWSTATE的隐藏的表单字段实现的。ASP.NET MVC模型绑定不同于服务端控件概念的Web表单,回递,或视图状态。ASP.NET MVC不会往你渲染的HTML页注入_VIEWSTATE隐藏字段。

 




文章作者:禁止吸烟

道本无话可说,皆当全力以赴。

博客地址:http://www.cnblogs.com/outtamyhead/

原文地址:https://www.cnblogs.com/outtamyhead/p/2988632.html