MVC系列文章终于开始了,前段时间公司项目结束后一直在封装一个html+ashx+js+easyui的权限系统,最近差不多也完成了,迟些时候会分享源码给大家。当然这个MVC系列结束后如果时间允许还会另写一个MVC + EF + ExtJs的权限系统作为总结,希望有兴趣的朋友保持关注。

本系列文章使用的环境是:VS2010 + 4.0 Framework、sql 2008企业版、MVC3、EF4.1
注:VS2010不带MVC3,ASP.NET MVC 3 RTM下载地址。装的时候vs不要打开,先装MVC3,再装汉化包。安装中不要强制取消,就算取消也要等回滚完,否则下次vs启动不了。

一、ASP.NET MVC 3 小试牛刀

新建项目 - 创建mvc3 web应用程序 - 模板选空、视图引擎Razor、不使用HTML5,建好项目后,解决方案是这样的:

和传统的webForm项目结构完全不一样,这里不写任何代码直接Ctrl+F5跑下程序,会得到:

webForm里直接运行项目会列出已有的文件让你点选然后运行,当然也可以在页面上右键 - 在浏览器中查看,mvc项目里无法通过在.cshtml视图文件上右键 - 在浏览器中查看的方式运行页面,而是通过【路由】找控制器Controller里的Action,Action再调用指定的视图View呈现页面。因此运行MVC项目只能Ctrl+F5或者F5调试的方式运行程序,要看不同的页面需要在路由里进行调整。后续会有文章详细讲解路由,这里仅快速理解下MVC:
M Model:实体模型 / 视图模型(ViewModel);
V View:视图。简单说就是html页面,这个html页面接收强类型的数据,方便展示数据;
C Controller:控制器。处理浏览器发过来的请求,根据url过来的路径去控制器里寻找对应的Action响应浏览器请求。

解决上面404错误问题先得看看Global.asax里默认路由的定义:

routes.MapRoute(
"Default", // 路由名称
"{controller}/{action}/{id}", // 带有参数的 URL
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 参数默认值
);

只有一个路由,按照路由匹配自上到下的顺序,那么本程序使用的就是这个名为Default的路由。根据这个路由的定义浏览器有类似这种Url:localhost:****/abc/def/e 请求过来自然就和这个路由匹配({controller}/{action}/{id})上了,那么找的是名称为abc的Controller,调用abcController里的名为def的Action,呈现的是def这个Action里指定的视图View。当然这种Url:localhost:**** 请求过来也能和这个名为Default的路由匹配上,因为这个默认路由定义了默认值:HomeController、Index Action、Id值可选。

到这里就很明显知道为何是404了,因为既没有HomeController,也没有Index这个Action,同时也没有任何视图View。在解决方案里的Controller目录上右键 - 创建控制器,为了和这个路由的定义搭上,控制器的名字就是HomeController,是一个空控制器。新建就好了后打开HomeController.cs:

public ActionResult Index()
{
return View();
}

HomeController里默认就有一个叫Index的Action,并且这个Action的返回值不是void,也不是bool,也不是string,而是ActionResult。这个ActionResult就可以理解是视图,返回一个视图给Action(上后面的代码直接return View();的话就是返回默认视图,即和Action同名的Index),然后响应给控制器,最终显示给用户。
注:常用的还有JsonResultRedirectResult等等。具体点这里

先不返回视图给Action,修改下返回值类型并返回一个string试试:

public string Index()
{
return "hElLo WOrLd!!!";
}

很明显,返回的不是ActionResult了,而是string,先按F6编译下再Ctrl+F5跑下程序,显示:

很容易理解:默认路由找HomeController下的名为Index的Action拿到数据显示到浏览器,这里拿到的数据就是Index这个Action返回的字符串。右键查看下页面的源码,很纯粹的源码,就一个“hElLo WOrLd!!!”字符串。
注意下上图的url:localhost:1030,其实完整的是localhost:1030/home/index(默认路由里已经指定了,所以可以省略),看的出路由里定义之后就体现在浏览器url里了:先找Home,再找Home下的Index。

再修改方法:

public ActionResult Index()
{
return View();
}

然后在这个方法里右键 - 添加视图 - 默认就是和Action同名的Index视图,不使用模板。添加后项目的Views目录下就多一个Home子目录,目录里有一个Index.cshtml,修改其中的代码为:

@{
Layout = null;
}
hElLo WOrLd!!!

Layout=null就是不使用模板。F6编译下项目再跑下程序发现和之前显示一样,查看源码也一样。这里很好理解:Controller找Action,Action调用对应的View呈现页面
完整的url是:localhost:1030/home/index 这里跟上面稍有不同:上面的index这个action直接就返回了一个string,未调用任何试图,这里的index显示的是view返回的string。

二、向视图中输出动态内容

asp、aspx等动态页面相比于静态的html好处就是可以根据传递的参数动态的显示内容,视图.cshtml自然也可以,可以通过ViewBag来实现,修改action:

public ActionResult Index()
{
int hour = DateTime.Now.Hour;
ViewBag.Greeting = hour < ? "早上好" : "下午好";
return View();
}

通过一个三元表达式给ViewBag.Greeting赋了一个值,然后在前台View加个@符号就可以调用了:@ViewBag.Greeting WOrLd!!!
F6编译下项目再跑下程序,如愿的显示了:下午好 WOrLd!!!
注:前台和后台的ViewBag打点都是无智能提示的,只要前后台的名称一样即可调用。

三、稍复杂的例子

ok,到这基本就已经知道mvc是怎么玩的了,现在来一个稍复杂的例子,模拟邀请客人参加party的小程序:
先到解决方案的Models目录下添加一个客人信息类:

/// <summary>
/// 客人信息类
/// </summary>
public class GuestResponse
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public bool? WillAttend { get; set; } //是否参加(可空类型)
}

修改之前的Index视图(Index.cshtml):

@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div>
@ViewBag.Greeting, WOrLd!!!
<p>
我们将举办一个Party<br />
</p>
@Html.ActionLink("填表参加", "RsvpForm")
</div>
</body>
</html>

跟普通html页面不同的是Rozar语法里所有的页面元素都是通过html辅助方法生成的,这里的@Html.ActionLink("填表参加", "RsvpForm")就是生成一个a标签。Rozar语法简单清爽,写个@符号就可以开始在页面上写代码了。

根据智能提示可知,第一个参数"RSVP Now"是a标签的文字,第二个参数"RsvpForm"是这个a标签跳转的地址,通过Action实现跳转自然需要添加这个Action,否则点击又找不到视图了:

public ViewResult RsvpForm()
{
return View();
}

一看到return View();自然返回的是默认的视图,即RsvpForm。继续去添加这个视图:在这个ResvpForm这个Action内右键 - 添加视图 - 勾选上“创建强类型视图” - 选择GuestResponse模型类(如果找不到就先按F6编译下项目)- 不使用模板类和脚本库,生成的View是:

@model SimpleMVC3Demo.Models.GuestResponse
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>RsvpForm</title>
</head>
<body>
<div>
</div>
</body>
</html>

注意第一行:@model SimpleMVC3Demo.Models.GuestResponse 这就是勾选强类型视图的好处,直接把强类型抛到了View里,其实手动写这一行也是一样的效果(同样需要编译项目)
继续完善RsvpForm这个视图,在body里去掉div然后添加表单和一些文本框下拉列表供用户填写信息:

    @using (Html.BeginForm("RsvpForm", "Home", FormMethod.Post))
{
<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 = "我会参加Party", Value = bool.TrueString},
new SelectListItem() {Text = "我不参加Party", Value = bool.FalseString}
}, "Choose an option")
</p>
<input type="submit" value="提交" />
}

这里的x => x.属性方式其实就是lambda表达式的写法,这里不用x,用任何字母都可以,前提是必须已经把强类型的抛到View里。

这就是这个表单提交的地址,其实就等于webForm里的:

<form action="/Home/RsvpForm" method="post">
<input type="text" name="username" />
<input type="text" name="userpwd" />
<input type="submit" value="提交" />
</form>

这个Form提交的地址是指定的HomeController下的RsvpForm,之前已经有一个:

public ViewResult RsvpForm()
{
return View();
}

其实提交表单处理的不是这个Action,这个是get请求的Action,需要另加一个post的Action(mvc里默认的action响应的是get方式的请求,用于请求页面内容,响应post请求的action一般是提交表单等):

[HttpPost]
public ViewResult RsvpForm(GuestResponse guestResponse)
{
return View("Thanks", guestResponse);
}

这只是个简单的Action,并没有对用户提交的表单内容进行保存,而是直接返回了一个Thanks视图,并且把用户对象也就是用户在表单里填写的用户信息传到了Thanks这个视图,来看看这个用户对象guestResponse如何传递的:在这个HttpPost下面按F9加个断点,然后按F5调试下项目,当提交表单时,这个对象其实就已经有属性了:

很明显这个对象已经有值了,继续在Views目录下的Home目录上右键 - 添加视图 - 视图名称Thanks - 勾选强类型视图 - 不使用模板页和脚本库。ok,这个视图也拿到了传递过去的用户对象,其实就可以在body里输出一些友好的回复:

    <div>
<h1>
Thank you, @Model.Name!</h1>
@if (Model.WillAttend == true)
{
@:你能来真的太好了,饮料和零食都为你准备好了!
}
else
{
@:很遗憾你不能来。
}
</div>

注意看,Razor视图引擎里可以直接写if else逻辑语句。

写到这里该跑下程序了,再次F6编译下后按Ctrl + F5跑下程序。
第一个页面:

源码:

    <div>
下午好, WOrLd!!!
<p>
我们将举办一个Party<br />
</p>
<a href="/Home/RsvpForm">填表参加</a>
</div>

第二个页面:

源码:

<form action="/Home/RsvpForm" method="post">
     <p>
Your name: <input id="Name" name="Name" type="text" value="" />
</p>
<p>
Your email: <input id="Email" name="Email" type="text" value="" /></p>
<p>
Your phone: <input id="Phone" name="Phone" type="text" value="" /></p>
<p>
Will you attend?
<select id="WillAttend" name="WillAttend">
      <option value="">请选择</option>
      <option value="True">我会参加Party</option>
      <option value="False">我不参加Party</option>
    </select>
</p>
<input type="submit" value="提交" />
</form>

第三个页面:

源码:

    <div>
<h1>
Thank you, 汪杰!</h1>
你能来真的太好了,饮料和零食都为你准备好了!
</div>

注意看上面两个图的url地址一样,都是:localhost:1031/Home/RsvpForm
但是页面显示的内容不同,因为一个是get请求,一个提交表单是post请求,提交后被导向另一个视图了。

验证表单:刚才的表单就算任何都不填写也可以提交,显然不符合常理,验证需要三步,第一去模型类里定义验证规则:

[Required(ErrorMessage = "姓名不能为空")]
public string Name { get; set; } [Required(ErrorMessage = "邮箱地址不能为空")]
[RegularExpression(".+\\@.+\\..+", ErrorMessage = "请输入合法的邮箱地址")]
public string Email { get; set; } [Required(ErrorMessage = "电话不能为空")]
public string Phone { get; set; } [Required(ErrorMessage = "请选择是否参加")]
public bool? WillAttend { get; set; } //是否参加(可空类型)

这是Data Annotation标注的形式来验证的,如果还不知道Data Annotation为何物,建议先去了解一下
注:需要引用命名空间System.ComponentModel.DataAnnotations;

第二去控制器里判断是否通过验证:

if (ModelState.IsValid)
{
return View("Thanks", guestResponse);
}
else
{
return View();
}

直接调用的ModelState.IsValid方法来验证模型是否通过验证。当然到这里还不算结束,第三去View里应用验证:
在using语句下面加上:@Html.ValidationSummary() 表示此表单需要验证,这时候如果表单数据不合法则不会被导向到Thanks视图。

到此,再跑程序生成的html页面就又不同了,会有验证信息在里面。一旦用户输入的内容不符合在模型类里定义的规则,那么验证信息就会显示出来。看源码:

<form action="/Home/RsvpForm" method="post">
<div class="validation-summary-valid" data-valmsg-summary="true">
<ul><li style="display:none"></li></ul>
</div>
     <p>
Your name: <input data-val="true" data-val-required="姓名不能为空" id="Name" name="Name" type="text" value="" />
</p>
<p>
Your email: <input data-val="true" data-val-regex="请输入合法的邮箱地址" data-val-regex-pattern=".+\@.+\..+" data-val-required="邮箱地址不能为空" id="Email" name="Email" type="text" value="" /></p>
<p>
Your phone: <input data-val="true" data-val-required="电话不能为空" id="Phone" name="Phone" type="text" value="" /></p>
<p>
Will you attend?
<select data-val="true" data-val-required="请选择是否参加" id="WillAttend" name="WillAttend">
      <option value="">请选择</option>
      <option value="True">我会参加Party</option>
      <option value="False">我不参加Party</option>
     </select>
</p>
<input type="submit" value="提交" />
</form>

高亮错误信息行其实很简单,直接在RsvpForm视图里添加一个默认css的引用:

<link rel="Stylesheet" href="@Href("~/Content/Site.css")" type="text/css"/>

再提交非法表单信息就高亮了错误行:

其实提交表单的一刹那浏览器还是有刷新的,说明这是个服务器端验证,实际项目中更应该服务端和客户端双验证,后续文章会有详细介绍。

本文源码

系列文章导航

ASP.NET MVC 初体验的更多相关文章

  1. ASP.NET MVC学习---(五)MVC初体验

    经过之前n多的铺垫 我们已经大概了解了这个姓m名vc的家伙了 那么今天我们就来体验一把 怎么体验呢? 就来做一个小例子吧~ mvc增删改查的例子 数据库还是之前我们的老朋友 关系图: 表中的数据已填好 ...

  2. MVC – 4.mvc初体验(2)

    5.显示学员列表 效果 数据表 5.1 首先,在文件夹Models新建一个新建项(W),选择ADO.NET 实体数据模型 (SingleTest.edmx) 5.2 建一个控制器,StudentsCo ...

  3. MVC – 4.mvc初体验(1)

    1.MVC请求模式   2.MVC简单请求流程图 展开 折叠     3.返回string的mvc方法 展开 折叠   4.加载视图的方法  

  4. 第23章 Spring MVC初体验

    23.1 鸟瞰Spring MVC 粗略的介绍了SpringMVC的主要组成部分,SpringMVC作为一个Web层的框架,最大的作用是把我从繁重的web.xml文件编写中解救出来,再也不要不停的添加 ...

  5. MVC – 4.mvc初体验(2)

    5.显示学员列表 效果 数据表 5.1 首先,在文件夹Models新建一个新建项(W),选择ADO.NET 实体数据模型 (SingleTest.edmx) 5.2 建一个控制器,StudentsCo ...

  6. MVC – 4.mvc初体验(1)

    1.MVC请求模式 2.MVC简单请求流程图 展开 折叠 3.返回string的mvc方法 展开 折叠 4.加载视图的方法

  7. spring mvc(2) spring mvc初体验

    前面讲到随着前端技术的发展,web的开发已经实现了前后分离,并且spring mvc经过多个版本的更新,使用上也变得不一样,并且提供了很多便捷的功能.但是为了便于理解,我们使用比较原始的方式搭建spr ...

  8. asp.net mvc 5 初体验

    参考:http://www.asp.net/mvc/tutorials/mvc-5/introduction/getting-started 1. 新建 ASP.Net Web 应用程序,跟着向导一路 ...

  9. ASP.NET Core 3.0 上的gRPC服务模板初体验(多图)

    早就听说ASP.NET Core 3.0中引入了gRPC的服务模板,正好趁着家里电脑刚做了新系统,然后装了VS2019的功夫来体验一把.同时记录体验的过程.如果你也想按照本文的步骤体验的话,那你得先安 ...

随机推荐

  1. ASP.NET MVC Model元数据(三)

    ASP.NET MVC Model元数据(三) 前言 在上篇中我们大概的讲解了Model元数据的生成过程,并没有对Model元数据本身和详细的生成过程有所描述,本篇将会对详细的生成过程进行讲解,并且会 ...

  2. 到爱尔兰敲代码 / Come, Coding in Ireland

    这是我在都柏林的第四个月,该办的证也都办完了,该安定下来的也安定下来了,所以也简单介绍下到爱尔兰做IT的相关过程和政策. 如果有兴趣在英语环境工作的话,我也可以帮忙推荐或者找找. 去年15年1月正好开 ...

  3. ABP框架 - 数据传输对象

    文档目录 本节内容: DTO 必要性 领域层的抽象 数据隐藏 序列化和延迟加载问题 DTO 约定和验证 示例 DTO和实体间自动映射 使用特性和扩展方法进行映射 辅助接口和类 Data Transfe ...

  4. 关于apue.3e中apue.h的使用

    关于apue.3e中apue.h的使用 近来要学一遍APUE第三版,并于此开博做为记录. 先下载源文件: # url: http://http//www.apuebook.com/code3e.htm ...

  5. Android Toolbar 开发总结

    初识 Toolbar Toolbar是在 Android 5.0 开始推出的一个 Material Design 风格的导航控件 ,Google 非常推荐大家使用 Toolbar 来作为Android ...

  6. .NET 程序集单元测试工具 SmokeTest 应用指南

    Smoke Test(冒烟测试),也称Regression Test(回归测试),是对软件的安装和基本功能的测试.一般地我们使用脚本来实现Smoke Test的自动化,可借用虚拟机的snapshot机 ...

  7. SQL Server中TOP子句可能导致的问题以及解决办法

    简介      在SQL Server中,针对复杂查询使用TOP子句可能会出现对性能的影响,这种影响可能是好的影响,也可能是坏的影响,针对不同的情况有不同的可能性.      关系数据库中SQL语句只 ...

  8. Android自定义控件之自定义ViewGroup实现标签云

    前言: 前面几篇讲了自定义控件绘制原理Android自定义控件之基本原理(一),自定义属性Android自定义控件之自定义属性(二),自定义组合控件Android自定义控件之自定义组合控件(三),常言 ...

  9. [Android]在Dagger 2中使用RxJava来进行异步注入(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客: # 在Dagger 2中使用RxJava来进行异步注入 > 原文: 几星期前我写了一篇关于在Dagger 2中使用*Producers*进行 ...

  10. Oracle基础维护02-表、主键、索引、表结构维护手册

    目录 一.项目新建表.主键.索引注意事项 二.举例说明建表.主建.索引的操作方法 2.1 设定需求如下 2.1.1 查询数据库有哪些表空间 2.1.2 本文档假设数据库有这两个业务用户的表空间 2.2 ...