跟我学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. ...
随机推荐
- Cocoa公历和中国农历直接的转换
看过某书上面的做法是先生成一个公历的calendar,使用的是: NSCalendar *cal = [NSCalendar currentCalendar]; 然后用它生成一个NSDateCompo ...
- Android 获取View的高度或TextView的行数, 实现自适应的textview
大家都遇到过项目中需要获控件的的高度或者列如文章开头说TextView的行数 但是很多人在实际操作中getLineCount()获取到值是零,其实只是我们没在正确的位置获取. 这是因为activtiy ...
- objective-c中的method swizz实现"猴打补丁"
ruby中的猴打补丁很好实现,下面给出例子: class String alias :org_upcase :upcase def upcase puts("trace me if you ...
- Sharepoint 2010 自定义WebService 找不到网站应用程序
错误描述:Net 开发WebService调用Microsoft.SharePoint.dll的服务器端对象模型,出现找不到网站的应用程序,或者出现500错误. 错误截图: [Webservice调用 ...
- SharePoint 2010 之soap:Server服务器无法处理请求
算是一个下马威?!刚刚部署上的SharePoint2010环境,感觉很欣喜,开始操作,结果装上Designer2010,打开页面,居然报错 错误内容:soap:Server服务器无法处理请求. --- ...
- Oracle :%TYPE 和 %ROWTYPE
1. 使用%TYPE 在许多情况下,PL/SQL变量可以用来存储在数据库表中的数据.在这种情况下,变量应该拥有与表列相同的类型.例如,students表的first_name列的类型为VARCHAR2 ...
- AMDP + XLSX Workbench 报表开发模式
本文介绍了我和同事通过使用AMDP + XLSX Workbench缩短报表开发周期.分离数据查询处理逻辑和前端展示工作的经验.欢迎讨论. 前言 最近接到了一套人力资源报表的开发需求,需要以EXCEL ...
- angularjs作用域之transclude
transclude是一个可选的参数.如果设置了,其值必须为true,它的默认值是false.嵌入有时被认为是一个高级主题,但某些情况下它与我们刚刚学习过的作用域之间会有非常好的配合.使用嵌入也会很好 ...
- mini-tabs多个div并列,并可隐藏某个div
<div class="mini-tabs" activeIndex="0" id="tabs"> <div title= ...
- ORACLE数据库部分面试题目
1. 解释冷备份和热备份的不同点以及各自的优点 解答:热备份针对归档模式的数据库,在数据库仍旧处于工作状态时进行备份.而冷备份指在数据库关闭后,进行备份,适用于所有模式的数据库.热备份的优点在于当备份 ...