asp.net 下页面文件结构

构建高性能ASP.NET站点之一 剖析页面的处理过程(前端)
发表于 00:51|
来源博客园|
作者燕洋天
摘要:导读: 作者燕洋天 写了一系列关于ASP.NET的文章。 在对 ASP.NET 网站进行优化的时候,往往不是只是懂得 ASP.NET 就足够了的。 在优化的过程中,一般先是找出问题可能存在的地方,然后证明找出
导读:作者燕洋天写了一系列关于ASP.NET的文章。在对ASP.NET网站进行优化的时候,往往不是只是懂得ASP.NET就足够了的。在优化的过程中,一般先是找出问题可能存在的地方,然后证明找出的问题就是要解决的问题,确认之后,在进行一些措施。系列文章在结构上的安排是这样的:先讲述前端的调优,我会在文章的标题后面标上&前端&,如果是后台代码的调优,我会在标题上标上&后端&,如果是数据库设计的调优,我会在标题上标上&数据库&,希望大家多多提建议。
剖析页面的解析过程
页面的解析过程,这里说的过程不是我们常说的ASP.NET页面的生命周期的过程,而且浏览器请求一个页面,然后浏览器呈现页面的过程。
在本篇的文章中,我会先阐述页面的解析过程,显示从整体上阐述,然后在每一个点上提出优化的方法。先整体,后局部。
当浏览器在请求一个Web页面是从URL开始的。下面就是过程描述:
1.&输入URL地址或者点击URL的一个链接。
2.&浏览器根据URL地址,结合DNS,解析出URL对应的IP地址。
3.&发送HTTP请求。
4.&开始连接请求的服务器并且请求相关的内容(至于请求时怎么被处理的,我们这里暂时不讨论,只是后面的文章要讨论的问题)。
5.&浏览器解析从服务器端返回的内容,并且把页面显现出来,同时也继续进行其他的请求。
上面基本上就是一个页面被请求到现实的过程。下面我们就开始剖析这个过程。
当输入URL之后,浏览器就要知道这个URL对应的IP是什么,只有知道了IP地址,浏览器才能准备的把请求发送到指定的服务器的具体IP和端口号上面。
浏览器的DNS解析器负责把URL解析为正确的IP地址。这个解析的工作是要花时间的,而且这个解析的时间段内,浏览器不是能从服务器那里下载到任何的东西的。但是这个解析的过程是可以优化的。试想,如果每次浏览器每次请求一个URL都需要解析,那么每次的请求都有一点的时间消耗,可能这个时间消耗很短,但是性能的提升就是一点点的&调&出来的。如果把对应URL和IP地址缓存起来,那么当再次请求相同的URL时,浏览器就不用去解析,而是直接读取缓存,这样势必会快一点。
其实浏览器和操纵系统是提供了这样的支持的。
当获得了IP地址之后,那么浏览器就向服务器发送HTTP的请求,下面我们就稍微看下这个发送请求是怎么样被发送的:
1.浏览器通过发送一个TCP的包,要求服务器打开连接。
2.服务器也通过发送一个包来应答客户端的浏览器,告诉浏览器连接开了。
3.浏览器发送一个HTTP的GET请求,这个请求包含了很多的东西了,例如我们常见的cookie和其他的head头信息。
这样,一个请求就算是发过去了。
请求发送去之后,之后就是服务器的事情了,服务器端的程序,例如,浏览器清楚的文件是一个ASP.NET的页面,那么服务器端就把请求通过IIS交给ASP.NET 运行时,最后进行一系列的活动之后,把最后的结果,当然,一般是以是以html的形式发送到客户端。
其实首先到达浏览器的就是html的那些文档,所谓的html的文档,就是纯粹的html代码,不包含什么图片,脚本,css等的。也就是页面的html结构。因为此时返回的只是页面的html结构。这个html文档的发送到浏览器的时间是很短的,一般是占整个响应时间的10%左右。
这样之后,那么页面的基本的骨架就在浏览器中了,下一步就是浏览器解析页面的过程,也就是一步步从上到下的解析html的骨架了。
如果此时在html文档中,遇到了img标签,那么浏览器就会发送HTTP请求到这个img响应的URL地址去获取图片,然后呈现出来。如果在html文档中有很多的图片,flash,那么浏览器就会一个个的请求,然后呈现。
到这里,大家也许感觉到这种方式有点慢了。确实这个图片等资源文件的请求的部分也是可以优化的。暂不说别的,如果每个图片都要请求,那么就要进行之前说的那些步骤:解析url,打开tcp连接等等。开连接也是要消耗资源的,就像我们在进行数据库访问一样,我们也是尽可能的少开数据库连接,多用连接池中的连接。道理一样,tcp连接也是可以重用的。但是重用也有问题:如果两个图片它们的url地址如下:
&img&src=&q1.gif&&height=&16&&width=&16&&/&
&img&src=&q2.gif&&height=&16&&width=&16&&/&
&img&src=&q3.gif&&height=&16&&width=&16&&/&
&img&src=&q4.gif&&height=&16&&width=&16&&/&
&img&src=&q5.gif&&height=&16&&width=&16&&/&
&img&src=&q6.gif&&height=&16&&width=&16&&/&
&img&src=&q7.gif&&height=&16&&width=&16&&/&
&img&src=&q8.gif&&height=&16&&width=&16&&/&
&img&src=&q9.gif&&height=&16&&width=&16&&/&
&img&src=&q10.gif&&height=&16&&width=&16&&/&
请求这些图片的时间消耗如下图:
大家首先看到最上面的黄线的部分,这个黄线就代表了浏览器打开连接,黄线的后半部分为蓝色,就表示浏览器请求到了html的文档。
最上面的第二条蓝线就表示第一个图片已经请求到了,此时请求这个图片使用还是之前的一个tcp的连接。
大家在看到第三条线,前部分是黄色的,表示请求第二个图片的时候又开了一个tcp的连接,这条线的后半部分为蓝色,表示图片已经请求到了。
剩下的要请求的一些图片都使用上一个tcp连接。
确实,tcp的连接时充分的被使用了,但是图片下载的速度确实慢了,从图中看出,图片是一个个的顺序的下载下来的。整个页面的响应时间可想而知。
如果采用下一种方式,如:
可以看出连接时多了,但是图片的几乎都是并行下载下来的,相比而言就快多了。
其实这就是一个权衡的问题了。
实际上浏览器也是内置了以一些优化方式的,例如缓存图片,脚本等。或者采用并行下载图片的方式,谈到并行下载,就如上图所看到的,势必会消耗更多的连接资源。
原文链接:
推荐阅读相关主题:
网友评论有(0)
CSDN官方微信
扫描二维码,向CSDN吐槽
微信号:CSDNnews
相关热门文章asp.net基础学习之前端页面布局
作者:lw_zhaoritian
字体:[ ] 类型:转载 时间:
这篇文章主要为大家详细介绍了asp.net基础学习之前端页面布局,什么是母版页,如何创建母版页,感兴趣的小伙伴们可以参考一下
前端就是给人看的界面,后台人员不仅要知道后台代码的编写,更要知道前端的布局。有时候要比前端人员知道的还要多,因为有可能前端人员是个21天精通ps的大师级人物。这时候你可以自己写前端。
&•CSS(Cascading Style
Sheet),中文译为层叠样式表,是用于控制网页样式并允许将样式信息与网页内容分离的一种标记性语言。就语法而言,
&•CSS是一种容易学习的语言。它的“语法”仅由几个概念组成,使得它相当容易入门。CSS的难点在于所有主流浏览器呈现页面的方式。尽管实际上每种浏览器都能够理解CSS,但当根据CSS标准显示页面时,它们都有各自的“怪癖”。
&•CSS规定了两种定义样式的方法,分别是内联式和级联式。
&•直接将样式控制放在单个HTML元素内,称为内联式或行内样式。该样式通过style属性来控制每个元素的外观,直观但是很繁琐。除非具有相同样式的元素较少,否则很少采用。
在网页的head部分定义或导入的样式,称为级联式样式。该样式可以实现将网页结构和表现分离,这样,当修改某些元素的样式时,只需要修改head部分定义或引入的样式,该网页内所有具有相同样式的元素都会自动应用新的样式。
&•级联式样式又可以分为两种方式:内嵌式和链接式。
&•在head部分直接实现的CSS样式,称为内嵌式。这种CSS一般位于HTML文件的头部,即在与标签内,并且以结束。
采用内嵌式比内联式方便了很多,body内的代码也相对简洁,修改某个元素的样式时只需修改head内的代码即可。
&•在head部分通过导入以扩展名为.css的文件来实现CSS样式,称为链接式。利用这种方法在网页中可以调用已经定义好的样式表来实现样式表的应用,定义好的样式表通常单独以文件的形式存放在站点目录中。这种方法实现了将网页结构和表现的彻底分离,最适合大型网站的CSS样式定义。
&•如果某个元素既引用了链接样式文件中定义的样式,又在head部分定义了新的样式,或者在元素内部通过style属性定义了新的样式,那么该标记元素最终呈现的效果会是什么样呢?这就是样式嵌套中的冲突问题,浏览器解决这种问题的方法就是一旦发现样式冲突,则通过“就近使用”原则,而不冲突的样式则通过顺序组合后形成最终样式进行显示。
&•一般情况下,在样式表(.css)文件中定义适合大多数网页公用的样式,在网页内部采用内嵌式定义该页面特有的样式。
&•属性是元素的一部分,可通过样式表修改。CSS规范定义了一个长属性列表,但在大多数Web站点中不会用到所有项。
下面介绍这几种不同的方式来设置:&
&h1 style="color:"&CSS样式&/h1&
color:gray
&h1 id="h1"&CSS样式&/h1&
color:gray
&h1 class="h1"&CSS样式&/h1&
&link href="StyleSheet.css" rel="stylesheet" /&
//直接插入即可引用
html的标签,”id”对应”#”,”class”对应”.” .id是唯一标志的,在同一页面中不能有相同的值,class则没这约束。如:
h1{......}
#div1{......}
//引用时,使用id属性声明即可 id="div1"
.div2{......}
//引用时,使用class属性声明即可 class="div2"
关联选择符:
&•关联选择符是一个用空格隔开的两个或更多的单一标记选择符组成的字符串。一般格式如下:选择符1 选择符2 …… {属性:值; ……}
•这些选择符具有层次关系,并且它们的优先级比单一的标记选择符大。例如: p h2{ color:red }
&•这种定义方式只对p元素所包含的h2元素起作用,单独的p或者单独的h2元素均无法应用该样式。&
并列选择符
&•如果有多个不同的元素定义的样式相同,则可以使用并列选择符简化定义。例如: h1,h2,h3{ color:blue}
&•每个元素之间用逗号隔开,表示所有的h1、h2、h3标记中的内容都将以蓝色样式显示。&
&•伪类是CSS中非常特殊的类,它能自动地被支持CSS的浏览器所识别。伪类可以指定XHTML中的&a&元素以不同的方式显示链接(links)、已访问链接(visited
links)和可激活链接(active links)。甚至不同字体大小和风格。
&•CSS中用4个伪类来定义链接的样式,分别是:a:link、a:visited、a:hover和a:active,例如:
a:link{font-weight :text-decoration :color : #C00000 ;}
a:visited {font-weight :text-decoration :color :
#C30000 ;}
2.页面布局:
DIV和CSS布局
&1.页面水平居中
设置页面水平居中的方法是在body的style样式中设置text-align属性的值为center。如果还希望页面的宽度固定,则可以通过设置div的width属性来实现。
&2.页面满宽度显示
设置页面满宽显示的方法是将div的固定宽度设置为百分比即可
&3.页面元素
定位页面元素的定位分为流布局和坐标定位布局两种,其中,坐标定位布局又分为绝对定位和相对定位,这里仅介绍流布局和坐标绝对定位
如果采用该布局,则页面中的元素将按照从左到右、从上到下的顺序显示,各元素之间不能重叠。如果不设置元素的定位方式,则默认就是流式布局。
在使用坐标绝对定位之前,必须先将style元素的position属性设置为absolute,然后就可以由style元素的left、top、right、bottom和z-index属性来决定元素在页面中的绝对位置。left属性表示元素的x坐标,top属性表示元素的y坐标,坐标的位置是以它最近的具有position属性的父容器为参照物的。
&4.表格布局
坐标定位布局又分为绝对定位和相对定位,这里仅介绍流布利用表格可以将网页中的内容合理地放置在相应的区域,每个区域之间互不干扰。
&5.盒子模型
自从 1996 年CSS1 的推出,W3C 组织就建议把所有网页上的对象都放在一个盒子(box)中,设计师可以通过创建定义来控制这个盒子的属性,这些对象包括段落、列表、标题、图片以及层。盒子模型主要定义了4个区域:内容(content)、边框距(padding)、边界(border)和边距(margin),
&6.层的定位
float浮动属性是DIV和CSS布局中的一个非常重要的属性。大部分的DIV布局都是通过float的控制来实现的。具体参数如下。
float:none用于设置是否浮动
float:left用于表示对象向左浮动
float:right用于表示对象向右浮动
主题是定义页面和控件外观的文件的集合。它通常包含外观文件(扩展名为.skin)、级联样式表文件(扩展名为.css)、图片和其他资源等的组合,但一个主题至少包含一个外观文件。
--------------------------------------------------------------------------------
什么是母版页:
&•母版页是用于设置页面外观的模板,是一种特殊的ASP.NET网页文件,同样也具有其他ASP.NET文件的功能,如添加控件、设置样式等,只不过它的扩展名是.master。
&•引用母版页的.aspx页面称为内容页,在内容页中,母版页的ContentPlaceHolder控件预留的可编辑区会被自动替换为Content控件,只需在Content控件区域中填充内容即可,在母版页中定义的其他标记将自动出现在引用该母版页的内容页中。
&•当创建新的Web站点时,总是先添加作为所有其他页面基础的母版页。即使站点中只有少数几个页面,母版页仍然可以帮助确保整个站点拥有一致的外观。
&•在某种程度上,母版页看起来就像正常的ASPX页面。创建母版页的方法也和创建一般页面的方法非常相似,区别是母版页无法单独在浏览器中查看,必须通过创建内容页才能浏览。
&创建母版页:
&•每当创建一个新的母版页时都会自动添加此占位符,在内容页中可以用它来添加页面特有的位于页面的标记之间的内容,比如CSS(包括内嵌样式表和外部样式表)和JavaScript。
&•母版页中的ContentPlaceHolder如果有内容,是可以作为内容页的默认新项,当基于该母版页新建页面时,内容页即可以重写这部分内容,也可以不重写。
&&& 嵌套母版页
&•母版页也可以嵌套。嵌套母版页是基于另一个母版页的母版页。内容页面则可以基于嵌套母版页。如果有一个目标为不同区域仍然需要共享相同外观的Web站点,采用嵌套母版页就比较有用。
&•嵌套母版页的创建很简单:当添加母版页时选中“选择母版页”复选框即可,就像后面介绍的添加内容页一样。然后,在内容页中要重写的位置将控件添加到控件中。&
创建内容页
&•母版页如果没有内容页来使用它,那就没有任何用处。通为了将一个内容页基于一个母版页,通常在添加新网页到站点时,就指定母版页,为此,只需选中“添加新项”对话框底部的“选择母版页”复选框即可。当然,也可以在直接在页面的@Page指令中设置MasterPageFile属性。
&•内容页中只能含有映射到母版页中的控件的控件。而这些控件又可以包含标准标记,如HTML元素和服务器控件声明
在内容页访问母版页中的成员
&•在内容页中可以通过编程的方式访问母版页中的成员,包括母版页上的任何公共属性或方法以及任何控件。要实现内容页对母版页中定义的属性或方法进行访问,则该属性或方法必须声明为公共成员(public),也可以对母版页动态地进行访问。&
下面来个示例,使用母版页创建内容页:
&•母版页包括一个或多个 &asp:ContentPlaceHolder ID="TestContentPlaceHolder"
runat="server"/& 控件,在内容页中可以定义要替换的内容。
&•内容页中通过添加 Content 控件并将这些控件映射到母版页上的 ContentPlaceHolder控件来创建内容。
&添加新项&&母版页&&
MasterPage.master
&form id="form1" runat="server"&
&div id="main"&
&div id="head"&
&h1 style="margin:10px 0 0 10px"&母版页测试&/h1&
&div id="content"&
&div id="left"&
&h3 style=" margin:10px 0 0 10px"&左侧导航&/h3&
&div style=" margin-left:20 font-size:18 font-family:Verdana"&
&a href="TestPage.aspx"&asp.net&/a&&br /&
&a href="AnotherTestPage.aspx"&CSS&/a&&br /&
&a href="#"&HTML&/a&&br /&
&a href="#"&JQuery&/a&
&div id="center"&
&asp:ContentPlaceHolder ID="TestContentPlaceHolder" runat="server"&
可以被重写的部分
&/asp:ContentPlaceHolder&
重写之前的样式:
添加新项&&web窗体
勾选母版页:
选中刚才的母版页即可:
在代码中间重写母版页的内容:
&asp:Content ID="Content1" ContentPlaceHolderID="TestContentPlaceHolder" Runat="Server"&
&/asp:Content&
&asp:Content ID="Content1" ContentPlaceHolderID="TestContentPlaceHolder" Runat="Server"&
&div style=" width:100%; height:100%; background-color:#666666"&
&div style=" margin:10px 0 0 10px"&
这里是另一个内容页(AnotherTestPage.aspx)
&p style=" font-size:12 font-family:宋体"&
&&&&Master Page 使您有能力为 web 应用程序中的所有页面(或页面组)创建一致的外观和行为。
Master Page 为其他页面提供了模版,带有共享的布局和功能。Master Page 为内容定义了可被内容页面覆盖的占位符。而输出结果就是 Master Page 和内容页面的组合。&br /&
&&&&内容页包含您希望显示的内容。
当用户请求内容页时,ASP.NET 会对页面进行合并以生成输出,输出结果对 Master Page 的布局和内容页面的内容进行了合并。
&/asp:Content&
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具asp.net网站通常由哪些类型的文件和文件夹组成_百度知道当前位置: →
→ 了解ASP.NET底层架构
了解ASP.NET底层架构
& 作者及来源: 醒了又睡的鱼.NET - 博客园 &
&收藏到→_→:
摘要: 了解ASP.NET底层架构
"了解ASP.NET底层架构"::
这篇文章以非常底层的视角讲述了web请求(request)在asp.net框架中是如何流转的,从web服务器,通过isapi直到请求处理器(handler)和你的代码.看看在幕后都发生了些什么,不要再把asp.net看成一个黑盒了.
asp.net是一个非常强大的构建web应用的平台,它提供了极大的灵活性和能力以致于可以用它来构建所有类型的web应用.绝大多数的人只熟悉高层的框架如webforms和webservices-这些都在asp.net层次结构在最高层.在这篇文章中我将会讨论asp.net的底层机制并解释请求(request)是怎么从web服务器传送到asp.net运行时然后如何通过asp.net管道来处理请求.
对我而言了解平台的内幕通常会带来满足感和舒适感,深入了解也能帮助我写出更好的应用.知道可以使用哪些工具以及他们是怎样作为整个复杂框架的一部分来互相配合的可以更容易地找出最好的 ,更重要的是可以在出现问题时更好的解决它们.这篇文章的目标是从系统级别了解asp.net并帮助理解请求(request)是如何在asp.net的处理管道中流转的.同样,我们会了解核心引擎和web请求如何在那里结束.这些信息大部分并不是你在日常工作时必须了解的,但是它对于理解asp.net架构如何把请求路由到你的代码(通常是非常高层的)中是非常有益的.
不管怎么样,asp.net从更低的层次上提供了更多的灵活性.http运行时和请求管道在构建webforms和webservices上提供了同样的能力-它们事实上都是建立在.net上的.而且所有这些同样的功能对你也是可用的,你可用决定你是否需要建立一个比webforms稍低一点层次的定制的平台.
webforms显然是最简单的构建绝大多数web接口的方法,不过如果你是在建立自定义的内容处理器(handler),或者有在处理输入输出内容上有特殊的要求,或者你需要为另外的应用建立一个定制的服务接口,使用这些更低级的处理器(handler)或者模块(module)能提供更好的性能并能对实际请求处理提供更多的控制.在webforms和webservices这些高层实现提供它们那些能力的同时,它们也对请求增加了一些额外负担,这些都是在更底层可以避免的.
asp.net是什么
让我们以一个简单的定义开始:什么是asp.net?我喜欢这样定义asp.net:
&&&&&&&&&&
asp.net是一个复杂的使用来从头到尾处理web请求的引擎.
它并不只是webforms和webservies…&&&
asp.net是一个请求处理引擎.它接收一个发送过来的请求,把它传给内部的管道直到终点,作为一个的你可以在这里附加一些代码来处理请求.这个引擎是和http/web服务器完全分隔的.事实上,http运行时是一个组件,使你可以摆脱iis或者任何其他的服务器程序,将你自己的程序寄宿在内.例如,你可以将asp.net运行时寄宿在一个windows& form程序中(查看http://www./presentations/aspnetruntime/aspnetruntime.asp可以得到更加详细的信息)
运行时提供了一个复杂但同时非常优雅的在管道中路由请求的机制.其中有很多相关的对象,大多数都是可扩展的(通过继承或者事件接口),在几乎所有的处理流程上都是如此.所以这个框架具有高度可扩展性.通过这个机制,挂接到非常底层的接口(比如缓存,认证和授权)都变得可能了.你甚至可以在预处理或者处理后过滤内容,也可以简单的将符合特殊标记的请求直接路由你的代码或者另一个url上.存在着许多不同的方法来完成同一件事,但是所有这些方法都是可以简单直接地实现的,同时还提供了灵活性,可以得到最好的性能和开发的简单性.
整个asp.net引擎是完全建立在上的,所有的扩展功能也是通过扩展来提供的
整个asp.net引擎是完全建立在上的,所有的扩展功能也是通过扩展来提供的.这是对.net框架具有构建复杂而且高效的框架的能力的最好的证明.asp.net最令人印象深刻的地方是深思熟虑的设计,使得框架非常的容易使用,又能提供挂接到请求处理的几乎所有部分的能力.
通过asp.net你可以从事从前属于isapi扩展和iis过滤器领域的任务-有一些限制,但是比起asp来说是好多了.isapi是一个底层的win32风格的api,有着非常粗劣的接口而且难以用来开发复杂的程序.因为isapi非常底层,所以它非常的快,但是对于应用级的开发者来说是十分难以管理的.所以,isapi通常用来提供桥接的接口,来对其他应用或者平台进行转交.但是这并不意味者isapi将消亡.事实上,asp.net在微软的平台上就是通过isapi扩展来和iis进行交互的,这个扩展寄宿着.net运行时和asp.net运行时.isapi提供了核心的接口,asp.net使用非托管的isapi代码通过这个接口来从web服务器获取请求,并发送响应回客户端.isapi提供的内容可以通过通用对象(例如httprequest和httpresponse)来获取,这些对象通过一个定义良好并有很好访问性的接口来暴露非托管数据.
从浏览器到asp.net
让我们从一个典型的asp.net web请求的的起点开始.当用户输入一个url,点击了一个或者提交了一个html表单(form)(一个post请求,相对于前两者在一般意义上都是get请求).或者一个客户端程序可能调用了一个基于asp.net的webservice(同样由asp.net来处理).在web服务器端,iis5或6,获得这个请求.在最底层,asp.net和iis通过isapi扩展进行交互.在asp.net环境中这个请求通常被路由到一个扩展名为.aspx的页面上,但是这个流程是怎么工作的完全依赖于处理特定扩展名的http handler是怎么实现的.在iis中.aspx通过’扩展’(又称为脚本映射)被映射到asp.net的isapi扩展dll-aspnet_isapi.dll.每一个请求都需要通过一个被注册到aspnet_isapi.dll的扩展名来触发asp.net(来处理这个请求).
依赖于扩展名asp.net将请求路由到一个合适的处理器(handler)上,这个处理器负责获取这个请求.例如,webservice的.asmx扩展名不会将请求路由到磁盘上的一个页面,而是一个由特殊属性(attribute)标记为webservice的类上.许多其他处理器和asp.net一起被安装,当然你也可以自定义处理器.所有这些httphandler在iis中被配置为指向asp.net isapi扩展,并在web.config(译著:asp.net中自带的handler是在machine.config中配置的,当然可以在web.config中覆盖配置)被配置来将请求路由到指定的http handler上.每个handler都是一个处理特殊扩展的.net类,可以从一个简单的只包含几行代码的hello world类,到非常复杂的handler如asp.net的页面或者webservice的handler.当前,只要了解asp.net的映射机制是使用扩展名来从isapi接收请求并将其路由到此文来自: 马开东博客
转载请注明出处 网址:
处理这个请求的handler上就可以了.
对在iis中自定义web请求处理来说,isapi是第一个也是最高效的入口
isapi是底层的非托管win32 api.isapi定义的接口非常简单并且是为性能做了优化的.它们是非常底层的-处理指针和函数指针表来进行回调-但是它们提供了最底层和面向效率的接口,使开发者和工具提供商可以用它来挂接到iis上.因为isapi非常底层所以它并不适合来开发应用级的代码,而且isapi倾向于主要被用于桥接接口,向上层工具提供 类型的功能.例如,asp和asp.net都是建立在isapi上的,cold fusion,运行在iis上的多数perl,php以及 p实现,很多第三方 (如我的wisual foxpro的web连接框架)都是如此.isapi是一个杰出的工具,可以为上层应用提供高效的管道接口,这样上层应用可以抽象出isapi提供的信息.在asp和asp.net中,将isapi接口提供的信息抽象成了类型request和response这样的对象,通过它们来读取isapi请求中对应的信息.将isapi想像成管道.对asp.net来说,isapi dll是非常的”瘦”的,只是作为一个路由机制来将原始的此文来自: 马开东博客
转载请注明出处 网址:
请求转发到asp.net运行时.所有那些沉重的负担和处理,甚至请求线程的管理都发生在asp.net引擎内部和你的代码中.
作为协议,isapi同时支持isapi扩展和isapi过滤器(filter).扩展是一个请求处理接口,提供了处理web服务器的输入输出的逻辑-它本质上是一个处理(事物?)接口.asp和asp.net都被实现为isapi扩展.isapi过滤器是挂接接口,提供了查看进入iis的每一个请求的能力,并能修改请求的内容或者改变功能型的行为,例如认证等.顺便提一下,asp.net通过了两种概念映射了类似isapi的功能:http handler类似扩展,http module类似过滤器.我们将在后面详细讨论它们.
isapi是开始一个asp.net请求的最初的入口.asp.net映射了好几个扩展名到它的isapi扩展,此扩展位于.net框架的目录下:
&.net frameworkdir&\aspnet_isapi.dll
你可以在iis服务管理界面上看到这些映射,如图1.查看网站根目录的属性中的主目录配置页,然后查看配置|映射.
图1:iis映射了多种扩展名如.aspx到asp.net的isapi扩展.通过这个机制请求会在web服务器这一层被路由到asp.net的处理管道.
由于.net需要它们中的一部分,你不应该设置手动这些扩展名.使用aspnet_regiis.exe这个工具来确保所有的映射都被正确的设置了:
cd &.netframeworkdirectory&
aspnet_regiis – i
这个命令将为整个web站点注册特定版本的asp.net运行时,包括脚本 (扩展名) 映射和客户端脚本库(包括进行控件验证的代码等).注意它注册的是&.netframeworkdirectory&中安装的特定版本的clr(如1.1,2.0).aspnet_regiis的选项令您可以对不同的虚拟目录进行配置.每个版本的.net框架都有自己不同版本的aspnet_regiis工具,你需要运行对应版本的aspnet_regiis来为web站点或者虚拟目录来配置指定版本的.net框架.从asp.net2.0开始提供了asp.net配置页面,可以通过这个页面在iis管理控制台来交互的配置.net版本.
iis6通配符映射
如果你有一个asp.net需要处理虚拟目录的(或者是整个web站点,如果配置为根目录的话)每一个请求,iis6引入了新的称为通配符映射的概念.一个映射到通配符的isapi扩展在每个请求到来时都会被触发,而不管扩增名是什么.这意味着每个页面都会通过这个扩展来处理.这是一个强大的功能,你可以用这个机制来创建虚拟url和不使用文件名的unix风格的url.然而,使用这个设置的时候要注意,因为它会把所有的东西都传给你的应用,包括静态htm文件,图片,样式表等等.
iis 5 和6以不同的方式工作
当一个请求来到时,iis检查脚本映射(扩展名映射)然后把请求路由到aspnet_isapi.dll.这个dll的操作和请求如何进入asp.net运行时在iis5和6中是不同的.图2显示了这个流程的一个粗略概览.
在iis5中,aspnet_isapi.dll直接寄宿在inetinfo.exe进程中,如果你设置了web站点或虚拟目录的隔离度为中或高,则会寄宿在iis单独的(被隔离的)工作进程中.当第一个asp.net请求来到,dll(aspnet_isapi.dll)会开始另一个新进程aspnet_wp.exe并将请求路由到这个进程中来进行处理.这个进程依次加载并寄宿.net运行时.每个转发到isapi dll的请求都会通过命名管道调用被路由到这个此文来自: 马开东博客
转载请注明出处 网址:
图2-从较高层次来看请求从iis到asp.net运行时,并通过请求处理管道的流程.iis5和iis6通过不同的方式与asp.net交互,但是一旦请求来到asp.net管道,整个处理流程就是一样的了.
不同于以前版本的服务器,iis6为asp.net做了全面的优化
iis6-池万岁
iis6对处理模型做了意义重大的改变,iis不再直接寄宿象isapi扩展这样的外部可执行代码.iis总是创建一个独立的工作线程-一个池-所有的处理都发生在这个进程中,包括isapi dll的执行.池是iis6的一个很大的改进,因为它允许对指定线程中将会执行什么代码进行非常细粒度的控制.池可以在每个虚拟路径上或者整个web站点上进行配置,这样你可以将每个web应用隔离到它们自己的进程中,这样每个应用都将和其他运行在同一台机器上的web应用完全隔离.如果一个进程崩溃了,不会影响到其他进程(至少在web处理的观点上来看是如此).
不止如此,池还是高度可配置的.你可以通过设置池的执行扮演级别(execution impersonation level )来配置它们的运行安全环境,这使你可以定制赋予一个web应用的权限(同样,粒度非常的细).对于asp.net的一个大的改进是,池覆盖了在machine.config文件中大部分的processmodel节的设置.这一节的设置在iis5中非常的难以管理,因为这些设置是全局的而且不能在的web.config文件中被覆盖.当运行iis6是,processmodel相关的设置大部分都被忽略了,取而代之的是从池中读取.注意这里说的是大部分-有些设置,如线程池的大小还有io线程的设置还是从machine.config中读取,因为它们在线程池的设置中没有对应项.
因为池是外部的可执行程序,这些可执行程序可以很容易的被监控和管理.iis6提供了一系列的进行系统状况检查,重启和超时的选项,可以很方便的用来检查甚至在许多情况下可以修正程序的问题.最后iis6的池并不像iis5的隔离模式那样依赖于com+,这样做一来可以提 ,二来提高了稳定性(特别对某些内部需要调用com组件的应用来说)
尽管iis6的池是单独的exe,但是它们对http操作进行了高度的优化,它们直接和内核模式下的http.sys驱动程序进行通讯.收到的请求被直接路由给适当的池.inetinfo基本上只是一个管理程序和一个配置服务程序-大部分的交互实际上是直接在http.sys和池之间发生,所有这些使iis6成为了比iis5更加的稳定和高效的环境.特别对静态内容和asp.net程序来说这是千真万确的.
一个iis6池对于asp.net有着天生的认识,asp.net可以在底层的api上和它进行交互,这允许直接访问http缓存api,这样做可以将asp.net级别的缓存直接下发到web服务器.
在iis6中,isapi扩展在池的工作进程中运行. .net运行时也在同一个进程中运行,所以isapi扩展和.net运行时的通讯是发生在进程内的,这样做相比iis5使用的命名管道有着天生的性能优势.虽然iis的寄宿模型有着非常大的区别,进入的接口却异常的相似-只有路由消息的过程有一点区别.
isapiruntime.processrequest()函数是进入asp.net的第一站
进入.net运行时
进入.net运行时的真正的入口发生在一些没有被文档记载的类和接口中(译著:当然,你可以用reflector来查看j).除了微软,很少人知道这些接口,微软的家伙们也并不热衷于谈论这些细节,他们认为这些实现细节对于使用asp.net开发应用的并没有什么用处.
工作进程(iis5中是aspnet_wp.exe,iis6中是w3wp.exe)寄宿.net运行时和isapi dll,它(工作进程)通过调用com对象的一个小的非托管接口最终将调用发送到isapiruntime类的一个实例上(译注:原文为an instance subclass of the isapiruntime class,但是isapiruntime类是一个sealed类,疑为作者笔误,或者这里的subclass并不是子类的意思).进入运行时的第一个入口就是这个没有被文档记载的类,这个类实现了iisapiruntime接口(对于调用者说明来说,这个接口是一个com接口)这个基于iunknown的底层com接口是从isapi扩展到asp.net的一个预定的接口.图3展示了iisapiruntime接口和它的调用签名.(使用了lutz roeder出色的.net reflector 工具/roeder/dotnet/).这是一个探索这个步步为营过程的很好的方法.
图3-如果你想深入这个接口,打开reflector,指向system.web.hosting. isapi dll通过调用一个托管的com接口来打开进入asp.net的入口,asp.net接收一个指向isapi ecb的非托管指针.这个ecb包含访问完整的isapi接口的能力,用来接收请求和发送响应回到iis.
iisapiruntime接口作为从isapi扩展来的和asp.net之间的接口点(iis6中直接相接,iis5中通过命名管道).如果你看一下这个类的内部,你会找到含有以下签名的processrequest函数:
&[return: marshalas(unmanagedtype.i4)]int processrequest([in] intptr ecb,&&&&&&&&&&&&&&&&&&& [in, marshalas(unmanagedtype.i4)] int useprocessmodel);
其中的ecb参数就是isapi扩展控制块(extention control block),被当作一个非托管资源传递给processrequest函数.这个函数接过ecb后就把它做为基本的输入输出接口,和request和response对象一起使用.isapi ecb包含有所有底层的请求信息,如服务器变量,用于表单(form)变量的输入流和用于回写数据到客户端的输出流.这一个ecb引用基本上提供了用来访问isapi请求所能访问的资源的全部功能,processrequest是这个资源(ecb)最初接触到的入口和出口.
isapi扩展异步地处理请求.在这个模式下isapi扩展马上将调用返回到工作进程或者iis线程上,但是在当前请求的上ecb会保持可用.ecb含有使isapi知道请求已经被处理完的机制(通过ecb.serversupportfunction方法)(译注:更多信息,可以参考开发isapi扩展的文章),这使得ecb被释放.这个异步的处理方法可以马上释放isapi工作线程,并将处理传递到由asp.net管理的一个单独的线程上.
asp.net接收到ecb引用并在内部使用它来接收当前请求的信息,如服务器变量,post的数据,同样它也返回信息给服务器.ecb在请求完成前或超时时间到之前都保持可访问(stay alive),这样asp.net就可以继续和它通讯直到请求处理完成.输出被写入isapi输出流(使用ecb.writeclient())然后请求就完成了,isapi扩展得到请求处理完成的通知并释放ecb.这个实现是非常高效的,因为.net类本质上只是对高效的、非托管的isapi ecb的一个非常”瘦”(thin)的包装器.
装载.net-有点神秘
让我们从这儿往回退一步:我跳过了.net运行时是怎么被载入的.这是事情变得有一点模糊的地方.我没有在这个过程中找到任何的文档,而且因为我们在讨论本机代码,没有很好的办法来反编译isapi dll并找出它(装载.net运行时的代码)来.
我能作出的最好的猜测是当isapi扩展接受到第一个映射到asp.net的扩展名的请求时,工作进程装载了.net运行时.一旦运行时存在,就可以为指定的虚拟目录请求一个isapiruntime的实例(如果这个实例还不存在的话).每个虚拟目录拥有它自己的域(appdomain),当一个独立的应用(指一个asp.net程序)开始的时候isapiruntime从启动过程就一直在域中存在.实例化(译注:应该是指isapiruntime的实例化)似乎是通过com来进行的,因为接口方法都被暴露为com可调用的方法.
当第一个针对某虚拟目录的请求到来时,system.web.hosting.appdomainfactory.create()函数被调用来创建一个isapiruntime的实例.这就开始了这个应用的启动进程.这个调用接收这个应用的类型,模块名称和虚拟目录信息,这些信息被asp.net用来创建域并启动此虚拟目录的asp.net程序.这个httpruntime实例(译注:原文为this httpruntime derived object,但httpruntime是一个sealed类,疑为原文错误)在一个新的域中被创建.每个虚拟目录(即一个asp.net寄)宿在一个独立的域中,而且他们也只有在特定的asp.net程序被请求到的时候才会被载入.isapi扩展管理这些httpruntime对象的实例,并根据请求的虚拟目录将内部的请求路由到正确的那个httpruntime对象上.
图4-isapi请求使用一些没有文档记载的类,接口并调用许多工厂方法传送到asp.net的http管道的过程.每个web程序/虚拟目录在它自己的域中运行,调用者(译注:指isapi dll)保持一个iisapiruntime接口的引用来触发asp.net的请求处理.
回到运行时
在这里我们有一个在isapi扩展中活动的,可调用的isapiruntime对象的实例.每次运行时是启动的并运行着的时候(译注:相对的,如果运行时并没有启动,就需要象上一章所说的那样载入运行时),isapi的代码调用isapiruntime.processrequest()方法,这个方法是真正的进入asp.net管道的入口.这个流程在图4中显示.
记住isapi是多线程的,所以请求也会通过appdomainfactory.create()(译注:原文为applicationdomainfactory,疑有误)函数中返回的引用在多线程环境中被处理.列表1显示了isapiruntime.processrequest()方法中反编译后的代码,这个方法接收一个isapi ecb对象和服务类型(workerrequesttype)作为参数.这个方法是线程安全的,所以多个isapi线程可以同时在这一个被返回的对象实例上安全的调用这个方法.
列表1:processrequest方法接收一个isapi ecb并将其传给工作线程
publicintint
ifnull)) ||
stringtrue
这里实际的代码并不重要,记住这是从内部框架代码中反编译出来的,你不能直接处理它,它也有可能在将来发生改变.它只是用来揭示在幕后发生了什么.processrequest方法接收非托管的ecb引用并将它传送给isapiworkerrequest对象,此对象负责为当前请求创建创建请求上下文.在列表2中显示了这个过程.
system.web.hosting.isapiworkerrequest类是httpworkerrequest类的一个抽象子类(译注:httpworkerrequest和isapiworkerrequest都是抽象类,并且isapiworkerrequest继承自httpworkerrequest),它的工作是构建一个作为web应用输入的输入输出的抽象视角.注意这里有另一个工厂方法:createworkerrequest,通过判断接受到的第二个参数来创建对应的workerrequest对象.有三个不同的版本:isapiworkerrequestinproc,isapiworkerrequestinprocforiis6,isapiworkerrequestoutofproc.每次有请求进入,这个对象被创建并作为请求和响应对象的基础,它会接收它们的数据和由workerrequest提供的数据流.
抽象的httpworkerrequest类在低层接口上提供一个高层的抽象,这样就封装了数据是从哪里来的,可以是一个cgi web服务器,web浏览器控件或者是一些你用来给http运行时”喂”数据的自定义的机制.关键是asp.net能用统一的方法来接收信息.
在使用iis的情况下,这个抽象是建立在isapi ecb块周围.在我们的请求处理过程中,isapiworkerrequest挂起isapi ecb并根据需要从它那里取出信息.列表2显示了请求字符串值(query string value)是如何被取出来的.
列表2:使用非托管数据的isapiworkerrequest方法
// *** implemented in isapiworkerrequest
publicoverridebyte
bytenewbytethis
intthisthis
thrownew);
// *** implemented in a specific implementation class isapiworkerrequestinprociis6
internaloverrideintintint
returnthis
isapiworkerrequest实现了一个高层次的包装方法,它调用了低层的核心方法,负责真正的访问非托管apis-或称为”服务级别的实现”(service level implementation).这些核心方法在特殊的isapiworkerrequest子类中为它寄宿的环境提供特殊的实现.这实现了简单的扩展的(pluggable)环境,这样一来当以后新的web服务器接口或其他平台成为了asp.net的目标时附加的实现类可以在被此文来自: 马开东博客
转载请注明出处 网址:
简单的提供出来.这里还有一个协助类(helper class)system.web.unsafenativemethods.里面许多对isapi ecb结构的操作实现了对isapi扩展的非托管操作.
httpruntime,httpcontext和httpapplication
当一个请求到来时,它被路由到isapiruntime.processrequest()方法.这个方法调用httpruntime.processrequest方法,它作一些重要的事情(用reflector查看system.web.httpruntime.processrequestinternal方法):
·为请求创建一个新的httpcontext实例
·获取一个httpapplication实例
·调用httpapplication.init()方法来设置管道的事件
·init()方法触发开始asp.net管道处理的httpapplication.resumeprocessing()方法
首先一个新的httpcontext对象被创建并用来传递isapiworkerrequest(isapi ecb的包装器).这个上下文在整个请求的总都是可用的并总可以通过静态属性httpcontext.currect来访问.正像名字所暗示的那样,httpcontext对象代表了当前活动请求的上下文因为他包含了在请求中所有典型的你需要访问的重要对象:request,response,application,server,cache.在请求处理的任何时候httpcontext.current给你访问所有这些的能力.
httpcontext对象也包含一个非常有用的items集合,你可以用它来保存针对特定请求的数据.上下文对象在请求周期的开始时被创建,在请求结束时被释放,所有在items集合中保存的数据只在这个特定的请求中可用.一个很好的使用的例子是请求日志机制,当你通过想通过在global.asax中挂接application_beginrequest和application_endrequest方法记录请求的开始和结束时间(象在列表3中显示的那样).httpcontext对你就非常有用了-如果你在请求或页面处理的不同部分需要数据,你自由的使用它.
列表3-使用httpcontext.items集合使你在不同的管道事件中保存数据
protectedvoid
//*** request logging
context.items.add("weblog_starttime",datetime.now);
protectedvoid
// *** request logging
timespan span = datetime.now.subtract(
(datetime) context.items["weblog_starttime"] );
// do your logging
webrequestlog.log(app.configuration.connectionstring,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& true
一旦上下文被设置好,asp.net需要通过httpapplication对象将收到的请求路由到适合的/虚拟目录.每个asp.net必须被设置到一个虚拟目录(或者web根目录)而且每个””是被单独的处理的.
httpapplication类似仪式的主人-它是处理动作开始的地方
asp.net2.0中的变化
asp.net2.0并没有对底层架构做很多改变.主要的新特性是httpapplication对象有了一系列新的事件-大部分是预处理和后处理事件钩子-这使得事件管道变得更加的颗粒状了.asp.net2.0也支持新的isapi功能- hse_req_exec_url-这允许在asp.net处理的内部重定向到另外的url上.这使得asp.net可以在iis中设置一个通配符扩展,并处理所有的请求,其中一部分被http处理器(handler)处理,另一部分被新的defaulthttphandler对象处理. defaulthttphandler会在内部调用isapi来定位到原始的url上.这允许asp.net可以在其他的页面,如asp,被调用前处理认证和登录等事情.
域的主人:httpapplication
每个请求都被路由到一个httpapplication对象上.httpapplicationfactory类根据的负载为你的asp.net应用创建一个httpapplication对象池并为每个请求分发httpapplication对象的引用.对象池的大小受machine.config文件中processmodel键中的maxworkerthreads设置限制,默认是20个(译注:此处可能有误,根据reflector反编译的代码,池的大小应该是100个,如果池大小小于100,httpapplicationfactory会创建满100个,但是考虑到会有多个线程同时创建httpapplication的情况,实际情况下有可能会超过100个).
对象池以一个更小的数字开始;通常是一个然后增长到和同时发生的需要被处理的请求数量一样.对象池被监视,这样在大负载下它可能会增加到最大的实例数量,当负载降低时会变回一个更小的数字.
httpapplication是你的web程序的外部包装器,而且它被映射到在global.asax里面定义的类上.它是进入httpruntime的第一个入口点.如果你查看global.asax(或者对应的代码类)你会发现这个类直接继承自httpapplication:
public class global : system.web.httpapplication
httpapplication的主要职责是作为http管道的事件控制器,所以它的接口主要包含的是事件.事件挂接是非常广泛的,包括以下这些:
l&&&&&&&& beginrequest
l&&&&&&&& authenticaterequest
l&&&&&&&& authorizerequest
l&&&&&&&& resolverequestcache
l&&&&&&&& aquirerequeststate
l&&&&&&&& prerequesthandlerexecute
l&&&&&&&& …handler execution…
l&&&&&&&& postrequesthandlerexecute
l&&&&&&&& releaserequeststate
l&&&&&&&& updaterequestcache
l&&&&&&&& endrequest
每个事件在global.assx文件中以application_前缀开头的空事件作为实现.例如, application_beginrequest(), application_authorizerequest()..这些处理器为了便于使用而提供因为它们是在程序中经常被使用的,这样你就不用显式的创建这些事件处理委托了.
理解每个asp.net虚拟目录在它自己的域中运行,而且在域中有多个从asp.net管理的池中返回的httpapplication实例同时运行,是非常重要的.这是多个请求可以被同时处理而不互相妨碍的原因.
查看列表4来获得域,线程和httpapplication之间的关系.
private void page_load(object sender, system.eventargs e)
&&& // put user code to initialize the page here
&&& this.applicationid = ((howaspnetworks.global)
&&&&&&&&&& httpcontext.current.applicationinstance).
&&& this.threadid = appdomain.getcurrentthreadid();
&&& this.domainid = appdomain.currentdomain.
&&& this.threadinfo = "threadpool thread: " +
&&&&&&&&&&& system.threading.thread.currentthread.isthreadpoolthread.tostring() +
&&&&&& &&&&&&&&&&&&&&& "&br&thread apartment: " +
&&&&&&&&&&& system.threading.thread.currentthread.apartmentstate.tostring();
&&& // *** simulate a slow request so we can see multiple
&&& //&&&& requests side by side.
&&& system.threading.thread.sleep(3000);
这是随sample提供的demo的一部分,运行的结果在图5中显示.运行两个浏览器,打开这个演示页面可以看到不同的id.
图5-你可以通过同时运行多个浏览器来简单的查看域,池实例和请求线程是如何交互的.当多个请求同时发起,你可以看到线程id和id变化了,但是域还是同一个.
你可能注意到在大多数请求上,当线程和httpapplication id变化时域id却保持不变,虽然它们也可能重复(指线程和httpapplication id).httpapplication是从一个集合中取出,在随后到来的请求中可以被复用的,所以它的id有时是会重复的.注意application实例并不和特定的线程绑定-确切的说它们是被指定给当前请求的活动线程.
线程是由.net的线程池管理的,默认是多线程套间(mta)线程.你可以在asp.net的页面上通过指定@page指令的属性aspcompat=”true”来覆盖套间属性.aspcompat意味着为com组件提供一个安全的执行环境,指定了这个属性,就会为这些请求使用特殊的单线程套间(sta).sta线程被存放在单独的线程池中,因为它们需要特殊的处理.
这些httpapplication对象全部在同一个域中运行的事实是非常重要的.这是为什么asp.net可以保证对web.config文件或者单独的asp.net页面的修改可以在整个域中生效.改变web.config中的一个值导致域被关闭并重启.这可以保证所有的httpapplication可以”看到”这个修改,因为当域重载入的时候,所做的修改(译注:即被修改的文件)会在启动的时候被重新读入.所有的静态引用也会被重载,所以如果程序通过app configuration settings读取值,这些值也会被刷新.
为了在sample中看到这点,点击applicationpoolsandthreads.aspx页面并记下域id.然后打开并修改web.config(加入一个空格并保存).然后重新载入页面.你会发现一个新的域已经被创建了.
本质上当上面的情况发生时,web应用/虚拟目录是完整的”重启”了.所有已经在管道中被处理得请求会继续在现存的管道中被处理,当任何一个新的请求来到时,它会被路由到新的域中.为了处理”被挂起的请求”,asp.net在请求已超时而它(指请求)还在等待时强制关闭域.所有事实上是可能出现一个对应两个域,此时旧的那个正在关闭而新的正在启动.两个域都继续为它们的客户服务,直到老的那个处理玩正在等待处理的请求并关闭,此时只有一个域在运行.
“流过”asp.net管道
httpapplication触发事件来通知你的程序有事发生,以此来负责请求流转.这作为httpapplication.init()函数的一部分发生(用reflector查看system.web.httpapplication.initinternal()方法和httpapplication.resumesteps()方法来了解更多详情),连续设置并启动一系列事件,包括执行所有的处理器(handler).这些事件处理器映射到global.asax中自动生成的哪些事件中,同时它们也映射到所有附加的httpmodule(它们本质上是httpapplication对外发布的额外的事件接收器(sink)).
httpmodule和httphandler两者都是根据web.config中对应的配置被动态载入并附加到事件处理链中.httpmodule实际上是事件处理器,附加到特殊的httpapplication事件上,然而httphandler是用来处理”应用级请求处理”的终点.
httpmodule和httphandler两者都是在httpapplication.init()函数调用的一部分中被载入并附加到调用链上.图6显示了不同的事件,它们是何时发生的以及它们影响管道的哪一部分.
图6-事件在asp.net http管道中流转的过程.httpapplication对象的事件驱动请求在管道中流转.http module可以拦截这些事件并覆盖或者扩展现有的功能.
httpcontext, httpmodules 和 httphandlers
httpapplication它本身对发送给的数据一无所知-它只是一个通过事件来通讯的消息对象.它触发事件并通过httpcontext对象来向被调用函数传递消息.实际的当前请求的状态数据由前面提到的httpcontext对象维护.它提供了所有请求专有的数据并从进入管道开始到结束一直跟随请求.图7显示了asp.net管道中的流程.注意上下文对象(即httpcontext),这个从请求开始到结束一直都是你”朋友”的对象,可以在一个事件处理函数中保存信息并在以后的事件处理函数中取出.
一旦管道被启动,httpapplication开始象图六那样一个个的触发事件.每个事件处理器被触发,如果事件被挂接,这些处理器将执行它们自己的任务.这个处理的主要任务是最终调用挂接到此特定请求的httphandler.处理器(handler)是asp.net请求的核心处理机制,通常也是所有级别的代码被执行的地方.记住asp.net页面和web服务框架都是作为httphandler实现,这里也是处理请求的的核心之处.模块(module)趋向于成为一个传递给处理器(handler)的上下文的预处理或后处理器.asp.net中典型的默认处理器包括预处理的认证,缓存以及后处理中各种不同的编码机制.
有很多关于httphandler和httpmodule的可用信息,所以为了保持这篇文章在一个合理的长度,我将提供一个关于处理器的概要介绍.
httpmodule
当请求在管道中传递时,httpapplicaion对象中一系列的事件被触发.我们已经看到这些事件在global.asax中作为事件被发布.这种方法是特定于的,可能并不总是你想要的.如果你要建立一个通用的可用被插入任何web的httpapplication事件钩子,你可用使用httpmodule,这是可复用的,不需要特定语代码的,只需要web.config中的一个条目.
模块本质上是过滤器(fliter)-功能上类似于isapi过滤器,但是它工作在asp.net请求级别上.模块允许为每个通过httpapplication对象的请求挂接事件.这些模块作为外部中的类存贮.,在web.config文件中被配置,在启动时被载入.通过实现特定的接口和方法,模块被挂接到httpapplication事件链上.多个httpmodule可用被挂接在相同的事件上,事件处理的顺序取决于它们在web.config中声明的顺序.下面是在web.config中处理器定义.
&configuration&
& &system.web&
&&& &httpmodules&
& &add name= "basicauthmodule"
&&&&& type="httphandlers.basicauth,webstore" /&
&&& &/httpmodules&
& &/system.web&
&/configuration&
注意你需要指定完整的类型名和不带dll扩展名的名.
模块允许你查看每个收到的web请求并基于被触发的事件执行一个动作.模块在修改请求和响应数据方面做的非常优秀,可用为特定的程序提供自定义认证或者为发生在asp.net中的每个请求增加其他预处理/后处理功能.许多asp.net的功能,像认证和会话(session)引擎都是作为httpmodule来实现的.
虽然httpmodule看上去很像isapi过滤器,它们都检查每个通过asp.net应用的请求,但是它们只检查映射到单个特定的asp.net应用或虚拟目录的请求,也就是只能检查映射到asp.net的请求.这样你可以检查所有aspx页面或者其他任何映射到asp.net的扩展名.你不能检查标准的.htm或者图片文件,除非你显式的映射这些扩展名到asp.net isapi dll上,就像图1中展示的那样.一个常见的此类应用可能是使用模块来过滤特定目录中的jpg图像内容并在最上层通过gdi+来绘制’样品’字样.
实现一个http模块是非常简单的:你必须实现之包含两个函数(init()和dispose())的ihttpmodule接口.传进来的事件参数中包含指向httpapplication对象的引用,这给了你访问httpcontext对象的能力.在这些方法上你可以挂接到httpapplication事件上.例如,如果你想挂接authenticaterequest事件到一个模块上,你只需像列表5中展示的那样做
列表5:基础的http模块是非常容易实现的
public class basicauthcustommodule : ihttpmodule
&&& public void init(httpapplication application)
&&&&&& // *** hook up any httpapplication events
&&&&&& application.authenticaterequest +=
&&&&&&&&&&&&&&& new eventhandler(this.onauthenticaterequest);
&&& public void dispose() { }
&&& public void onauthenticaterequest(object source, eventargs eventargs)
&&&&&& httpapplication app = (httpapplication)
&&&&&& httpcontext context = httpcontext.
&&&&&& … do what you have to do…&&&&&&&&&&&&&&&&&&&&&&& }
记住你的模块访问了httpcontext对象,从这里可以访问到其他asp.net管道中固有的对象,如请求(request)和响应(response),这样你还可以接收用户输入的信息等等.但是记住有些东西可能是不能访问的,它们只有在处理链的后段才能被访问.
你可以在init()方法中挂接多个事件,这样你可以在一个模块中实现多个不同的功能.然而,将不同的逻辑分到单独的类中可能会更清楚的将模块进行(译注:这里的和前面的模块没有什么关系)在很多情况下你实现的功能可能需要你挂接多个事件-例如一个日志过滤器可能在beginrequest事件中记录请求开始时间,然后在endrequest事件中将请求结束写入到日志中.
注意一个httomodule和httpapplication事件中的重点:response.end()或pleterequest()会在httpapplication和module的事件链中”抄近道”.看”注意response.end()”来获得更多信息.
注意response.end()
当创建httpmodule或者在global.asax中实现事件钩子的时候,当你调用response.end或 pleterequest的时候要特别注意.这两个函数都结束当前请求并停止触发在http管道中后续的事件,然后发生将控制返回到web服务器中.当你在处理链的后面有诸如记录日志或对内容进行操作的行为时,因为他们没有被触发,有可能使你上当.例如,sample中logging的例子就会失败,因为如果调用response.end()的话,endrequest事件并不会被触发.
httphandlers
模块是相当底层的,而且对每个来到asp.net的请求都会被触发.http处理器更加的专注并处理映射到这个处理器上的请求.
http处理器需要实现的东西非常简单,但是通过访问httpcontext对象它可以变得非常强大.http处理器通过实现一个非常简单的ihttphandler接口(或是它的异步版本,ihttpasynchandler),这个接口甚至只含有一个方法-processrequest()-和一个属性isreusable.关键部分是processrequest(),这个函数获取一个httpcontext对象的实例作为参数.这个函数负责从头到尾处理web请求.
单独的,简单的函数?太简单了,对吧?好的,简单的接口,但并不弱小!记住webform和webservice都是作为http处理器实现的,所以在这个看上去简单的接口中包装了很强大的能力.关键是这样一个事实,当一个请求来到http处理器时,所有的asp.net的内部对象都被准备和设置好来处理请求了.主要的是httpcontext对象,提供所有相关的请求功能来接收输入并输出回web服务器.
对一个http处理其来说所有的动作都在这个单独的processrequest()函数的调用中发生.这像下面所展示的这样简单:
public void processrequest(httpcontext context)
context.response.write("hello world");
也可以像一个可以从html模板渲染出复杂表单的webform页面引擎那么完整,复杂.通过这个简单,但是强大的接口要做什么,完全取决于你的决定.
因为context对象对你是可用的,你可用访问request,response,session和cache对象,所以你拥有所有asp.net请求的关键特性,可以获得用户提交的内容并返回你产生的内容给客户端.记住httpcontext对象-它是你在整个asp.net请求的中的”朋友”.
处理器的关键操作应该是将输出写入response对象或者更具体一点,是response对象的outputstream.这个输出是实际上被送回到客户端的.在幕后,isapiworkerrequest管理着将输出流返回到isapi ecb的过程.writeclient方法是实际产生iis输出的方法.
图7-asp.net请求管道通过一系列事件接口来转发请求,提供了更大的灵活性.application当请求到来并通过管道时作为一个载入web应用并触发事件的宿主容器.每个请求都沿着配置的http过滤器和模块的路径走(译注:原文为http filters and modules,应该是指http module和http handler).过滤器可以检查每个通过管道的请求,handler允许实现逻辑或者像web form和webservice这样的应用层接口.为了向应用提供输入输出,context对象在这个处理过程中提供了特定于请求的的信息.
webform使用一系列在框架中非常高层的接口来实现一个http处理器,但是实际上webform的render()方法简单的以使用一个htmltextwriter对象将它的最终结果输出到context.response.outputstream告终.所以非常梦幻的,终究即使是向webform这样高级的工具也只是在request和response对象之上进行了抽象而已.
到了这里你可能会疑惑在http handler中你到底需要处理什么.既然webform提供了简单可用的http handler实现,那么为什么需要考虑更底层的东西而放弃这扩展性呢?
webform对于产生复杂的html页面来说是非常强大的,业务层逻辑需要图形布局工具和基于模块的页面.但是webform引擎做了一系列overhead intensive的任务.如果你想要做的是从系统中读入一个文件并通过代码将其返回的话,不通过webform框架直接返回文件会更有效率.如果你要做的是类似从 中读出图片的工作,并不需要使用页面框架-你不需要模板而且确定不需要web页面并从中捕捉用户事件.
没有理由需要建立一个页面对象和session并捕捉页面级别的事件-所有这些需要执行对你的任务没有帮助的额外的代码.
所以自定义处理器更加有效率.处理器也可用来做webform做不到的事情,例如不需要在硬盘上有物理文件就可用处理请求的能力,也被称为虚拟url.要做到这个,确认你在图1中展示的应用扩展对话框中关掉了”检查文件存在”选项.
这对于内容提供商来说非常常见,象动态图片处理,xml服务,url重定向服务提供了vanity urls,下载管理以及其他,这些都不需要webform引擎.
异步http handler
在这篇文章中我大部分都在讨论同步处理,但是asp.net运行时也可以通过异步http handler来支持异步操作.这些处理器自动的将处理”卸载”到独立的线程池的线程中并释放主asp.net线程,使asp.net线程可以处理其他的请求.不幸的是在1.x版的.net中,”卸载”后的处理还是在同一个线程池中,所以这个特性之增加了一点点的性能.为了创建真正的异步行为,你必须创建你自己的线程并在回调处理中自己管理他们.
当前版本的asp.net 2.0 beta 2在ihttphandlerasync(译注:此处应该是指ihttpasynchandler,疑为作者笔误)接口和page类两方面做了一些对异步处理的改进,提供了更好的性能,但是在最终发布版本中这些是否会保留.
我说的这些对你来说够底层了吗?
唷-我们已经走完了整个请求处理过程了.这过程中有很多底层的信息,我对http模块和http处理器是怎么工作的并没有描述的非常详细.挖掘这些信息相当的费时间,我希望在了解了asp.net底层机制后,你能获得和我一样的满足感.
iis(handler),
iis获得请求
检查脚本映射中,此请求是否映射到aspnet_isapi.dll
启动工作进程 (iis5中为aspnet_wp.exe,iis6中为w3wp.exe)
.net运行时被载入
isapiruntime.processrequest()被调用
为每个请求创建一个isapiworkerrequest
httpruntime.processrequest()被工作进程调用
以isapiworkerrequest对象为参数创建httpcontext对象
调用httpapplication.getapplicationinstance()来从池中取得一个对象实例
调用httpapplication.init()来开始管道事件并挂接模块和处理器
httpapplicaton.processrequest被调用以开始处理.
管道中的事件被依次触发
处理器被调,processrequest函数被触发
<li class=msonormal style="margin: 0cm搜索此文相关文章:此文来自: 马开东博客
网址: 站长QQ
了解ASP.NET底层架构_博客园相关文章
博客园_总排行榜
博客园_最新
博客园_月排行榜
博客园_周排行榜
博客园_日排行榜

我要回帖

 

随机推荐