摘要:

在这篇文章中,我将在一个例子中实际地展示MVC。

场景

假设一个朋友决定举办一个新年晚会,她邀请我创建一个用来邀请朋友参加晚会的WEB程序。她提出了四个注意的需求:

  • 一个首页展示这个晚会
  • 一个表单用来提交申请
  • 验证表单内容,成功后显示谢谢页面
  • 完成后给主人发送申请邮件

添加Model类GuestResponse

任何程序都应该是以数据为中心。因此,首先,在工程内添加Domain Model。
在工程根部创建Model文件夹。
在Model文件夹内创建GuestResponse.cs代码文件。
修改GustResponse代码。

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace PartyInvite.Models
{
public class GuestResponse
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public bool? WillAttend { get; set; }
}
}

修改Index视图。

我将打算将Home/Index作为首页,修改Index视图。

 @{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="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 party.<br />
(To do: sell it better. Add pictures or something.)
</p>
@Html.ActionLink("RSVP Now", "RsvpForm")
</div>
</body>
</html>

执行程序,在浏览器中得到运行结果。

在页面底部出现了一个“RSVP Now链接”,这个链接是方法Html.ActionLink得到的。

将鼠标移到该链接上,可以看到链接指向了/Home/RsvpForm链接。

为工程指定默认页面

鼠标右键工程PartyInvites,在弹出的菜单中选择Properties。在Web选项卡中选择“Specific page”,在输入框中输入Home/Index。

为RSVP添加Action和视图

返回HomeController,添加Action。

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc; namespace PartyInvites.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ViewResult Index()
{
int hour = System.DateTime.Now.Hour;
ViewBag.Greeting = hour < ? "Good Moring" : "Good Afternoon";
return View();
} public ViewResult RsvpForm()
{
return View();
}
}
}

为RsvpForm这个Action添加视图。

模板选择“Empty”,Model class选择刚创建的Domain Model类GuestResponse。点击“Add”按钮添加。

修改添加的视图RsvpForm.cshtml。

 @model PartyInvite.Models.GuestResponse

@{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<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>
一些解释:

@model PartyInvite.Models.GuestResponse:此视图以GuestResponse类作为它的Model。
@using (Html.BeginForm()) {}: 调用Html帮助类,创建一个表单,大括号里面的内容是表单内容。
@Html.TextBoxFor、@Html.DropDownListFor:调用Html帮助类,传入lamda表达式,为表单创建输入HTML元素。
执行程序,在浏览器中得到的Index页面中点击“RSVP Now”链接,得到下面的页面。

处理表单

我还没有告诉MVC当表单提交到服务器端的时候我的响应是什么。按照目前的情况,点击提交按钮只是清空你刚才填写的信息。这是因为表单提交到Home控制器的RsvpForm行为方法,这个方法只是告诉MVC再次呈现这个视图。

获取和处理提交的表单数据,我将要使用一个聪明的方法。我将添加第二个RsvpForm行为方法来做下面的事情:

  • 一个方法响应HTTP GET请求。GET请求是每当用户点击一个链接时浏览器发出的。当用户第一次访问/Home/RsvpForm的时候展示初始空白表单,调用这个版本的方法。
  • 一个响应HTTP提交请求的方法:默认的,使用Html.BeginForm()方法呈现的表单由浏览器提交一个POST请求。这个版本的行为方法负责获取提交数据以及决定拿到数据做什么事情。

在分开的C#方法中处理GET和POST请求让我的控制器打开变得简洁,因为两个方法有不同的责任。两个行为方法都由相同的URL激活,但是MVC根据处理的是一个GET请求还是POST请求确保合适的方法被调用。下面是修改后的HomeController类。

 using PartyInvite.Models;
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 = System.DateTime.Now.Hour;
ViewBag.Greeting = hour < ? "Good Moring" : "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,传入一个GustResponse参数,用HttpPost特性修饰。这个特性告诉MVC这个心的方法处理Post请求。

呈现其他视图

第二个重载的RsvpForm行为方法也展示了怎样告诉MVC为请求的响应呈现一个具体的视图,而不是默认的视图,这是相关的语句:

return View("Thanks", guestResponse);

这调用View方法告诉MVC寻找并呈现一个名字叫“Thanks”的视图,并传递GuestResponse对象给这个视图。

在Views/Home文件夹中创建Thanks视图。

修改视图代码:

@model PartyInvite.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>
<div>
@if (Model.WillAttend == true)
{
<p>It's great that you're coming. The drinks are already in the fridge!</p>
}
else {
@:Sorry to hear that you can't make it, but thanks for letting us know.
}
</div>
</div>
</body>
</html>

基于我传给视图方法RsvpForm的参数GuestResponse对象属性值,这个Thanks视图使用Razor展示内容。Razor表达式@Model引用我写的Domain Model类型,得到对象的属性值Model.Name。

执行程序,在RsvpForm表单中填写数据,点击“Submit” 按钮,跳转到Thanks页面。

添加验证

我现在要给我的应用程序添加验证。没有验证,用户可能输入无意义的数据或者甚至提交空的表单。在MVC应用程序里,把Domain Model(域模型)应用到验证,而不是用户接口。这意味着我可以在一个地方定义验证准则,在模块类被使用的应用程序里任何地方都生效。ASP.NET MVC支持定义System.ComponentModel.DataAnnotations名称空间的特性声明式验证规则,意味着验证约束使用标准的C#特性来表达。

修改GuestResponse类。

 using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web; namespace PartyInvite.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在模型-绑定过程中自动探测特性并使用他们验证数据。

在Controller类里,我使用ModelState.IsValid属性检验是否有验证问题。下面是修改后的Controller类RsvpForm方法。

         [HttpPost]
public ViewResult RsvpForm(GuestResponse guestResponse)
{
if (ModelState.IsValid)
{
return View("Thanks", guestResponse);
}
else
{
return View();
}
}

如果没有验证错误,我告诉MVC呈现Thanks视图,像我之前那样。如果有验证错误,我调用不含参数的View方法重新呈现这个表单。

只是显示这个表单而不显示错误信息是没有什么帮助的-我还需要为用户提供一些错误提示信息以及为什么我不能获得表单的提交数据。我在RsvpForm视图里使用Html.ValidationSummary帮助方法来达到这个目的。下面是修改后的RsvpForm视图。

如果没有错误,Html.ValidationSummary方法在表单内创建一个隐藏的列表项作为占位符。MVC使得占位符变得可见并将定义在验证属性的错误消息显示在上面。

提交空白的RsvpForm表单,得到下面的运行结果。

将不会给用户显示Thanks视图,直到所有的运用在GuestResponse类的验证约束都被满足。注意输入到在视图被呈现的时候,表单的数据被保留了并和验证摘要信息一起重新显示。

高亮显示非法输入字段

HTML帮助方法创建文本框、下拉框和其他的元素,这些元素有现成的可以用于模型绑定的特性。相同的保留用户输入到表单的数据机制同样被用来高亮显示验证失败的字段。

当一个模型类属性验证失败了,HTML帮助方法将产生稍微不同的HTML。

例如,下面是调用Html.TextBoxFor(x=>x.Name)方法后,验证没有错误的文本框:

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

下面是调用相同方法后,用户没有输入值(这是一个验证错误,因为我在Name属性运用了Request特性)的文本框:
<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类样式的样式表。

在根目录下添加Content文件夹,在Content文件夹内添加样式表文件Styles.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视图,添加样式表引用。

 @model PartyInvite.Models.GuestResponse

 @{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" type="text/css" href="~/Content/Styles.css" />
<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>

当RsvpForm表单输入错误的时候,页面将显示如下所示、

给视图添加样式

基本的应用程序功能已经做好了-除了发送邮件,但是整体的外观很简陋。

这里我使用Bootstrap库,这是早先是Twitter公司开发的现在用得很广的一个CSS库。

使用NutGet导入Bootstrap库。Bootstrap库的CSS文件将导入到Content文件夹下,JavaScript文件将导入到Scripts文件夹下。

修改Index视图。

 @{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<link href="~/Content/bootstrap.css" rel="stylesheet" />
<link href="~/Content/bootstrap-theme.css" rel="stylesheet" />
<title>Index</title>
<style>
.btn a {
color: white;
text-decoration: none;
} body {
background-color: #F1F1F1;
}
</style>
</head>
<body>
<div class="text-center">
<h2>We're going to have an exciting party!</h2>
<h3>And you are invited</h3>
<div class="btn btn-success">
@Html.ActionLink("RSVP Now", "RsvpForm")
</div>
</div>
</body>
</html>

修改RsvpForm视图。

 @model PartyInvite.Models.GuestResponse

 @{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<link href="~/Content/bootstrap.css" rel="stylesheet" />
<link href="~/Content/bootstrap-theme.css" rel="stylesheet" />
<title>RsvpForm</title>
</head>
<body>
<div class="panel panel-success">
<div class="panel-heading text-center"><h4>RSVP</h4></div>
<div class="panel-body">
@using (Html.BeginForm())
{
@Html.ValidationSummary()
<div class="form-group">
<label>Your name:</label>@Html.TextBoxFor(x => x.Name)
</div>
<div class="form-group">
<label>Your Email:</label> @Html.TextBoxFor(x => x.Email)
</div>
<div class="form-group">
<label>Your Phone:</label> @Html.TextBoxFor(x => x.Phone)
</div>
<div class="form-group">
<label>
Will you attend?
</label>
@Html.DropDownListFor(x => x.WillAttend,
new[] {
new SelectListItem() { Text="Yes, I will be there" , Value = bool.TrueString },
new SelectListItem() { Text = "Yes, I will be there", Value = bool.TrueString }
}, "Choose an option")
</div>
<div class="text-center">
<input class="btn btn-success" type="submit"
value="Submit RSVP" />
</div>
}
</div>
</div>
</body>
</html>

修改Thanks视图。

 @model PartyInvite.Models.GuestResponse

 @{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<link href="~/Content/bootstrap.css" rel="stylesheet" />
<link href="~/Content/bootstrap-theme.css" rel="stylesheet" />
<title>Thanks</title>
<style>
body {
background-color: #F1F1F1;
}
</style>
</head>
<body>
<div class="text-center">
<h1>Thank you, @Model.Name!</h1>
<div class="lead">
@if (Model.WillAttend == true)
{
<p>It's great that you're coming. The drinks are already in the fridge!</p>
}
else {
@:Sorry to hear that you can't make it, but thanks for letting us know.
}
</div>
</div>
</body>
</html>

运行程序,在浏览器中得到运行结果。

Index页面:

RsvpForm页面:

Thanks页面:

完成这个例子

我的例子的最后一个需求是给晚会主办者发送提交邮件。

修改Thanks视图,添加发送邮件代码。

 @model PartyInvite.Models.GuestResponse

 @{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<link href="~/Content/bootstrap.css" rel="stylesheet" />
<link href="~/Content/bootstrap-theme.css" rel="stylesheet" />
<title>Thanks</title>
<style>
body {
background-color: #F1F1F1;
}
</style>
</head>
<body>
@{
try
{
WebMail.SmtpServer = "smtp.example.com";
WebMail.SmtpPort = 25;
WebMail.EnableSsl = true;
WebMail.UserName = "example@163.com";
WebMail.Password = "password";
WebMail.From = "example@163.com";
WebMail.Send("example@163.com", "RSVP Notification", Model.Name + " is " + ((Model.WillAttend ?? false) ? "" : "not") + "attending");
}
catch (Exception e)
{
<b>
Sorry - we couldn't send the email to confirm your RSVP.
</b>
}
}
<div class="text-center">
<h1>Thank you, @Model.Name!</h1>
<div class="lead">
@if (Model.WillAttend == true)
{
<p>It's great that you're coming. The drinks are already in the fridge!</p>
}
else
{
@:Sorry to hear that you can't make it, but thanks for letting us know.
}
</div>
</div>
</body>
</html>

至此,一个完整的ASP.NET MVC程序就完成了。

跟我学ASP.NET MVC之三:完整的ASP.NET MVC程序-PartyInvites的更多相关文章

  1. ASP.NET + MVC5 入门完整教程三 (下) ---MVC 松耦合

    建立松耦合组件 MVC 模式最重要的特性之一视他支持关注分离,希望应用程序中的组件尽可能独立,只有很少的几个可控依赖项.在理想的情况下,每个组件都不了解其他组件,而只是通过抽象接口来处理应用程序的其他 ...

  2. [原创]java WEB学习笔记20:MVC案例完整实践(part 1)---MVC架构分析

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

  3. ASP.NET + MVC5 入门完整教程七 -—-- MVC基本工具(上)

    https://blog.csdn.net/qq_21419015/article/details/80474956 这里主要介绍三类工具之一的 依赖项注入(DI)容器,其他两类 单元测试框架和模仿工 ...

  4. 005. Asp.Net Routing与MVC 之三: 路由在MVC的使用

    上次讲到请求如何激活Controller和Action,这次讲下MVC中路由的使用.本次两个关注点: 遗留:ModelBinder.BindModel的过程 MVC中路由的使用 MVC 5中的Acti ...

  5. ASP.NET Core 中文文档 第四章 MVC(01)ASP.NET Core MVC 概览

    原文:Overview of ASP.NET Core MVC 作者:Steve Smith 翻译:张海龙(jiechen) 校对:高嵩 ASP.NET Core MVC 是使用模型-视图-控制器(M ...

  6. 七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC

    系列文章 七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC 七天学会ASP.NET MVC (二)——ASP.NET MVC 数据传递 七天学会ASP.NET MVC (三)— ...

  7. [ASP.NET MVC 小牛之路]01 - 理解MVC模式

    本人博客已转移至:http://www.exblr.com/liam  PS:MVC出来很久了,工作上一直没机会用.出于兴趣,工作之余我将展开对MVC的深入学习,通过博文来记录所学所得,并希望能得到各 ...

  8. 003. Asp.Net Routing与MVC 之一: 请求如何到达MVC

    基础知识 本文用到的基础知识:URL.HttpModule 与 HttpHandler.IIS 的请求处理过程. URL HttpModule与HttpHandler IIS7.0的请求处理过程 OK ...

  9. ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇

    在之前的文章中,我为大家介绍了OWIN和Katana,有了对它们的基本了解后,才能更好的去学习ASP.NET Identity,因为它已经对OWIN 有了良好的集成. 在这篇文章中,我主要关注ASP. ...

随机推荐

  1. iOS下FMDB的多线程操作(一)

    iOS中一些时间比较长的操作都应该放在子线程中,以避免UI的卡顿.而sqlite 是非线程安全的,故在多线程中不能共用同一个数据库连接,否则会导致EXC_BAD_ACCESS.所以我们可以在子线程中创 ...

  2. leetcode之旅(11)-Integer to Roman

    题目描述: Given an integer, convert it to a roman numeral. Input is guaranteed to be within the range fr ...

  3. Java IO学习--(四)网络

    Java中网络的内容或多或少的超出了Java IO的范畴.关于Java网络更多的是在我的Java网络教程中探讨.但是既然网络是一个常见的数据来源以及数据流目的地,并且因为你使用Java IO的API通 ...

  4. 细说Web页面与本地电脑通讯

    话说在很久很久以前.Web页面与客户的本地电脑Localhost通讯,有两种方式: 1.Flash 2.ActiveX控件 由于Flash本人不是很了解,也给出不了什么示例代码, 对于ActiveX控 ...

  5. DIV与SPAN之间有什么区别

    DIV与SPAN之间有什么区别 DIV 和 SPAN 元素最大的特点是默认都没有对元素内的对象进行任何格式化渲染.主要用于应用样式表(共同点). 两者最明显的区别在于DIV是块元素,而SPAN是行内元 ...

  6. java并发包分析之———AQS框架

    一.什么是同步器   多线程并发的执行,之间通过某种 共享 状态来同步,只有当状态满足 xxxx 条件,才能触发线程执行 xxxx . 这个共同的语义可以称之为同步器.可以认为以上所有的锁机制都可以基 ...

  7. 老马Repository模式原文

    A system with a complex domain model often benefits from a layer, such as the one provided by Data M ...

  8. windows系统下输入法图标显示设置

    原先任务栏有两个搜狗输入法的标志,还有一个"中/英"的图标:甚至桌面还悬浮这一个搜狗输入法图标. 打开vscode等工具时,桌面悬浮的图标有时可能会遮挡到一些信息,十分不爽. 如今 ...

  9. lodash中Collection部分所有方法的总结

    总结一下lodash中Collection的所有的方法,方便对比记忆,也便于使用时候查找. 1.    判断是否符合条件:返回bool: a)  every: 判断每一值是不是都符合条件: 通过 pr ...

  10. java float double精度为什么会丢失?浅谈java的浮点数精度问题 【转】

    由于对float或double 的使用不当,可能会出现精度丢失的问题.问题大概情况可以通过如下代码理解: public class FloatDoubleTest { public static vo ...