MVC运行机制

ASP.NET是一种建立动态Web应用程序的技术。它是.NET框架的一部分,可以使用任何.NET兼容的语言编写ASP.NET应用程序。相对于Java、PHP等,ASP.NET具有方便性、灵活性、性能优、生产效率高、安全性高、完整性强及面向对象等特性,是目前主流的网络编程技术之一.它可以让开发者快速高效的创建应用程序而不必关注Http,Html,Javascript等底层的详细信息。随着越来越多的企业将网站升级为APS.NET,web应用程序的复杂性不断增加,即使当初MS推出ASP.NET的时候,如何强调codebehind的优势,但是它在架构上回避不了一个问题:如果全部codebehind的话,.cs势必需要担负起同时处理事务逻辑和视图各方面属性的任务。一个是对逻辑的控制,另一个是对视图(服务器控件)的控制。 当预期超过了一定的阈值可能使的Web Forms不再是一种最佳的选择。由于它牺牲掉一个优秀模型的某些方面,比如可维护性,可读性,可测试性以及对HTMl的可控性,越来越多的人认为ASP.NET Web Forms model不太理想。

ASP.NET MVC是创建APS.NET应用的一种新的平台,和传统的 Web Forms一样运行于ASP.NET run-time environment。MVC专注于用户浏览页面时的行为,并且具有不同的试图引擎来控制生成的标记。在标准的ASP.NET runtime environment之上,MVC创建了自身的shell,该shell一端与ASP.NET run-time objects(例如:request,HttpContext)相连,另一端将它本身对象集暴露给内部的组件,非常有意思的是对象集是被注入到ASP.NET MVC runtime shell中的。因此它具有很高的可测试性。MVC是怎么样处理请求的呢,再这之前,我们先了解下ASP.NET的运行机制。

 

I:The ASP.NET Runtime 机制

从开发者的角度来看,Web Forms和MVC是两种不同的开发框架,但是它们具有很多共同点。特别是它们运行于相同的运行时环境:the standard ASP.NET runtime environment.

通常来讲,运行时环境就是一组托管在Web服务器上的组件,用来处理到达服务器的Request并向客户端返回Response。ASP.NET Web Forms and ASP.NET MVC运行时机制是相同的,这就意味着ASP.NET pages and ASP.NET MVC可以同时存在于一个应用程序中。尽管底层机制是相同的,但是ASP.NET MVC and a Web Forms处理request的步骤却完全不同。MVC在ASP.NET runtime之上有自己的run-time shell并且实现自己的管线来处理它获得的请求。

我们通过创建虚拟目录将资源HostIIS下,原则上,我们可以通过IIS访问置于虚拟目录下的所有Resource,这部仅仅包含一些静态资源文件,比如图片、纯Html文件、CSSJS等等,也包含一些需要动态执行的文件,比如aspxasmx等等,我们还可以将RemotingWCF Service HostIIS下。对于这些静态的文件,IIS直接提取对应的文件将其作为Http Response返回给Client,但是对于这些需要进一步处理的动态执行的文件,IIS必须将Request进一步传递给对应的处理程序,待处理程序执行完毕获得最终的Http Response通过IIS返回给Client。对于IIS来说,这些处理程序通过ISAPI Extension来体现。对于基于ASP.NETResource,其对应的ISAPI ExtensionASP.NET ISAPI,通过一个aspnet_isapi.dll承载。IISMetadata database维护着一个称为ISAPI Extension Mapping的数据表,负责将不同类型的Resource影射到对应的ISAPI Extension.

IIS5.x

在IIS5.x中,web服务和ASP.NET应用是分离开来的。IIS5.x运行在一个inetinfo.exe的进程中,InetInfo.exe是一个Native Executive,并非一个托管的程序。IIS分析Request的目标资源文件的扩展名(这里是aspx),通过ISAPI Extension Mapping获知对应的ISPAIASP.NET ISAPI,于是加载aspnet_isapi.dll。到此为止,该Request的处理交由ASP.NET ISAPI处理。ASP.NET ISAPI会创建一个叫做aspnet_wp.exeWorker Process(如果该进程不存在的话),在aspnet_wp.exe初始化的时候会加载CLR,从而为ASP.NET Application创建一个托管的运行环境,CLR初始化的使用会加载两个重要的dllAppManagerAppDomainFactoryISAPIRuntime。通过AppManagerAppDomainFactoryCreate方法为Application创建一个Application Domain;通过ISAPIRuntimeProcessRequest处理Request,进而将流程拖入到ASP.NET Http Runtime Pipeline的范畴,ASP.NET Http Runtime PipelineHttp Request的处理是一个相对复杂的过程,相关的介绍会放在本篇文章的下一部份。在这里我们可以把它看成是一个黑盒,它接管Request,最终生成Html

当IIS5.x运行的进程和asp.net运行的进程相互独立时,它们之间采用了Named Pipe来进行通讯。IIS5.x主要应用在Windows XP或Windows 2000操作系统中。

    • 1. 首先,同一台主机上再同一时间只能运行一个aspnet_wp进程,每个基于虚拟目录的ASP.NET Application对应一个Application Domain,也就是说每个Application都运行在同一个Worker Process中,Application之间的隔离是基于Application Domain的,而不是基于Process的。
    • 2. 其次,ASP.NET  ISAPI不但负责创建aspnet_wp Worker Process,而且负责监控该进程,如果检测到aspnet_wp的Performance降低到某个设定的下限,ASP.NET  ISAPI会负责结束掉该进程。当aspnet_wp结束掉之后,后续的Request会导致ASP.NET ISAPI重新创建新的aspnet_wp Worker Process。
    • 3. 最后,由于IIS和Application运行在他们各自的进程中,他们之间的通信必须采用特定的通信机制。本质上IIS所在的InetInfo进程和Worker Process之间的通信是同一台机器不同进程的通信(local interprocess communications),处于Performance的考虑,他们之间采用基于Named pipe的通信机制。ASP.NET ISAPI和Worker Process之间的通信通过他们之间的一组Pipe实现。同样处于Performance的原因,ASP.NET ISAPI通过异步的方式将Request 传到Worker Process并获得Response,但是Worker Process则是通过同步的方式向ASP.NET ISAPI获得一些基于Server的变量。

IIS6.0

    • IIS6.0引入了应用程序池(Application Pool)的概念。IIS6.0先通过组件http.sys来接收客户端的请求,http.sys接收到一个基于aspx的http request,然后它会根据IIS中的Metabase查看该基于该Request的Application属于哪个Application Pool,如果该Application Pool不存在,则创建之。否则直接将request发到对应Application Pool的Queue中。我上面已经说了,每个Application Pool对应着一个Worker Process:w3wp.exe,毫无疑问他是运行在User Mode下的。在IIS Metabase中维护着Application Pool和worker process的Mapping。WAS(Web Administrative service)根据这样一个mapping,将存在于某个Application Pool Queue的request 传递到对应的worker process(如果没有,就创建这样一个进程)。在worker process初始化的时候,加载ASP.NET ISAPI,ASP.NET ISAPI进而加载CLR。最后的流程就和IIS 5.x一样了:通过AppManagerAppDomainFactory的Create方法为Application创建一个Application Domain;通过ISAPIRuntime的ProcessRequest处理Request,进而将流程进入到ASP.NET Http Runtime Pipeline。
    • 每个应用程序池对应着一个w3wp.exe的工作进程。在IIS Metabase 中维护着应用程序池和工作进程的对照。WAS(Web Administrative service)根据这样一个对照,将存在于某个应用程序池队列的客户端请求传递到对应的工作进程(如果没有,就创建这样一个进程)。在工作进程初始化的时候,加载ASP.NET ISAPI,ASP.NET ISAPI 进而加载CLR。最后的流程就和IIS 5.x一样了:通过AppManagerAppDomainFactory的Create方法为Application创建一个Application Domain;通过ISAPIRuntime的ProcessRequest处理Request,进而将流程进入到ASP.NET Http Runtime Pipeline(Pipeline是接下来要关注的重点)。
    • IIS6.0主要应用在Windows 2003 server操作系统中。

IIS7.0

    •  IIS7.0完全整合了.NET。ASP.NET从ISAPI Extension的角色进入了IIS的核心。IIS7.0处理请求的流程如下:
    • 当客户端浏览器开始HTTP请求一个WEB服务器的资源时,HTTP.sys 拦截到这个请求。 
    • HTTP.sys联系WAS向配置存储中心获取信息。
    • WAS 向配置存储中心请求配置信息。applicationHost.config。 
    • WWW 服务接受到配置信息,配置信息指类似应用程序池配置信息,站点配置信息等等。 
    • WWW 服务使用配置信息去配置 HTTP.sys 处理策略。 
    • WAS为请求的应用程序池启动一个工作进程。
    • 工作进程处理请求,并把回复信息返回给HTTP.sys。
    • 客户端接受到处理结果信息。

 

II:HTTP运行期

HTTP运行期处理客户端应用程序(例如Web浏览器)进入的一个Web请求,通过处理它的应用程序的适当组件路由请求,然后产生响应并发回提出请求的客户端应用程序。

进入的HTTP Web请求最先由IIS Web服务器接收到,它在此请求基于ASP.NET已注册处理的扩展名传送到ASP.NET ISAPI上。

HTTP运行期首先创建一个HttpContext对象的实例,它包含了当前正在处理的请求信息,接着创建在处理逻辑中涉及到的所有其他组件都可以使用的上下文对象。HttpContext实例提供了对请求对象(HttpRequest类的实例)和响应对象(HttpResponse类的实例)的访问。

HttpContext

HttpContext体现当前Request的上下文信息,它的生命周期知道整个Request处理结束或者处理超时。通过HttpContext对象我们可以访问属于当前Request的一系列常用的对象:ServerSessionCacheApplicationRequestResponseTraceUserProfile等等。此外我们可以认为将一些数据放在Items属性中作为状态管理的一种方式,不过这种状态管理和其他一些常用的方式,比如SessionCacheApplicationCookie等,具有根本性的不同之处是其生命周期仅仅维持在当前RequestContext中。

HttpApplication

就像其名称体现的一样,HttpApplication基本上可以看成是真个ASP.NET Application的体现。HttpApplication和置于虚拟根目录的Gloabal.asax对应。通过HttpApplicationFactory.GetApplicationInstance创建一个基于Gloabal.asaxHttpApplication对象。在HttpApplicationFactory.GetApplicationInstance方法返回创建的HttpApplication对象之前,会调用一个名为InitInternal的内部方法,该方法会做一些列的初始化的操作,在这些初始化操作中,最典型的一个初始化方法为InitModules(),该方法的主要的目的就是查看Config中注册的所有HttpModule,并根据配置信息加载相应的Assembly,通过Reflection创建对应的HttpModule,并将这些Module加到HttpApplication _moduleCollection Filed中。

HttpApplication本身并包含对Request的任何处理,他的工作方式是通过在不同阶段出发不同Event来调用我们注册的Event Hander

 HttpModule

我们上面提到HttpApplication就是一个ASP.NET Application的体现,HttpApplication本身并不提供对Request的处理功能,而是通过在不同阶段出发不同的Event。我们能做的只能是根据我们具体的需求将我们的功能代码作为Event Handler注册到需要的HttpApplication Event上面。注册这些Event Handler,我们首先想到的肯定就直接在HttpApplication对应的Global.asax中定义我们的EventHandler好了。这是最直接的办法,而且Global.asax提供一个简洁的方式是我们的实现显得简单:不需要向一般注册Event一样将Delegate添加到对应的Event上面,而是直接通过方法名称和对应的Event匹配的方式直接将对应的方法作为相关的Event Handler。比如Application_ AcquireRequestState就是AcquireRequestState Event handler

但是这种方式在很多情况下却达不到我们的要求,更多地,我们需要的是一种Plug-in的实现方式:我们在外部定义一些Request Processing的功能,需要直接运用到我们的Application之中。通过使用HttpModule封装这些功能模块,并将其注册到我们的Application的发式可以很简单的实现这种功能。更多内容参考:http://www.cnblogs.com/jyan/articles/2563794.html

HttpHandler

如果说HttpModule关注的是所有Inbound Request的处理的话,Handler确实关注基于某种类型的ASP.NET ResourceRequest。比如一个.apsxWeb Page通过一个System.Web.UI.Page来处理。HttpHandler和他所处理的Resource通过Config中的system.web/handlers section来定义.需要注意的是,我们不但可以单纯地定义一个实现了System.Web.IHttpHandlerType,也可以定义一个实现了System.Web.IHttpHandlerFactory TypeSystem.Web.UI.Page是一个典型的Httphandler,相信对此大家已经很熟悉了。在最后还说说另一个典型的HttpHandlerSystem.Web.HttpForbiddenHandler,从名称我们不难看出,它用于那些禁止访问的Resource,现在应该知道了为了Global.asax不同通过IIS访问了吧.

更多内容参考:http://www.cnblogs.com/jyan/articles/2563801.html

 

 III:MVC请求过程

 

 

 图中可以初步看出一个HttpRequest是如何被ASP.NET和ASP.NET MVC框架执行的:经过IIS和ASP.NET处理后,Core Routing会首先根据URL匹配物理路径上的文件,如果不能匹配则由核心路由模块执行路由,路由被匹配后,MvcRouteHandler会将这个请求“带入”MVC框架,执行Controller和Action,Action可以直接注入response,或者更平常的是返回一个ActionResultActionResultExecutedResult方法将被调用,如果是个ViewResult(继承自ActionResult),则会使用WebFormViewEngine转化一个Html,并注入到response。

下图是Web Forms与MVC中运行时环境,可以看出,MVC运行时环境是原始ASP.NET运行时环境的定制版本。

 

 

[参考]http://www.cnblogs.com/artech/archive/2007/09/13/891266.html

posted @ 2012-06-29 17:08  Johnny Yan  阅读(11330)  评论(4编辑  收藏  举报