Asp.Net页面生存周期(转)

相信对asp.net的页面生存周期深入理解后一定能让你的水平上一个很大的台阶(我们经理常用的话) 呵呵~

 

首页我们先来简单的介绍一下.netpage请求过程。

我们先来看一幅图:


 

如上图如示,我们向web服务器请求一个.aspx页面,首先是经过IISIIS发现自己处理不了这个请求,于是通过aspnet_isapi.dll调度给asp.net引擎来处理。.net首先初始化HTTPMODULE,比如说,CacheModule,Custommodule,SessionModule,AuthModule等,最终通过HTTPHANDLE处理程序来处理并最终生成HTML代码,返回client.

Http Module概述

HttpModule 是实现了IHttpModule接口的程序集。IHttpModule 接口本身并没有什么好大写特写的,由它的名字可以看出,它不过是一个普普通通的接口而已。实际上,我们关心的是实现了这些接口的类,如果我们也编写代码实 现了这个接口,那么有什么用途。一般来说,我们可以将Asp.Net中的事件分成三个级别,最顶层是应用程序级事件、其次是页面级事件、最下面是控件级事件,事件的触发分别与应用程序周期、页面周期、控件周期紧密相关。 Http Module 的作用是与应用程序事件 密切相关的。

注册 Http Module

在介绍httpmodule的注册之前,我们先来看一下asp.net内置的一些httpmodule

我们找到C:"WINDOWS"Microsoft.NET"Framework" v2.0.50727"CONFIG 目录下的 web.config 文件。找到 <httpModules/> 结点,你可以看到下面的内容:

<httpModules>
    <add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />
    <add name="Session" type="System.Web.SessionState.SessionStateModule" />
    <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />
    <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
    <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" />
    <add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
    <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
...

</httpModules>

Name.net内置的httpmodule名称,type就是指定该httpmodule所在的类。

现在我写一个简单的demo来介绍一下如何使用自定义的httpmodule

需求:

在每个请求的结果页面中,头尾处必须要打印出一串字符http://www.joy.cn..

如:

http://www.joy.cn

page内容

http://www.joy.cn

实现方案如下:

我们实现一个自定义的TagHttpModule,在页面开始请求的时候(BeginRquest)先打印出我们所需要的字符串http://www.joy.cn,页面请求结束之前(EndRequest)在一次打印出我们所需要的字符串http://www.joy.cn.

代码如下:

using System;

using System.Web;

namespace megajoydemo.HttpModule

{

    public class TagModule:IHttpModule

    {

        #region IHttpModule 成员

        public void Dispose()

        {

            throw new Exception("The method or operation is not implemented.");

        }

        public void Init(HttpApplication context)

        {

            context.BeginRequest += new EventHandler(BeginRequestEvent);

            context.EndRequest += new EventHandler(EndRequestEvent);

        }

        public void BeginRequestEvent(object obj,EventArgs e)

        {

            HttpContext.Current.Response.Write("http://www.joy.cn");

        }

        public void EndRequestEvent(object obj, EventArgs e)

        {

            HttpContext.Current.Response.Write("http://www.joy.cn");

        }

        #endregion

    }

}

TagHttpModule实现了我们所需要的功能,接下来我们需要在webconfig文件中注册这个httpmodule

    <system.web>

      <httpModules>

        <addname="TagModule"type="megajoydemo.HttpModule.TagModule,megajoydemo"/>

      </httpModules>

</system.web>

OK到此我们学会了怎么实现自定义的HTTPModule.

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的用法就显示出来了。

注册 HttpHandler

同样在介绍httphandler的注册之前,我们先来看一下asp.net内置的一些httphandler

我们找到C:"WINDOWS"Microsoft.NET"Framework" v2.0.50727"CONFIG 目录下的 web.config 文件。找到 <httpHandler/> 结点,你可以看到下面的内容:

<httpHandlers>

      <add path="*.axd" verb="*" type="System.Web.HttpNotFoundHandler" validate="true"/>

    <add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="true"/>

 <add path="*.ashx" verb="*" type="System.Web.UI.SimpleHandlerFactory" validate="true"/>

…………………………….

</httpHandlers>

Path就是指定请求的扩展名,verb就是请求的方式,如post,get等,type指定处理类。

OK,现在我也同样来写一个简单的Demo来进一步的了解HTTPHANDLER的作用..

需求:实现图片的水印效果

现代码如下:

using System;

using System.Drawing;

using System.Configuration;

using System.Web;

using System.Web.UI;

public class ImageHandler : IHttpHandler

{

    public ImageHandler()

    {

    }

    #region IHttpHandler 成员

    public bool IsReusable

    {

        get { return false; }

    }

    private Image GetOriginalImage(HttpContext context)

    {

        Image originalImage = Image.FromFile(

                        context.Server.MapPath("~/images/DefaultVideoCover.jpg"));

        return originalImage.Clone() as Image;

    }

    public void ProcessRequest(HttpContext context)

    {

        Image originalImage = GetOriginalImage(context);

        Graphics graphic = Graphics.FromImage(originalImage);

        Font font = new Font("幼圆", 24.0f, FontStyle.Regular);

        string query = HttpUtility.UrlDecode(context.Request.QueryString["query"]);

        graphic.DrawString(

            query,

            font,

            new SolidBrush(Color.Red),

            20.0f,

            originalImage.Height - font.Height+9);

        originalImage.Save(context.Response.OutputStream,

            System.Drawing.Imaging.ImageFormat.Jpeg);

        graphic.Dispose();

        originalImage.Dispose();

    }

    #endregion

}

ImageHandler实现了我们所需要的功能,接下来我们需要在webconfig文件中注册这个ImageHandler

    <system.web>

    <httpHandlers>

      <addverb="*"path="*.jpg"type="ImageHandler"/>

    </httpHandlers>

</system.web>

OK到此我们学会了怎么实现自定义的HTTPHANDLER.

上面我们简单的介绍了httpmodule,httphandler的使用方法,下面我们来认识一下.net控件是如何工作的,我按几个步骤来介绍。

我们就以TextBox控件来介绍吧。。。。

步骤1TextBox是如何解析成对象的?

我新建了一个aspx页面,代码如下:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ParseTextBox.aspx.cs" Inherits="megajoydemo.ParseTextBox" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>无标题页</title>

</head>

<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>

</html>

当我们编译这个工程时,.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;
}
 
 

这时也许有人会问,.net是怎么知道我们输入的red是 Text的值呢?

我们可以利用System.Web.UI.ParseChildrenAttribute类控制控件对其内容的解析行为。ParseChildrenAttribute的构造函数有四个版本,如下表如示:

名称

说明

ParseChildrenAttribute()

childrenAsProperties属性默认为false

ParseChildrenAttribute(Boolean)

其中bool参数的值将赋值给ChildrenAsProperties属性,以显式指定控件的内容解析为控件的属性

ParseChildrenAttribute(Type)

其中Type参数将赋值给ChildrenControlType属性,以显式指定控件内可接受的哪些类型的子控件

ParseChildrenAttribute(Boolean,string)

两个参数将分别赋值给ChildrenAsPropertiesDefaultProperty属性,DefaultProperty用于指定控件内容解析成哪个属性的值

通过查看TextBox的源代码我们可以清楚的看到,TextBoxParseChildren属性被设成ParseChildren(True,”Text”)

现在我们知道了TextBox是如何把www.joy.cn解析为Text属性的。接下来我们来自定义一个TextBox控件,并且增加一个TextColor属性,如

    [ParseChildren(true, "TextColor")]

    public class CustomTextBox :TextBox

    {

        private string textColor;

        public string TextColor

        {

            get {

                return textColor;

            }

            set {

                textColor = value;

            }

        }

}

我们在页面中引用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属性的值了。

2:如何持久化TextColor

   说到持久化,我们很自然的就想到了ViewState。实现代码如下:

        public string TextColor

        {

            get {

    

                if (ViewState["TextColor"]==null)

                {

                    return string.Empty;

                }

                return ViewState["TextColor"].ToString();

            }

            set {

                ViewState["TextColor"] = value;

            }

        }

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接口。

posted @ 2008-03-28 21:00  KenShinNan  阅读(2778)  评论(4编辑  收藏  举报