跟我学ASP.NET MVC之三:完整的ASP.NET MVC程序-PartyInvites
摘要:
在这篇文章中,我将在一个例子中实际地展示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的更多相关文章
- ASP.NET + MVC5 入门完整教程三 (下) ---MVC 松耦合
建立松耦合组件 MVC 模式最重要的特性之一视他支持关注分离,希望应用程序中的组件尽可能独立,只有很少的几个可控依赖项.在理想的情况下,每个组件都不了解其他组件,而只是通过抽象接口来处理应用程序的其他 ...
- [原创]java WEB学习笔记20:MVC案例完整实践(part 1)---MVC架构分析
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- ASP.NET + MVC5 入门完整教程七 -—-- MVC基本工具(上)
https://blog.csdn.net/qq_21419015/article/details/80474956 这里主要介绍三类工具之一的 依赖项注入(DI)容器,其他两类 单元测试框架和模仿工 ...
- 005. Asp.Net Routing与MVC 之三: 路由在MVC的使用
上次讲到请求如何激活Controller和Action,这次讲下MVC中路由的使用.本次两个关注点: 遗留:ModelBinder.BindModel的过程 MVC中路由的使用 MVC 5中的Acti ...
- ASP.NET Core 中文文档 第四章 MVC(01)ASP.NET Core MVC 概览
原文:Overview of ASP.NET Core MVC 作者:Steve Smith 翻译:张海龙(jiechen) 校对:高嵩 ASP.NET Core MVC 是使用模型-视图-控制器(M ...
- 七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC
系列文章 七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC 七天学会ASP.NET MVC (二)——ASP.NET MVC 数据传递 七天学会ASP.NET MVC (三)— ...
- [ASP.NET MVC 小牛之路]01 - 理解MVC模式
本人博客已转移至:http://www.exblr.com/liam PS:MVC出来很久了,工作上一直没机会用.出于兴趣,工作之余我将展开对MVC的深入学习,通过博文来记录所学所得,并希望能得到各 ...
- 003. Asp.Net Routing与MVC 之一: 请求如何到达MVC
基础知识 本文用到的基础知识:URL.HttpModule 与 HttpHandler.IIS 的请求处理过程. URL HttpModule与HttpHandler IIS7.0的请求处理过程 OK ...
- ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇
在之前的文章中,我为大家介绍了OWIN和Katana,有了对它们的基本了解后,才能更好的去学习ASP.NET Identity,因为它已经对OWIN 有了良好的集成. 在这篇文章中,我主要关注ASP. ...
随机推荐
- 【Android 应用开发】BluetoothAdapter解析
这篇文章将会详细解析BluetoothAdapter的详细api, 包括隐藏方法, 每个常量含义. 一 BluetoothAdapter简介 1.继承关系 该类仅继承了Object类; 2.该类作用 ...
- ZeroMQ 教程 001 : 基本概览
介绍性的话我这里就不翻译了, 总结起来就是zmq很cool, 你应该尝试一下. 如何安装与使用zmq 在Linux和Mac OS上, 请通过随机附带的包管理软件, 或者home brew安装zmq. ...
- Android 应用内直接跳转酷市场
不确定酷市场后期是否还会该包名或者路径,目前的7.9 版本测试通过. private void gotoCoolapkMarket() { try { Intent intent = new Inte ...
- JVM学习--(六)类加载器原理
我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...
- 10.API 接口自动化测试的基本原理
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 28.0px Helvetica } p.p2 { margin: 0.0px 0.0px 0.0px 0. ...
- Web前端文件上传进度的显示
跟后台关系不大,主要是前端js实现,具体使用了XMLHttpRequest的ProgressEvent事件,可以参考MDN中的Using XMLHttpRequest https://develope ...
- 几大时尚前端UI框架的IE支持
这个文章的Topic比较符合我们这些身在Stone Age用户环境中的开发者所考虑的因素 1.先说目前最火最酷的:Semantic-UI 目前版本:0.17.0 Browser Support Las ...
- nginx flv点播服务器搭建
首先选用Nginx+Nginx-rtmp-module作为点播服务器,安装文章:https://www.atlantic.NET/community/howto/install-rtmp-ubuntu ...
- JavaScipt浅谈——全局变量和局部变量
全局变量的作用域为所属的整个程序. 全局变量的定义形式有: (1)在函数外定义 (2)在函数内定义,但不加var声明 (3)使用 window.变量名 的形式定义 (4) ...
- 搭建centos7的开发环境1-系统安装及Python配置
在公司开发部干活的好处是可以再分配一台高性能的PC,有了新的工作电脑,原来分配的笔记本电脑就可以安装linux系统做开发了,主要有两方面的开发计划,一个是计划中要参与爬虫系统的开发,第二个是大数据环境 ...
