一、ASP.NET运行原理
当一个http(abbr. 超文本传输协议 hypertext transport protocol )请求发送过来并被IIS机收到之后,IIS(IIS 互联网信息服务信息服务(Internet Information Services Internet)首先通过你请求的页面类型为其加载相应的dll文件,然后在处理过程中将这条请求发送给能够处理这条请求的模块,而在ASP.NET ( 动态服务器页面 Active Server Page,ASP )中这个模块就叫做HttpHandler,为什么aspx这样的文件可以被服务器处理,那是因为在服务器端有默认的HttpHandler专门处理aspx文件,IIS再将这条请求发送给能够处理这条请求的模块之前,还需要经过一些HttpModule,这些都是系统默认的Modules,而且在这个http请求传到HttpHandler之前要经过不同的HttpModuls的处理,这就像我们如果要乘坐国际航班飞抵异国他乡的话,在 你真正坐上飞机前,要经过购买机场建设费,办理登记手续核实身份,进行行李打包托运,进行安全检察,现在又要进行体温等检察一样,需要经一系列的严格的手 续。这样做有什么好处,一是为一些必须的过程,二是为了安全性,三是为了提高效率,四是为了我们能够在更多的环节上进行控制,即增强了我们的控制能力。
ASP.NET运行原理如图:
dispatched 英 /dɪ'spætʃt/ 派遣 [计] 发送 makeup 英 /meɪkʌp/n. 组成,构造;化妆品;<美>补考;天性,性格;补给,补充 复数 makeups
说明:HttpModule模块是一个组件,可以注册为ASP.NET 3.5请求生命周期的一部分,当处理该组件时,该组件可以读取或更改请求或响应。HttpModule模块通常用于执行需要监视每个请求的特殊任务,如安全或站点统计信息。
二、ASP.NET运行机制
图中为我们展示了一个http请求有可能经过的四条路线。
当你第一次访问这个页面时这个请求首先依次经过HttpModuls和HttpHandler的处理,而在HttpHandler的处理中服务器会为你转到你真正要访问的页面,然后通过ASP Engine来找到这个页面背后的类,并实例化为一个临时对象,在此过程中会触发一系列的事件,其中一部分的事件需要经过对象中的方法处理,之后服务器会将这个处理后页面移交给Response对象,然后由Response对象将这个页面发送到客户端。这就是第一条路线(123456)。
而当你在这个页面上进行重新提交一些信息,并继续向服务器发送请求时,因为你的服务器之间的会话已经建立,对于你所在的那个临时对象在服务器中已经建立,所以不用再经过初始化页面的工作,故这第二条路线是按照 HttpModuls,HttpHandler,然后直接与临时对象交互,然后返回的(789)。
第三条路线与第二条不同的是在处理请求时如果涉及到需要调用ASP Cache,既ASP 缓存的,而临时对象将会直接从ASP 缓存提取信息并返回(10、11、12、13)。
第四条路线就是当你刷新这个页面的时候,服务器接收到时发现这个请求先前已经处理过了,并将处理结果存储到由一个默认的HttpModule管理的输出缓存中了,那么我们就可以直接从这个缓存提取信息并返回,而无需再重新处理一遍了(14、15)。作,故这第二条路线是按照 HttpModuls,HttpHandler,然后直接与临时对象交互,然后返回的(789)。
第三条路线与第二条不同的是在处理请求时如果涉及到需要调用ASP Cache,既ASP 缓存的,而临时对象将会直接从ASP 缓存提取信息并返回(10、11、12、13)。
第四条路线就是当你刷新这个页面的时候,服务器接收到时发现这个请求先前已经处理过了,并将处理结果存储到由一个默认的HttpModule管理的输出缓存中了,那么我们就可以直接从这个缓存提取信息并返回,而无需再重新处理一遍了(14、15)。
------------------------------------------------------------------------------------------------------------------------------
一、请求
用户在浏览器的地址栏中输入一个地址:url,点击确定按钮,这个时候一个Web请求就产生了。浏览器根据我们请求的地址封装http请求报文,,然后通过socket通信将请求报文发送到服务器端。至于具体的浏览器如何封装请求报文、客户端服务器端通过三次握手建立通信、DNS域名解析、Http协议等内容。至此,浏览器的工作告一段落,它就等着从服务器端接收响应。
当敲一个域名到浏览器上面,然后回车的时候,如:http://www.baidu.com/index.aspx
浏览器会按照HTTP协议的语法生成相应的请求报文。浏览器检查本机是否保存了http://www.baidu.com/index.aspx域名对应的服务器IP地址。如果没有,则发送请求到所在城市网中最近的DNS服务器(域名解析服务器),它会根据我们发送来的域名查询到该域名对应的服务器IP地址,并发送回浏览器。浏览器DNS服务器获得了这个域名所对应的服务器电脑的IP然后在庞大的互联网中找到这个对应的服务器HTTP协议规定服务器软件如(iis)使用的默认端口是80端口,也就是说浏览器默认的将HTTP请求报文发送到对应服务器的80端口。服务器接受到浏览器发送到的HTTP请求报文(具体报文用httpwatcher查看)。
下面是发送到百度的请求报文:
服务器分析请求报文中的路径和文件名,在服务器找到这个文件。如果是CSS和html,js,图片等文件就直接在服务器上面读取这个文件发送给浏览器客户端。
下面是对ASP.NET动态页面的处理
当服务器IIS发现你请求的页面是动态页面,他发现他自己处理不了。打开IIS,会发现里面有个处理程序映射,也就是说IIS会将我们的.aspx后缀的文件交给对应的处理程序(aspnet_isapi.dll)。这里简称ISAPI。
ISAPI就会将请求交给.NET framework。
再有ISAPI将请求交给ASP.NET,也就是一个名为aspnet_wp.exe的工作进程aspnet_wp.exe就调用FrameWork里的类 ------- ISAPIRuntime。
下面是用反编译工具来分析的这个类ISAPIRuntime看到这个方法,就会想到了垃圾回收。其实这不是重点。重点是这个processrequest方法 点击这个方法进去 首先会创建一个ISAPIWorkerRequest的对象wr,将请求报文封装到wr中 然后调用这个类的CreateWorkerRequest方法实例化这个对象 进入这个方法,这个方法会根据当前的IIS版本创建不同的对象再回到ProcessRequest方法接着就会调用HttpRuntime的ProcessRequestNoDemand方法。将wr传进去进入这个方法,在这个方法的最后会调用ProcessRequestNow方法。处理请求这个方法有调用了另一个方法。进去这里就会发现一个熟悉的东西,HttpContext(上下文对象)
这个方法会根据上面创建的ISAPIWorkerRequest对象wr(封装了请求报文)创建HttpContext。如果创建出错,就会返回一个400的错误。
判断是否是第一次请求之前(如果是第一次请求,就设置当前的时间为第一次请求的起始时间。初始化第一次请求。设置第一次请求为false)初始化Response.
当httpwriter为空的时候,就创建。可以看到,context.response中有2个写出器
一个是httpwriter,一个是textwriter然后通过HttpApplicationFactory创建一个HttpApplication对象(此对象负责真正处理页面对象的创建和执行,先在httpapplication池中看又没有这个对象,没有就new一个) 下面打开HttpApplication这个类 在这里面最重要的就是这25个事件。其中有19个事件开放给我们使用。
二、处理
Http请求报文通过Socket通信就这样来到了服务器端,这个时候该服务器登台表演了。先简单了解一下IIS。IIS是一个简单的Web服务器,IIS6.0中将其分为了两部分:内核模块和用户模块,服务器就是通过监听内核模块中的80端口来接收Http请求的,如下图所示。
在内核模块中有一个Http.sys文件,它会对Http请求报文进行简单的解析,如使用的什么协议,请求的地址是什么。接着它会查看服务器上的注册表,如果服务器上安装了IIS,就把请求报文交给IIS,如果没有安装则会直接返回。
在用户模块中有一个w3svc服务和一个IIS主进程(Inetinfo.exe)。Inetinfo.exe中存放着IIS所有的配置信息,如.aspx .ashx扩展的文件交给谁处理。w3svc服务会对请求的地址进行分析,分析请求的后缀,分析请求具体交给谁来处理。Inetinfo= internet infomation
浏览器请求的内容要分两种情况,是静态文件还是aspx、ashx等动态文件。
(1)如果是.css、html、js等静态文件,它会直接把这些静态文件读取进来,转换为字节数组,然后放到响应报文中,按照原路返回给浏览器。
(2)如果是aspx、ashx、cshtml等动态文件,IIS会读取Inetinfo.exe中的配置信息,将请求交给具体的扩展,在这里是asp.net_isapi.dll扩展(所谓的扩展就是实现了IIS接口的一些类,针对某些特殊的请求而服务)。接着请求报文会由w3svc服务交给具体的工作进程。
在进一步学习之前,我们先来了解一下IIS5.0 和IIS6.0的区别。
(2)IIS5.0中的工作进程是asp.net_wp.exe ,IIS5.0版本中将每个网站放在一个单独的应用程序域中,通过应用程序域隔离各个网站之间的相互影响,但是这样并不能完全的避免各个网站之间的相互影响:应用程序域之间是共享进程资源的,某个网站出错,可能会造成资源的不能及时释放,造成进程间的死锁。一个进程中可能包含多个应用程序域。
(1)IIIS6.0以上的版本进行了改进,工作进程为w3wp.exe,把每个网站都放在一个单独的工作进程中,这样就可以做到绝对的隔离各个网站之间的相互影响。一个应用程序池中还可能包含有多个工作进程。
在这个过程之前的操作都是非托管代码。
请求交给asp.net_isapi.dll扩展之后,扩展会查看asp.net运行环境是否启动了起来,如果没启动,它会启动asp.net的运行环境,这个时候就进入了托管环境。
asp.net_isapi.dll扩展会将请求交给ISAPIRuntime,ISAPIRuntime会调用自己的ProcessRequest()方法。在方法的参数列表中有一个ecb句柄,指向了当前请求报文的地址(里面放着请求的所有信息),会根据这个句柄创建一个HttpWorkRequest对象。
接着会调用HttpRuntime的ProcessRequest方法,这个方法以就以前面创建的HttpWorkRequest对象为参数。
在该方法的内部创建了两个对象:
(1)HttpContext对象
该对象是通过HttpWorkRequest对象创建的,里面封装了请求、响应报文的内容。
(2)HttpApplication对象
HttpApplication对象的创建是通过对象工厂创建的,其中也使用到了对象池技术、生产者、消费者模式等内容。简单点来说HttpApplicationFactory会首先查看对象池中有没有空闲的对象,如果有则直接返回,如果没有则会通过global.asax文件编译的类型反射出一个实例出来。Application对象在创建完成之后需要初始化,在这个初始化的过程中我们可以进行管道事件的注册操作。具体操作方法详见后续文章。
注意,两个对象的创建是有先后顺序的,先创建上下文对象在创建Application对象(Application对象的管道流通需要使用上下文对象作为参数)。接下来就是执行Application对象的ProcessRequest()方法执行管道,进行管道的流通。
请求-->IIS--->ISAPIRuntime-->HttpWorkRequest-->HttpRuntime-->HttpContext-->找到Global文件,并且编译该文件-->确保Global文件中Application_Start被调用-->创建HttpApplication(池 栈)如果池中没有根据Global文件编译的类型通过反射的形式创建出HttpApplication-->获取所有的在配置文件中的HttpModues,这时System.Web.Routing下的UrlRoutingModule也获取了,执行每个Modules下的Init方法,那么UrlRoutingModule中的Init方法完成了请求管道第7个事件的注册。--->进入管道-->第7个事件触发了,执行相应的方法.--->完成了MVCHanlder的创建。---》请求管道的11与12事件之间执行MvcHandler中的ProcessRequest方法。那么该方法中去找控制器,找方法,执行方法中的代码,然后找视图最后渲染视图。
编译Global文件的时候执行:
先执行右侧再执行左侧 右侧创建MvcApplication(HttpApplication)的时候执行依次初始化各Module, 左侧在执行到UrlRoutingModule 时候注册第七个事件,然后在11、12个事件之间执行下面代码,创建(MVCHandler)httpHandler,将当前请求映射到此(MVCHandler)HttpHandler ,如果是一般处理程序 执行程序员自定义的代码,如果是aspx页执行PageLoad 如果是MVC 查找控制器执行对应方法,渲染视图
如果是mvcHandler 按如下流程: