ihttpmodule是个什么东西呢?对我们web开发有什么用呢?
先从名字来看他是一个接口,接口就是让人来继承的,我们要用它就得继承他,并实现他的方法。module的意思是模块、组件的意思。如果说我们实现了这个接口,并配置了web.config,让iis的知道我们的web程序使用了这个组件;那么我们的程序是不是就比默认的web程序多了个组件?!显然,而且在必要的时候会调用我们组件里定义的方法,这就是httpmodule的用处。说白了,就是我们给iis写扩展,但该扩展仅仅是针对于使用了(配置config)的web程序。其实每个web应用程序都是一个iis进程,而这个进程的配置文件就是web.config。
弄明白了他的意义,我们就开始!我们创建一个web应用程序,并创建一个类,继承ihttpmodule,并实现他的方法,在config的modules节点里
namespace webapplication1
{
public class myhttpmodule : ihttpmodule
{
public void dispose()
{
}public void init(httpapplication context)
{
context.context.response.write(1);
}
}
}
requests="true">
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.asp教程x 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方法)
internal static ihttphandler getapplicationinstance(httpcontext context)
{
if (_customapplication != null)
{
return _customapplication;
}
if (context.request.isdebuggingrequest)
{
return new httpdebughandler();
}
_theapplicationfactory.ensureinited();
_theapplicationfactory.ensureapps教程tartcalled(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)
bad request");
{
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("
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);
}
}
另外,httpapplication,意味着他是整个站点的boss,我们定义的myhttpmodule不过是他众多modules里的其中之一。而且我们也可以定义多个module,config里面add多个就可以了。
在application初始化的过程中,有初始化module的一段,我贴出来大家看看:
private void initmodules()
{
this._modulecollection = runtimeconfig.getappconfig().httpmodules.createmodules();
this.initmodulescommon();
}
最后,初始化所有的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里。