`
izuoyan
  • 浏览: 8937102 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

从.NET类库代码来看ASP.NET运行时

阅读更多
  写在前面的话:网上讲ASP.net运行模式的好文章已经很多了,笔者本不用多此一举,另成一文。但从笔者自己的学习经验看,如果学到的这些知识不能对应到类库中的源代码,印象总归不够深刻,大有隔靴搔痒之感。只好自己写 三.ASP.net运行时,我们等待已久的纯托管代码环境

  好,经过上面长久的铺垫,我们终于进入了托管代码的领域。经过前面的内容,我们知道,在托管代码中首先被执行的是一个ISAPIRuntime对象的ProcessRequest方法,那么下面我们就来看一看这个方法主要做了些什么:

1/**//*ISAPIRuntime的方法,处理请求的入口。*/
2public int ProcessRequest(IntPtr ecb, int iWRType)
3{
4 try
5 {
6 /**//*这里ecb被作为参数传入,返回一个HttpWorkerRequest类型的对象,作为对一个请求的数据的封装。但HttpWorkerRequest
7 *只是一个抽象基类,CreateWorkerRequest作为一个工厂方法,返回的实际类型是ISAPIWorkerRequestInProc,
8 *ISAPIWorkerRequestInProcForIIS6或ISAPIWorkerRequestOutOfProc。这些类型里面提供的方法,其实大多
9 *围绕着如何从ecb中去获取数据,所以都包含了很多对System.Web.UnsafeNativeMethods类型中静态方法的调用。
10 **/
11 HttpWorkerRequest wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, iWRType);
12 string appPathTranslated = wr.GetAppPathTranslated();
13 string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal;
14 if ((appDomainAppPathInternal == null) || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal))
15 {
16 /**//*从这里开始,对请求的处理流程就交给了HttpRuntime。需要注意的是,ISAPI是多线程的,而且对ProcessRequest的调用是异步的,
17 *这就要求HttpRuntime.ProcessRequest方法是线程安全的。看一看HttpRuntime.ProcessRequestNoDemand里的代码大家就清楚,
18 *所有的请求会被排成一个队列,顺次执行,保证了并发安全。
19 *最终,HttpRuntime.ProcessRequestInternal方法会被调用,我们接下来就去看看那个方法。
20 **/
21 HttpRuntime.ProcessRequestNoDemand(wr);
22 return 0;
23 }
24 HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString("Hosting_Phys_Path_Changed", new object[] { appDomainAppPathInternal, appPathTranslated }));
25 }
26 catch (Exception exception)
27 {
28 Misc.ReportUnhandledException(exception, new string[] { SR.GetString("Failed_to_process_request") });
29 throw;
30 }
31 return 1;
32}

  上面的代码段最主要的作用就是调用了HttpRumtime.ProcessRequestInternal方法,下面我们就一起来看看这个方法的实现:

1/**//*在HttpRuntime.ProcessRequestInternal()方法里,有如下几个重要的对象被创建出来:
2 *(1)HttpContext(包括其中的HttpRequest,HttpResponse)
3 *(2)HttpApplication
4 *同时,会执行HttpApplication对象的ProcessRequest方法,
5 */
6private void ProcessRequestInternal(HttpWorkerRequest wr)
7{
8 /**//*HttpContext对象在这里被创建。HttpWorkerRequest做为构造参数,而HttpWorkerRequest本身
9 *又围绕着对ecb的处理建立了一群高层的方法,它的实例会被HttpContext传给HttpRequest和HttpResponese
10 *做为他们的构造参数。所以,这里也能更清楚地看出HttpWorkerRequest作为ecb的托管环境封装器的实质。
11 *另外,这里也能清楚地反映出,每一个请求都有一个自己的HttpContext对象(而每一个Htt瀀?潳楬?pContext对象都管理着
12 *一个HttpSession对象--参见HttpContext的Session属性,这也就保证了每个访问者有自己的session对象。),你可以
13 *使用HttpContext.Current来访问到这个对象。
14 */
15 HttpContext extraData = new HttpContext(wr, false);
16 wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, extraData);
17 Interlocked.Increment(ref this._activeRequestCount);
18 HostingEnvironment.IncrementBusyCount();
19 try
20 {
21 try
22 {
23 this.EnsureFirstRequestInit(extraData);
24 }
25 catch
26 {
27 if (!extraData.Request.IsDebuggingRequest)
28 {
29 throw;
30 }
31 }
32 extraData.Response.InitResponseWriter();
33 /**//*用应用程序工厂返回一个HttpApplication对象。
34 *和线程池对线程的管理相似,HttpApplicationFactory中以stack维护了一个HttpApplication的列表(参见HttpApplicationFactory
35 *的_freeList变量)。在这句方法调用的最后,实际是调用了 _theApplicationFactory.GetNormalApplicationInstance(context),
36 *里面就是从_freeList的栈顶pop出一个已经构造的HttpApplication实例。
37 *所以,对于每一个请求,由HttpContext作为上下文,由一个HttpApplication对象来控制整个应用处理的pipeline,整个
38 *处理过程是在由工作进程管理的线程池中的某个线程内完成的。
39 *另外,在一个应用程序域内,由于可以同时处理多个请求,所以就有多个HttpApplication实例和多个活动线程(您可以使用windbg的sos
40 *扩展来观察它们之间的关系,本文就不继续深入了)。
41 *还有,对所有HttpModules的加载就是发生在HttpApplication对象的创建过程之中(包括系统已经提供的Authentication等模块儿和我们
42 *的自定义模块)。我们可以在Web.config里声明自己的自定义模块。这些模块的作用就是在整个HttpApplication处理管线的相关事件点上,
43 *挂上自己的处理。注意一下IHttpModule接口的Init()方法的声明,这个方法的传入参数就是要被创建的HttpApplication对象,所以,如果
44 *你自己的模块想在缓存读取上加入一些自定义操作,你只需进行如下处理即可:
45 public class YourCustomModule : IHttpModule
46 {
47 public void Init(HttpApplication application)
48 {
49 application.ResolveRequestCache += new EventHandler(this.YourCustomResolveRequestCache);
50 }
51 }
52 *另外,通过对HttpApplicationFactory.GetApplicationInstance方法内部实现方式的阅读,你会发现在每一个HttpApplication对象被创建
53 *之后,会立刻调用这个对象的InitInternal方法,而这个方法里面做了很多重要的初始化操作,内容较多,我们将在下文中单独介绍。
54 *
55 */
56 IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(extraData);
57 if (applicationInstance == null)
58 {
59 throw new HttpException(SR.GetString("Unable_create_app_object"));
60 }
61 if (EtwTrace.IsTraceEnabled(5, 1))
62 {
63 EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, extraData.WorkerRequest, applicationInstance.GetType().FullName, "Start");
64 }
65 /**//*看一下System.Web.HttpApplication的类型声明
66 *public class HttpApplication : IHttpAsyncHandler, IHttpHandler, IComponent, IDisposable
67 *你会发现它同时实现了同步和异步的IHandler,所以在默认情况下,ASP.NET对请求的处理是异步的。
68 */
69 if (applicationInstance is IHttpAsyncHandler)
70 {
71 IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
72 alt="" src="/imagelist/2007/236/b2dc295wopin.gif" align=top> extraData.AsyncAppHandler = handler2;
73 /**//*BeginProcessRequest会调用HttpApplication的ResumeSteps()方法,在ResumeSteps()中完成了整个应用程序周期的所有操作,
74 *包括对所有事件的触发、执行,对Handler的调用等。后文会有专门的小节进行介绍。*/
75 handler2.BeginProcessRequest(extraData, this._handlerCompletionCallback, extraData);
76 }
77 else
78 {
79 applicationInstance.ProcessRequest(extraData);
80 this.FinishRequest(extraData.WorkerRequest, extraData, null);
81 }
82 }
83 catch (Exception exception)
84 {
85 extraData.Response.InitResponseWriter();
86 this.FinishRequest(wr, extraData, exception);
87 }
88}

  从上面的代码可以看出,HttpApplication对象是经由HttpApplicationFactory.GetApplicationInstance(并最终调用HttpRuntime.CreateNonPublicInstance)创建的。HttpApplicationFactory对象会先解译目录中的Global.asax文件,接着加载虚拟目录内的Application Assembly(Global.dll),而后合并两者创建出一个Ghost Application Class,最后编译此Class后取得对象实例后返回至HttpR瀀?潳楬?untime对象,这个对象实例就是HttpApplication对象。解译与编译.asax文件的动作只发生于此虚拟目录第一次处理用户要求,或是Global.asax、Global.dll在前次执行后又做了变动时。至于HttpRuntime.CreateNonPublicInstance方法内部到底是通过什么样的代码,先后调用了Parser和Compiler,根据machine.config和web.config来生成一个HttpApplication对象,笔者未能通过自己的努力追踪到,盼高手补充。

  总之,通过machine.config及web.config档中对<httpHandlers>节点的说明,HttpApplication可以从HanlerFactory的缓存池HandlerFactoryCache中,根据当前请求的页面类型名称,获得所需的HandlerFactory。对于我们通常的aspx页面,返回的将是PageHandlerFactory(HttpApplication通过GetFactory()方法返回它),而PageHandlerFactory将接下来完成对所请求页面的Parser,并返回一个代表所请求页面的Page类:

Page page = BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(Page), context, true, true) as Page;

  上面这句代码是被PageHandleFactory的GetHandle方法间接调用的(大家可以从HttpApplication.MapHttpHandler方法),调用返回的page对象是一个非常非常关键的实例(具体的方法调用过程中应当包括了对PageParser和PageBuilder的调用,望高手补充),因为它就是我们普通的aspx页面处理流程中,那个扮演着IHttpHandler的角色!也正因为此,我们在HttpRuntime.ProcessRequestInternal()方法里看到的applicationInstance.ProcessRequest(extraData)调用,实际上是调用的一个System.Web.UI.Page类型实例的ProcessRequest方法,整个执行流也因此进入了Page.ProcessRequestMain()的里面。我们平常所说的一个页面的生命周期的若干事件,您只要好好看看这个方法的实现,就都能明白了。由于这个方法估计是大家平时看的比较多的,对之也比较熟悉,本文这里就不多解释了。

  四.HttpApplication的事件机制

  到上面介绍的内容为止,整个处理流程基本上就讲完了。但如果只介绍到这里,恐怕大家对HttpApplication, IHttpModule, IHttpHandler三者的关系还是不太清楚。一个请求过程的所有事件(如BeginRequest、AuthenticateRequest等)是如何被触发的?如何通过自己的自定义Module来处理这些事件?Handler的处理又是在什么位置切入的?其实,这一切都是以HttpApplication内置的事件机制为核心的,下面就让我们来一步步揭示出它的实现方式(对.net中事件机制不够熟悉的读者可以先参看笔者另一篇文章:Part I of Events in Asp.Net: Events in .Net)。这里面涉及的事件很多,我们就以BeginRequest这个事件为例来说明:

  (1)首先当然是要对事件本身进行定义

public event EventHandler BeginRequest

  (2)由于有不止一个事件,为了方便对所有事件的管理,给每一个事件定义了一个唯一key,用作在事件容器中查找指定事件的标志

private static readonly object EventBeginRequest;

  (3)把对一个个事件触发定义为一个个“执行步骤的执行”,下面是对“执行步骤”这个接口的定义

internal interface IExecutionStep
{
// 每一个“执行步骤”的执行命令
void Execute();
// Properties
bool CompletedSynchronously { get; }
bool IsCancellable { get; }
}

  这里为什么要看似多此一举把对事件的触发再做一层封装,封装为所谓“执行步骤”,在后文介绍。

  (4)用一个数组来保存所有的执行步骤。可以想象,真正到执行的时候,从数组里取出每一项IExecutionStep,再执行其Execute()方法即可。而Execute里面,肯定是对事件委托链的调用无疑
private IExecutionStep[] _execSteps;

  (5)HttpModule在它初始化的时候完成对事件的注册。在创建HttpApplication对象的时候,会调用这个对象的InitInternal方法,这个方法内部会调用InitModules() ,它用来初始化和这个应用有关的所有HttpModule,而在这个方法里面,最重要的就是调用了每个Module的Init()方法。那么,如果我们有一个自定义的HttpModule,并希望这个Module去响应BeginRequest事件,我们应该这样定义自己的Module的Init()方法:

public void Init(HttpApplication application)
{
application.BeginRequest += new EventHandler(this.YourCustomMethodForBeginRequestEvent);
}

  这样就完成了注册。当然,此时事件还没有执行,你也还没有看到事件和执行步骤的关系,HttpHandler也尚未登场。

  (6)前面提到过所谓“执行步骤”,让我们先来看两个在HttpApplication中被定义的IExecutionStep,因为这两个执行步骤中所完成的,一个是对HttpHandler的解析和实例化,一个是调用HttpHandler的ProcessRequest方法,他们分别是MapHandlerExecutionStep和CallHandlerExecutionStep。读者可以自己去读一下这两个类的Execute()方法的代码,当您第一次从CallHandlerExecutionStep.Execute()中看到handler.ProcessRequest(context)和handler2.BeginProcessRequest(context, this._completionCallback, null)这样的调用语句,从MapHandlerExecutionStep.Execute()中看到context.Handler = this._application.MapHttpHandler(context, request.RequestType, request.FilePathObject, request.PhysicalPathInternal, false);这样的调用语句,一定会有一种“哎呀,原来你们在这里”的快感,呵呵。

  (7)对“执行步骤数组”的初始化操作,就放在HttpApplication.InitInternal()方法里,具体就是下面这段语句:

  执行步骤的初始化:

1this.CreateEventExecutionSteps(EventBeginRequest, steps);
2this.CreateEventExecutionSteps(EventAuthenticateRequest, steps);
3this.CreateEventExecutionSteps(EventDefaultAuthentication, steps);
4this.CreateEventExecutionSteps(EventPostAuthenticateRequest, steps);
5this.CreateEventExecutionSteps(EventAuthorizeRequest, steps);
6this.CreateEventExecutionSteps(EventPostAuthorizeRequest, steps);
7this.CreateEventExecutionSteps(EventResolveRequestCache, steps);
8this.CreateEventExecutionSteps(EventPostResolveRequestCache, steps);
9steps.Add(new MapHandlerExecutionStep(this));
10this.CreateEventExecutionSteps(EventPostMapRequestHandler, steps);
11this.CreateEventExecutionSteps(EventAcquireRequestState, steps);
12this.CreateEventExecutionSteps(EventPostAcquireRequestState, steps);
13this.CreateEventExecutionSteps(EventPreRequestHandlerExecute, steps);
14steps.Add(new CallHandlerExecutionStep(this));//从这里,您可以很容易看到Handler对页面的解蚊
15 //有哪些应用程序事件在它之前,哪些在它之后
16this.CreateEventExecutionSteps(EventPostRequestHandlerExecute, steps);
17this.CreateEventExecutionSteps(EventReleaseRequestState, steps);
18this.CreateEventExecutionSteps(EventPostReleaseRequestState, steps);
19steps.Add(new CallFilterExecutionStep(this));
20this.CreateEventExecutionSteps(EventUpdateRequestCache, steps);
21this.CreateEventExecutionSteps(EventPostUpdateRequestCache, steps);
22this._endRequestStepIndex = steps.Count;
23this.CreateEventExecutionSteps(EventEndRequest, steps);
24steps.Add(new NoopExecutionStep());
25this._execSteps = new IExecutionStep[steps.Count];
26steps.CopyTo(this._execSteps);//把整个数组拷贝给HttpApplication的私有变量_execSteps

  这种执行步骤是和事件无关的,只是在整个事件流中的特定位置执行一些特定的操作(实例化Handler之类的)。

  而另一种是要把相关事件的处理方法列表添加到步骤中,每一个步骤其实是对一个事件的处理:

this.CreateEventExecutionSteps(EventBeginRequest, steps);

  (8)执行步骤数组的执行。

  其实在前文中已经提到过了,整个执行步骤数组的执行,是在HttpApplication.ResumeSteps()中调用的。恐怕就算不看这个方法的代码,大家也能想象得出,它是遍历整个执行步骤数组,然后调用其中每一项的Execute方法。这里大家大概也就清楚了,为什么会有一个执行步骤的概念。以笔者看来,首先它在概念上很好理解,完全贴合整个应用处理的管道模型pipeline;第二它屏蔽了引发事件的应用执行步骤和普通的内置执行步骤之间的差别;第三是比较容易在以后对整个流程进行改进和扩展。

  本文结束,但对Asp.net运行时和.Net框架的探究其实只是刚刚开了个头。

  查阅关于 ASP.NET 的全部文档
分享到:
评论

相关推荐

    ASP.NET的网页代码模型及生命周期

    代码隐藏页模型与单文件页模型不同的是,代码隐藏页模型将事物处理代码都存放在cs文件中,当ASP.NET网页运行的时候,ASP.NET类生成时会先处理cs文件中的代码,再处理.aspx页面中的代码。这种过程被成为代码分离。 ...

    如何在ASP.NET Core类库项目中读取配置文件详解

    主要给大家介绍了关于如何在ASP.NET Core类库项目中读取配置文件的相关资料,这是朋友提的一个问题,文中通过示例代码介绍的非常详解,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起...

    asp.net知识库

    深入剖析ASP.NET组件设计]一书第三章关于ASP.NET运行原理讲述的补白 asp.net 运行机制初探(httpModule加载) 利用反射来查看对象中的私有变量 关于反射中创建类型实例的两种方法 ASP.Net应用程序的多进程模型 NET委托...

    Asp.net MVC2 Preview2

    ASP.NET MVC Framework是微软在ASP.NET中所添加的一组类别库,这组类库可利用Model-View-Controller的设计模式来开辟ASP.NET的应用法度圭臬标准    Model:包含数据、验证规定规矩、数据接见和营业逻辑等应用...

    ASP.NET Core开发教程之Logging利用NLog写日志文件

    本文主要介绍了ASP.NET Core 开发-Logging 使用NLog 写日志文件的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 NLog 可以适用于 .NET Core 和 ASP.NET Core 。 ASP.NET Core已经内置...

    《精通c#(第6版)》

    全书分为八部分:C# 和.NET 平台、C# 核心编程结构、C# 面向对象编程、高级C# 编程结构、用.NET 程序集编程、.NET 基础类库、WPF 和ASP.NET Web Forms。第6 版是对第5 版的进一步更新和完善,内容涵盖了最先进的.NET...

    Asp.Net实现FORM认证的一些使用技巧(必看篇)

    最近因为项目代码重构需要重新整理用户登录和权限控制的部分,现有的代码大体是参照了.NET的FORM认证,并结合了PORTAL KITS的登录控制,代码比较啰嗦,可维护性比较差。于是有了以下的几个需求(大多数系统应该都会...

    C#微软培训资料

    2.2 公用语言运行时环境与公用语言规范.13 2.3 开 发 工 具 .17 2.4 小 结 .19 第三章 编写第一个应用程序 .20 3.1 Welcome 程序 .20 3.2 代 码 分 析 .20 3.3 运 行 程 序 .23 .4 添 加 注 释 .25 ...

    [ASP.NET Ajax] ECMAScript基础类以及Asp.net Ajax对类lt;Objectgt;的扩展

    由于我对JavaScript和薄弱,所以在看Asp.net Ajax类库的同时也翻出了ECMAScript顺便学习学习。在.NET中所有类都是继承自Object,同样在JS中也一样说有都继承Object,因此如果对Object进行了扩展,那所有继承Object类...

    c#学习笔记.txt

    另外我发现论坛上学习Java的人都非常的有个性,当有人问起学习哪种语言更好时,他会打出几百个“JAVA”来,填满整个屏幕,也不说是为了什么。我觉得这样做未免有些太霸道了,如果你说这叫偏执狂我也不反对,虽然我...

    JAVA上百实例源码以及开源项目源代码

     Java二进制IO类与文件复制操作实例,好像是一本书的例子,源代码有的是独立运行的,与同目录下的其它代码文件互不联系,这些代码面向初级、中级Java程序员。 Java访问权限控制源代码 1个目标文件 摘要:Java源码,...

    蓝丽留言版

    修复的漏洞和改进的程序,LD4操作不便以及明显存在漏洞的地方现进行了全面的升级及修复,尽量使其达到目标要求除了LD4中已支持了11种语言类库,现预计开发Asp.net,Jsp,Java类库,其中Asp.net语言库已开始制作短期内...

    ExtAspNet v2.2.1 (2009-4-1) 值得一看

    ExtAspNet v2.2.1 ExtAspNet是一组专业的Asp.net控件库,拥有原生的AJAX支持和丰富的UI效果, 目标是创建没有JavaScript,没有... -在Page_Load中设置了哪些需要在AJAX中更新的Asp.net控件会在回发时保持状态,可以...

    JAVA上百实例源码以及开源项目

     Java二进制IO类与文件复制操作实例,好像是一本书的例子,源代码有的是独立运行的,与同目录下的其它代码文件互不联系,这些代码面向初级、中级Java程序员。 Java访问权限控制源代码 1个目标文件 摘要:Java源码,...

    ExtAspNet_v2.3.2_dll

    ExtAspNet - ExtJS based ASP.NET Controls with Full AJAX Support ExtAspNet是一组专业的Asp.net控件库,拥有原生的AJAX支持和丰富的UI效果, 目标是创建没有ViewState,没有JavaScript,没有CSS,没有...

    算法导论中文版

    不了解汇编,编译原理,怎么知道程序运行时要多长时间要多少内存,就不能编出高效的代码。 如果没有学好基础一开始就去学.net,java这些越往后就会觉得越吃力,它们涉及的技术太多了,而且不但 在更新,对于三层啊,...

    ExtJs 2.0 与sql server 增加 删除 过滤功能的实现 事例

    之前在网上看了不少的ExtJs文章,对...新加的这个功能要实现对分行信息的增、删、改、查,如果这个用常规的asp.net方式来开发,我想对大家来说,这个是家常便饭,现在我们用ExtJs+WCF来实现。 先来一张最终的效果图: ...

    微信公众号平台接口开发 获取access_token过程解析

    新建Asp.net MVC 4.0项目 WeChatSubscript是项目UI层 WeChatTools是封装操作访问公众号接口的一些方法类库 获取AccssToken 我们要的得到AccessToken,这是所有接口访问的基础,我们看看官方给出的接口调用文档 很...

Global site tag (gtag.js) - Google Analytics