您的位置:首页 > 理论基础 > 计算机网络

ASP.NET处理用户请求的流程 IHttpModule , IHttpHandler 管道事件

2017-09-27 14:30 926 查看
参考资料:ASP.NET运行时详解 集成模式和经典模式   ,Asp.net 处理程序(第五篇)

IIS处理用户请求流程

当浏览器向服务器发送一个Http请求,首先浏览器是根据用户输入的URL地址去访问DNS,然后获取URL对应的IP地址和端口号,然后来封装一个http请求报文,然后通过Socket发送到服务器 (我们知道Socket与服务器连接需要IP地址和端口号的),在服务器端 有一个运行在windows内核模式下的http.sys核心组件,这个核心组件是可以监听任何浏览器对服务器80端口的请求

【注:windows把cpu运行的时候分成2中模式,一种是内核模式,另外一种是用户模式。

用户模式:我们一般运行的应用程序是在用户模式下运行的,用户模式下,当你启动一个应用程序以后,它会给你启动对应的进程,然后会给这个进程分配对应的地址空间来运行我们的程序,这样即便我们的应用程序崩溃了也不会影响其他的应用程序的正常运行。  

内核模式:windows操作系统核心的组件是在内核模式下运行的,所有的内核模式下运行的代码是共享单个虚拟地址空间的(即:共享一个地址空间的)所以内核模式下的代码一旦奔溃,操作系统就崩溃了,而用户模式下的某个应用程序崩溃并不会影响操作系统的正常运行。

windows操作CPU 会自动切换内核模式和用户模式来执行程序】

http.sys这个运行在内核模式的核心组件监听到浏览器的http请求以后,并不是由它来处理浏览器的请求,它首先会读取系统的注册表,然后从注册表中查询当前哪个程序可以处理对80端口的http请求,如果系统中安装了IIS,它就会监测到注册表里IIS这个程序(进程)可以对这个80端口的http请求进行处理(如果没有安装,这个http.sys核心组件发现没有可以处理对这个80端口的请求的应用程序,,http.sysy就不不理会了,所以就也不会有响应了)  因为inetinfo.exe是IIS后台服务的核心进程,所以http.sys会把请求交给inetinfo.exe这个进程去处理。
 

IIS接收到浏览器对这个80端口请求以后,会查找IIS自己的配置信息,根据请求报文的URL后缀名来判断浏览器请求的是什么类型的资源(静态资源或动态资源)

如果浏览器请求的是静态资源,会启动一个叫w3wp.exe的进程(因为w3wp.exe是浏览器请求的网站的应用程序池里面的进程,而IIS开始处理请求的时候又会启动一个w3wp.exe进程,所以我猜测IIS是根据浏览器请求url的端口号来确定到底是启动哪个网站对应的应用程序池里的w3wp.exe进程),

 w3wp.exe会根据URL路径去读取磁盘上的静态资源,然后把读取到的结果返回给inetinfo.exe(即:IIS),然后IIS又把结果返回给http.sys这个内核模块的核心组件,由它来将结果返回给浏览器。

如果浏览器请求的是动态资源,IIS无法直接处理,于是它会在启动w3wp.exe这个进程之前先寻找存放在Metabase中的处理程序映射表(又名扩展程序映射表,ISAPI扩展)找处理这个应用程序(aspx,ashx)所对应的ISAPI DLL组件,于是找到了aspnet_isapi.dll这个组件(aspx,ashx对应的处理程序是aspnet_isapi.dll),

然后启动w3wp.exe进程,w3wp.exe会加载并运行aspnet_isapi.dll这个组件来对Http请求做处理(注:aspnet_isapi.dll是一个C++写的文件,不是.net程序集,所以这个aspnet_isapi.dll会在寄宿在w3wp.exe这个进程中开始执行) 

(注:处理程序映射表不但配置了处理应用程序所对应的ISAPI DLL组件,还配置了这个应用程序应该使用哪个Handler来处理。从IIS的处理程序映射中可以看到aspx使用System.Web.UI.PageHandlerFactory工厂类来创建处理它的HttpHandler ,而ashx使用System.Web.UI.SimpleHandlerFactory工厂类来创建处理它的HttpHandler)

这里我们说说ISAPI扩展

除了映射文件与其对应的处理程序以外,ISAPI 还需要做一些其他的工作:

1>从HTTP.SYS中获取当前的Httq请求信息,并且将这些信息保存到 HttpWorkerRequest 类中。

2>在相互隔离的应用程序域AppDomain中加载HttpRuntime。

3>调用 HttpRuntime的ProcessRequest方法。

接下来才是程序员通常编写的代码所完成的工作了,然后,IIS 接收返回的数据流,并重新返还给 HTTP.SYS,最后,HTTP.SYS 再将这些数据返回给客户端浏览器。

那这个aspnet_isapi.dll是如何执行的呢?

第一步:aspnet_isapi.dll开始启动.net运行时(运行环境),创建应用程序域(即:启动.net runtime, 启动.net运行时后会执行这么几个方法:
1>获取一个实现了IISAPIRuntime接口类型的对象,这个类型就是ISAPIRuntime,然后调用该对象的public int ProcessRequest(IntPtr ecb, int iWRType)方法,这个参数ecb其实就是HTTP的请求报文,

在这个方法中对浏览器的请求报文做了简单的封装,把请求报文数据封装成了一个ISAPIWorkerRequest类型的对象(这个对象名字叫wr),

注意:这个封装是及其简单的,不像HttpContext那么详细。 

2>开始调用HttpRuntime.ProcessRequestNoDemand(wr)方法,在方法中又调用了ProcessRequestNow(wr)方法,而在ProcessRequestNow(wr)方法中又调用了HttpRuntime._theRuntime.ProcessRequestInternal(wr);方法,然后在这个方法中创建了上下文容器类对象【context = new HttpContext(wr, false)】 并将这个wr作为参数传创建并初始化了HttpRequest,HttpResponse类对象(注:HttpContext对象里面有HttpRequest类型的属性Request,和HttpResponse类型的属性Response)。【注:当Http请求进入
Asp.Net Runtime以后,它的管道由托管模块(NOTE:Managed Modules)和处理程序(NOTE:Handlers)组成,并且由管道来处理这个 Http请求,标志着应用程序生命周期开始】

3>同样在这个private void ProcessRequestInternal(HttpWorkerRequest wr)方法中接着又执行了

IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(httpContext);代码块,将上下文容器类对象作为参数,

以工厂的形式创建了HttpApplication类对象(这个HttpApplication对象其实就是global.asax,global.asax是继承自HttpApplication的,前提是项目根目录下有global.asax文件,如果没有的话它就是创建的HttpApplication类对象) ,在获取这个HttpApplication类对象的过程中执行了Application_Start(object sender, EventArgs
e)方法

第二步: 既然已经创建了HttpApplication对象了,那么在HttpApplication这个类中又做了什么事情呢?

1>创建并初始化了HttpModules类对象(创建HttpModules类对象分集成模式下和经典模式下创建,请看下面代码)

//注:我们在IIS中找到自己的网站,然后点击右边的"基本设置" ->选择(应用程序池)来选择这个网站使用集成模式还是使用经典模式
if (HttpRuntime.UseIntegratedPipeline) //HttpRuntime的UseIntegratedPipeline作为判断条件。UseIntegratedPipeline用来判断是否是集成模式,也就是IIS7模式。
{
try
{
context.HideRequestResponse = true;
this._hideRequestResponse = true;
this.InitIntegratedModules(); //如果是集成模式(IIS7模式)就调用this.InitIntegratedModules()方法来创建并初始化所有的HttpModule
goto IL_6B;
}
finally
{
context.HideRequestResponse = false;
this._hideRequestResponse = false;
}
}
this.InitModules(); //如果是经典模式(IIS6)则调用this.InitModules()方法来创建并初始化所有的HttpModule ,其中包括了配置文件中的和动态注册的


2>注册了19个管道事件(我数了一下,好像是22个事件)

3>创建并初始化了HttpHandler类对象

4>调用HttpHandler类对象执行ProcessRequest(context)方法

5>HttpHandler处理完以后,Http请求再一次回到Module,此时Module可以做一些某个工作已经完成了之后的事情。

请看以下代码:[主干代码] 这是在HttpRuntime类中

//从System.Web.Hosting命名空间的开始进入到ISAPIRuntime类中的public int ProcessRequest(IntPtr ecb, int iWRType) 方法【 这个参数ecb就是上面所说的HTTP请求报文】
//然后执行到HttpRuntime.ProcessRequestNoDemand(iSAPIWorkerRequest)代码块,在这个代码块中进入到了System.Web下面的 HttpRuntime类中的private void ProcessRequestInternal(HttpWorkerRequest wr)方法]
private void ProcessRequestInternal(HttpWorkerRequest wr)
{
Interlocked.Increment(ref this._activeRequestCount);
if (this._disposingHttpRuntime)
{
try
{
wr.SendStatus(503, "Server Too Busy");
wr.SendKnownResponseHeader(12, "text/html; charset=utf-8");
byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Server Too Busy</body></html>");
wr.SendResponseFromMemory(bytes, bytes.Length);
wr.FlushResponse(true);
wr.EndOfRequest();
}
finally
{
Interlocked.Decrement(ref this._activeRequestCount);
}
return;
}
HttpContext httpContext;
try
{
//创建上下文容器类对象
httpContext = new HttpContext(wr, false); //在这里容器类中创建了HttpRequest,HttpResponse类对象(调用wr参数初始化这两个类对象)
}
catch
{
try
{
wr.SendStatus(400, "Bad Request");
wr.SendKnownResponseHeader(12, "text/html; charset=utf-8");
byte[] bytes2 = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");
wr.SendResponseFromMemory(bytes2, bytes2.Length);
wr.FlushResponse(true);
wr.EndOfRequest();
return;
}
finally
{
Interlocked.Decrement(ref this._activeRequestCount);
}
}
wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, httpContext);
HostingEnvironment.IncrementBusyCount();
try
{
try
{
this.EnsureFirstRequestInit(httpContext);
}
catch
{
if (!httpContext.Request.IsDebuggingRequest)
{
throw;
}
}
httpContext.Response.InitResponseWriter();

//接着在这里创建了HttpApplication类对象(通过工厂的形式创建)参数是上下文容器类对象httpContext,其实在获取这个HttpApplication对象的过程中就已经执行了Application_Start(object sender, EventArgs e)方法
IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(httpContext);
if (applicationInstance == null)
{
throw new HttpException(SR.GetString("Unable_create_app_object"));
}
if (EtwTrace.IsTraceEnabled(5, 1))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, httpContext.WorkerRequest, applicationInstance.GetType().FullName, "Start");
}
if (applicationInstance is IHttpAsyncHandler)
{
IHttpAsyncHandler httpAsyncHandler = (IHttpAsyncHandler)applicationInstance;
httpContext.AsyncAppHandler = httpAsyncHandler;
httpAsyncHandler.BeginProcessRequest(httpContext, this._handlerCompletionCallback, httpContext);//启动对 HTTP 处理程序的异步调用。
}
else
{
applicationInstance.ProcessRequest(httpContext);

this.FinishRequest(httpContext.WorkerRequest, httpContext, null);
}
}
catch (Exception e)
{
httpContext.Response.InitResponseWriter();
this.FinishRequest(wr, httpContext, e);
}
}
请看HttpRequest与HttpResponse类对象的创建与初始化代码 [主干代码里的子代码块]

internal HttpContext(HttpWorkerRequest wr, bool initResponseWriter)
{
this._wr = wr;
this.Init(new HttpRequest(wr, this), new HttpResponse(wr, this)); //在这里调用ws参数创建并初始化了HttpRequest,HttpResponse两个类对象
if (initResponseWriter)
{
this._response.InitResponseWriter();
}
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING);
}


在来看HttpApplication类中的InitInterrnal方法 就是在这里面创建并初始化了HttpModuler和HttpHandler

internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers)
{
this._state = state;
PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES);
try
{
try
{
this._initContext = context;
this._initContext.ApplicationInstance = this;
context.ConfigurationPath = context.Request.ApplicationPathObject;
using (new DisposableHttpContextWrapper(context))
{
//注:我们在IIS中找到自己的网站,然后点击右边的"基本设置" ->选择(应用程序池)来选择这个网站使用集成模式还是使用经典模式 if (HttpRuntime.UseIntegratedPipeline) //HttpRuntime的UseIntegratedPipeline作为判断条件。UseIntegratedPipeline用来判断是否是集成模式,也就是IIS7模式。 { try { context.HideRequestResponse = true; this._hideRequestResponse = true; this.InitIntegratedModules(); //如果是集成模式(IIS7模式)就调用this.InitIntegratedModules()方法来创建并初始化所有的HttpModule goto IL_6B; } finally { context.HideRequestResponse = false; this._hideRequestResponse = false; } } this.InitModules(); //如果是经典模式(IIS6)则调用this.InitModules()方法来创建并初始化所有的HttpModule ,其中包括了配置文件中的和动态注册的
IL_6B:
if (handlers != null)
{
//HookupEventHandlersForApplicationAndModules方法接收了一个handlers参数,handler中存放了Application的所有Application事件(如:BeginRequest,AuthorizeRequest等等22个事件)
//所有HookupEventHandlersForApplicationAndModules的工作就是遍历handlers事件,分别执行。
this.HookupEventHandlersForApplicationAndModules(handlers);
}
this._context = context;
if (HttpRuntime.UseIntegratedPipeline && this._context != null)
{
this._context.HideRequestResponse = true;
}
this._hideRequestResponse = true;
try
{
this.Init();
}
catch (Exception error)
{
this.RecordError(error);
}
}
if (HttpRuntime.UseIntegratedPipeline && this._context != null)
{
this._context.HideRequestResponse = false;
}
this._hideRequestResponse = false;
this._context = null;
this._resumeStepsWaitCallback = new WaitCallback(this.ResumeStepsWaitCallback);
if (HttpRuntime.UseIntegratedPipeline)
{
/*
在IIS7中,ASP.NET请求处理管道Pipeline直接覆盖了IIS本身的管道Pipeline,IIS整个流程按照ASP.ENT管道流程执行,例如BeginRequest、AuthenticateRequest、…、EndRequest。而不是通过插件扩展(ISAPI)形式,不同的内容(jpg、html、php等)通过不同的插件来执行。
IIS7的优势体现在哪里?开发人员可以实现自定义的HttpModule或者HttpHandler,然后直接集成到IIS上,例如,我可以自定义一个JpgHttpHandler,然后通过IIS的Handler部署方式,把JpgHttpHandler部署到IIS上,处理所有的请求格式为*.jpg的请求。
至于怎样把自定义的HttpModule或者HttpHandler部署到IIS上并处理指定格式的请求。这里我就不详细介绍了
*/
/*
在new HttpApplication.PipelineStepManager(this) 这里new了一个PipelineStepManager类对象,传递了一个this(这个this就是一个HttpApplication类对象)
在这个PipelineStepManager类中有一个internal override void BuildSteps(WaitCallback stepCallback) 方法
在这个方法中有一个句HttpApplication.IExecutionStep step = new HttpApplication.MaterializeHandlerExecutionStep(application); 代码块,
这句代码创建了一个MaterializeHandlerExecutionStep类对象,传递的参数是一个HttpApplication对象
这个类对象中执行了一句IHttpHandlerFactory factory = this._application.GetFactory(text); 代码 这段代码就是通过工厂的模式创建了一个IHttpHandlerFactory对象
接着又执行了handler = factory.GetHandler(context, request.RequestType, request.FilePath, physicalPathInternal);代码 创建了处理这个应用程序的真正处理HttpHandler对象
接着又执行了HttpApplication.IExecutionStep step2 = new HttpApplication.CallHandlerExecutionStep(application);代码 C在这里allHandlerExecutionStep类中执行了handler.ProcessRequest(context);方法
而这个handler.ProcessRequest(context);方法就是处理用户请求的真正方法。

*/
this._stepManager = new HttpApplication.PipelineStepManager(this); //如果是集成模式(IIS7)
}
else
{
this._stepManager = new HttpApplication.ApplicationStepManager(this); //如果是经典模式(IIS6)
}
this._stepManager.BuildSteps(this._resumeStepsWaitCallback);
}
finally
{
this._initInternalCompleted = true;
context.ConfigurationPath = null;
this._initContext.ApplicationInstance = null;
this._initContext = null;
}
}
catch
{
throw;
}
}

在来看看HttpApplication类中执行了this._stepManager = new HttpApplication.PipelineStepManager(this);这段代码,然后从这段代码进入到PipelineStepManager类中

在这个类中有一个internal override void BuildSteps(WaitCallback stepCallback)方法

在这个方法中执行了一段HttpApplication.IExecutionStep step = new HttpApplication.MaterializeHandlerExecutionStep(application);代码

而在这个MaterializeHandlerExecutionStep类中获取到了最终处理用户请求的HttpHandler

internal class MaterializeHandlerExecutionStep : HttpApplication.IExecutionStep
{
private HttpApplication _application;

bool HttpApplication.IExecutionStep.CompletedSynchronously
{
get
{
return true;
}
}

bool HttpApplication.IExecutionStep.IsCancellable
{
get
{
return false;
}
}

internal MaterializeHandlerExecutionStep(HttpApplication app)
{
this._application = app;
}

void HttpApplication.IExecutionStep.Execute()
{
HttpContext context = this._application.Context;
HttpRequest request = context.Request;
IHttpHandler handler = null;
string text = null;
if (EtwTrace.IsTraceEnabled(5, 1))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_MAPHANDLER_ENTER, context.WorkerRequest);
}
IIS7WorkerRequest iIS7WorkerRequest = context.WorkerRequest as IIS7WorkerRequest;
if (context.RemapHandlerInstance != null)
{
iIS7WorkerRequest.SetScriptMapForRemapHandler();
context.Handler = context.RemapHandlerInstance;
}
else if (request.RewrittenUrl != null)
{
bool flag;
text = iIS7WorkerRequest.ReMapHandlerAndGetHandlerTypeString(context, request.Path, out flag);
if (!flag)
{
throw new HttpException(404, SR.GetString("Http_handler_not_found_for_request_type", new object[]
{
request.RequestType
}));
}
}
else
{
text = iIS7WorkerRequest.GetManagedHandlerType();
}
if (!string.IsNullOrEmpty(text))
{
IHttpHandlerFactory factory = this._application.GetFactory(text);
string physicalPathInternal = request.PhysicalPathInternal;
try
{
//在这里拿到了最终处理用户请求的HttpHandler
handler = factory.GetHandler(context, request.RequestType, request.FilePath, physicalPathInternal);
}
catch (FileNotFoundException innerException)
{
if (HttpRuntime.HasPathDiscoveryPermission(physicalPathInternal))
{
throw new HttpException(404, null, innerException);
}
throw new HttpException(404, null);
}
catch (DirectoryNotFoundException innerException2)
{
if (HttpRuntime.HasPathDiscoveryPermission(physicalPathInternal))
{
throw new HttpException(404, null, innerException2);
}
throw new HttpException(404, null);
}
catch (PathTooLongException innerException3)
{
if (HttpRuntime.HasPathDiscoveryPermission(physicalPathInternal))
{
throw new HttpException(414, null, innerException3);
}
throw new HttpException(414, null);
}
context.Handler = handler;
if (this._application._handlerRecycleList == null)
{
this._application._handlerRecycleList = new ArrayList();
}
this._application._handlerRecycleList.Add(new HandlerWithFactory(handler, factory));
}
if (EtwTrace.IsTraceEnabled(5, 1))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_MAPHANDLER_LEAVE, context.WorkerRequest);
}
}
}

总结:

由ISAPI aspnet_isapi.dll加载CLR环境。

1>IsapiRuntime运行时被加载,接管了HTTP请求后,创建IsapiWorkerRequest对象来封装当前的HTTP请求,随后把对象传递给ASP.NET运行时HttpRuntime。

2>HttpRuntime会根据IsapiWorkerRequest对象创建表示当前HTTP请求的上下文对象HttpContext。

3>HttpApplicationFactory会创建或者从HttpApplication对向池中选取可用的HttpApplication对象,处理完后释放。

4>HttpApplication在初始化中,ASP.NET会根据配置文件加载HttpModule对象(用于注册HttpApplication对应的事件,将所需的操作注入Http请求的处理流程)

5>接下来Http请求通过一系列Module,这些Module对Http请求具有完全的控制权。这些Module可以做一些执行某个实际工作前的事情。

6> Http请求经过所有的Module之后,它会被HttpHandler处理。在这一步,执行实际的一些操作,通常也就是.aspx页面所完成的业务逻辑。可能你会觉得在创建.aspx页面并没有体会到这一过程,但是,你一定知道,.aspx 页面继承自Page类,我们看一下Page类的签名:

//摘要: 表示从 ASP.NET Web 应用程序的宿主服务器请求的 .aspx 文件(又称为 Web 窗体页)。
public class Page : TemplateControl, IHttpHandler
{
//代码省略
}


6>Http请求最终由HttpHandler处理。不同的资源类型对应不同的HttpHandler。

7>HttpHandler处理完以后,Http请求再一次回到Module,此时Module可以做一些某个工作已经完成了之后的事情。



最后我们通过代码来看看,Application_Start(object sender, EventArgs e)方法是在哪里执行的

//从System.Web.Hosting命名空间的开始进入到ISAPIRuntime类中的public int ProcessRequest(IntPtr ecb, int iWRType) 方法【 这个参数ecb就是上面所说的HTTP请求报文】
//然后执行到HttpRuntime.ProcessRequestNoDemand(iSAPIWorkerRequest)代码块,在这个代码块中进入到了System.Web下面的 HttpRuntime类中的private void ProcessRequestInternal(HttpWorkerRequest wr)方法]
//然后执行到IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(httpContext); 这段代码,这个代码是获取HttpApplication类对象
//那我们就进入这个HttpApplicationFactory类的 GetApplicationInstance(HttpContext context)中看看,
// System.Web.HttpApplicationFactory
internal static IHttpHandler GetApplicationInstance(HttpContext context)
{
if (HttpApplicationFactory._customApplication != null)
{
return HttpApplicationFactory._customApplication;
}
if (context.Request.IsDebuggingRequest)
{
return new HttpDebugHandler();
}
HttpApplicationFactory._theApplicationFactory.EnsureInited();

//EnsureAppStartCalled(context); 方法中执行了Global类中的 Application_Start(object sender, EventArgs e)方法
HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context);

//然后将HttpApplication类返回(这个HttpApplication类对象其实就是Global)
return HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context);
}

//在来看看HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context); 方法
// System.Web.HttpApplicationFactory
private void EnsureAppStartCalled(HttpContext context)
{
if (!this._appOnStartCalled)
{
lock (this)
{
if (!this._appOnStartCalled)
{
using (new DisposableHttpContextWrapper(context))
{
WebBaseEvent.RaiseSystemEvent(this, 1001);
this.FireApplicationOnStart(context); //在这里执行了Application_Start(object sender, EventArgs e)方法
}
this._appOnStartCalled = true;
}
}
}
}

//那就在来看看FireApplicationOnStart(context)方法

// System.Web.HttpApplicationFactory
private void FireApplicationOnStart(HttpContext context)
{
if (this._onStartMethod != null)
{
HttpApplication specialApplicationInstance = this.GetSpecialApplicationInstance();
//这个this._onStartMethod 其实就是Application_Start(object sender, EventArgs e)方法
specialApplicationInstance.ProcessSpecialRequest(context, this._onStartMethod, this._onStartParamCount, this, EventArgs.Empty, null);
this.RecycleSpecialApplicationInstance(specialApplicationInstance);
}
}




Application中有26个方法,从上到下有19个请求管道事件

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: