ASP.NET进阶(8):HttpModule和HttpApplication

    前面三节讲了控件的构造、呈现和数据绑定,我想该差不多了。本想讲一个自定义控件来终结控件部分,但是我个人不太喜欢控件这些东西,所以也就懒的写相关的内容,抱歉了。虽然我不喜欢使用控件,但我还是喜欢整个WebForm的设计。一个字:“太神了”。前面章节将Page生命周期的时候有朋友评论说内容太少了,今天开始就从来围绕生命周期的话,讲讲相关的内容吧。

    IHttpModule是个什么东西呢?对我们Web开发有什么用呢?
    先从名字来看他是一个接口,接口就是让人来继承的,我们要用它就得继承他,并实现他的方法。Module的意思是模块、组件的意思。如果说我们实现了这个接口,并配置了web.config,让IIS的知道我们的web程序使用了这个组件;那么我们的程序是不是就比默认的web程序多了个组件?!显然,而且在必要的时候会调用我们组件里定义的方法,这就是HttpModule的用处。说白了,就是我们给IIS写扩展,但该扩展仅仅是针对于使用了(配置config)的web程序。其实每个web应用程序都是一个IIS进程,而这个进程的配置文件就是web.config。
    弄明白了他的意义,我们就开始!我们创建一个Web应用程序,并创建一个类,继承IHttpModule,并实现他的方法,在Config的Modules节点里<add name="" type=""/>,OK!

namespace WebApplication1 
{ public class MyHttpModule : IHttpModule { public void Dispose() { }public void Init(HttpApplication context) { context.Context.Response.Write(1); } } 
}

<?xml version="1.0"?> 
<configuration> <system.web> <compilation debug="true" targetFramework="4.0" /> <httpModules> <add name="MyHttpModule" type="WebApplication1.MyHttpModule,WebApplication1"/> </httpModules> </system.web><system.webServer> <modules runAllManagedModulesForAllRequests="true"> <add name="MyHttpModule" type="WebApplication1.MyHttpModule,WebApplication1"/><!--注意:type="类的FullName,类所在的dll名"--> </modules> </system.webServer> 
</configuration> 

web.config的配置有2个,上面的那个是给非IIS7用的,下面的显然就是给IIS7用的。启动程序,what happend?! 是不是页的头部多了个1,有木有!!我们打开任何页面都会有个1,说明我们的模块起到作用了,也说明每个请求都会执行HttpModule的Init方法?是不是呢?
我们把代码改一下:
 

       public void Init(HttpApplication context) { context.Context.Response.Write(1); context.BeginRequest += OnBeginRequest; }public void OnBeginRequest(object sender, EventArgs e) { var app = (HttpApplication)sender; var url = app.Context.Request.RawUrl; app.Context.Response.Write(url); }

分别给Init和OnBeginRequest 两个方法加断点,重新编译下,然后F5看看。Init只走1次,而OnBeginRequest却走了3次,ur的值l分别是  default.aspx   style.css 和 favorite.ico;可以看出任何url请求,包括静态文件,都会经过执行我们定义的事件方法!看来这要比只处理aspx慢不少!
Init的必须走一次啊,要不然事件不被订阅3次了?,但为什么只走1次呢?这到底是为什么呢? 呵呵,其实很简单,MyHttpModule就实例化一次哦,实例化后执行Init初始化,然后就驻留在应用程序池了,直到应用程序池被回收,或他被各种原因搞崩溃;而OnBeginRequest是被HttpApplication类的BeginRequest事件订阅的。事件订阅是个什么概念?事件是个特殊的委托,委托是个什么概念?委托是个方法指针。所以,只要委托被执行,就会执行他指向的方法体,也就是OnBeginRequest,可见OnBeginRequest的执行,是和HttpApplication的BeginRequest有关系的,和MyHttpModule本身已经没关系了。
    走了3次说明3个Request都执行了BeginRequest,难道每个请求都实例化一个HttpApplication?从名字我就能看出不会的,因为Application(应用程序)嘛,我们目前运行的就一个,怎么会不断的实例化!想刨根问题,彻底整明白,就得翻出Framework的源码,调试!
(------------声明,下面的源码可以不用完全理解,也可以跳过,只要知道跟Request有关就行了------------)
下面来调查下HttpApplication的初始化过程!
用Reflector查阅System.Web名字空间下的类,可以看到HttpApplicationFactory类,他负责HttpApplication的创建。当我们启动站点后,第一次的时候比较慢,为什么呢? 因为初始化的构建工作。
System.Web.Complilation名字空间下有一堆的构建类,其中就有构建Global.asax的,也就是我们的HttpApplication类,然后缓存到Factory的堆栈里,我们需要的时候pop出来。 (你可能有疑问,pop了不就没了吗? 其实app在执行的时候还会push回去,详见HttpApplication.ReleaseAppInstance方法)
HttpApplicationFactory有个GetApplicationInstance方法,就是用来获取HttpApplication的: 

internal static IHttpHandler GetApplicationInstance(HttpContext context) 
{ if (_customApplication != null) { return _customApplication; } if (context.Request.IsDebuggingRequest) { return new HttpDebugHandler(); } _theApplicationFactory.EnsureInited(); _theApplicationFactory.EnsureAppStartCalled(context); return _theApplicationFactory.GetNormalApplicationInstance(context); 
}private HttpApplication GetNormalApplicationInstance(HttpContext context) 
{ HttpApplication application = null; lock (this._freeList) { if (this._numFreeAppInstances > 0) { application = (HttpApplication) this._freeList.Pop();//如果_freeList里有,就直接获取,只有第一次构建的时候没有 this._numFreeAppInstances--; if (this._numFreeAppInstances < this._minFreeAppInstances) { this._minFreeAppInstances = this._numFreeAppInstances; } } } if (application == null) { application = (HttpApplication) HttpRuntime.CreateNonPublicInstance(this._theApplicationType); using (new ApplicationImpersonationContext()) { application.InitInternal(context, this._state, this._eventHandlerMethods);//这里是初始化application的,并且会经过复杂的一坨代码后push到_freeList里 } } return application; 
} 

跟踪这个方法,我们可以断定,Application是被缓存起来的,不是每次都是实例化的。
通过Reflector的分析,我们能发现这个GetApplicationInstance方法是被HttpRuntime.ProcessRequestNow调用的,终于回到我们的Request来了。

private void ProcessRequestNow(HttpWorkerRequest wr) 
{ HttpContext context; try { context = new HttpContext(wr, false);//实例化上下文 } catch { wr.SendStatus(400, "Bad Request"); wr.SendKnownResponseHeader(12, "text/html; charset=utf-8"); byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>"); wr.SendResponseFromMemory(bytes, bytes.Length); wr.FlushResponse(true); wr.EndOfRequest(); return; } wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, context); Interlocked.Increment(ref this._activeRequestCount); HostingEnvironment.IncrementBusyCount(); try { try { this.EnsureFirstRequestInit(context); } catch { if (!context.Request.IsDebuggingRequest) { throw; } } context.Response.InitResponseWriter();//实例化HttpWriter,输出用的,我们的控件输出全靠他 IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);//获取handler也就是httpapplication if (applicationInstance == null) { throw new HttpException(SR.GetString("Unable_create_app_object")); } if (EtwTrace.IsTraceEnabled(5, 1)) { EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, applicationInstance.GetType().FullName, "Start"); } if (applicationInstance is IHttpAsyncHandler)//是否异步的,显然我们的HttpApplication是继承这个接口的,所以走这个if { IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance; context.AsyncAppHandler = handler2; handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);//从BeginRequest就开始执行application的step } else { applicationInstance.ProcessRequest(context);//直接执行 this.FinishRequest(context.WorkerRequest, context, null); } } catch (Exception exception) { context.Response.InitResponseWriter(); this.FinishRequest(wr, context, exception); } 
} 

很好,上面的代码让我们看清了一个Request的执行过程。而每个Request都会走这个方法!HttpRuntime有个RequestQueue(请求队列),会依次执行所有的Request。终于知道为什么走3次了吧:) 就是application被用了3次。感兴趣的同学可以再去跟踪RequestQueue,我就不贴了。

另外,HttpApplication,意味着他是整个站点的boss,我们定义的myhttpmodule不过是他众多Modules里的其中之一。而且我们也可以定义多个module,config里面add多个就可以了。
在Application初始化的过程中,有初始化module的一段,我贴出来大家看看:

private void InitModules() 
{ this._moduleCollection = RuntimeConfig.GetAppConfig().HttpModules.CreateModules(); this.InitModulesCommon(); 
} 

其中CreateModules就是从web.config的module节点实例化我们配置的module
internal HttpModuleCollection CreateModules() 
{ HttpModuleCollection modules = new HttpModuleCollection(); foreach (HttpModuleAction action in this.Modules) { modules.AddModule(action.Entry.ModuleName, action.Entry.Create()); } modules.AddModule("DefaultAuthentication", new DefaultAuthenticationModule()); return modules; 
} 

最后,初始化所有的module,包括系统的一些module。
private void InitModulesCommon() 
{ int count = this._moduleCollection.Count; for (int i = 0; i < count; i++) { this._currentModuleCollectionKey = this._moduleCollection.GetKey(i); this._moduleCollection[i].Init(this);//初始化每个HttpModule } this._currentModuleCollectionKey = null; this.InitAppLevelCulture(); 
}


(--------------跳到这里----------------)
    HttpApplication类公开了很多事件。上面的示例程序用到了BeginRequest,这个事件是最先开始执行的事件;其作用很明白,就是Request开始执行时,我们要准备点什么?或许你可能需要urlrewriter:)。下面插播“事件广告”,广告之后马上飞来。
上面我只是简单的提了一句“事件是特殊的委托”,并没有详细说为什么特殊。不知道同学你是否理解事件的意义呢?事件的意义是什么?
我是这么理解的。“事件”,代表着一件事情的发生。我们打一个比方,我把每天的生活设计成一个类。那么,一天的生活包含什么?包含从早到晚,包含很多事情要去做,甚至包含一些固定的事情。
细品这3个包含:
从早到晚,意味着这是一个过程,从头到尾,从始到终;很多事情要去做,说明在这个过程中要执行很多事;而一些固定的事情,比如吃饭,睡觉。
我们可以把早和晚,看作是构造函数和析构函数;把很多事情要做看作是事件;把固定的事情看作是方法。
因为每个人一天的生活都不一定是相同的,所以每天要去做的事我们没法写成方法!我们最多只能定义一些固有的模式的方法抽象,比如起床后做什么,午饭后做什么,睡觉前做什么。这不就是事件么?
我们在设计类到时候,如果类的使用有时候也涉及到一些执行过程的问题,而在这个过程中会发生一些未知的事情(未知意味着由外部类来提供,自己提供就是已知了),我们便把这些未知设计成抽象的方法。
由于过程的顺序是固定的,比如午饭后做什么就必须实在午饭后,所以午饭后做什么事件不能被别人在早上使用(你就是上帝不能把午饭的事情给我挪到早饭,挪了就叫早饭后了)。
同样的道理,事件的执行不能由外部来决定,这就是事件有别于委托的地方(委托没有使用限制,随时随地都可以用),这也是事件的意义。 整个过程也就是所谓的“生命周期”。
代码和现实就是这么的一致,耐人寻味。

广告回来~~ 继续看HttpApplication的事件,我把他们按执行的顺序贴了出来;从名字就能看出大概的作用。有些我从来没用过:)
BeginRequest        //请求开始
AuthenticateRequest   
PostAuthenticateRequest   
AuthorizeRequest       
PostAuthorizeRequest
ResolveRequestCache   
PostResolveRequestCache
PostMapRequestHandler
AcquireRequestState    //获得请求状态,这时候已经有session了
PostAcquireRequestState
PreRequestHandlerExecute    //准备交给HttpHandler处理
Error            //请求出现了异常!!!
PostRequestHandlerExecute
ReleaseRequestState    //发布请求的状态
PostReleaseRequestState
UpdateRequestCache
PostUpdateRequestCache
EndRequest        //结束请求
PreSendRequestHeaders    //准备发送请求头信息,在这我们还能修改内容
PreSendRequestContent    //准备发送请求内容,这里就改不了了

这才是真正的整个生命周期,是不是!而面试题一般考的是Page类的生命周期,这已经过时了,web开发又不光Webform,所以考page类,没技术含量:)
在HttpApplication里,把这些事件作为Step,Step by Step的执行下去,下面是HttpApplication构建Step的代码:

internal override void BuildSteps(WaitCallback stepCallback) 
{ ArrayList steps = new ArrayList(); HttpApplication app = base._application; bool flag = false; UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings; flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > 0); steps.Add(new HttpApplication.ValidatePathExecutionStep(app)); if (flag) { steps.Add(new HttpApplication.UrlMappingsExecutionStep(app)); } 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(new HttpApplication.CallHandlerExecutionStep(app)); app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps); app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps); steps.Add(new HttpApplication.CallFilterExecutionStep(app)); app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps); this._endRequestStepIndex = steps.Count; app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps); steps.Add(new HttpApplication.NoopExecutionStep()); this._execSteps = new HttpApplication.IExecutionStep[steps.Count]; steps.CopyTo(this._execSteps); this._resumeStepsWaitCallback = stepCallback; 
} 

从构建的顺序我们也能看出执行的顺序,每个Step都有一个Execute的方法,挨个执行下去,如果程序出现异常,则直接跳出。而我们的Page执行是在CallHandlerExecutionStep这个Step里。

    好啦,就讲到这呗?今天汉字比较少,代码比较多;您还有啥不能明白的就评论里聊吧。没用过的同学动写个吧,HttpModule是个好东西哦。PS:我们公司面试的笔试题里有道题“给一个正在运行的网站增加一个异常监控功能,该怎么实现?” 你能想到怎么做吗?:)

转载于:https://www.cnblogs.com/mad/archive/2011/04/11/asp-net-httpmodule-and-httpapplication.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/362277.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

移动端网页宽度值(未加meta viewport标签)

移动端网页宽度值&#xff08;未加meta viewport标签&#xff09;: iphone:980px Galaxy&#xff08;盖乐世&#xff09;&#xff1a;980px Nexus&#xff1a;980px blackberry&#xff08;黑莓&#xff09;&#xff1a;980px LG&#xff1a;980px Nokia&#xff1a;980p…

简而言之:JRunner

关于JUnit测试要点的多篇教程的第四章介绍了该工具可交换测试运行器体系结构的目的&#xff0c;并介绍了一些可用的实现。 正在进行的示例通过编写参数化测试的不同可能性扩大了主题。 由于我已经发布了JUnit Rules的介绍&#xff0c;因此我决定跳过关于该主题的已宣布部分。 …

cmake how to create vs file filters

cmake how to create vs file filters 用cmakelists构建出来的工程&#xff0c;没有文件filters&#xff0c;可采用如下方法解决 set(SOURCE_LIST"lotteryTicket.cpp""stdafx.cpp""stdafx.h""test/main.cpp" )add_executable(lotteryT…

Cannot retrieve mapping for action

想必用过Struts的朋友都遇到过这个异常吧&#xff01;没遇到的也可能&#xff0c;只能说你很强或运气不错。 我遇到该异常的解释是我不强&#xff0c;用Struts不是很多&#xff0c;或者说根本不熟练&#xff0c;对一些知识了解得并不深&#xff0c;仅仅皮毛而已&#xff0c;所以…

休眠字节码增强

介绍 既然您了解了Hibernate脏检查的基础知识 &#xff0c;我们就可以研究增强的脏检查机制。 虽然默认的图遍历算法对于大多数用例可能已足够&#xff0c;但有时您需要优化的脏检查算法&#xff0c;并且检测方法比构建自己的自定义策略更方便。 使用Ant休眠工具 传统上&#…

Hibernate核心接口

一、Configuration类&#xff1a;1、 作用&#xff1a;&#xff08;1&#xff09;管理hibernate配置信息&#xff08;2&#xff09;读取hibernate.cfg.xml文件&#xff08;3&#xff09;加载hibernate的驱动&#xff0c;例如&#xff1a;url,用户名&#xff08;4&#xff09;管…

CSS实现垂直居中的方法

CSS实现垂直居中的方法 1、relative absolute定位&#xff1a; (1)css html代码 1 <!doctype html>2 <html lang"en">3 4 <head>5 <meta charset"UTF-8" />6 <title>Document</title>7 …

Neo4j:收集多个值

在Neo4j的密码查询语言中&#xff0c;我最喜欢的功能之一是COLLECT&#xff0c;它使我们能够将项目分组到一个数组中以备后用。 但是&#xff0c;我注意到人们有时很难弄清楚如何使用COLLECT收集多个项目&#xff0c;并且很难找到一种方法。 考虑以下数据集&#xff1a; cre…

spring 概念理解(资料)

一、Spring的IoC(Inversion of Control)。这是Spring中得有特点的一部份。IoC又被翻译成“控制反转”&#xff0c;也不知道是谁翻译得这么别扭&#xff0c;感觉很深奥的词。其实&#xff0c;原理很简单&#xff0c;用一句通俗的话来说&#xff1a;就是用XML来定义生成的对象。I…

运用flask、flask-restful开发rest风格的接口,并使用蓝图增加代码的延展性和可扩展性。...

本人做为一个测试人员&#xff0c;之前也有写过&#xff0c;想要测试好接口&#xff0c;那必须要知道如何开发一个接口的重要性。 之前也写过通flask或者flask-retful开发接口&#xff0c;但那些只是一些最简单的demo&#xff0c;不具有很好延展性和扩展性。 此次我们带一整个完…

2014年夏末大Java新闻

正如即将到来的JavaOne那样 &#xff0c;最近在Java社区中已经有很多重大新闻。 这篇文章简要地引用了其中的三个项目&#xff08;Java SE 8更新&#xff0c;Java SE 9和Java EE 8&#xff09;&#xff0c;并对我发现这是我在类路径/类加载器问题上见过的更清楚的文章之一进行了…

初学 Ajax(涉及 php)

一直知道 ajax 但是尚未真正了解&#xff0c; 这次看了慕课网的《Ajax全接触》&#xff0c;算是有所收获&#xff0c;入了个门。需要用到php&#xff0c;因为 Ajax也是向服务器请求&#xff08;不知道这么解释对不对&#xff09;&#xff0c; 所以还需要配置环境&#xff0c; …

php分页显示

<?php /*** Page Class* 实现各种分页样式* author yangsh*/ class Pager {/*** 数据总数* var integer*/private $totalItems;/*** 每页显示数* var integer*/private $pageSize 20;/*** 页面显示的页码标号的数量* var integer*/private $codeNum 10;/*** 跳转链接* va…

DI容器是代码污染者

尽管依赖项注入 &#xff08;也称为“ DI”&#xff09;是一种在OOP中组成对象的自然技术&#xff08;在Martin Fowler引入该术语之前就已知道&#xff09;&#xff0c;但Spring IoC &#xff0c; Google Guice &#xff0c; Java EE6 CDI &#xff0c; Dagger和其他DI框架将其…

java程序-类的高级特性

创建Employee类&#xff0c;在类中定义三个属性&#xff1a;编号&#xff0c;姓名&#xff0c;年龄&#xff0c;然后在构造方法里初始化这三个属性&#xff0c;最后在实现接口中的定义的CompareTo方法&#xff0c;将对象按编号升序排列。 代码如下&#xff1a;(程序可能有些错误…

js 数组去重

数组去重的思路&#xff0c;突然感觉挺有趣的&#xff0c;来整理一下 两个 for 循环比较的&#xff0c;如下function removeRepeat(arr){ for( var i 0; i < arr.length; i ){ for ( var j i 1; j < arr.length; i ){ if ( a…

CSS中实现水平/垂直居中

CSS中实现水平/垂直居中 在CSS中实现水平居中相对简单&#xff0c;但是却没有一个明确的属性表示这是实现垂直居中的&#xff0c;这就导致垂直居中的实现相对初学者来说难上许多。但是在实际的开发中垂直居中的需求常常出现&#xff0c;例如一行中有左右两部分&#xff0c;左边…

SWF 文件不能访问本地资源 只有仅限于文件系统的 SWF 文件和可信的本地 SWF 文件可以访问本地资源。...

错误信息&#xff1a;SecurityError: Error #2148: SWF 文件 D:/demo/test/index.swf 不能访问本地资源 D:/demo/test/bin-debug/textLayout_4.0.0.10485.swf。只有仅限于文件系统的 SWF 文件和可信的本地 SWF 文件可以访问本地资源。 at flash.net::URLStream/load() at fla…

高并发系统之大忌-慢查询

最近又遇到了一次慢查把db&#xff08;mariadb10)几乎打挂的案例&#xff0c;作为一个核心支付系统的技术负责人&#xff0c;真是每日如履薄冰。因为之前支付系统经常出问题&#xff0c;现在各个BG对支付系统都盯得很紧。这次要不是我及时让DB给暴力清理数据&#xff0c;没准又…

CSS媒体查询

格式&#xff08;style&#xff1a;{}&#xff09;元素设置&#xff1a; body{background-color:#0033FF;}/*媒体查询:当页面宽度最大为960px时*/media screen and (max-width: 960px){/*body背景颜色为*/body{background-color:#FF6699}}media screen and (max-width: 768px){…