摘要:

在这篇文章中,我将在一个例子中实际地展示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. DB Query Analyzer 6.02 is released, 71 articles concerned have been published

    DB Query Analyzer is presented by Master Genfeng, Ma from Chinese Mainland. It has English version n ...

  2. Oracle ERP系統借贷关系表

    系统分步骤产生的分录: 1)库存模块作接收时产生的分录为: 借:材料采购 (采购单价X订单数量) 贷:应计负债 (采购单价X订单数量) 2)库存模块作检验入库时产生的分录为: 系统产生的分录分别为: ...

  3. HBase flush

    flush触发方式 1. Server端执行更新操作(put.delete.multi(MultiAction<R>multi).(private)checkAndMutate.mutat ...

  4. 【Android 应用开发】Activity生命周期 与 Activity 之间的通信

    一. Activity生命周期 上图 1. Activity状态 激活状态 : Activity出于前台 , 栈顶位置; 暂停状态 : 失去了焦点 , 但是用户仍然可以看到 , 比如弹出一个对话框 , ...

  5. C# 视频多人脸识别

    上一篇内容的调整,并按 @轮回 的说法,提交到git了,https://github.com/catzhou2002/ArcFaceDemo 基本思路如下: 一.识别线程 1.获取当前图片 2.识别当 ...

  6. 使用 Helm - 每天5分钟玩转 Docker 容器技术(163)

    Helm 安装成功后,可执行 helm search 查看当前可安装的 chart. 这个列表很长,这里只截取了一部分.大家不禁会问,这些 chart 都是从哪里来的? 前面说过,Helm 可以像 a ...

  7. MariaDB/MySQL用户和权限管理

    本文目录: 1.权限验证 1.1 权限表 1.2 图解认证和权限分配的两个阶段 1.3 权限生效时机 2.用户管理 2.1 创建用户 2.2 create user和alter user 2.3 记录 ...

  8. Web服务cxf框架发布2

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本人声明.否则将追究法律责任. 作者:永恒の_☆ 地址:http://blog.csdn.net/chenghui0317/ ...

  9. 致IT之路的先驱者和旅人

    1,图灵和香农 故事的开始,要从计算机之父图灵和信息论的创始人香农开始说起.图灵最大的贡献是发明了图灵机,关于图灵机如果要让人明白究竟有什么用,从如何实现一个半导体电路图灵机这方面理解比较好.只要一个 ...

  10. Application "org.eclipse.ui.ide.workbench" could not be found in the registry.问题的解决

    今天升级Eclipse,升级完Restart,碰到启动不了让看日志,日志里主要错误信息即是Application "org.eclipse.ui.ide.workbench" co ...