sen

导航

[Asp.net] HttpApplication管线研究

Posted on 2010-04-22 16:35  sen  阅读(934)  评论(1编辑  收藏  举报

转自 Web开发学习心得6——HttpApplication管线研究 

经过Asp.net设计思想的研究,我们对HttpApplication的管线已经有了一个本质的了解。所谓管线,实际上就是生产流水线,由一系列的步骤所组成,而HttpContext,就是这条流水线上待加工的产品。现在,我们来对这条生产流水线进行更进一步的了解。

      首先请看ApplicationStepManager.BuildSteps方法。

1、ValidatePathExecutionStep负责对请求的路径进行安全检查,禁止非法路径访问。深入到该类的Execute方法我们发现,该类直接调用HttpContext.ValidatePath方法,而后一个方法在检测到非法路径时,直接抛出HttpException。

2、UrlMappingsExecutionStep负责对Web.Config中设置的<urlMappings>配置节进行应用,将某一url映射为一个新的url地址。深入该类的Execute方法我们发现,该类在检测到需要url映射时,调用HttpContext.RewritePath方法实现地址重写。

3、引发BeginRequest事件:该步骤没啥好说的,就是通知开始处理请求了。然而,我在查阅有哪些Module订阅该事件时,发现该事件被UrlMappingsModule订阅,而该Module处理的事情跟UrlMappingsExecutionStep一模一样。我很纳闷,这不多此一举吗?

4、引发AuthenticateRequest、DefaultAuthenticateRequest、PostAuthenticateRequest事件负责对用户的身份进行验证。说白了,就是确定当前请求者的身份,从代码层次上说,就是设置好HttpContext的User属性。

      a) AuthenticateRequest事件根据web.config的配置,可被FormsAuthenticationModule、PassportAuthenticationModule、WindowsAuthenticationModule订阅,这3者分别对应处理不同的身份验证方式。

      b) DefaultAuthenticateRequest事件是个internal的事件,可被DefaultAuthenticationModule订阅,该module在AuthenticateRequest事件中无法确定用户身份(即没有设置HttpContext.User属性)时,给出一个默认的用户身份,从而确保在管线的后续处理中,HttpContext.User总是非空的。然而,查看该Module的源代码后,发现,该Module在IIS7.0的集成管线模式下时,并不订阅DefaultAuthenticationRequest事件,而是订阅PostAuthenticateRequest事件。估计是在IIS7.0的集成管线模式下时,该Module必须在某一订阅PostAuthenticateRequest的Module之后执行,从而打的一个补丁吧。

      c) PostAuthenticateRequest事件可被AnonymousIdentificationModule与RoleManagerModule订阅。AnonymousIdentificationModule管理应用程序的匿名标识符,可使应用程序进行匿名访问,并保持Session的连贯性。RoleManagerModule可确定用户的身份。

5、引发AuthorizeRequest、PostAuthorizeRequest事件:在经过第4个步骤确定用户身份后,接下来就是授权的问题了。即判断用户是否具有权限访问所请求的资源。在通过该步骤后,就确认用户具有对请求资源访问的权限。

      a) AuthorizeRequest事件可被FileAuthorizationModule、UrlAuthorizationModule订阅,分别用于判断用户是否具有访问指定File或Url的权限。

      b) PostAuthorizeRequest事件默认不被任何Module订阅。

6、引发ResolveRequestCache、PostResolveRequestCache事件:该步骤即是输出缓存发挥作用的时候。如果用户请求的资源可由输出缓存直接提供,则跳过后续步骤,从而起到加快处理速度,提高性能的作用。

      a) ResolveRequestChche事件可被OutputCacheModule订阅,该Module提供输出缓存的功能。

      b) PostResolveRequestCache事件默认不被任何Module订阅。

7、MapHandlerExecutionStep、引发PostMapRequestHandler事件:当无法由输出缓存进行处理时,则进入了正规的处理过程中。而正规处理的第一步 ,就是根据配置获取IHttpHandler的实例对象,从代码层次上说,就是设置好HttpContext.Handler属性。该Handler将具体根据上下文信息生成具体的Html等代码。

      最最常见的Handler对象就是Page类及其各个子类,也就是我们编写的每一个aspx页面,都是一个具体的Handler,MapHandlerExecutionStep类将根据用户访问各个aspx页面的url从而确定具体的page子类。

      另外,还有HttpForbiddenHandler,当用户访问App_Code、App_Data等目录中的资源时,MapHandlerExecutionStep就返回HttpForbiddenHandelr,该Handler将仅仅告诉用户该资源无法访问等信息,从而起到保护特定资源的作用。

      还有TraceHandler,该Handler当且仅当用户访问根目录下的"trace.axd"资源时返回,从而实现Asp.net跟踪的功能。

      用户可以注册更多的自定义Handler,已满足更多的特定的需求。

      在IIS7.0的集成管线模式下,还将引发MapRequestHandler事件。

      PostMapRequestHandler事件默认不被任何Module订阅。

8、引发AcquireRequestState、PostAcquireRequestState事件负责获取用户的会话状态、个性化数据等信息。从代码层次上说,就是设置好HttpContext.Session与HttpContext.Profile属性。

      a) AcquireRequestState事件可被SessionStateModule、ProfileModule订阅,这两个Module分别提供Session与Profile功能。

      b) PostAcquireRequestState事件默认不被任何Module订阅。

9、引发PreRequestHandlerExecute事件、CallHandlerExecutionStep、PostRequestHandlerExecute事件:至此,一切准备就绪,该执行第7部中获取的Handler的ProcessRequest的时候了。如果Handler为Page类或其子类,则该方法将引发Page的生命周期。

      a) PreRequestHandlerExecute与PostRequestHandlerExecute事件默认不被任何Module订阅。

      b) CallHandlerExecutionStep最主要的工作就是调用Handler的PrecessRequest方法,该方法负责生成Html等代码,并将这些代码写入HttpContext的Response中。因此,该步骤是最重要的一个步骤,也是最需要开发人员去定制的步骤,事实上,我们开发Asp.net应用程序,绝大部分就是在定制这一步的步骤,我们写出的一个又一个的不同的aspx页面,实际上就是一个又一个不同的Handler,而这些一个又一个不同的Handler,就生成了一个又一个不同的html页面。经过此步骤之后,Html等代码就基本完成了(为什么说基本完成了?是因为后续还有一个步骤将可能改变html代码,请看第11个步骤),后续的步骤,主要是一些收尾的工作。

10、引发ReleaseRequestState、PostReleaseRequestState事件:对应于第8个步骤,该步骤负责将用户的会话状态等信息保存起来,以供下次处理用户请求时所用。

      a) ReleaseRequestState事件可被SessionStateModule订阅,该Module在该步骤中保存可能被第9步骤所改变的会话状态。该事件并不被ProfileModule订阅,因为ProfileModule另外订阅EndRequest事件,在该事件中完成保存Profile数据的工作。

      b) PostReleaseRequestState事件默认不被任何Module订阅。

11、CallFilterExecutionStep:该步骤将执行HttpContext.Response.FilterOutput方法,将对已经写入Response的Html代码进行最后的过滤。而所谓过滤,实际上也就是改变html的一个过程,比如将html全部改为小写或全部改为大写,就是某一种形式的过滤。当然,你也可以说要过滤掉前面100个字符等,虽然通常这没有什么意义。要设置过滤器,请参考Response.Filter属性。

12、引发UpdateRequestCache、PostUpdateRequestCache事件:对应于第6个步骤,该步骤将生成的Html代码保存到输出缓存中,以供下次处理用户请求时所用。

      a) UpdateRequestCache事件可被OutputCacheModule订阅。

      b) PostUpdateRequestCache事件默认不被任何Module订阅。

13、引发EndRequest事件:对应于第3个步骤,该步骤通知请求已经处理完毕。该步骤有个特别之处,就在于前面那些步骤不一定在每次处理请求时都发生,而该步骤却必然发生,因此,该步骤也就成了很多Module处理扫尾等工作的订阅事件。ProfileModule、FormsAuthenticationModule、PassportAuthenticationModule、RoleManagerModule、SessionStateModule都订阅该事件,而各个Module订阅该事件所做的工作却又各不相同。

      a) ProfileModule订阅该事件是为了完成对Profile数据的保存。

      b) FormsAuthenticationModule订阅该事件是为了处理当用户未登录时将用户重定向到指定的Login页面的工作。

      c) PassportAuthenticationModule订阅该事件是为了处理当用户未登陆时将用户重定向到指定Passport地址的工作。

      d) RoleManagerModule订阅该事件的作用我还不大清楚,等待高手的补充或我的继续探索中。

      e) SessionStateModule订阅该事件的作用我也还不大清楚,等待高手的补充或我的继续探索中。

14、NoopExecutionStep:这是最后一个步骤,但从源代码上看,该步骤不做任何的事情。我纳闷,该步骤到底有啥作用?

      好了,经过以上的分析,我想你应该对HttpApplication的管线有了一个比较清楚的了解了吧。从下篇开始,我们将按功能来讨论各个模块的具体实现。