p.Net MVC已经到第三版了,相信你们也都熟悉了,我也再也不重复相关概念性的东西了。可是你们必定要了解,Asp.Net MVC是微软的一个开源的UI层框架,是AspNet的另一种开发模式。好废话很少说,那咱们开始进入Asp.Net MVC3 的学习中来,工欲善其事,必先利其器!因此咱们必须搭建好本身的开发环境才能为咱们下一步的学习和开发提供更好的支持。html
那你的机器的必备条件是:web
1)VS2010(固然你非要使用VS2008那我也不能说什么了)api
2)SqlServer 2000/2005/2008浏览器
3)Asp.Net MVC3安装包(应该是须要VS2010SP1)服务器
下面提供一些URL连接方便你们下载学习:mvc
1)Asp.Net MVC3的官网:http://www.asp.net/mvc/mvc3 app
以下图所示:框架
这个你们在安装过程当中可能时间会很是长(>=20分钟),你们忍耐一下!asp.net
2)安装Asp.Net MVC3安装包ide
http://www.microsoft.com/web/gallery/install.aspx?appid=MVC3
3)Asp.Net MVC3源码开源地址:
http://aspnet.codeplex.com/releases/view/58781
以下图所示:
第二节:第一个Asp.Net MVC3项目
1)建立项目:
2)选择项目的默认视图引擎
咱们选择一个Empty模板,而后选择Razor视图引擎(Asp.Net MVC3中提供的新的视图引擎)选择HTML5标记支持打上勾(这块我也不了解,呵呵,但愿作过这块研究的高手跟我分享一下!)。
3)建立后的项目:
Asp.Net MVC3貌似跟以前的版本建立的项目模板没什么大的不一样,文件夹也基本类似。固然咱们看到文件夹内的Jquery的包更新到了1.5.1 。后面的文章中会对每一个文件夹都作相关的介绍。在此就很少啰嗦了。
4)添加代码,跑起来咱们的第一个Demo
首先:在Controller文件夹上右击,选择添加菜单,而后选择Controller,以下图所示:
而后弹出对话框,将Controller命名为HomeController,注意后面的Controller不要去掉,否则它就不会被识别为Controller了,最后点击添加。以下图所示:
设计器自动为咱们生成代码以下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
}
下一步咱们在Action上添加咱们须要的视图,以下所示:
5)在前台页面添加咱们本身的Html标签
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<h1>Hello! It's my first Asp.Net MVC Web!</h1>
注意:红色为咱们本身添加的部分
6)最终结果:
在上一篇文章Asp.Net MVC3 简单教程(一)环境准备 中我简单介绍了Asp.Net MVC3项目的安装和第一个Asp.Net MVC3项目的基本状况。没有详细介绍项目中各个文件夹的做用,以及建立的第一个页面是怎样运行起来的?还有好多的疑问,那在这篇文章中咱们将详细介绍项目中各个文件夹的做用,并真的第一个项目咱们简要介绍一下Asp.Net MVC的URL驱动的是怎么回事。
第一节:Asp.Net MVC3项目介绍
让咱们先看一下,一个普通的Asp.Net MVC3项目的样例,以下图所示
跟WebFrom仍是有区别的,若是你已经了解Asp.Net MVC2的话,那就感受异常熟悉了!但仍是有些区别的。无论怎样咱们都一一介绍一下。
颇有意思的事情是即便咱们建立一个空的MVC项目,VS也自动帮咱们建立以上图所示的目录,这是为什么呢?这是因为MVC秉承了“约定大于配置”的思想,咱们在使用Asp.Net MVC3开发项目时也要注意,必定要按照它的约定办事,好比:Controller在返回Action后须要一个View进行展现(固然是调用了View()方法时),这时候Asp.Net MVC回到Views文件夹下找到Controller名字相同的文件夹下面找到具体的页面进行渲染,固然若是找不到会去Shared文件夹下去找。看下表所示的就是Asp.Net MVC3中各个文件夹的做用。
文件夹 |
做用 |
/Controllers |
存放控制器类【职责是:处理用户的请求,指挥具体的页面进行渲染交给客户端】 |
/Views |
存放各个控制器对应的视图文件,若是是Razor引擎的话那后缀是cshtml.若是使用的WebFrom的视图引擎的话,那仍是Aspx后缀。 |
/Content |
主要存放照片、CSS、Flash等文件 |
/Scripts |
主要存放脚本文件【微软默认给咱们提供了JQuery1.5.1的包,看来JQuery已经成为默认的工业标准了!咱们没有退路了,呵呵,固然我我的也很是喜欢JQuery】 |
/Models |
主要存放ViewModel类【固然这个不是严格这样要求的,而是推荐你这么作。】 |
其余的几个比较有意思的文件:
一个是Web.Config,另一个是Global.asax虽然咱们你们都很是熟悉,可是跟以前咱们WebFrom仍是有不少的区别的。WebConfig文件中,配置了启用客户端脚本验证、配置了System.Web.Routing、System.Web.Mvc等组件。而Global.asax则在应用启动的时候注册了全局的Area【区域,后面会相信讲解】、全局Filter、路由等。
第二节:Asp.Net MVC的请求处理模型
在上一篇中咱们也简单作了个小例子,直接添加一个Controller,而后在Action上添加一个View,直接运行,而后就在咱们面前呈现了一个普通的Html页面。那咱们详细解释一下这种开发方式或者说开发模型。在讲解以前咱们先认识几个概念:
Controller:控制器。在Contrller文件夹添加的以Controller结尾的类就是控制器,它的每一个方法就是一个Action。它的职责是从Model中获取数据,并将数据交给View,它是个指挥家的角色,它并不控制View的显示逻辑,只是将Model的数据交给View,而具体的怎样展现数据那是View的职责,因此Controller跟View是一个弱耦合的状态,并且Controller能够任意指定具体的View进行渲染。因此达到了UI层的代码和实体良好的分离。
View:视图.负责数据的展现,固然这个视图代码的编写应该是更接近纯净的Html的,而View层代码的书写又直接跟视图引擎解析的规则有关,因此Razor的语法跟webFrom视图引擎的语法大相径庭。而笔者更倾向更喜欢Razor语法的简洁、方便。
Model:不少人把Model理解成领域模型,而MVC自己是一个表现模式,它是更倾向于UI层的一个框架,因此通常咱们指定的Model呢在使用时通常做为ViewModel来用,可是总的MVC的思想呢,Model仍是领域相关的东西吧。
通过MVC3个模块的了解分析,咱们大致也知道了Asp.Net MVC的一些基本的概念。接下来咱们分析一个完整的Http的处理过程。看下面一个图:
客户端发送一个Http请求,首先被咱们的IIS捕获到,而后根据Url请求的格式,最终交给咱们的Route组件,而后它负责解析出咱们的Url具体请求的是哪一个Controller下的哪一个Action。而后MVC通过处理调用咱们的Action执行。在Action中咱们通常会从业务的Façade层取出数据,而后将传输层的数据转换成ViewModel再交给View的视图引擎渲染,最终生成Html的字节流写回客户端。
回到咱们第一个项目中的状况是,请求:Http://localhost/Home/Index请求过来,由Route组件解析出Controller是Home,Action是Index,则经过工厂建立一个Controller的实例,而后调用InvokeAction方法,执行Index的方法,最终执行View()方法返回一个ViewResult实例,再调用本身的ExcuteResult方法,将数据上下文和输出流交给视图引擎,而后最终渲染成Html页面交给客户端,最终就看到了咱们的第一个页面。
总结一下:
Asp.Net MVC全部的请求都归结到Action上,并且Asp.Net MVC请求--处理--响应的模型很是清晰,并且没有WebFrom那种复杂的生命周期,整个请求处理很是明晰简单,又回归到了最原始的Web开发方式,就是简单的请求处理响应!
前言
前面两篇写的比较简单,刚开始写这个系列的时候我面向的对象是刚开始接触Asp.Net MVC的朋友,因此写的尽可能简单。因此写的没多少技术含量。把这些技术总结出来,而后一简单的方式让更多的人很好的接受这是我一直努力的方向。后面会有稍微复杂点的项目!让咱们一块儿期待吧!
此文我将跟你们介绍一下Asp.Net MVC3 Filter的一些用法。你会了解和学习到全局Fileter,Action Filter等经常使用用法。
第一节:Filter知识储备
项目大一点总会有相关的AOP面向切面的组件,而MVC(特指:Asp.Net MVC,如下皆同)项目中呢Action在执行前或者执行后咱们想作一些特殊的操做(好比身份验证,日志,异常,行为截取等),而不想让MVC开发人员去关心和写这部分重复的代码,那咱们能够经过AOP截取实现,而在MVC项目中咱们就能够直接使用它提供的Filter的特性帮咱们解决,不用本身实现复杂的AOP了。
Asp.Net MVC提供了如下几种默认的Filter:
Filter Type |
实现接口 |
执行时间 |
Default Implementation |
Authorization filter |
IAuthorizationFilter |
在全部Filter和Action执行以前执行 |
AuthorizeAttribute |
Action filter |
IActionFilter |
分别在Action执行以前和以后执行。 |
ActionFilterAttribute |
Result filter |
IResultFilter |
分别在Action Result执行以后和以前 |
ResultFilterAttribute |
Exception filter |
IExceptionFilter |
只有在filter, 或者 action method, 或者 action result 抛出一个异常时候执行
|
HandleErrorAttribute |
你们注意一点,Asp.Net MVC提供的ActionFilterAttribute默认实现了IActionFilter和IResultFilter。而ActionFilterAttribute是一个Abstract的类型,因此不能直接使用,由于它不能实例化,因此咱们想使用它必须继承一下它而后才能使用,下图所示的是ActionFilterAttribute的实现:
因此咱们在实现了ActionFilterAttribute,而后就能够直接重写一下父类的方法以下:
public virtual void OnActionExecuted(ActionExecutedContext filterContext);//在Action执行以后执行
public virtual void OnActionExecuting(ActionExecutingContext filterContext); //在Action执行前执行
public virtual void OnResultExecuted(ResultExecutedContext filterContext);//在Result执行以后
public virtual void OnResultExecuting(ResultExecutingContext filterContext); //在Result执行以前
而后咱们就能够直接在Action、Result执行以前以后分别作一些操做。
第二节:Action Filter实战
光说不练假把式,那如今咱们就直接作一个例子来实际演示一下。
首先咱们添加一个普通的类,直接上代码吧:
public class DemoActionAttributeFilter : ActionFilterAttribute
{
public string Message { get; set; }
public override void OnActionExecuted(ActionExecutedContext filterContext)
{ //在Action执行以后执行 输出到输出流中文字:After Action Excute xxx
filterContext.HttpContext.Response.Write(@"<br />After Action Excute" + "\t " + Message);
base.OnActionExecuted(filterContext);
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{ //在Action执行前执行
filterContext.HttpContext.Response.Write(@"<br />Before Action Excute" + "\t " + Message);
base.OnActionExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{ //在Result执行以后
filterContext.HttpContext.Response.Write(@"<br />After ViewResult Excute" + "\t " + Message);
base.OnResultExecuted(filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{ //在Result执行以前
filterContext.HttpContext.Response.Write(@"<br />Before ViewResult Excute" + "\t " + Message);
base.OnResultExecuting(filterContext);
}
}
写完这个代码后,咱们回到Action上,打上上面的标记以下所示:
[DemoActionAttributeFilter(Message = "action")]
public ActionResult Index()
{ //Action 执行时往输出流写点代码
this.ControllerContext.HttpContext.Response.Write(@"<br />Action Excute");
return Content("Result Excut! ");
}
而后执行F5,页面上则会显示为:
最终咱们看到了在Action执行以前和以后都执行了咱们的重写的DemoActionAttributeFilter方法,Result执行先后也执行了咱们的Filter的方法。
总的执行顺序是:
Action执行前:OnActionExecuting方法先执行→Action执行→OnActionExecuted方法执行→OnResultExecuting方法执行→返回的ActionRsult中的ExcuteResult方法执行→OnResultExecuted执行。最终显示的效果就是如上图所示。
感受很爽吧!呵呵!
若是咱们将此标签打到Controller上的话,DemoActionAttributeFilter将做用到Controller下的全部的Action。例如以下代码所示:
[DemoActionAttributeFilter(Message = "controller")]
public class HomeController : Controller
{
[DemoActionAttributeFilter(Message = "action")]
public ActionResult Index()
{
this.ControllerContext.HttpContext.Response.Write(@"<br />Action Excute");
return Content("<br/>Result Excut! ");
}
}
那就有个问题了咱们再执行显示的页面会有什么状况呢?Controller上的Filter会执行吗?那标签的做用会执行两次吗?下面是最后的执行结果以下图所示:
结果说明:默认状况下Action上打了DemoActionAttributeFilter 标签后,虽然在Controller上也打上了此标签,但它只有Action上的标签起做用了。Index 执行时,Filter的方法只执行了一次,而某些状况下咱们也想让Controller上的FilterAttribute也执行一次DemoActionAttributeFilter
那咱们怎么才能让Controller上的[DemoActionAttributeFilter(Message = "controller")]也起做用呢?
答案是:咱们只需在DemoActionAttributeFilter类的定义上打上标记[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]便可【下面类的最上面红色字体部分】,也就是让其成为能够屡次执行的Action。代码以下:
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class DemoActionAttributeFilter : ActionFilterAttribute
{
public string Message { get; set; }
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.HttpContext.Response.Write(@"<br />After Action Excute" + "\t " + Message);
base.OnActionExecuted(filterContext);
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Response.Write(@"<br />Before Action Excute" + "\t " + Message);
base.OnActionExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.Write(@"<br />After ViewResult Excute" + "\t " + Message);
base.OnResultExecuted(filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.HttpContext.Response.Write(@"<br />Before ViewResult Excute" + "\t " + Message);
base.OnResultExecuting(filterContext);
}
}
而后咱们执行的效果如图所示:
咱们看到的结果是Controller上的ActionFilter先于Action上打的标记执行。一样Result执行ExcuteResult方法以前也是先执行Controller上的Filter标记中的OnResultExcuteing方法。
最后的执行顺序是:Controller上的OnActionExecuting→Action上的OnActionExecuting→Action执行→Action上的OnActionExecuted→Controller上的OnActionExecuted
到此Action就执行完毕了,咱们看到是一个入栈出栈的顺序。后面是Action返回ActionResult后执行了ExecuteResult方法,但在执行以前要执行Filter。具体顺序为:
接上面→Controller的OnResultExecuting方法→Action上的OnResultExecuting→Action返回ActionResult后执行了ExecuteResult方法→Action上的OnResultExecuted执行→Controller上的OnResultExecuted执行→结束
第三节:Gloable Filter实战
又接着一个问题也来了,咱们想有些公共的方法须要每一个Action都执行如下,而在全部的Controller打标记是很痛苦的。幸亏Asp。Net MVC3带来了一个美好的东西,全局Filter。而怎么注册全局Filter呢?答案就在Global.asax中。让咱们看如下代码,我是如何将上面咱们定义的DemoActionAttributeFilter 注册到全局Filter中。上代码:
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalFilters.Filters.Add(new DemoActionAttributeFilter() { Message = "Gloable" });
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
跟普通的MVC2.0中的Global.asax的区别就是红色部分的代码,咱们看到代码中我将本身定义的DemoActionAttributeFilter的实例加入到GlobalFilters.Filters集合中,而后下面一句就是注册全局Filter:RegisterGlobalFilters(GlobalFilters.Filters);
这样咱们全部的Action和Result执行先后都会调用咱们的DemoActionAttributeFilter的重写的方法。
再次运行咱们的demo看到的结果是:
咱们看到的结果是全局的Action首先执行,而后才是Controller下的Filter执行,最后才是Action上的标签执行。固然这是在DemoActionAttributeFilter类的定义上打上标记[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]的前提下。否则 若是Action打上了标签跟Controller的相同则它只会执行Action上的Filter。
总结
通过这一篇文章的介绍咱们大致了解了Filter的使用方法,还了解到全局Filter的用法,尤为是当相同的Filter重复做用到同一个Action上时,若是没有设置可屡次执行的标签那只有Action上的Filter执行,而Controller和全局Filter都被屏蔽掉,可是设置可屡次执行,那首先执行全局Filter其次是Controller再次之就是Action上的Filter了。
今天写点关于Asp.Net MVC的PipeLine。首先咱们确认一点,Asp.Net WebFrom和Asp.Net MVC是在.Net平台下的两种web开发方式。其实他们都是基于Asp.Net Core的不一样表现而已。看下面一张图,咱们就能理解了WebForm和Asp.Net MVC的一个关系了。
那好咱们了解了Asp.Net平台下的两种开发方式,相信你们对于WebForm的Pipeline都很是熟悉了,固然这也是你熟悉Asp.Net开发的必经之路。而看了不少关于Asp.Net MVC的资料不多有把整个Pipeline讲的很是清楚的。我暂时将本身浅陋的整理和理解总结以下,欢迎高手拍砖!
第一阶段:客户端请求
客户端经过浏览器、其余软件、本身编写WebClinet、模拟HttpRequest等方法来请求一个URL。固然在Asp.Net WebFrom下,全部的请求都是归结到Handler上,普通的Aspx、Ascx等都是继承自IHttpHandler接口的一些实例,因此我总结出来:WebFrom下全部的请求都是请求的Handler【不考虑Url重写】。而作Asp.Net MVC的项目呢,全部的请求是都归结到Action上,Url应该是直接请求Action。
第二阶段:IIS Web服务器
当一个请求到达IIS服务器后,Windows系统的内核模块 HTTP.SYS就能监听到这次请求,并将这次请求的URL、IP以及端口等信息解析出来并将此请求交给注册的应用来处理:也就是IIS的站点。请求此时就到达了IIS,IIS【此处仅表明IIS6.0版本】就会去检查这次请求的URL的后缀并将相应的请求交给配置的处理后缀相应的isapi。若是是.aspx或者ascx等直接交给默认设置了此处理项的AspNet_isapi.dll来处理,若是咱们想处理Asp.Net MVC的请求的话,咱们须要在IIS里面设置处理*.*请求交给AspNet_isapi.dll来处理,才能将一个普通的MVC请求的URL:Http://localhost/DemoController/DemoAction交给AspNet_Isapi.dll来处理。
第三阶段:Asp.Net 运行时
此时请求到AspNet_Isapi.dll后,它负责启动Asp.Net RunTime【如过启动了,直接将请求交给RunTime】。Asp.Net 运行时【HttpRuntime】此时会初始化一下HttpContext上下文,并从HttpApplicationFactory去建立一个HttpApplication对象,并将HttpContext赋值给HttpApplication,此后HttpContext的信息就会一直在管道内往下传递。
HttpApplication对象开始初始化WebConfig文件中注册的IHttpModule,请求带着请求信息【HttpContext】随着管道流过多个HttpModule【通常能够作为权限校验、行为记录、日志等等,就是在到达Handler以前咱们均可以直接处理这次Http请求,甚至能够重写URL】,固然也会通过咱们注册的一些自定义的IHttpModule,在.Net 4.0的machine 的config文件中默认配置了一个URLRouteModule,这个也就是咱们普通的Asp.Net MVC项目中的路由DLL引用【System.Web.Routing】内部的一个实现了IHttpModule接口的实例类。请求最终流向了路由组件。
第四阶段:Routing组件
若是你用的是MVC 2+ .NET 3.5,则你会在你的web项目中发现UrlRoutingModule就配置在你的Web.Config。.NET 4倒是在.Net的默认配置文件中配置的。
UrlRoutingModule作了这么几个工做:首先他会拿着你的请求到路由表中去匹配相应的路由规则。而路由表规则的定义是在HttpApplication初始化的时候由静态方法执行的,且看一个普通的Asp.Net MVC项目的Global.asax
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)//定义路由表规则
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // 路由名称
"{controller}/{action}/{id}", // 带有参数的 URL
new { controller = "Home", action = "Index", id = UrlParameter.Optional } //参数默认值
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);//注册路由表
}
}
而路由表的规则的注册是在 Application_Start() 方法内部,那此时请求在URLRouteModule内部到路由表中的全部规则进行匹配,并把匹配的Controller的信息和Action的信息以及RouteData等信息都解析处理,而后将请求进一步交给:实现了IRouteHandler【实现了IHttpHandler接口】 的一个实例,下面是IRouteHandler的源码:
namespace System.Web.Routing
{
public interface IRouteHandler
{
IHttpHandler GetHttpHandler(RequestContext requestContext);
}
}
若是你想本身来实现这个接口而后在Web.Config中配置一下,那么请求就到了你本身的自定义的RouteHandler来执行后续的请求处理操做了。若是你使用的是默认的配置,那么请求会传递到MvcRouteHandler,那么请求f附加着HttpContext就会到达Asp.Net MVC的处理中了。
第五阶段:MvcRouteHandler建立Controller
请求到此,其实跟WebForm都是一致的,然后面才出现了一些不一样,此时请求才真正的进入System.Web.Mvc控制的领域内。后面全部的东西咱们均可以直接经过源码来介绍了,而上面的全部的请求处理只能经过反射等方式来看或者学习,然后面的内容,咱们能够幸福的直接看源码了。那就跟我走进它的管道怎么流动的吧...
接着上面讲,请求到了MvcRouteHandler类,而此类的源码以下:
namespace System.Web.Mvc
{
using System.Web.Routing;
using System.Web.SessionState;
public class MvcRouteHandler : IRouteHandler
{
private IControllerFactory _controllerFactory;
public MvcRouteHandler()
{
}
public MvcRouteHandler(IControllerFactory controllerFactory)
{
_controllerFactory = controllerFactory;
}
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
}
.....
}
MvcRouteHandler的GetHttpHandler方法被URLRouteModule调用,而看上面的红色源码部分咱们看到,它将请求上下文交给了MVCHandler,并返回了MVCHandler。
而我查看源码得知:MVCHandler实现了IHttpHandler,此时它的ProcessRequest方法被调用。且看MVCHandler的部分源代码:
1 public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
2 {
3 protected internal virtual void ProcessRequest(HttpContextBase httpContext)
4 {
5 SecurityUtil.ProcessInApplicationTrust(() =>
6 {
7 IController controller;
8 IControllerFactory factory;
9 ProcessRequestInit(httpContext, out controller, out factory);//初始化了ControllerFactory
10 try
11 {
12 controller.Execute(RequestContext);
13 }
14 finally
15 {
16 factory.ReleaseController(controller);
17 }
18 });
19 }
20 }
从源码中咱们得知:请求交给MVCHandler后,它首先从ControllerBuilder获取到当前的实现了IControllerFactory接口的ControllerFactory【也能够本身定义相关的CustomerControllerFactory,而后在Glable中注册使用】。而后根据上下文中请求的Controller的字符串信息建立出实现了IController接口的控制器。而后调用了上面代码中红色部分,也就是controller.Execute(RequestContext);那此时请求就交给了controller。
第六阶段:Controller调用Action返回ActionResult
因为此文过长,并且时间已经到了凌晨。源码我就不贴了,简单介绍一下流程,后面再作详细赘述。
Controller的Execute方法是在基类ControllerBase中的方法,而此方法又调用ExecuteCore方法,而后此方法内部执行以下代码:
string actionName = RouteData.GetRequiredString("action");
if (!ActionInvoker.InvokeAction(ControllerContext, actionName))
{
HandleUnknownAction(actionName);
}
首先从RouteData中获取Action的名字,而后调用ActonInvoker的InvokeAction方法,调用Action执行。Action的返回的ActionResult的ExecuteResult(controllerContext)方法被执行,那此时就出现了分叉。若是直接返回的非ViewResult的话,那就直接协会到Respose流了返回客户端了,若是是ViewResult的话,那就进入View的领域了。
第七阶段:View视图加载成Page类,并Render成Html
21 public override void ExecuteResult(ControllerContext context)
22 {
23 if (context == null)
24 {
25 throw new ArgumentNullException("context");
26 }
27 if (String.IsNullOrEmpty(ViewName))
28 {
29 ViewName = context.RouteData.GetRequiredString("action");
30 }
31 ViewEngineResult result = null;
32 if (View == null)
33 {
34 result = FindView(context);
35 View = result.View;
36 }
37 TextWriter writer = context.HttpContext.Response.Output;
38 ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer);
39 View.Render(viewContext, writer);
40 if (result != null)
41 {
42 result.ViewEngine.ReleaseView(context, View);
43 }
44 }
内部主要是经过ViewResult的FindView方法经过ViewEngine去加载具体的Aspx页面或者是cshtml页面生成对应的page类【针对Aspx】,而后再调用IView接口的Render方法将请求信息+ViewData的信息以等一块渲染成Html并写回到客户端。
在此阶段咱们发现IViewEngine内部的实现这是到规定路径下去加载Aspx页面生成对应的ViewPage类。
IView接口的Render方法才是真正的去将Html和数据装配的到一块。自此请求结束。
总结:
客户端请求→路由器→IIS服务器内核模块HTTP.SYS→IIS→AspNet_isapi.dll→Asp.Net Runtime→Application→IHttpModule....IHttpModule→MVCRouteModule→MVCRouteHandler→MVCHandler→ControllerFactory
→Controller→ActionInvoke→Aciton→ActiongResult.ExcuteReuslt()【若是是ViewResult】→IViewEngine FindView→IView Render→Response
最后附两张关于此请求管道的两张图,以飨读者。
引子
本文将主要演示怎么将多个Asp.Net MVC项目部署到一个IIS站点中的例子,主要使用的是Asp.Net MVC提供的区域的功能。
Asp.Net MVC提供了区域的功能,能够很方便的为大型的网站划分区域。可让咱们的项目不至于太复杂而致使管理混乱,有了区域后,每一个模块的页面都放入相应的区域内进行管理很方便。而随着项目的复杂,每一个开发人员开发的模块呢也多是一个完整的解决方案,而他要开发的UI项目呢只是主站点项目的一个区域,而若是把全部的UI项目放到一个UI项目,在团队开发时就不很方便了,而咱们想达到的效果是:每一个模块都对应一个UI项目【这里指Asp.Net MVC项目】,最后部署的时候将子项目都配置成区域,而总的项目就是一个站点。
1、项目建立
首先建立一个主Asp.Net MVC项目,而后建立一个子Asp。Net MVC项目。项目的结构以下:
注:
一、AreasDemo【子项目,做为主项目的一个Area】、MvcAppMain【主Web项目】都是普通的Asp.Net MVC3项目
二、MVCControllers是一个类库项目
三、补充:Asp.Net MVC的控制器:Controller是能够放到站点的任何DLL中的,它在搜索控制器时,会搜索站点下的全部DLL,当类符合条件:不是静态类,类名以Controller结尾,实现了Controller基类【其实最主要是IController接口】的条件时它就会被识别为控制器。因此咱们能够把控制器放到任何的其余项目中,只有将此控制器所在的DLL拷贝到、主站点的Bin目录或者对应的DLL目录就能够了。固然也能够放在默认的Web项目中的Controller文件夹下。
2、添加测试的Controller和Action
在子区域Web项目AreasDemo项目中添加一个Action,而后添加一个对应的视图
在主Web项目MvcAppMain中添加一个HomeController和相应的Index.cshtml视图文件。
在MVCAppMain项目中添加一个Admin区域,作测试使用。
项目最终截图为:
咱们看到,在主站点里添加了一个Admin区域后,默认建立了一个Areas文件夹,并且内部就是存放区域项目的页面。
3、在子项目中添加Areas Registration类
打开AreasDemo项目,添加一个AreasDemoRegistration类文件,其代码以下:
1 public class AreasDemoRegistration : AreaRegistration//在主站点注册区域
2 {
3 public override string AreaName
4 {
5 get { return "AreasDemo"; }
6 }
7 public override void RegisterArea(AreaRegistrationContext context)
8 {
9 context.MapRoute(
10 "AreasDemo_default",//路由名字,必须惟一
11 "AreasDemo/{controller}/{action}/{id}",//路由规则
12 new { action = "Index", id = UrlParameter.Optional }//默认值
13 );
14 }
15 }
其实就是一个普通的类,它实现了AreaRegistration基类。而后咱们注册区域路由就会在Global.asax的Application_Start事件方法中去执行注册到主站点的路由表中。具体
能够参考Global.asax中红色代码部分:
16 protected void Application_Start()
17 {
18 AreaRegistration.RegisterAllAreas();//注册全部区域
19 RegisterGlobalFilters(GlobalFilters.Filters);
20 RegisterRoutes(RouteTable.Routes);
21 }
至此咱们基本的测试的基础工做都作好了,下面就是到了部署阶段了。
4、部署咱们的项目
首先,咱们须要将子项目的引用到主项目中。而后咱们发布主项目到一个磁盘文件夹。而后,将子项目AreasDemo的Views文件夹拷贝到主项目发布后的文件夹对应的Areas\AreasDemo文件夹下。其中AreasDemo是areaname,此文件夹须要咱们本身手动建立。而后,观察发布后的bin目录下有没有AreasDemo.dll动态连接库【Web子项目】。
而后,咱们将此文件夹发布为IIS里的一个网站。最终演示效果为:
注:这是默认主Web的inde页面
注:这是主站点里添加的Admin区域
注:这是子项目action请求返回的页面