浦口区建设中学网站,百度拉新推广平台,网站建设时间,厦门市同安区建设工程质量安全监督站网站开篇#xff1a;上一篇我们了解了一个请求从客户端发出到服务端接收并转到ASP.Net处理入口的过程#xff0c;这篇我们开始探索ASP.Net的核心处理部分#xff0c;借助强大的反编译工具#xff0c;我们会看到几个熟悉又陌生的名词#xff08;类#xff09;#xff1a;Http…开篇上一篇我们了解了一个请求从客户端发出到服务端接收并转到ASP.Net处理入口的过程这篇我们开始探索ASP.Net的核心处理部分借助强大的反编译工具我们会看到几个熟悉又陌生的名词类HttpRuntime、HttpWorkerRequest、HttpContext、HttpApplication等。 1Part 1前奏 2Part 2核心 3Part 3管道 4Part 4WebForm页面生命周期 5Part 5MVC页面声命周期 一、第一个入口ISAPIRuntme.ProcessRequest() ISAPIRuntime是进入NET托管环境的入口它在方法中通过一个ecb句柄指向了当前请求报文体的内存地址将HTTP请求报文简单封装为一个HttpWorkerRequest对象然后就是各种我们经常听到的PRProcessRequest方法了。
①调用ISAPIRuntime对象的ProcessRequest方法进入ASP.NET处理流程 通过Reflector我们可以看到在ISAPIRuntime中的这个入口方法ProcessRequest ②首先根据ecb句柄创建HttpWorkerRequest对象封装原始请求报文 关于HttpWorkerRequest 在Asp.Net中准备用于处理的请求都必须封装为HttpWorkerRequest类型的对象HttpWorkerRequest是一个抽象类型。这里创建的是一个ISAPIWorkerRequest类型它继承于HttpWorkerRequest类。 [ComVisible(false)]
public abstract class HttpWorkerRequest
{// Fieldsprivate DateTime _startTime;private Guid _traceId;......
} 转到ISAPIWorkerRequest类的CreateWorkerRequest方法中看到首先判断当前IIS服务器的版本IIS6 or IIS7?然后创建适合不同IIS的具体WorkerRequest对象默认都是InProc进程内的当然也有OutOfProc进程外的。 internal static ISAPIWorkerRequest CreateWorkerRequest(IntPtr ecb, bool useOOP)
{if (useOOP){EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero);if (EtwTrace.IsTraceEnabled(5, 1)){EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, false);}return new ISAPIWorkerRequestOutOfProc(ecb);}int num UnsafeNativeMethods.EcbGetVersion(ecb) 0x10;if (num 7){EtwTrace.TraceEnableCheck(EtwTraceConfigType.IIS7_ISAPI, ecb);}else{EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero);}if (EtwTrace.IsTraceEnabled(5, 1)){EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, true);}if (num 7){return new ISAPIWorkerRequestInProcForIIS7(ecb);}if (num 6){return new ISAPIWorkerRequestInProcForIIS6(ecb);}return new ISAPIWorkerRequestInProc(ecb);
} 由于HttpWorkerRequest类封装的请求报文很原始很复杂所以微软没有将其公开出来。
二、第二个入口HttpRuntime.ProcessRequest() HttpRuntime是ASP.NET请求处理的第二个入口。当请求进来首先进入HttpRuntime由HttpRuntime来决定如何处理请求。默认情况下在machine.config和Web.config中并没有显式定义httpRuntime节点但该节点是有默认值的如下 httpRuntime apartmentThreadingfalseappRequestQueueLimit5000delayNotificationTimeout5enabletrueenableHeaderCheckingtrueenableKernelOutputCachetrueenableVersionHeadertrueencoderType System.Web.Util.HttpEncoderexecutionTimeout110maxQueryStringLength 2048maxRequestLength4096maxUrlLength 260maxWaitChangeNotification0minFreeThreads8minLocalRequestFreeThreads4relaxedUrlToFileSystemMapping FalserequestLengthDiskThreshold80requestPathInvalidCharacters ,,*,%,,:,\requestValidationMode 4.0requestValidationType System.Web.Util.RequestValidatorrequireRootedSaveAsPathtruesendCacheControlHeadertrueshutdownTimeout90useFullyQualifiedRedirectUrlfalsewaitChangeNotification0 / 通常情况下我们可以在Web.config中更改httpRuntime节点的默认值如下 configurationsystem.webhttpRuntime maxRequestLength4000enable TruerequestLengthDiskThreshold512useFullyQualifiedRedirectUrlTrueexecutionTimeout45versionHeader1.1.4128//system.web
/configuration ①其次执行HttpRuntime的ProcessRequestNoDemand方法封装HttpContext对象 在HttpRuntime类中有一个称为ProcessRequestNoDemand的静态方法。这里创建并初始化HttpWorkerRequest对象后调用了HttpRuntime的这个ProcessRequestNoDemand方法。于是我们转到ProcessRequestNoDemand方法可以看到如下代码 internal static void ProcessRequestNoDemand(HttpWorkerRequest wr)
{RequestQueue queue _theRuntime._requestQueue;wr.UpdateInitialCounters();if (queue ! null){wr queue.GetRequestToExecute(wr);}if (wr ! null){CalculateWaitTimeAndUpdatePerfCounter(wr);wr.ResetStartTime();ProcessRequestNow(wr);}
} 该方法先从请求队列中取出一个请求然后更新请求的引用计数器的信息然后再将HttpWorkerRequest对象传入ProcessRequestNow方法来处理请求。 这里我们还可以看到_theRuntime这个字段它是HttpRuntime类的一个静态字段在HttpRuntime的静态构造函数中进行初始化。 public sealed class HttpRuntime
{// Fields......private static HttpRuntime _theRuntime;......
} 再回到ProcessRequestNoDemand方法中我们看到最后是由ProcessRequestNow方法来接棒。于是我们转到ProcessRequestNow方法再来看看
internal static void ProcessRequestNow(HttpWorkerRequest wr)
{_theRuntime.ProcessRequestInternal(wr);
}我们看到在这个方法中又调用了_theRuntime的实例方法ProcessRequestInternal还是把HttpWorkerRequest对象作为参数传递了过去。于是我们再转到ProcessRequestInternal这个方法中发现了一个重要的对象HttpContext对象。 private void ProcessRequestInternal(HttpWorkerRequest wr)
{Interlocked.Increment(ref this._activeRequestCount);if (this._disposingHttpRuntime){......}else{HttpContext context;try{context new HttpContext(wr, false);}catch{try{wr.SendStatus(400, Bad Request);wr.SendKnownResponseHeader(12, text/html; charsetutf-8);byte[] data Encoding.ASCII.GetBytes(htmlbodyBad Request/body/html);wr.SendResponseFromMemory(data, data.Length);wr.FlushResponse(true);wr.EndOfRequest();return;}finally{Interlocked.Decrement(ref this._activeRequestCount);}}...... }
} 在HttpContext的构造函数中根据HttpWorkerRequest对象创建了HttpContext对象这是一个重要的Http上下文对象两个重要类型的字段也随之被初始化HttpRequest对象和HttpResponse对象。 相信大家在进行ASP.NET开发时经常使用这两个类型的实例。例如我们可以通过HttpContext.Current获取到这个实例且该实例会在整个生命周期中存活我们通过它可以获取到一些常用对象如RequestResponseSession 等。 ②通过HttpApplicationFactory得到一个具体的HttpApplication实例 让我们再次回到HttpRuntime的ProcessRequestInternal这个方法中刚刚HttpContext对象被创建后紧接着又干了什么事让我们看看源码 private void ProcessRequestInternal(HttpWorkerRequest wr)
{Interlocked.Increment(ref this._activeRequestCount);if (this._disposingHttpRuntime){......}else{HttpContext context;...... IHttpHandler applicationInstance HttpApplicationFactory.GetApplicationInstance(context);......if (applicationInstance is IHttpAsyncHandler){IHttpAsyncHandler handler2 (IHttpAsyncHandler) applicationInstance;context.AsyncAppHandler handler2;handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);}else{applicationInstance.ProcessRequest(context);this.FinishRequest(context.WorkerRequest, context, null);}...... }
} 首先我们看到了一个非常熟悉的字眼IHttpHandler我们的ashx、aspx不都是实现了这个IHttpHandler接口的吗于是我们来看看这句IHttpHandler applicationInstance HttpApplicationFactory.GetApplicationInstance(context); 它是通过一个叫做HttpApplicationFactory的工厂类来获取了一个HttpApplication的实例并将HttpContext上下文对象作为参数传递了进去。于是怀着好奇心我们转到这个方法内部去看看 internal static IHttpHandler GetApplicationInstance(HttpContext context)
{......_theApplicationFactory.EnsureInited();_theApplicationFactory.EnsureAppStartCalled(context);return _theApplicationFactory.GetNormalApplicationInstance(context);
} 原来它是调用了一个实例方法GetNormalApplicationInstance来获取HttpApplication。于是我们再转到这个方法的内部去看看 private HttpApplication GetNormalApplicationInstance(HttpContext context)
{HttpApplication state null;lock (this._freeList){if (this._numFreeAppInstances 0){state (HttpApplication) this._freeList.Pop();this._numFreeAppInstances--;if (this._numFreeAppInstances this._minFreeAppInstances){this._minFreeAppInstances this._numFreeAppInstances;}}}if (state null){state (HttpApplication) HttpRuntime.CreateNonPublicInstance(this._theApplicationType);using (new ApplicationImpersonationContext()){state.InitInternal(context, this._state, this._eventHandlerMethods);}}......return state;
} 通过查看这段代码它首先维护着一个HttpApplication池_freeList本质上就是一个Stack栈然后判断可用的HttpApplication实例的数量_numFreeAppInstances是否大于0如果存在可用的则从池中出栈然后将可用数量减1。最后再判断可用的数量是否小于最低限制的数量如果小于那么则将最低限制的数量设置为目前可用的数量。 那么如果目前HttpApplication池暂时没有可用的实例呢我们看到了这一句state (HttpApplication) HttpRuntime.CreateNonPublicInstance(this._theApplicationType); 通过此段代码新建了一个新的HttpApplication实例通过继续深入查看原来是通过反射的方式将Global文件所编译后的类封装出来一个HttpApplication实例。 补充之一_theApplicationType是_theApplicationFactory.EnsureInited();中被赋值的 private void CompileApplication()
{this._theApplicationType BuildManager.GetGlobalAsaxType();......
} 补充之二全局事件中例如Application_Start方法如何保证只执行一次在_theApplicationFactory.EnsureAppStartCalled(context);方法中判断_appOnStartCalled标志如果是false则调用FireApplicationOnStart方法触发Application_Start方法然后更改_appOnStartCalled标志。 private void EnsureAppStartCalled(HttpContext context)
{if (!this._appOnStartCalled){lock (this){if (!this._appOnStartCalled){using (new DisposableHttpContextWrapper(context)){......this.FireApplicationOnStart(context);}this._appOnStartCalled true;}}}
} 三、第三个入口HttpApplication.Init() 在前两个入口中HttpApplication实例被创建现在HttpApplication需要进行初始化请求处理管道来分别处理ASP.Net WebForm或ASP.Net MVC等类型的页面的响应操作。
①初始化HttpModules 让我们再次回到HttpRuntime的ProcessRequestInternal这个方法中刚刚HttpApplication实例被创建后开始了一系列的初始化操作如下图所示调用了其InitInternal方法进行了初始化。 转到InitInternal方法内部发现调用了一个非常重要的方法InitModules()。 internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers)
{this._state state;PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES);try{......this.InitModules();......if (HttpRuntime.UseIntegratedPipeline){this._stepManager new PipelineStepManager(this);}else{this._stepManager new ApplicationStepManager(this);}this._stepManager.BuildSteps(this._resumeStepsWaitCallback);} catch { ...... }
} 在InitModules这个方法中首先通过读取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();
} 然后调用InitModulesCommon方法遍历上面这个_moduleCollection集合分别对其每一个HttpModule执行其对应的Init方法。 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();
} ②注册19个请求处理管道事件 接上述操作之后InitInternal方法内部还执行了这样一句this._stepManager.BuildSteps(this._resumeStepsWaitCallback); 它完成了19个请求处理管道事件的注册工作。 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.ValidateRequestExecutionStep(app));steps.Add(new HttpApplication.ValidatePathExecutionStep(app));if (flag){steps.Add(new HttpApplication.UrlMappingsExecutionStep(app));}/* 以下代码完成19个事件的注册 */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(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;
} 从上面的代码可知ApplicationStepManager对象的BuildSteps方法被调用完成HttpApplication 19个管道事件的注册。这个方法很重要它将创建各种HttpApplication.IExecutionStep保存到一个数组列表 _execSteps 中 internal override void BuildSteps(WaitCallback stepCallback)
{.....this._execSteps new HttpApplication.IExecutionStep[steps.Count];steps.CopyTo(this._execSteps);.....
} 这样做的目的在于便于在后面的BeginProcessRequest方法内部调用ResumeSteps方法依次执行这些对象的Execute()方法完成各个事件的执行。
③开始依次处理请求处理管道中的各个事件 让我们再返回到HttpRuntime中的ProcessRequestInternal方法中HttpApplication实例已创建好HttpModules已初始化请求处理管道中的19个事件也已经注册好现在需要的只是一一调用HttpModule中各个事件对应的执行方法即可。 private void ProcessRequestInternal(HttpWorkerRequest wr)
{......if (applicationInstance is IHttpAsyncHandler){IHttpAsyncHandler handler2 (IHttpAsyncHandler) applicationInstance;context.AsyncAppHandler handler2;handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);}......
} 在上述代码中通过执行BeginProcessRequest方法触发了ResumeSteps方法依次执行每个请求处理管道事件也就进入了我们所说的“请求处理管道”中。 IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{this._context context;this._context.ApplicationInstance this;this._stepManager.InitRequest();this._context.Root();HttpAsyncResult result new HttpAsyncResult(cb, extraData);this.AsyncResult result;if (this._context.TraceIsEnabled){HttpRuntime.Profile.StartRequest(this._context);}// 依次执行各个请求处理管道事件this.ResumeSteps(null); return result;
} 关于请求处理管道 HttpApplication 采用处理管道的方法进行处理将处理的过程分成多个步骤每个步骤通过事件的形式暴露给程序员这些事件按照固定的处理顺序依次触发程序员通过编写事件处理方法就可以自定义每一个请求的扩展处理过程。 对于 HttpApplication 来说到 ASP.NET 4.0 版本提供了19 个标准事件如下图所示 至于在请求处理管道中的细节我们在Part 3中再看今天就到此为止谢谢
四、核心过程总览
①ISAPIRuntime-HttpWorkerRequest-HttpRuntime ②HttpRuntime-HttpContext-HttpApplication ③到目前为止的总体流程概览 首先我们从自己的浏览器通过网络访问Web服务器当ASP.NET接收到第一个请求时将会创建一个应用程序域然后会创建一个宿主环境然后ASP.NET创建并初始化核心对象HttpContext、HttpRequest和HttpResponse然后创建HttpApplication对象的实例来启动应用程序通过进入请求处理管道来处理具体的请求
参考资料
1Darren Ji《ASP.NET MVC请求处理管道声明周期的19个关键环节》http://www.cnblogs.com/darrenji/p/3795661.html
2木宛城主《ASP.NET那点不为人知的事儿》http://www.cnblogs.com/OceanEyes/archive/2012/08/13/aspnetEssential-1.html
3Tony He《ASP.NET请求处理机制》http://www.cnblogs.com/cilence/archive/2012/05/28/2520712.html
4两会的博客《IIS是怎样处理ASP.NET请求的》http://www.cnblogs.com/hkncd/archive/2012/03/23/2413917.html
5wjn2000《ASP.NET请求处理过程IIS6》http://www.cnblogs.com/wjn2010/archive/2011/04/21/2024341.html
6农村出来的大学生《ASP.NET网页请求处理全过程反编译》http://www.cnblogs.com/poorpan/archive/2011/09/25/2190308.html 作者周旭龙
出处http://edisonchou.cnblogs.com/
本文版权归作者和博客园共有欢迎转载但未经作者同意必须保留此段声明且在文章页面明显位置给出原文链接。