.Net的Page请求过程:

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

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

注册HttpModule
在介绍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:

需求:在每个请求的结果页面中,头尾处都要打印出一串字符:www.cnblogs.com

如:

www.cnblogs.com

page内容

www.cnblogs.com

实现方案:

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

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("www.cnblogs.com");
        }
               //结束请求前
        public void EndRequestEvent(object obj, EventArgs e)
        {
            HttpContext.Current.Response.Write("www.cnblogs.com");
        }
        #endregion
    }
}

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

<system.web>
  <httpModules>
    <add name="TagModule" type="megajoydemo.HttpModule.TagModule,megajoydemo"/>
  </httpModules>
</system.web>

name是自定义HttpModule的名称,type指定所在的类,最后type="megajoydemo.HttpModule.TagModule,megajoydemo"表示命名空间。
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文件。找到<httpHandlers>结点:

<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指定处理类。

用简单的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+);
        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。

下面我们来认识一下.Net控件是如何工作的,就以TextBox控件来介绍吧。

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

我新建了一个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的构造函数有四个版本,如下表如示:

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

现在我们知道了TextBox是如何把red解析为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接口。

站外扩展阅读:

asp.net页面生存周期

ASP.NET页面生存周期的更多相关文章

  1. ASP.NET页面与IIS底层交互和工作原理详解

    转载自:http://www.cnblogs.com/lidabo/archive/2012/03/13/2393200.html 第一回: 引言 我查阅过不少Asp.Net的书籍,发现大多数作者都是 ...

  2. ASP.NET页面与IIS底层交互和工作原理详解(第二回)

    引言 在 Part.1 Http请求处理流程 一文中,我们了解了Http请求的处理过程以及其它一些运作原理.我们知道Http管道中有两个可用接口,一个是IHttpHandler,一个是IHttpMod ...

  3. ASP.net 页面生命周期

    ASP.NET 页面生命周期 Page_Preinit(); 在页初始化开始时发生 Page_Init(); 在所有控件初始化且应用外观设置后引发 Page_InitComplete(); 在页初始化 ...

  4. asp.net页面生命周期

    Asp.Net页面生命周期 本文转载自:http://www.cnblogs.com/xhwy/archive/2012/05/20/2510178.html 一.什么是Asp.Net页面生命周期 当 ...

  5. asp.net 页面上的点击事件

    asp.net 页面上 服务器端控件Button 有两个click事件如 <asp:Button ID="Button1" runat="server" ...

  6. ASP.NET页面的字符编码设置

    在用ASP.NET写网上支付的接口程序时,遇到一个奇怪问题,通过表单提交过去的中文全是乱码,英文正常.而用asp程序进行测试,可以正常提交中文,asp页面中有这样的HTML代码: <meta h ...

  7. [转] c# 模拟Asp.net页面中的某个按钮的点击,向web服务器发出请求

    在没有做题目中所述的内容的时候,感觉这应该是很简单的东西,但是当真正开始做的时候却发现,有很多问题现在在这里写出来,供和我一样水平不高的参考一下. 在写本文之前参照了一下文章 欢迎使用CSDN论坛阅读 ...

  8. 如何提高ASP.NET页面载入速度的方法

    前言 本文是我对ASP.NET页面载入速度提高的一些做法,这些做法分为以下部分: 1.采用 HTTP Module 控制页面的生命周期. 2.自定义Response.Filter得到输出流stream ...

  9. ASP.NET页面中去除VIEWSTATE视图状态乱码

    保存页的所有视图状态信息和控件状态信息. 基于SEO技术的开发,在没有接触MVC框架 Razor 引擎的时候,我们需要使用ASP.NET引擎,如果使用ASP.NET引擎的服务器端控件,那么在ASP.N ...

随机推荐

  1. thikphp创建共享数据config.php

    要求:前台,后台:只需要配置一个config.php 其他文件共享 默认配置是 Index/Conf/config.php Admin/Conf/config.php 代码: return array ...

  2. Spell checker

     Spell checker Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Subm ...

  3. jquery 删除数组元素

    expertsId.splice($.inArray(thisID.split('&')[0],expertsId),1); 1. expertsId数组名2. thisID.split('& ...

  4. django models使用学习记录

    如何更新单个数据 example = User.objects.get(id=1) example.is_acitve=1 example.save() 如何更新多个数据 examples = Use ...

  5. ResponsiveSlides.js最轻量级的幻灯片插件

    摘要:ResponsiveSlides.js是一个展示同一容器内图片的轻量级响应式jQuery幻灯片插件它支持包括IE6在内的几乎所有的浏览器,在IE6中还支持最大宽度属性,但在其它浏览器中并不原生支 ...

  6. NOIP2013Day1解题报告

    本来今天晚上拿13年NOIP的题目来做一下,测测能够得多少分,结果一晚上把Day1写完竟然AK了,吼吼吼 D1T1,题目:http://codevs.cn/problem/3285/ 很水的一道快速幂 ...

  7. 【Windows】如何判断当前鼠标是否按下左键或右键

    在delphi中,很多窗体和控件的鼠标事件里面已经将鼠标按键状态封装好传给响应事件的函数,所以这种情况直接使用就可以,但在某些时候,我们没有这些事件可以处理时,想判断鼠标按键是否按下的状态,就需要借助 ...

  8. SecureCRT配色方案

    SecureCRT是一款支持SSH(SSH1和SSH2)的终端仿真程序,简单地说是Windows下登录UNIX或Linux服务器主机的软件.作为一款经常使用的终端软件,一个好的配色方案可以大大的提高学 ...

  9. lintcode:Remove Nth Node From End of Lis 删除链表中倒数第n个节点

    题目: 删除链表中倒数第n个节点 给定一个链表,删除链表中倒数第n个节点,返回链表的头节点.  样例 给出链表1->2->3->4->5->null和 n = 2. 删除 ...

  10. WebFormJS注册位置

    1. int height = Request.Browser.ScreenPixelsHeight; int width = Request.Browser.ScreenPixelsWidth; R ...