ASP.NET 3.5 MVC架构与实战学习总结
Web开发新体验:ASP.NET 3.5 MVC架构与实战
电子书的网址(目前有第四、第十三章):http://book.51cto.com/art/200909/151515.htm
第1篇 体验篇
第1章 初识asp.net 3.5 mvc开发
第2章 路由进阶
第3章 htmlhelper控件解析
第4章 自定义gridview控件开发
第5章 自定义视图引擎
第6章 图表开发
第7章 大、中型asp.net 3.5 mvc项目开发
第8章 过滤器解析、开发
第9章 控制器与视图之间的数据传递
第10章 jquery视图开发
第11章 多项目混合开发
第12章 单元测试
第13章 网站部署
第2篇 架 构 篇
第14章 初识联系人管理网站
第15章 联系人管理网站初探
第16章 联系人管理网站进阶
第17章 联系人管理网站高级实现
第3篇 实战篇
第18章 博客网站初探
第19章 多层架构的博客网站
第20章 博客网站页面总体设计
第22章 博客网站管理页面
第23章 博客网站安全
1 体验篇
1.1 初识ASP.NET 3.5 MVC开发
ASP.NET 3.5 MVC的特点
1) 易于调试
2) 易于实施测试驱动开发
3) 可扩展、可替换
如视图引擎、URL路由策略
4) 支持web forms的相关性
在视图模板中支持当前的web forms页面(.aspx)、用户控件(.ascx)及母版页(.master)等还支持嵌套母版也、内联表达式、数据绑定、本地化、数据缓存等。
5) URL被映射到控制器
文件夹:
对于视图组建中的公用部分,可以创建一个名称为Shared的文件夹,该目录不属于单个的控制器,而是属于所有的控制器,在此文件夹中可以存放母版页、css样式表等文件。
配置httpmodules节点
注册了该模块,asp.net程序就会使用asp.net 3.5 mvc框架,将页面的请求转换为url路由,并调用相关控制器中的相关方法,实现指定视图的输出。
1.2 路由进阶
路由解析分析
在传统的asp.net应用程序中,每个请求的页面对应着文件系统中的一个文件,没有就会出错。这些页面事实上都被表示为一个类,而该类实现了IHttpHandler接口,每当一个页面请求时,就会调用该类中的processRequest()方法,执行processRequest()方法之后,就会将指定的内容返回到浏览器中。
在基于asp.net 3.5 mvc框架的网站中,每个被请求的页面被映射到相应的控制器中的相关方法,控制器负责将指定的内容返回到浏览器中。
路由解析分析,主要说明路由解析的主要流程,也就是路由解析管道,实现路由解析的UrlRoutingModule类、IRouteHandler接口和IHttpHandler接口。
UrlRoutingModule类是ASP.NET3.5 MVC 网站中处理程序的入口,每当用户在浏览器中键入一个URL地址,就发出一个用户请求,UrlRoutingModule类就响应用户的请求,处理用户的请求;检索RoutTable类中的RoutCollection集合,获得匹配的路由;通过路由解析,得到Route类的实例化对象;将用户的请求分发到实现接口IRouteHandler的路由处理程序,并输入RequestContext参数;最后再次分发到实现接口IHttpHandler的MvcHandler处理程序,定位到相关的控制器,从而执行控制器中的相关动作方法,实现响应的输出。
UrlRoutingModule类主要实现路由的处理,如检索、匹配等解析过程。使用时在Web.Config中的配置:
<httpModules>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
httpModules>
UrlRoutingModule类在获得URL路由后,将用户的请求分发到实现接口 IRouteHandler的MvcRouteHandler类,并传入RequestContext参数。
URL路由
Ø 定义URL路由
将用户请求的URL路由解析为一系列的离散值,该URL路由通过占位符定义URL的模式。在URL路由中,通过大括号({})定义占位符,这些占位符就是URL路由参数,而字符串中的“/”、“.”等符号则作为分隔符被URL路由解析这些离散的数据,对于不在小括号或者方括号中的信息则被视为一个常量。
有效的URL路由定义 | 匹配的URL例子 |
{controller}/{Action}/{id} | /products/show/beverages |
{table}/Details.aspx | /products/ Details.aspx products:表名 Details.aspx为常量 |
blog/{Action}/{entry} | / blog(常量) /show(相关控制器重的方法)/123(一个entry变量) |
{reporttype}/{year}/{month}/{day} | /sales(reporttype变量)/2008(year变量)/1(month变量)/5(day变量) |
Ø 设定URL路由参数的默认值
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = ""
} // Parameter defaults
Ø 设定URL路由通配符
为了实现对一类URL路由的定义,可以使用星号来定义URL路由通配符。
假定url路由通配符为:query/{queryname}/{*queryvalues},则
URL | 通配符参数值 |
/query/select/bikes/onsale | Bikes/onsale |
/query/select/bikes | bikes |
/query/select | 空白字符串 |
Ø 添加URL路由参数的约束
Constraints=new RouteValueDictionary{{"locale","{a-z}{2}-{A-Z}{2}"},{year,@"\d{4}"}};
Locale必须为英文字母,前面2位英文字母必须小写的,后面2位英文字母必须大写的。而year必须是4位数字。
URL | 匹配结果 |
/en-us/2008 | 不匹配 |
/en-US/08 | 不匹配 |
/en-US/2008 | Locale=”en-US”,year=”2008” |
Routing的扩展方法
扩展方法的由来:路由程序集(System.Web.Routing)是在2008年8月11日更新 的.NET 3.5框架SP1版本中发布的,而ASP.NET 3.5 MVC 1.0版本是在2009年3月18日才正式发布。在ASP.NET MVC版本的不断改进中,微软的开发团队感觉到以往的路由设置给开发者带来不便,但是路由的程序集已经发布,如何在路由程序集中添加新的功能呢?所以将扩展方法加在了 System.Web.Mvc程序集的RouteCollectionExtemsion类中。
优化路由设置
Ø 设置路由名称:在路由的设置中,路由名称是可选的输入参数,路由名称可以用来生成URL路由,但是在路由解析中没有什么作用。当开发者使用路由名称来生成url路由的时候,路由模块将快速定位到指定名称的路由。则不会通过查询的方式,一个接一个的查询。而是直接跳转到路由表中的对应路由名称的路由。
Ø 将常用的路由存放在路由表的最前面。该方法不仅提高生成url路由的效率,而且还提高路由解析的效率。这是因为在解析路由的过程中,一旦选找到匹配的路由,就停止路由解析。
但需要说明的是,在改变路由的存放位置时,需要注意路由的次序改变是否实质性影响匹配。
自定义路由约束
1.3 HtmlHelper概述
在视图ViewPage类中,专门设置了一个html属性即为HtmlHelper类的类型。
1.7 大、中型asp.net 3.5 mvc项目开发
个性化目录
Ø 设置视图路径
首先:View/home/Product文件夹
然后: public ActionResult List() {
return View(“Product/List”);}
缺点:
不便于项目的维护,控制器最好不需要知道视图的存储位置,以便实现控制器与视图之间良好的分离。
(一) 简单MVC目录结构分析
以上是一个再简单不过的MVC目录结构了,这也是微软MVC解决方案的基础目录结构,在我们一般的MVC开发项目中,基本上就是套用了这种格式。我们不需要去自己去定义一个目录,因为微软已经帮我们定义好了。我们只需要在原有的基础上添加和修改。
Content 文件夹,内容文件夹,姑且这样理解,我们主要存放样式文件,图片文件,xml文件以及其他的一些资源文件。这个没有太多的介绍。当然我们不一定要放到这个 里面去,也可以自己定义一个文件夹。不过我个人还是趋向于放在这个里面,毕竟是别人已经定义好了的,"拿来主义",直接用就可以了。
Scripts 文件夹,则主要存放的是Javascript 的js文件,其中里面自带了Jquery的核心包和微软自带的Ajax相关的js文件。如果我们没有用到可以删除(我想你会喜欢上它,舍不得将它删除)。 还有我们自己定义的一些js文件也可以存放在这个文件夹中。这样是为了使目录结构清楚,方便于文件的管理
Views文件夹,在我们新建MVC项目的时候,这个文件夹下有三个文件夹,分别是Shared,Home,Account。其中 Home,Account存放的一些aspx页面。我想你一定会主要到得里面只有aspx页面而没有cs页面。这就是MVC与WebForm很大不同的地 方。Shared文件夹则是存放了母版页和用户控件等文件,当然它可以存放aspx页面,比如我们的错误页面。只要是我们想让他共享的页面文件都可以存放 在这个里面。这里的Home,Account 这两个文件夹不是普通的文件夹(这是要注意的地方),因为它与控制器相关联。
Controllers文件夹,这个文件夹这是存放 控制器类的文件夹,MVC的核心文件夹,一切控制请求转发都是这个里面的类文件来处理。Controller 主要作用是捕获请求和控制流程转发,其作用可想而知。我们主要到每个控制器都是以***Controller 来命名的,如果你第一眼能看出来说明你很聪明。这里的每个控制器都是与Views文件夹下的文件夹对应,Home文件夹则对应 HomeController 类,Home文件夹下的所有文件都可以由HomeController来处理。当然不是一定由它处理。要分清楚,Views文件夹下的文件夹就一定对应一 个Controller,反之的理解是狭隘的
(二)视图路径的设置
当我们苦恼于如果页面文件很多的时候,1000个aspx页面我要放在一个文件下,这些文件怎么管理啊。呵呵MVC可以再新建视图路径,如上 图。我们可以再Home 文件夹中再建立一个文件夹,这儿里面同样可以存放aspx页面。既然可以新建文件夹,那么管理文件就方便多了
访问方式: <%=Html.ActionLink("Detail目录结构","List") %>
public ActionResult List()
{
return View("Detail/StuDetail");
}
使用这种方式就OK了
Ø 设置视图查询路径
(一) 浅谈MVC目录结构
在上一篇(《MVC进阶学习--个性化目录结构(一)》)中了解到了MVC 的基本目录结构,以及各个目录的作用。我们只是说到了表面的目录结构,没有了解到它运行的原理。是不是MVC的目录结构只能有那种固定的模式呢,我们能否 根据自己的需要扩展这些目录结构呢。答案是肯定的。因为asp.net MVC中引用了WebFromViewEngine 这个视图引擎
(二) WebFormViewEngine视图引擎
1.IView接口 IView接口是对MVC结构中View对象的抽象, 此接口只有一个方法:
void Render(ViewContext viewContext, TextWriter writer); Render方法的作用就是展示View对象,
通常是将页面HTML写入到Writer中供浏览器展示.
2.IViewEngine接口 MVC中ViewEngine的抽象,该接口有两个方法:
ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
该方法的作用就是寻找视图对象,值得注意的是上面两个方法返回的都不是ViewPage 页面对象,而是ViewEngineResult 对象。我们可以将ViewEngineResult理解为一次查询的结果, 在ViewEngineResult对象中包含有本次找到的IView对象. 这句话我也不是太懂,看很多相关资料是这样写的,我也姑且这样写吧
3.IViewEngine 的实现类
IViewEngine有两个实现类。 WebFormViewEngine :VirtualPathProviderViewEngine:IViewEngine 这个是它们的关系。而MVC中的试图引擎就是使用的WebFormViewEngine。 WebFormViewEngine包括了三个属性:
MasterLocationFormats 可以个性化母版页的路径
ViewLocationFormats 可以个性化试图页面的路径
PartialViewLocationFormats 可以个性化用户控件的路径
4.在Global.asax 中注册个性化路径
1 protected void Application_Start()
2 {
3 ViewEngines.Engines.Clear();
4
5 ViewEngines.Engines.Add(new WebFormViewEngine()
6 {
7 ViewLocationFormats = new string[] {
8 "~/{0}.aspx",
9 "~/{0}.ascx",
10 "~/Views/{1}/{0}.aspx",
11 "~/Views/{1}/{0}.ascx",
12 "~/Views/Shared/{0}.aspx",
13 "~/Views/Shared/{0}.ascx"
14 }
15 });
16 }
Ø 控件器分组开发
(一) 目录结构
看到上面的目录结构 会有耳目一新的感觉,终于突破了原有的MVC目录结构。该目录结构除了使用原有的Controllers 文件夹之外,我们还使用了Games自定义文件夹中的Controllers。在Users文件夹中也同样还有一个Controllers文件夹,这些文 件夹都是存放控制器类的。我们可以根据自己的需要建立不同的Controller 组。
在大型项目开发的过程中,模块的数量可能会很多,仅仅只靠原有的目录结构是不能满足我们的要求的,而且扩展性也不是太好,因为思想就被束缚在它 本身的结构上,扩展谈何容易。但真正的作为一个企业级架构这肯定是不行的,最起码走起来比较困难。于是控制器组,个性化的目录就是解决方案的开始。
(二) 扩展路由配置(RouteCollection 类的扩展)
我们都知道 .net3.0 3.5 中出现的扩展方法使.net 出现了新的生机,我们不必在为系统方法的不适用而烦恼,也不用为其思维的束缚的叫苦不迭。扩展方法能使我们能够扩展系统中的任何一个类,给它重新添加自己想要的方法。
1 public class Student
2 {
3 private int _id;
4
5 public int Id
6 {
7 get { return _id; }
8 set { _id = value; }
9 }
10
11 private string _name;
12
13 public string Name
14 {
15 get { return _name; }
16 set { _name = value; }
17 }
18 }
我定义一个实体类,这个类中只有两个属相,没有任何自定方法。现在我们用扩展方法给Student添加一个Write()自定义方法
1 public static class StudentExtension
2 {
3 public static void Write(this Student stu, string name)
4 {
5 Console.WriteLine(name);
6 }
7 }
扩展方法的使用代码:
1 public class Test
2 {
3 public void Show()
4 {
5 Student stu = new Student();
6 stu.Id = 1;
7 stu.Name = "ddd";
8 stu.Write(stu.Name);
9 }
10 }
注意 扩展方法必须在静态类中,而且扩展方法也必须用static修饰。扩展方法的第一个参数用this修饰,参考上面的就可以知道,当我们使用这个扩展方法的时候 参数就static 修饰方法中的非this修饰的参数,说的有点绕口。相信能够明白。
RouteCollection扩展类,扩展路由设置
1 public static class AreaRouteHelper
2 {
3 public static void CreateArea(this RouteCollection routes,
4 string areaName,
5 string controllerNameSpace,
6 params Route[] routeEntries)
7 {
8 foreach (Route route in routeEntries)
9 {
10 if (route.Constraints == null)
11 {
12 route.Constraints = new RouteValueDictionary();
13 }
14
15 if (route.Defaults == null)
16 {
17 route.Defaults = new RouteValueDictionary();
18 }
19
20 if (route.DataTokens == null)
21 {
22 route.DataTokens = new RouteValueDictionary();
23 }
24
25 route.Constraints.Add("area",areaName);
26 route.Defaults.Add("area",areaName);
27 route.DataTokens.Add("namespaces",new string[]{controllerNameSpace});
28
29 if (!routes.Contains(route))
30 {
31 routes.Add(route);
32 }
33 }
34 }
35 }
对于自定义视图的关键还是如下:WebFormViewEngine 定义了视图文件的显示,我们可以继承改类从而重载覆盖父类的方法达到自己定义文件映射路径的目的
1 public class AreaViewEngine:WebFormViewEngine
2 {
3 public AreaViewEngine()
4 : base()
5 {
6 //定义页面文件或者用户控件文件的路径规则
7 ViewLocationFormats = new string[] {
8 "~/{0}.aspx",
9 "~/{0}.ascx",
10 "~/Views/{1}/{0}.aspx",
11 "~/Views/{1}/{0}.ascx",
12 "~/Views/Shared/{0}.aspx",
13 "~/Views/Shared/{0}.ascx"
14 };
15
16 //第一母版页文件路径规则
17 MasterLocationFormats = new string[] {
18 "~/{0}.master",
19 "~/Shared/{0}.master",
20 "~/Views/Shared/{0}.master",
21 "~/Views{1}/{0}.master"
22 };
23
24 PartialViewLocationFormats = ViewLocationFormats;
25 }
26
27
28 /// <summary>
29 /// 匹配页面文件的,寻找相应的部分视图文件
30 /// </summary>
31 /// <param name="controllerContext"></param>
32 /// <param name="partialViewName"></param>
33 /// <param name="useCache"></param>
34 /// <returns></returns>
35 public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
36 {
37 ViewEngineResult viewResult = null;
38 if (controllerContext.RequestContext.RouteData.Values.ContainsKey("area"))
39 {
40 string areaPartialName = FormatViewName(controllerContext,partialViewName);
41 viewResult = base.FindPartialView(controllerContext,areaPartialName,useCache);
42 if (viewResult != null && viewResult.View != null)
43 {
44 return viewResult;
45 }
46
47 string sharedAreaPartialName = FormatPartialViewName(controllerContext, partialViewName);
48 viewResult = base.FindPartialView(controllerContext,sharedAreaPartialName,useCache);
49 if (viewResult != null && viewResult.View != null)
50 {
51 return viewResult;
52 }
53 }
54 return base.FindPartialView(controllerContext, partialViewName, useCache);
55 }
56
57 /// <summary>
58 /// 匹配页面文件的,寻找相应的试图文件
59 /// </summary>
60 /// <param name="controllerContext"></param>
61 /// <param name="viewName"></param>
62 /// <param name="masterName"></param>
63 /// <param name="useCache"></param>
64 /// <returns></returns>
65 public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
66 {
67 ViewEngineResult viewResult = null;
68 if (controllerContext.RequestContext.RouteData.Values.ContainsKey("area"))
69 {
70 string areaPartialName = FormatViewName(controllerContext, viewName);
71 viewResult = base.FindView(controllerContext, areaPartialName,masterName, useCache);
72 if (viewResult != null && viewResult.View != null)
73 {
74 return viewResult;
75 }
76
77 string sharedAreaPartialName = FormatPartialViewName(controllerContext, viewName);
78 viewResult = base.FindView(controllerContext, sharedAreaPartialName,masterName, useCache);
79 if (viewResult != null && viewResult.View != null)
80 {
81 return viewResult;
82 }
83 }
84 return base.FindView(controllerContext, viewName, masterName, useCache);
85 }
86
87 /// <summary>
88 /// 这个匹配一般的页面文件或用户控件路径
89 /// </summary>
90 /// <param name="context"></param>
91 /// <param name="viewName"></param>
92 /// <returns></returns>
93 public string FormatViewName(ControllerContext context, string viewName)
94 {
95 //根据路由的参数值获得控制器的名称
96 string controllerName = context.RouteData.GetRequiredString("controller");
97
98 //根据路由的参数获得控制器组的名称
99 string area = context.RequestContext.RouteData.Values["area"].ToString();
100 return "Area/" + area + "/Views/" + controllerName + "/"+viewName ;
101 }
102
103 /// <summary>
104 /// 这个一般匹配母版页的文件路径
105 /// </summary>
106 /// <param name="context"></param>
107 /// <param name="viewName"></param>
108 /// <returns></returns>
109 public string FormatPartialViewName(ControllerContext context, string viewName)
110 {
111 //根据路由的参数获得控制器组的名称
112 string area = context.RequestContext.RouteData.Values["area"].ToString();
113 return "Area/" + area + "/Views/Shared/" + viewName;
114 }
115
116 }
全局应用程序类配置:
public class MvcApplication : System.Web.HttpApplication {
protected void Application_Start() {
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new AreaViewEngine());
RegisterRoutes(RouteTable.Routes);
//注册新的个性化视图引擎AreaViewEngine
}
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Routing config for the blogs area
routes.CreateArea("blogs", "AreasDemo.Areas.Blogs.Controllers",
routes.MapRoute(null, "blogs/{controller}/{action}", new { controller = "Home", action = "Index" })
);
// Routing config for the forums area
routes.CreateArea("forums", "AreasDemo.Areas.Forums.Controllers",
routes.MapRoute(null, "forums/{controller}/{action}", new { controller = "Home", action = "Index" })
);
// Routing config for the root area
routes.CreateArea("root", "AreasDemo.Controllers",
routes.MapRoute(null, "{controller}/{action}", new { controller = "Home", action = "Index" })
);
//创建不同的控件组
}
}
1.8 过滤器解析、开发
在Asp.netMvc中当你有以下及类似以下需求时你可以使用Filter功能
- 判断登录与否或用户权限
- 决策输出缓存
- 防盗链
- 防蜘蛛
- 本地化与国际化设置
- 实现动态Action
Filter是一种声明式编程方式,在Asp.net MVC中它只能限制于Action(或它的Controller)。
Filter要继承于ActionFilterAttribute抽象类,并可以覆写void OnActionExecuting(ActionExecutingContext)和
void OnActionExecuted(ActionExecutedContext)
以及void OnResultExecuting(ResultExecutingContext)和
void OnResultExecuted(ResultExecutedContext)
OnActionExecuting是Action执行前的操作,OnActionExecuted则是Action执行后的操作
而OnResultExecuting是解析ActionResult前执行,OnResultExecuted是解析ActionResult后执行。
一、应用于Action的Filter
下面我给大家一个示例,来看看它的的执行顺序
首先我们先建立 一个Filter,名字叫做TestFilter
public class TestFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Session["temp"] += "TestFilter OnActionExecuting<br/>";
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.HttpContext.Session["temp"] += "TestFilter OnActionExecuted<br/>";
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.HttpContext.Session["temp"] += "TestFilter OnResultExecuting<br/>";
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Session["temp"] += "TestFilter OnResultExecuted<br/>";
}
}
然后建立一个Action:
[TestFilter]//将此Filter应用于Action
public ActionResult filteraction()
{
return View();
}
在它的View中写入:
<%Session["temp"] += "View Execute<br/>"; %>
最后在其它页面得到Session["temp"]的输出结果:
TestFilter OnActionExecuting
TestFilter OnActionExecuted
TestFilter OnResultExecuting
View Execute
TestFilter OnResultExecuted
由此可得到它们的执行顺序也是如上
二、Controller的Filter
将Filter应用在Controller上有2种方式
1.直接将Filter应用在Controller上,如:
[TestFilter]
public class EiceController : Controller
{
}
2.重写Controller内的
OnActionExecuting/OnActionExecuted/OnResultExecuting/OnResultExecuted的四个方法。
下面我们说几个系统的Filter
三、AcceptVerbs
规定页面的访问形式,如
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Example(){
return View();
}
页面只能以Post形式访问,即表单提交。
四、ActionName
规定Action的名称。
应用场景:如果不想用方法名做为Action名,或Action名为关键字的话,如
[ActionName("class")]
public ActionResult Example(){
return View();
}
五、NonAction
当前方法仅是普通方法不解析为Action
六、OutputCache
为Action添加缓存
[OutputCache(Duration = 60, VaryByParam = "*")]
public ActionResult Example()
{
return View();
}
七、ValidateInput
该Action可以接受Html等危险代码(ASP.NET MVC在aspx中设置<%@ Page 的属性无法完成等同任务。)
[ValidateInput(false)]
public ActionResult Example()
{
return View();
}
八、ValidateAntiForgeryTokenAttribute
用于验证服务器篡改。
[ValidateAntiForgeryToken]
public ActionResult Example()
{
return View();
}
自定义异常处理过滤器
Ø 创建WebRequestErrorEventMvc类
在asp.net mvc网站中,需要捕获的异常事件为WebRequestErrorEvent,而该类的构造函数是私有的,不允许开发者直接实例化该事件对象。以此常见此类的子类WebRequestErrorEventMvc类
Ø 创建MyHandleErrorAttribute类
在asp.net mvc网站中,通过过滤器的方式来检测异常,这里开发了一个自定义的过滤器MyHandleErrorAttribute类。
Ø 设置web.config配置文件
当被检测的控制器或者其中的方法子啊运行时发生异常,要将该异常信息存储到相关的数据库中。
<healthMonitoring enabled="true">
<eventMappings>
<clear/>
<add name="MvcError" type="WebRequestErrorEventMvc" startEventCode="0" endEventCode="2147483647"/>
<!--希望被捕获的异常事件类型为WebRequestErrorEventMvc"-->
</eventMappings>
<providers>
<clear/>
<add connectionStringName="LocalSqlServer" maxEventDetailsLength="1073741823" buffer="false" name="SqlWebEventProvider" type="System.Web.Management.SqlWebEventProvider"/>
<!--存储异常信息的方式为sql2005,保存到数据库aspnetdb内的数据表aspnet_WebEvent_Events"-->
</providers>
<rules>
<clear/>
<add name="Controller Error" eventName="MvcError" provider="SqlWebEventProvider" profile="Default" minInstances="1" maxLimit="Infinite" minInterval="00:00:00" custom=""/>
<!--指定异常事件名称为“MvcError”提供程序为“SqlWebEventProvider”即只要WebRequestErrorEventMvc类型的异常发生,这些异常情况就会自动保存到上述的数据表aspnet_WebEvent_Events中"-->
</rules>
</healthMonitoring>
1.9 控制器与视图之间的数据传递
数据传递也就是控制器和视图之间的交互,比如在视图中提交的数据,在控制器怎么获取,或者控制器从业务层获得一些数据,怎么传递到视图中,让视图显示在客户端。
ViewData:只能在一个动作方法中设置,在相关视图页面读取,说得再白一点就是只能在一个页面中使用。
例 在Action方法中设置:ViewData["Message"]="Test";
在视图中读取: <%=Html.Encode(View["Message"]) %>
TempData:TempData(为TempDataDictionary) 和ViewData(为ViewDataDictionary)有一点区别,ViewData只是单个视图有效,而TempData则是两个视图有效,就是说TempData是可以在两个视图中传递 的,如果中间没有更新TempData的值的话,那么从第二个视图显示之后,TempData所保存的数据就会丢失。(注:也就是说,只有更新过的,以及 新添加的键值对才能再下次request中继续使用),微软为什么要这样做呢,应该是为了节省资源吧!因为我们都知道http是无状态的,每次请求的时 候,他都要重新保存TempData的值(利用session保存,顺便提一下,asp.net mvc里面的session对象和web from是一样的),但他又为了不想浪费资源,所以就只保存一次!使用方式和ViewData一致,就把ViewData关键字换成TempData即 可,在这里就不列出代码了!
Model:通过设置,读取ViewData和TempData属性,可以将数据从视图传到控制器,也可以从控制器传 到视图,看起来好像已经够用了,但必需说明的是ViewData和TempData都是弱类型的,所以编码的时候极容易出错。这时我们可以通过在控制器的 View()方法中,传递实例化的对象(例:View(类型)),传递实例化对象,由于是强类型原因,在视图读书时就有代码智能感应,有利于代码的书写和 减少错误!除了在Action里面View的返回时需要带上实例化对象外,在视图页面内还需要设置ViewPage的类型为model的类型。
例如:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcModelDemo.Controllers.ProductModel>" %>
从视图把数据传到控制器,有好几种方式
第一:通过Request.Form读取表单
读取视图中文本框的值
例: <% =Html.TextBox("Name") %> 视图代码
Request.Form["Name"] ; 控制器中Action代码
第二:通过FormCollection读取
读取视图文本框的值
例: 视图代码
<% using (Html.BeginForm("About","Home"))
{ %>
<%=Html.TextBox("Name")%>
<input type="submit" value="submit" name="submit" />
<%}%>
控制器代码
public ActionResult About(FormCollection form)
{
string s = form["Name"];
return View();
}
1.11 多项目混合开发
包括两个层面的混合开发:
第一层:asp.net 3.5 mvc项目中,添加传统的web forms项目,还可以添加动态数据web应用程序。
asp.net 3.5 mvc中添加动态数据web应用程序
Ø 首先:创建动态数据web应用程序
Ø 创建asp.net 3.5 mvc项目
Ø 复制动态数据web应用程序中的“DynamicData”文件夹、“Site.css”和“Site.master”文件到asp.net 3.5 mvc项目中。
Ø 创建LINQ to SQL 类即NorthWind.dbml
Ø 注册动态数据模型
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{resource}.aspx/{*pathInfo}");
MetaModel model = new MetaModel();
model.RegisterContext(typeof(NorthWindDataContext), new ContextConfiguration() { ScaffoldAllTables = true 表明动态数据框架会自动生成所有数据表的相关浏览、添加、编辑及删除代码});
routes.Add(new DynamicDataRoute("DD/{table}/{action}.aspx")
{
Constraints = new RouteValueDictionary(new { action = "List|Details|Edit|Insert" }),
Model = model
});
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
asp.net 3.5 mvc中添加动态数据web Forms页面
Ø 创建WebForms文件夹,在文件夹中添加普通的webforms页面WebForm.aspx。
Ø asp.net 3.5 mvc 框架将不会对普通页面进行路由分析,但是在处理普通web Forms页面
的时候,仍然会影响asp.net 3.5 mvc项目的运行性能,为此设置如下:
routes.IgnoreRoute("{resource}.aspx/{*pathInfo}");
设置了普通页面,不需要使用路由分析,就可以提高asp.net 3.5 mvc项目的运行性能
第二层:现有的web forms项目中,添加asp.net 3.5 mvc项目,实现传统web forms项目于asp.net 3.5 mvc项目的“混合开发”。
下面我们将一步一步操作:
1.新建一个mvc项目,等会可以从这里copy一些东西到webform项目里面的!
2.在旧的webForm项目里面添加引用这三个类库,分别是:system.web.mvc,system.web.abstractions,system.web.routing.
3.把mvc项目的结构copy到webform项目里面去(将content,controllers,scripts,views这四个文件夹copy到webform项目根目录下,把这四个文件添加到这个项目里面去)
4.接下来就是修改配置文件了,有三个地方要修改
1.在程序集节点<assembies>中添加3个相关的asp.net 3.5 mvc程序集
<add assembly="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
2.在页面节点<pages>添加相关的命名空间
<namespaces>
<add namespace="System.Web.Mvc"/>
<add namespace="System.Web.Mvc.Ajax"/>
<add namespace="System.Web.Mvc.Html"/>
<add namespace="System.Web.Routing"/>
<add namespace="System.Linq"/>
<add namespace="System.Collections.Generic"/>
</namespaces>
以便开发者在视图开发中不再需要引入这些命名空间就可以直接使用它们中的相关类。
3.在http模块节点<httpModules>添加程序入口
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
5.设置路由(记得引入system.web.mvc命名空间,要不然找不到IgnoreRoute这个方法)
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{resource}.aspx/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
routes.IgnoreRoute("{resource}.aspx/{*pathInfo}");表示aspx页面不经过路由处理.其它的应该看得懂了,就是路由基本规则了!
然后就大公告成了!
这时可能会问,那两者是否可以互相关联,答案是当然可以了!比如你在mvc页面里面可以直接转到webform页面 (Response.Redirect("default.aspx");),在webform页面想转到mvc页面也很简单 (Response.Redirect("Home/Index");)
至于数据的传递,可以使用session,在页面的session里面存入值之后,在控制器也可以直接用session取得!两个session没 有本质上的区别,在webform页面提交了表单之后,在控制器也可以使用form来获取 response.form[文本 id].tostring();就可以获得了!
基本就是这样了!
读取web form页面数据
在asp.net 3.5 mvc项目中读取web form项目中的页面数据,一般有两种方法:
Ø 通过post表单方式
<body>
<div>
<p></p>
在Web Form页面通过POST方法发送数据给控制器:
<p>
<form action="/Home/Post" method ="post">
</p><p>
<input type ="text" name="FirstName" />
</p> <p>
<input type ="text" name="LastName" />
</p><p>
<input type="submit" value ="发送数据给控制器" />
</p>
</form>
</div>
</body>
将浏览器中输入的数据发送到指定的路由/Home/Post
public ActionResult Post()
{
ViewData["FirstName"] = Request.Form["FirstName"].ToString();
ViewData["LastName"] = Request.Form["LastName"].ToString();
return View();
}
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h3>在ASP.NET MVC页面中读取Web Form页面数据</h3>
<p>
FirstName: <% =ViewData ["FirstName"] %>
</p>
<p>
LastName: <% =ViewData ["LastName"] %>
</p>
</asp:Content>
Ø 通过Session对象
protected void Button_Click(object sender, EventArgs e)
{
Session["myData"] = "This is data test";
Response.Redirect("/Home/SessionData",true);
}
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h3>在ASP.NET MVC页面中读取Web Form页面数据</h3>
<p>
myData: <% =ViewData ["myData"] %>
</p>
</asp:Content>
1.12 单元测试
Moq模拟框架
其他单元测试框架
1.13 网站部署
ASP.NET 3.5 MVC项目在不同版本IIS中的部署
为方便开发者了解ASP.NET 3.5 MVC项目在不同版本IIS中的部署,表13-1给出了不同版本IIS下的部署说明。
表13-1 不同版本IIS的配置说明
IIS版本 | Windows版本 | 说明 |
IIS 7.0(集成模式) | Windows Server 2008 Windows Vista (非Home Basic版本) | 不需要特别配置 |
IIS 7.0(经典模式) | Windows Server 2008 Windows Vista (非Home Basic版本) | 需要特别配置 |
IIS 6.0 | Windows Server 2003 | 需要特别配置 |
IIS 5.1 | Windows XP Professional | 需要特别配置 |
IIS 5.0 | Windows 2000 | 需要特别配置 |
从表中可以看出,在IIS 7.0集成模式下,开发者不需要进行特别的配置,就可以非常方便地部署ASP.NET 3.5 MVC项目;而在其他的IIS版本中,则需要进行特别的配置,才能正常运行ASP.NET 3.5 MVC项目,以下主要说明如何分别在IIS 6.0和IIS 7.0中部署ASP.NET 3.5 MVC项目。
在IIS 6.0中部署ASP.NET 3.5 MVC项目
这里假定需要部署的ASP.NET 3.5 MVC项目是:通过Visual Studio 2008开发工具中的"ASP.NET 3.5 MVC Web Application"项目模板所生成的"MVCApplication1"。
在Visual Studio 2008开发工具中,用鼠标右键单击"项目资源管理器"中的"MVCApplication1"项目,在弹出的快捷菜单中选择"属性"命令,打开如图13-4所示的对话框。
单击"Web"选项卡,选择"使用本地IIS Web服务器"单选按钮,输入需要部署的项目URL地址,单击右边的"创建虚拟目录"按钮,就可以为项目"MVCApplication1"在IIS 6.0中创建一个名称为"MVCApplication1"的网站。
打开IIS 6.0管理器,其运行界面如图13-5所示。
从图13-5中可以看出,已经将项目发布到IIS 6.0中,但现在还不能说已经成功发布了ASP.NET 3.5 MVC项目"MVCApplication1"。
13.2.1 使用.aspx应用程序扩展
在IIS 6.0中,ASP.NET ISAPI负责处理、分发用户的请求,在默认情况下,直接处理带扩展名为"aspx"的请求,因此,为了在IIS 6.0中,正常运行ASP.NET 3.5 MVC项目"MVCApplication1",需要设置如下的路由:
1. routes.MapRoute(
2. "Default",
3. "{controller}.aspx/{action}/{id}",
4. new { controller = "Home", action = "Index", id = "" }
5. );
在上述代码中,设置了名称"Default"的路由为"{controller}.aspx/{action}/{id}",其中控制器参数的后面添加了扩展文件名"aspx",以便IIS 6.0在默认情况下可以正常处理被新设置的路由。
在IIS 6.0中,浏览ASP.NET 3.5 MVC项目"MVCApplication1"的首页,打开如图13-6所示的运行界面。
从图中的浏览器地址框内的URL可以看出,对于控制器Home来说,由于添加了扩展名"aspx",IIS 6.0可以正常运行首页及其他页面。
但是上述的URL地址由于含有扩展名"aspx",非常不友好,应该说是难看,下面就说明如何添加个性化的扩展名。
13.2.2 使用.mvc应用程序扩展
在图13-5中的IIS 6.0管理器中,用鼠标右键单击网站名称"MVCApplication1",在弹出的快捷菜单中选择"属性"命名,打开如图13-7所示的对话框。
在"MvcApplication1属性"对话框中,单击"配置"按钮,打开如图13-8所示的"应用程序配置"对话框。
在图13-8中,单击"添加"按钮,打开如图13-9所示的对话框。
在"添加/编辑应用程序扩展名映射"对话框中,单击"浏览"按钮,设置可执行文件的路径为"C:\WINDOWS\Microsoft.NET \Framework\v2.0.50727\aspnet_isapi.dll";扩展名为".mvc",然后单击"确定"按钮,完成对个性化扩展名 为".mvc"的映射设置。
在全局应用程序类Global.asax.cs中,设置如下路由:
1. routes.MapRoute(
2. "Default",
3. "{controller}.mvc/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
4. );
在上述代码中,设置了名称"Default"的路由为"{controller}.mvc/{action}/{id}",其中控制器参数的后面添加了扩展文件名".mvc",以便IIS 6.0可以处理被新设置的路由。
在IIS 6.0中,浏览ASP.NET 3.5 MVC项目"MVCApplication1"的首页,打开如图13-10所示的运行界面。
从图中的浏览器地址框内的URL可以看出,对于控制器Home来说,由于添加了个性化的扩展名".mvc",IIS 6.0可以正常运行首页及其他页面。
但是上述的URL地址由于含有扩展名".mvc",还是不友好,应该说还是很难看,下面说明如何配置通配符应用程序映射,去掉控制器中的扩展名。
13.2.3 配置通配符应用程序映射
在图13-5中的IIS 6.0管理器中,用鼠标右键单击网站名称"MVCApplication1",在弹出的快捷菜单中选择"属性"命名,然后在弹出的对话框中,单击"配置"按钮,打开如图13-11所示的"应用程序配置"对话框。
在"应用程序配置"对话框中,单击"插入"按钮,打开如图13-12所示的"添加/编辑应用程序扩展名映射"对话框。
在图13-12中,单击"浏览"按钮,设置可执行文件的路径为"C:\WINDOWS\Microsoft.
NET\Framework\v2.0.50727\aspnet_isapi.dll";并注意不要选择"确认文件是否有效",然后单击"确定"按钮,打开如图13-13所示的运行界面。
在图13-13中,单击"确定"按钮,完成对通配符映射的设置。
在全局应用程序类Global.asax.cs中,设置如下路由:
1. routes.MapRoute(
2. "Default",
3. "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
4. );
上述代码设置了所需要的路由,其中控制器中的参数不再包括扩展名。在IIS 6.0中,此时再浏览ASP.NET 3.5 MVC项目"MVCApplication1"的首页,就会打开如图13-14所示的运行界面。
从图中浏览器地址框内的URL可以看出,ASP.NET 3.5 MVC项目"MVCApplication1"已成功发布在IIS 6.0中,可以正常运行首页及其他页面。
13.2.4 合理配置通配符应用程序映射
通过配置通配符应用程序映射,可以在IIS 6.0中成功部署ASP.NET 3.5 MVC项目"MVCApplication1",这里需要说明的是,这种通配符应用程序映射针对的是项目中的所有文件,不管是静态的,还是动态的,即使是 静态"Script"目录下的文件,也配置了这种映射,这样将会降低网站的运行性能。
下面说明,如何在ASP.NET 3.5 MVC项目"MVCApplication1"中根据实际需要,合理地配置通配符应用程序映射,也就是说,针对某些目录,删除对应的通配符应用程序映射。
在如图13-5所示的IIS 6.0管理器运行界面中,选择ASP.NET 3.5 MVC项目"MvcApplication1"中不需要通配符应用程序映射的文件夹,这里假如选择"Script"目录,用鼠标右键单击 该"Script"目录,在弹出的快捷菜单中选择"属性"命令,打开如图13-15所示的创建应用程序名对话框。
在图13-15中,单击"创建"按钮,就会创建一个名称为"Script"的应用程序名,如图13-16所示。
在图13-16中,单击"配置"按钮,打开如图13-17所示的"应用程序配置"对话框。
在图13-17中,单击"删除"按钮,将会打开一个"确实要删除选定的脚本映射吗?"的确认对话框,单击其中的"是"按钮,打开如图13-18所示的运行界面。
在图13-18中,单击"删除"按钮,就会删除名称为"Script"的应用程序名,然后单击"确定"按钮,返回到如图13-5所示的IIS 6.0管理器运行界面。
重复上述的步骤,以便删除其他不需要配置通配符应用程序映射的目录,提高网站的运行性能。
13.3 在IIS 7.0中部署ASP.NET 3.5 MVC项目
在IIS 7.0中部署ASP.NET 3.5 MVC项目,相对来说是非常简单的(这里使用的操作系统为Windows Server 2008)。
13.3.1 添加网站
在Windows Server 2008中,运行IIS 7.0管理器,打开如图13-19所示的运行界面。
在图13-19的左边窗格中,用鼠标右键单击"网站"目录,在弹出的快捷菜单中选择"添加网站"命令,打开如图13-20所示的"添加网站"对话框。
在图13-20中,设置网站的名称为"MvcApplication1";选择网站内容目录的物理路径为"C:\MvcApplication1\MvcApplication1";设定网站绑定的端口为"8080";选择"立即启动网站"复选框,然后单击"确定"按钮。
13.3.2 启动、浏览网站
此时打开如图13-21所示的运行界面。
从 图13-21的左边窗口中可以看出,新添加的网站"MvcApplication1"的左边图标出现了红色的"X",说明该网站没有正常运行;单击右边窗 口中的"重新启动"(位于"管理网站"的下方)链接,此时会重新启动网站"MvcApplication1",如果网 站"MvcApplication1"启动正常,则出现如图13-22所示的运行界面。
从图13-22的左边窗口中可以看出,网站"MvcApplication1"已经正常启动;单击右边窗口中的"浏览*8080(http)"(位于"浏览网站"的下方)链接,打开如图13-23所示的网站"MvcApplication1"的运行界面。
单击网站中的相关链接,如注册一个新用户,然后使用该注册用户名登录该网站,测试表明,网站"MvcApplication1"运行正常,已经成功地发布在IIS 7.0中了。
2 架构篇
1.1 三层架构
1 |
表现层 |
数据访问层 |
数据库 |
2 |
3 |
4 |
1.1.1 三层为:
表现层
数据访问层
数据库作为架构中的底层
表现层直接与中间的数据访问层相关联,中间层与数据库直接相连,从数据访问层中发送相关的SQL语句到数据库,数据库执行相关的SQL语句后,将执行结果返回到数据访问层。所以,这样数据访问层可以灵活的调整(sql的数据访问层或者ACCESS的数据访问层)。其他层没有任何的变化。
1.1.2 实现三层架构的Web应用
首先针对每一个数据表,构建相关的业务对象,包括业务对象的列表类,这个对象只用于相关对象的数据,因此其中只设置了对象的相关属性,而没有相关的方法。
然后针对每个数据表,创建中间数据访问层,设计新的数据访问类。该类主要实现与数据的连接,根据业务需要,实现对应的数据库表查询、插入、更新及删除操作。并将这些操作分别封装在相应的方法中。中间数据访问层的类以Manager为结尾的类。
接着在vs2008中,拖放ObjectDataSource数据源控件到Web应用页面中,选择中间数据访问层,设置相关的操作方法,这样ObjectDataSource控件就可以通过数据访问层,实现数据的查询、修改等数据操作。
最后使用GridView等数据编辑、显示控件,实现对数据源控件ObjectDataSource的绑定,显示指定的数据。
1.2
1 |
表现层 |
数据访问层 |
数据库 |
2 |
3 |
4 |
业务逻辑层 |
5 |
6 |
多层架构的执行流程可以划分为如下的6个步骤:
1. 表现层向业务逻辑层发出请求,请求获得一个业务对象。
2. 业务层收到表现层的请求后,根据业务需要,可以对用户的请求进行验证,然后把请求发送到数据访问层。
3. 数据访问层对数据库实现相关的操作,以便获得数据库中相关数据记录。
4. 当数据库中的相关记录获得后,把数据返回到数据访问层中。
5. 数据访问层把返回的数据记录,包装成特定的业务对象,返回到上一层的业务逻辑层中。
6. 业务逻辑层将返回得到的特定业务对象,返回到上一层的表现层,以便在web页显示此业务对象。
在asp.net3.5中,实现多层架构一般需要如下5个步骤:
1. 针对每一个数据表,构建相关的业务对象。
2. 针对每一个数据表,创建中间数据访问层,设计新的数据访问类。该类主要实现连接数据库,对数据库进行查询、插入、更新及删除操作。
3. 根据具体业务的需求,构建业务逻辑层,
4. 在vs2008中,拖放ObjectDataSource数据源控件到Web应用页面中,选择业务逻辑层,而不再是数据访问层,设置相关的操作方法。
5. 使用GridView等数据编辑、显示控件,实现对数据库源控件ObjectDataSource的绑定,显示指定的数据。
3 架构篇
多个项目,导出生成模板,以便以后方便使用
第一步:选择整个解决方案,然后选择“文件”->”导出模板”,弹出下面所示的图
在项目模板中选择要创建的项目模板,然后单击“下一步”进入
然后,重复各个项目,添加模板。最后,把所有的放在一个文件夹下,在文件夹下添加Icon.ico和MyTemplate.vstemplate两个文件。再把这个文件夹压缩成.zip文件,放到
C:\Documents and Settings\zxqc\My Documents\Visual Studio 2008\Templates\ProjectTemplates文件夹下。再创建项目中,我的模板中就可以看到了。