上一篇博文《ASP.NET那点不为人知的事(一)》中我们提到HttpApplication有19个标准事件,在HttpApplication的第8个事件PostMapRequestHandlerExcute触发的时候,标志着已经获取了处理当前请求的处理程序对象,在第11个事件PreRequestHandlerExcute之后,HttpApplication将执行这个处理程序,接下来我们继续讨论以下话题:

  • HttpContext状态管理
  • 处理HttpApplication的事件
  • 常见的HttpModule
  • 处理程序
  • 处理程序工厂
  • 页面处理程序工厂:PageHandlerFactory
  • Reflect反编译网站看究竟
  • 页面的事件处理管道

HttpContext状态管理

什么是HttpContext状态管理
HttpContext通过属性UserHandler传递了当前请求的用户和处理请求所使用的处理程序。如果我们还需要从HttpApplication 前面的事件向后面的事件处理程序传递一些参数,我们可以通过HttpContext的Items属性来完成,用Reflect查看可知这是一个字典:

public IDictionary Items
{get{if (this._items == null){this._items = new Hashtable();}return this._items;}
}

由于HttpContext对象贯穿了整个HttpApplication的管道事件的处理过程,所以,根据Items这个属性,从处理过程的前面阶段将数据传递给后面的处理过程。所以这种传递参数的方式称为基于HttpContext的状态管理。

处理HttpApplication的事件

有必要再回顾一下HttpApplication的19个管道事件。

HttpApplication提供了基于事件的扩展机制,允许程序员借助于处理管道中的事件进行处理过程的扩展。

由于HttpApplication对象是由ASP.NETt基础架构来创建和维护的,那么如何才能获取这个对象的引用呢以便于注册HttpApplication对象的事件?我们可以通过IHttpModule来创建HttpApplication的事件处理程序

public interface IHttpModule
{// Methodsvoid Dispose();void Init(HttpApplication context);
}

实现了IHttpModule接口的类称为HttpModule

IHttpModule接口中的Init方法,接受一个HttpApplicaton类型的参数。在ASP.NET中,每当创建一个HttpApplication对象实例将遍历注册的HttpModule类型,通过反射,依次创建每个注册HttpModule类型的一个对象,并将这个HttpApplication实例通过Init方法传递给各个HttpModule,这个HttpModule就可以再第一时间完成针对HttpApplication对象的事件注册了。

常见的HttpModule

在ASP.NET中已经预定了许多HttpModule,已经在服务器的网站配置文件(C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\web.config)中注册了:

<httpModules><add name="OutputCache" type="System.Web.Caching.OutputCacheModule" /><add name="Session" type="System.Web.SessionState.SessionStateModule" /><add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" /><add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" /><add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" /><add name="RoleManager" type="System.Web.Security.RoleManagerModule" /><add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" /><add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" /><add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule" /><add name="Profile" type="System.Web.Profile.ProfileModule" /><add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /><add name="ServiceModel" type="System.ServiceModel.Activation.HttpModule, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /><add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" /><add name="ScriptModule-4.0" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/></httpModules>

 根据前一篇文章分析,我们再来回顾一下HttpModule是怎样注册HttpApplication的事件的:

  •  HttpApplication实例的初始化:
internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers)
{ this._state = state;PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES);try{try{this._initContext = context;this._initContext.ApplicationInstance = this;//是在这儿初始化吗?我猜的( ⊙ o ⊙ )context.ConfigurationPath = context.Request.ApplicationPathObject;.....
}点击查看ApplicationInstance:public HttpApplication ApplicationInstance
{[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]get{return this._appInstance;}set{if ((this._isIntegratedPipeline && (this._appInstance != null)) && (value != null)){throw new InvalidOperationException(SR.GetString("Application_instance_cannot_be_changed"));}this._appInstance =value;}
}点击查看_appInstance 是什么类型
private HttpApplication _appInstance; 

  • 接着,初始化HttpModule,可以发现先从web.config文件中配置的所有HttpModule模块,然后再获取其余的HttpModule:
private void InitModules()
{HttpModuleCollection modules = RuntimeConfig.GetAppConfig().HttpModules.CreateModules();HttpModuleCollection other = this.CreateDynamicModules();modules.AppendCollection(other);this._moduleCollection = modules;this.InitModulesCommon();
}

  • 点击进入CreateModules方法,发现利用了反射来创建HttpModule(Activator.CreateInstance)
[PermissionSet(SecurityAction.Assert, Unrestricted=true)]
internal static object CreateNonPublicInstance(Type type, object[] args)
{return Activator.CreateInstance(type, BindingFlags.CreateInstance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, args, null);
}

  • 当创建了一个HttpApplication对象实例就会遍历注册的HttpModule类型,通过反射,依次创建每个注册HttpModule类型的一个对象,并将这个HttpApplication实例通过Init方法传递给各个HttpModule,这个HttpModule就可以再第一时间完成针对HttpApplication对象的事件注册了。
   if (HttpRuntime.UseIntegratedPipeline){this._stepManager = new PipelineStepManager(this);}else{this._stepManager = new ApplicationStepManager(this);}this._stepManager.BuildSteps(this._resumeStepsWaitCallback);}......

internal override void BuildSteps(WaitCallback stepCallback)
{......    app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps);app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps);app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps);app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps);app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps);app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps);app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps);app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps);steps.Add(new HttpApplication.MapHandlerExecutionStep(app));app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);steps.Add(app.CreateImplicitAsyncPreloadExecutionStep());steps.Add(newHttpApplication.CallHandlerExecutionStep(app));//---------------------->用于创建处理用户请求的对象(Handler)......}

了解了HttpModule对HttpApplication对象的事件注册后,我们再来分析一下:

  1. HttpApplication选择处理程序的依据是什么?
  2. HttpApplication作用是什么?
  3. HttpApplication如何得到这个处理程序对象?

接下来我们再一一分析下:

  • 当浏览器发送请求的时候,请求被处理需要用处理程序(必须实现了IHttpHandler接口或者IHttpAsyncHandler)来处理(在第8个事件PostMapRequestHandler触发获得处理当前请求的处理程序,在第11个事件PreRequestHandlerExcute之后,HttpApplication将执行这个处理程序),在ASP.NET中,所有请求都要经过HttpApplication管道的处理,根据请求的扩展名来确定使用哪种处理程序。
  • HttpApplication作用:可以将它看做请求到达处理程序和离开处理程序的一个管道,这个管道统一处理了所以的请求机制,使得我们可以在请求被真正处理之前和处理之后进行预处理和处理后工作(如获取Session,更新缓存等)。需要注意的是HttpApplication的事件是按照固定的次序依次触发

处理程序工厂

处理程序工厂(实现IHttpHandlerFactory接口)的优点:因为我们知道,实现了处理程序接口的类就可以被用来创建处理程序对象直接使用,如果需要对处理程序对象进行管理,例如:我们可以创建一个处理程序对象池,就可以不用再每次使用处理程序的时候创建一个新的对象,而是直接可以从池中取一个现有的对象直接使用,提高效率。

常见的处理程序工厂:

internal class SimpleHandlerFactory : IHttpHandlerFactory2, IHttpHandlerFactory
{// Methods[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]internal SimpleHandlerFactory();public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string virtualPath, string path);public virtual void ReleaseHandler(IHttpHandler handler);IHttpHandler IHttpHandlerFactory2.GetHandler(HttpContext context, string requestType, VirtualPath virtualPath, string physicalPath);
}

可以通过GetHandler方法来从这个处理程序工厂获得一个处理程序对象实例。通过配置文件,对于扩展名为ashx的请求是通过SimpleHandlerFactory处理程序工厂完成的,当请求一个ashx扩展名的服务器上资源时,SimpleHandlerFactory将找到对应的ashx文件,通过这个文件找到对应的处理程序。最后,SimpleHandlerFactory通过反射创建一个此类型处理程序对象实例
<add path="*.ashx" verb="*" type="System.Web.UI.SimpleHandlerFactory" validate="True" />

页面处理程序工厂:PageHandlerFactory(重点)

  • 对于Web开发,ASP.NET为了提高输出HTML代码效率,采用了模版的方式来生成一个处理程序。模版的扩展名为aspx,并且通过一个内置的处理工厂PageHandlerFactory,根据匹配请求名称的aspx文件,将aspx形式的模版编译生成处理程序代码,其实PageHandlerFactory通过aspx文件生成两个类,一个为与后台代码中定义的类同名的部分类(Partial),这个部分类(Partial)将与后台代码中定义的类在编译时合并为一个派生自Page的页面派生类,但是,在ASP.NET,创建实际的页面对象的类并不是这个类,而是第二个类,一般情况下,这个类的名字后面加上_aspx(注:这个类派生自前面所说的那个合成类)这才是实际创建页面对象的页面类,然后,将这个页面类(实现了IHttpHandler接口,即就是处理程序HttpHandler)反射出来返回给HttpApplication完成请求的处理。
  • 需要注意的是,aspx模版的解析和代码的生成仅仅出现在第一次处理的时候,以后的请求直接使用已经编译生成的程序集,所以这个处理过程并不会降低网站的处理速度。
[PermissionSet(SecurityAction.InheritanceDemand, Unrestricted=true), PermissionSet(SecurityAction.LinkDemand, Unrestricted=true)]
public class PageHandlerFactory : IHttpHandlerFactory2, IHttpHandlerFactory
{// Fieldsprivate bool _isInheritedInstance;// Methodsprotected internal PageHandlerFactory();public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string virtualPath, string path);private IHttpHandler GetHandlerHelper(HttpContext context, string requestType, VirtualPath virtualPath, string physicalPath);public virtual void ReleaseHandler(IHttpHandler handler);IHttpHandler IHttpHandlerFactory2.GetHandler(HttpContext context, string requestType, VirtualPath virtualPath, string physicalPath);
}

  •  根据配置文件可以看到,对于扩展名为aspx的请求,将由PageHandlerFactory这个处理程序工厂进行处理
<add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="True" />

  • 在PageHandlerFactory的内部,通过PageParser这个类解析指定的aspx文件生成Page类的派生类,而这个派生类即用来创建页面处理程序对象实例。

public sealed class PageParser : TemplateControlParser
{

......

public static IHttpHandler GetCompiledPageInstance(string virtualPath, string inputFile, HttpContext context);

......

}

  • PageParser的静态方法GetCompiledPageInstance方法可以通过一个aspx文件创建一个相应的页面处理程序对象实例,用于处理请求。
[SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
public static IHttpHandler GetCompiledPageInstance(string virtualPath, string inputFile, HttpContext context)
{if (!string.IsNullOrEmpty(inputFile)){inputFile = Path.GetFullPath(inputFile);}return GetCompiledPageInstance(VirtualPath.Create(virtualPath), inputFile, context);
}

  •  而GetCompiledPageInstance方法内部又使用了BuildManager类来创建页面对象实例
private static IHttpHandler GetCompiledPageInstance(VirtualPath virtualPath, string inputFile, HttpContext context)
{IHttpHandler handler;.......BuildResultCompiledType type = (BuildResultCompiledType) BuildManager.GetVPathBuildResult(context, virtualPath, false, true, true, true);handler = (IHttpHandler) HttpRuntime.CreatePublicInstance(type.ResultType);.......return handler;
}    

  • 最后通过GetVPathBuildResult方法通过页面的虚拟路径通过代码生成得到派生的页面类,然后通过反射创建这个页面对象:
internal static BuildResult GetVPathBuildResult(HttpContext context, VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, [Optional, DefaultParameterValue(true)] bool ensureIsUpToDate)
{if (HttpRuntime.IsFullTrust){return GetVPathBuildResultWithNoAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, true, ensureIsUpToDate);}return GetVPathBuildResultWithAssert(context, virtualPath, noBuild, allowCrossApp, allowBuildInPrecompile, true, ensureIsUpToDate);
}

Reflect反编译网站看究竟

由上面分析得知,下面是一个合并的类:

下面这个类派生自_Default类,最终通过反射创建实际的页面对象,它实现了IHttpHandler接口,也就是一个处理程序HttpHandler,所以页面毫无疑问也是一个处理程序

我们可以看到default_aspx里面主要是初始化了控件树(BuildControlTree)和ProcessRequest方法启动页面生成过程。

 

页面的事件处理管道

页面对象的ProcessRequest方法将会启动页面的生成过程,这个过程是通过页面的事件处理管道来完成,在处理过程中页面对象将会依次触发一系列事件。

 

 

 

总结

未完,待续。

转载于:https://www.cnblogs.com/OceanEyes/archive/2012/08/23/aspnetEssential-22.html

ASP.NET那点不为人知的事(二)相关推荐

  1. ASP.NET那点不为人知的事(四)

    虽然开发中很少接触多线程,但你必须承认你离不开他. 接着上篇博客(ASP.NET那点不为人知的事(三)),基本可以实现一个Mini版的服务器,但也有不足: 倘若客户端飞快地连接,服务端很快被压垮. 幸 ...

  2. Asp.net MVC验证那些事(4)-- 自定义验证特性

    在项目的实际使用中,MVC默认提供的Validation Attribute往往不够用,难以应付现实中复杂多变的验证需求.比如, 在注册用户的过程中,往往需要用户勾选"免责声明", ...

  3. Asp.net MVC验证哪些事(2)-- 验证规则总结以及使用

    上篇文章Asp.net MVC验证那些事(1)-- 介绍和验证规则使用中,介绍了Asp.net MVC中的验证功能以及如何使用.这里将对MVC中内置的验证规则进行总结. 一,查找所有验证规则 上篇文章 ...

  4. googleplay刷榜和APP Store 刷榜哪些不为人知的事

    @googleplay刷榜和APP Store 刷榜哪些不为人知的事TOC googleplay刷榜和APP Store 刷榜哪些不为人知的事 你好Google play市场: 我们能对产品的影响: ...

  5. ASP.NET MVC5 网站开发实践(二) Member区域 - 用户部分(2)用户登录、注销

    上次实现了用户注册,这次来实现用户登录,用到IAuthenticationManager的SignOut.SignIn方法和基于声明的标识.最后修改用户注册代码实现注册成功后直接登录. 目录: ASP ...

  6. [翻译]asp.net ajax xml-script教程(二)

    原文地址:http://dotnetslackers.com/articles/atlas/xml_script_tutorial_part2.aspx asp.net ajax xml-script ...

  7. ASP.NET MVC 2 学习笔记二: 表单的灵活提交

    ASP.NET MVC 2 学习笔记二:  表单的灵活提交 前面说到有做到公司内部的一个请假系统,用的是ASP.NET MVC 2+Entity Framework.虽然EF(Entity Frame ...

  8. 单片机的三大秘境之揭秘 KEIL 调试那些不为人知的事

    单片机的三大秘境之揭秘 KEIL调试那些不为人知的事 ///插播一条:我自己在今年年初录制了一套还比较系统的入门单片机教程,想要的同学找我拿就行了免費的,私信我就可以哦~点我头像黑色字体加我地球呺也能 ...

  9. ASP.NET中进行消息处理(MSMQ) 二

    在我上一篇文章<ASP.NET中进行消息处理(MSMQ)一>里对MSMQ做了个通俗的介绍,最后以发送普通文本消息和复杂的对象消息为例介绍了消息队列的使用. 本文在此基础上继续介绍MSMQ的 ...

最新文章

  1. urllib,urllib2和request模拟登陆人人网比对
  2. 使用CreateProcess创建进程
  3. inittab脚本启动解析 (zz)
  4. 解决win2008下IIS7的HTTP500错误
  5. Java 并发(JUC 包-01)
  6. 案例:对比使用Java代码与EL表达式获取信息
  7. C++ 枚举类型的思考
  8. 代码复审1234跟我一起来
  9. redis smembersmap_Redis五种数据类型
  10. linux cp覆盖每次都有提示
  11. CentOS7.0下安装PHP5.6.30服务
  12. 北京天通苑二房东、黑中介
  13. python数据分析论文报告电影_一个实战案例带你走完python数据分析全流程:豆瓣电影评论的关键词云图制作...
  14. JavaCV:将任意视频转码为h264编码的mp4格式视频
  15. 《人月神话》学习笔记
  16. 黄金周未来景区游客涨6倍 阿里巴巴数字经济体引领体验式消费升级
  17. 中型仓储货架是使用三立柱货架好,还是使用两立柱货架好?
  18. 智能手机的中文输入法统计
  19. 网络技术与应用概论(上)——“计算机网络”
  20. 电子邮箱哪家最安全,各家电子邮箱安全性盘点!

热门文章

  1. CentOS 6 编译安装subversion-1.8.10+Apache2.4
  2. modprobe命令使用方法
  3. php编辑页面代码,0069 PHP编程实现后台网页:新闻创建和编辑
  4. 全球链界科技发展大会_科技界女性占五席
  5. 软件开发向大数据开发过渡_如何过渡到开发人员关系职业
  6. (3)Node.js APIS
  7. ROS笔记(5) ROS架构
  8. c语言去掉数组中重复的,C语言删除无序整型数组中的重复元素及时间复杂度
  9. c 递归下降识别程序_程序员的算法课(3)-递归(recursion)算法
  10. devc++不兼容_Mac压缩文件Win不支持?BetterZip帮你解决!