文档章节

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

 木宛城主
发布于 2015/03/02 19:41
字数 2624
阅读 5
收藏 0
点赞 0
评论 0

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

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
{
    // Methods
    void 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
{
    // Fields
    private bool _isInheritedInstance;

    // Methods
    protected 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方法将会启动页面的生成过程,这个过程是通过页面的事件处理管道来完成,在处理过程中页面对象将会依次触发一系列事件。

 

 

 

总结

未完,待续。

© 著作权归作者所有

共有 人打赏支持
粉丝 2
博文 222
码字总数 199010
作品 0
黄浦
session共享那点事

二、如何实现session的共享? 首先我们应该明白,为什么要实现共享,如果你的网站是存放在一个机器上,那么是不存在这个问题的,因为会话数据就在这台机器,但是如果你使用了负载均衡把请求分...

yagujj
2015/09/16
29
0
[本周] 就来说说Asp.net 身份验证、授权

[本周]如约而至;时间是争取来的,这回的[本周]是把若干零碎的时间利用起来成文的,完成对Asp.net身份验证、访问授权等内容的梳理,可能漏掉的东西会比较多,漏掉的还是希望大家来补充。顺便说...

唐玄奘
2017/12/03
0
0
OSChina 技术周刊第八期 —— 10 大常见的 web 开发错误

每周技术抢先看,总有你想要的! 移动开发 【翻译】实现 iOS 上的井字游戏 前端开发 【软件】Twemoji —— Twitter 开源其完整的 Emoji 表情 【软件】LokiJS —— 高性能的 JavaScript 数据库...

OSC编辑部
2014/11/09
3.5K
5
ASP.NET MVC 随笔汇总

ASP.NET MVC 随笔汇总 为了方便大家浏览所以整理一下,有的系列篇幅中不是很全面以后会慢慢的补全的。 学前篇之: ASP.NET MVC学前篇之扩展方法、链式编程 ASP.NET MVC学前篇之Lambda表达式、...

jinyuan0829
2014/08/03
0
0
MVC 框架--ASP.NET MVC

ASP.NET MVC在现有的ASP.NET 3.5运行时的基础上提供了一个新的MVC框架。开发人员可以用MVC设计模式来构建Web应用,做到清晰的概念分离(UI或者视图与业务应用逻辑分离, 应用逻辑和后端数据分...

匿名
2009/04/03
57.8K
1
自学MVC看这里——全网最全ASP.NET MVC 教程汇总

MVC架构已深得人心,微软也不甘落后,推出了Asp.net MVC。小编特意整理博客园乃至整个网络最具价值的MVC技术原创文章,为想要学习ASP.NET MVC技术的学习者提供一个整合学习入口。本文从Why,W...

葡萄城控件技术团队
2016/08/18
204
0
分享33本关于.net方面的学习书籍(免费下载)

分享33本关于.net方面的学习书籍(免费下载) 1、程序员大本营.Net版(中文文档) 2、NHibernate2.0中文文档 3、C#+WinForm视频播放器(AxWindowsMediaPlayer) 4、C#+简单视频播放器 5、ASP.NET...

邓剑彬
2012/12/02
1K
2
学习ASP.NET Core Razor 编程系列十五——文件上传功能(三)

学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET Core Razor 编程系列三——创建数据表及创建项目...

DotNet菜园
07/11
0
0
ASP.NET WebApi技术从入门到实战演练

一、课程介绍 曾经有一位不知名的讲师说过这么一句名言: 一门RPC技术不会,那么千万万门RPC技术将都不会!在今天移动互联网的时代,作为攻城师的我们,谁不想着只写一套API就可以让我们的Web...

阿笨net
04/23
0
0
ASP.NET MVC和jQuery系列一:入门篇

我是ASP.NET MVC的新手,看了ASP.NET MVC和jQuery系列的几篇文章感觉不错,分享给大家,如果翻译的有问题请指出,谢谢。 原文连接:http://www.codeproject.com/Articles/74338/ASP-NET-MVC-a...

王二狗子11
01/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

轻松搭建svn版本管理工具+svnmanager管理客户端

前面的文章有写过svn版本管理工具的安装是基于svn的安装包进行安装,对于svn与apache的结合还得下svn和apache的模块进行结合过程比较繁琐,今天来介绍下通过centos的yum来安装svn能够快速安装...

javazyw
12分钟前
0
0
keepalived配置高可用集群

Linux集群概述 根据功能划分为两大类:高可用和负载均衡 高可用集群通常为两台服务器,一台工作,另外一台作为冗余,当提供服务的机器宕机,冗余将接替继续提供服务 实现高可用的开源软件有:...

TaoXu
18分钟前
0
0
mysql联表批处理操作

1 概述 mysql中的单表增删改查操作,可以说是基本中的基本. 实际工作中,常常会遇到一些基本用法难以处理的数据操作,譬如遇到主从表甚至多级关联表的情况(如一些历史问题数据的批量处理),考虑到...

社哥
21分钟前
0
0
IntelliJ IDEA 详细图解最常用的配置,适合刚刚用的新人。

刚刚使用IntelliJ IDEA 编辑器的时候,会有很多设置,会方便以后的开发,磨刀不误砍柴工。 比如:设置文件字体大小,代码自动完成提示,版本管理,本地代码历史,自动导入包,修改注释,修改...

kim_o
35分钟前
0
0
Google Java编程风格指南

目录 前言 源文件基础 源文件结构 格式 命名约定 编程实践 Javadoc 后记 前言 这份文档是Google Java编程风格规范的完整定义。当且仅当一个Java源文件符合此文档中的规则, 我们才认为它符合...

niithub
38分钟前
0
0
java.net.MalformedURLException异常说明

1.异常片段 Java代码中,在进行URL url = new URL(urllink)操作时,提示以下异常信息,该类异常主要问题出在参数urllink上面。 异常片段1 java.net.MalformedURLException at java.ne...

lqlm
38分钟前
1
0
CentOS7修改mysql5.6字符集

解决办法:CentOS7下修改MySQL数据库字符编码为UTF-8,UTF-8包含全世界所有国家所需要的字符集,是国际编码。 具体操作如下: 1.进入MySQL [root@tianqi-01 ~]# mysql -uroot -p Enter passw...

河图再现
39分钟前
0
0
DevExpress v18.1新版亮点——WPF篇(一)

用户界面套包DevExpress v18.1日前终于正式发布,本站将以连载的形式为大家介绍各版本新增内容。本文将介绍了DevExpress WPF v18.1 的新功能,快来下载试用新版本!点击下载>> Accordion Co...

Miss_Hello_World
42分钟前
0
0
Rancher 2.0集群与工作负载告警

Rancher 2.0操作指南。本文将step by step演示如何使用Rancher 2.0中集成的告警功能,包括设置通知程序、设置集群级别以及工作负载级别的告警。 在Rancher 1.x时期,告警功能是很多Rancher用...

RancherLabs
47分钟前
1
0
Python中字符串拼接的N中方法

python拼接字符串一般有以下几种方法: ①直接通过(+)操作符拼接 s = 'Hello'+' '+'World'+'!'print(s) 输出结果:Hello World! 使用这种方式进行字符串连接的操作效率低下,因为python中...

木头释然
48分钟前
9
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部