.net的page请求过程。
如上图如示,我们向web服务器请求一个.aspx页面,首先是经过IIS,IIS发现自己处理不了这个请求,于是通过aspnet_isapi.dll调度给asp.net引擎来处理。.net首先初始化HTTPMODULE,比如说,CacheModule,Custommodule,SessionModule,AuthModule等,最终通过HTTPHANDLE处理程序来处理并最终生成HTML代码,返回client端.
Http Module概述
HttpModule 是实现了IHttpModule接口的程序集。IHttpModule 接口本身并没有什么好大写特写的,由它的名字可以看出,它不过是一个普普通通的接口而已。实际上,我们关心的是实现了这些接口的类,如果我们也编写代码实现了这个接口,那么有什么用途。一般来说,我们可以将Asp.Net中的事件分成三个级别,最顶层是应用程序级事件、其次是页面级事件、最下面是控件级事件,事件的触发分别与应用程序周期、页面周期、控件周期紧密相关。而 Http Module 的作用是与应用程序事件 密切相关的。
HttpHandler概述
什么是httphandler?每个asp.net开发人员都已经写了成百上千个HttpHandler,其实普通的aspx页页其实就是一个HttpHandler,因为它实现了IHttpHandler接口。
HttpHandler就是最终响应Http请求,生成Http响应的处理器,它的实例是由asp.net运行时创建,并生成在asp.net运行时环境中。如果asp.net运行是是处理请求的工厂,那么HttpHandler就是处理请求的工人。
那么什么情况下,我们需要自定义的HttpHandler呢?一般情况下我们响应clienl请求的都是HTML页面,这种情况下,System.web.ui.page类这个默认的HttpHandler完全可以胜任这个工作,但有些时候我们响应给client端的不一定就是html页面,如果是xml或者图片呢?这个时候HTTPHANDLER的用法就显示出来了。
以TextBox控件来介绍.net控件是如何工作的
步骤1:TextBox是如何解析成对象的?
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="TextBox1" runat="server">red</asp:TextBox>
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" /></div>
</form>
</body>
当我们编译这个工程时,.net会在
C:"WINDOWS"Microsoft.NET"Framework"v2.0.50727"Temporary ASP.NET Files目录下,生成一个解析页面的程序集,这里我copy出解析TextBox控件的代码。
[DebuggerNonUserCode]
private TextBox __BuildControlTextBox1()
{
TextBox __ctrl = new TextBox();
base.TextBox1 = __ctrl;
__ctrl.ApplyStyleSheetSkin(this);
__ctrl.ID = "TextBox1";
__ctrl.Text = "red";
return __ctrl;
}
我们在页面中引用CustomTextBox
<cc1:CustomTextBox ID="CustomTextBox1" runat="server" >red</cc1:CustomTextBox>
.net的解析如下:
[DebuggerNonUserCode]
private CustomTextBox __BuildControlCustomTextBox1()
{
CustomTextBox __ctrl = new CustomTextBox();
base.CustomTextBox1 = __ctrl;
__ctrl.ApplyStyleSheetSkin(this);
__ctrl.ID = "CustomTextBox1";
__ctrl.TextColor = "red";
return __ctrl;
}
Red被解析为TextColor属性的值了。
3:数据回传
我们知道,数据回传其实就是web表单提交回服务器端,asp.net运行时将提交回的表单中的数据包装成一个NameValueCollection,其中的Name就是表单域的name属性(所以要实现数据回传就要为控件提供name属性),而value就是表单域的值。在处理页面时,asp.net将会遍历所有的子控件,如果子控件实现了ipostbackdatahandler接口,页面就会把该控件对应的名子和整个namevaluecollection作为参数,调用控件的loadpostdata()方法。因此,所有实现了ipostbackdatahandler接口的控件都获得了从回传表单中获取新值的机会。这也就是为什么TextBox禁用了viewstate,但还是可以保存值的原因了。。。。
再进一步,可以在loadpostdata()方法中判断控件的值和传回的新值相不相等,如果不相等,可以让loadpostdata()方法返回true值,此时页面会记下该控件数据发生了改变。在所有控件都回载完回传数据之后,页面将在RaiseChangeEvents过程中调用LoadPostData()方法返回true的控件的RaisePostDataChangedAEvent()方法。在RaisePostDataChangedEvent()方法里,我们可以根据需要触发相应的控件事件。
#region IPostBackDataHandler 成员
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
string text = this.Text;
string text2 = postCollection[postDataKey];
if (!text.Equals(text2, StringComparison.Ordinal))
{
this.Text = text2;
return true;
}
return false;
}
public void RaisePostDataChangedEvent()
{
EventHandler handler = (EventHandler)Events[EventTextChanged];
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
#endregion
4:事件回传
当页面发送到客户端浏览器后,用户对页面的操作本质上中会触发客户端的事件,比如用户点击一个<input type=’button’/>的按钮,触发的click事件其实只不过是客户端按钮的click事件,那么这个事件又是怎么“传染”到服务器端的呢?
我们先来看看服务器端控件生成的客户端代码,比如LinkButton呈现的代码如下:
<a id="LinkButton1" href="javascript:__doPostBack('LinkButton1','')">LinkButton</a>
在这句代码中,我们可以看到__doPostBack()这个function的身影,那么它又是何方神圣呢?
在需要回传的页面中,asp.net了两个隐藏表单域和一段javascript脚本,以支持所有控件的回传:
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE4NDIzMDg2NDRkZDtiLoa3YH4xxYC5CiEca+ZHJxn8" />
<script type="text/javascript">
//<![CDATA[
var theForm = document.forms['form1'];
if (!theForm) {
theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
//]]>
</script>
结合前面的代码断,可以看到需要触发服务器端事件的控件其实是调用了_ doPostBack这个function,分别把事件源和事件的参数赋值给表单中的两个隐藏域:__EVENTTARGET和__EVENTARGUMENT,然后提交表单到服务器端.
在页面的生命周期当中,有一个专门处理页面回传事件的阶段:RaisePostBack,在这个阶段,页面会根据_EVENTTARGET的值找到事件源控件,然后触发该控件的RaisePostBackEvent方法,并把__EVENTARGUMENT作为参数传给控件。
此时,页面要求控件具有RaisePostBack方法,也就是说该控件必须实现IPostBackEventHandler接口。