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

   2023-02-09 学习力0
核心提示:    前面三节讲了控件的构造、呈现和数据绑定,我想该差不多了。本想讲一个自定义控件来终结控件部分,但是我个人不太喜欢控件这些东西,所以也就懒的写相关的内容,抱歉了。虽然我不喜欢使用控件,但我还是喜欢整个WebForm的设计。一个字:“太神了”。前

    前面三节讲了控件的构造、呈现和数据绑定,我想该差不多了。本想讲一个自定义控件来终结控件部分,但是我个人不太喜欢控件这些东西,所以也就懒的写相关的 内容,抱歉了。虽然我不喜欢使用控件,但我还是喜欢整个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:我们 公司面试的笔试题里有道题“给一个正在运行的网站增加一个异常监控功能,该怎么实现?” 你能想到怎么做吗?:)  

出处:http://www.cnblogs.com/mad/archive/2011/04/11/asp-net-httpmodule-and-httpapplication.html

    前面三节讲了控件的构造、呈现和数据绑定,我想该差不多了。本想讲一个自定义控件来终结控件部分,但是我个人不太喜欢控件这些东西,所以也就懒的写相关的 内容,抱歉了。虽然我不喜欢使用控件,但我还是喜欢整个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:我们 公司面试的笔试题里有道题“给一个正在运行的网站增加一个异常监控功能,该怎么实现?” 你能想到怎么做吗?:)  

 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • 使用WebClient自动填写并提交ASP.NET页面表单的源代码
    使用WebClient自动填写并提交ASP.NET页面表单的
    转自:http://www.cnblogs.com/anjou/archive/2007/03/07/667253.html 在.NET中通过程序填写和提交表单还是比较简单。比如,要提交一个如下图所示的登录表单:           填写和提交以上表单的代码如下:       // 要提交表单的URI字符串
    02-09
  • asp.net mvc多条件+分页查询解决方案
    


            
asp.net mvc多条件+分页查询解决方案
    asp.net mvc多条件+分页查询解决方案
    http://www.cnblogs.com/nickppa/p/3232535.html开发环境vs2010css:bootstrapjs:jquery    bootstrap paginator原先只是想做个mvc的分页,但是一般的数据展现都需要检索条件,而且是多个条件,所以就变成了MVC多条件+分页查询因为美工不是很好,所以用的是
    02-09
  • ASP.NET操作Cookies的问题(Bug or Not)
    以下存和取都是在不同的页面中,如果是在同一个页面也没必要用cookies了。 Test1: 给Cookies赋值: const string AAA="aaa"; Response.Cookies[AAA].Value = "111;222;333"; 取值: string value = Request.Cookies[AAA].Value; // value为111 Test2: 给Cooki
    02-09
  • Asp.Net Core 自定义验证属性
      很多时候,在模型上的验证需要自己定义一些特定于我们需求的验证属性。所以这一篇我们就来介绍一下怎么自定义验证属性。  我们来实现一个验证邮箱域名的自定义验证属性,当然,最重要的是需要定义一个继承自ValidationAttribute的类,然后在实现其IsVal
    02-09
  • Asp.Net 之 枚举类型的下拉列表绑定
    有这样一个学科枚举类型:/// 学科 /// /summary public enum Subject {None = 0,[Description("语文")]Chinese = 1,[Description("数学")]Mathematics = 2,[Description("英语")]English = 3,[Description("政治")]Politics = 4,[Description("物理&qu
    02-09
  • [ASP.NET笔记] 1.Web基础知识
         1:http协议:     2:web服务器:     3:静态网页的概念     4:动态网页的概念       http协议:http(hypertext transfer protocol) 即超文本传输协议,这个协议是在internet上进行信息传送的协议任何网页之间要相互沟通,必须要尊循
    02-09
  • ASP.NET邮件发送 .net 发送邮件
      今天做了个ASP.NET做发送邮件功能,发现QQ邮箱好奇怪,当你用QQ邮箱做服务器的时候什么邮件都发送不出去(QQ邮箱除外)。而且爆出这样的错误:"邮箱不可用。 服务器响应为: Error: content rejected.http://mail.qq.com/zh_CN/help/content/rejectedmail.ht
    02-09
  • 由ASP.NET Core根据路径下载文件异常引发的探究
    前言    最近在开发新的项目,使用的是ASP.NET Core6.0版本的框架。由于项目中存在文件下载功能,没有使用类似MinIO或OSS之类的分布式文件系统,而是下载本地文件,也就是根据本地文件路径进行下载。这其中遇到了一个问题,是关于如何提供文件路径的,通
    02-09
  • ASP.NET的运行原理与运行机制 ASP.NET的开发模式包括
    ASP.NET的运行原理与运行机制 ASP.NET的开发模
    在Asp.net4和4.5中,新增了WebPages Framework,编写页面代码使用了新的Razor语法,代码更加的简洁和符合Web标准,编写方式更接近于PHP和以前的Asp,和使用WebForms这种模仿Windows Form编程方式有了很大不同,不再有大量控件和控件生成的大量不够灵活的代码
    02-09
  • ASP.NET 后台接收前台POST过来的json数据方法
     ASP.NET前后台交互之JSON数据 https://www.cnblogs.com/ensleep/p/3319756.html
    02-09
点击排行