前面三节讲了控件的构造、呈现和数据绑定,我想该差不多了。本想讲一个自定义控件来终结控件部分,但是我个人不太喜欢控件这些东西,所以也就懒的写相关的
内容,抱歉了。虽然我不喜欢使用控件,但我还是喜欢整个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 Init(HttpApplication context)
|
context.Context.Response.Write(1);
|
< compilation debug = "true" targetFramework = "4.0" />
|
< add name = "MyHttpModule" type = "WebApplication1.MyHttpModule,WebApplication1" />
|
< modules runAllManagedModulesForAllRequests = "true" >
|
< add name = "MyHttpModule" type = "WebApplication1.MyHttpModule,WebApplication1" />
|
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 ;
|
if ( this ._numFreeAppInstances > 0)
|
application = (HttpApplication) this ._freeList.Pop();
|
this ._numFreeAppInstances--;
|
if ( this ._numFreeAppInstances < this ._minFreeAppInstances)
|
this ._minFreeAppInstances = this ._numFreeAppInstances;
|
application = (HttpApplication) HttpRuntime.CreateNonPublicInstance( this ._theApplicationType);
|
using ( new ApplicationImpersonationContext())
|
application.InitInternal(context, this ._state, this ._eventHandlerMethods);
|
跟踪这个方法,我们可以断定,Application是被缓存起来的,不是每次都是实例化的。
通过Reflector的分析,我们能发现这个GetApplicationInstance方法是被HttpRuntime.ProcessRequestNow调用的,终于回到我们的Request来了。
private void ProcessRequestNow(HttpWorkerRequest wr)
|
context = new HttpContext(wr, false );
|
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.SetEndOfSendNotification( this ._asyncEndOfSendCallback, context);
|
Interlocked.Increment( ref this ._activeRequestCount);
|
HostingEnvironment.IncrementBusyCount();
|
this .EnsureFirstRequestInit(context);
|
if (!context.Request.IsDebuggingRequest)
|
context.Response.InitResponseWriter();
|
IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);
|
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)
|
IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
|
context.AsyncAppHandler = handler2;
|
handler2.BeginProcessRequest(context, this ._handlerCompletionCallback, context);
|
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());
|
最后,初始化所有的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 );
|
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;
|
UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;
|
flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > 0);
|
steps.Add( new HttpApplication.ValidatePathExecutionStep(app));
|
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);
|
-
六六分期app的软件客服如何联系?不知道吗?加qq群【895510560】即可!标题:六六分期
阅读:19266|2023-10-27
-
今天小编告诉大家如何处理win10系统火狐flash插件总是崩溃的问题,可能很多用户都不知
阅读:10011|2022-11-06
-
今天小编告诉大家如何对win10系统删除桌面回收站图标进行设置,可能很多用户都不知道
阅读:8338|2022-11-06
-
今天小编告诉大家如何对win10系统电脑设置节能降温的设置方法,想必大家都遇到过需要
阅读:8705|2022-11-06
-
我们在使用xp系统的过程中,经常需要对xp系统无线网络安装向导设置进行设置,可能很多
阅读:8652|2022-11-06
-
今天小编告诉大家如何处理win7系统玩cf老是与主机连接不稳定的问题,可能很多用户都不
阅读:9680|2022-11-06
-
电脑对日常生活的重要性小编就不多说了,可是一旦碰到win7系统设置cf烟雾头的问题,很
阅读:8639|2022-11-06
-
我们在日常使用电脑的时候,有的小伙伴们可能在打开应用的时候会遇见提示应用程序无法
阅读:8008|2022-11-06
-
今天小编告诉大家如何对win7系统打开vcf文件进行设置,可能很多用户都不知道怎么对win
阅读:8675|2022-11-06
-
今天小编告诉大家如何对win10系统s4开启USB调试模式进行设置,可能很多用户都不知道怎
阅读:7544|2022-11-06
请发表评论