使用 HTTP 模块在 Web 应用程序的所有网页中添加页眉和页脚控件






3.21/5 (21投票s)
2004年6月11日
5分钟阅读

118971

1203
使用 HTTP 模块自动为所有 ASP.NET 页面添加页眉和页脚控件。
引言
在开发 Web 应用程序的表示层时,我们通常会为所有网页遵循一个模板结构。该模板通常包含某种公司徽标和导航的页眉部分,以及一些法律信息和联系信息的页脚部分等。这些页眉和页脚部分的 HTML 代码通常在整个站点中是相同的,因此可能需要在每个网页中复制和粘贴。为了避免页眉和页脚部分的 HTML 代码四处复制,我们使用“include”指令在每个页面中包含一段 HTML 代码。
使用 #Include 指令的问题
然而,正如大多数 Web 开发人员(包括我)已经经历过的那样,这种“include”方法存在一些缺点。
- 我们需要记住在每个 ASP 页面中插入 include指令的确切位置。
- 没有逻辑可以对页眉包含文件和页脚包含文件进行分组,如果我们希望为站点的不同部分(例如管理部分、已登录部分和公共部分)拥有多套页眉和页脚,我们最终会得到六个不同的页眉和页脚文件,它们之间完全不相关。
- 我们无法在运行时动态包含不同的文件。
使用控件的替代方法
这个简单的演示说明了一种为 Web 应用程序的所有网页添加页眉和页脚用户控件的替代方法。基本思想是使用 HTTP 模块以编程方式将控件插入到页面中的正确位置。
以下是实现 HTTP 模块的步骤概述
- 创建一个参与页面执行周期的 HTTP 模块。
- 为页面 init 事件注册一个事件处理程序。
- 在页面 init 事件处理程序中,查找“Form”控件。
- 实例化页眉和页脚控件,并将其添加到“Form”控件中。
- 在 Web.Config 中注册 HTTP 模块。
实现细节
创建 HTTP 模块
要创建 HTTP 模块,我们只需编写一个实现 System.Web.IHttpModule 接口的类。该接口要求实现两个方法:Init 和 Dispose。通常,在实现 HTTP 模块时,我们会在 Init 方法中注册事件处理程序,以便在 HTTP 请求的处理到达某个阶段时执行它们。在我们的示例中,我们将在处理 HTTP 请求的处理程序实例准备好的阶段注册事件处理程序,即 PreRequestHandlerExecute 事件。
// Note: Make sure you have the following namespace included in your
// code:
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
-----------------------------------------
public class HeaderFooterModule : IHttpModule{
    public HeaderFooterModule()
    {
    }
    public void Init(HttpApplication context)
    {
        //Register the event handler for PreRequestHandlerExecute event in 
        //the HTTP Request handling cycle
        context.PreRequestHandlerExecute += 
                   new EventHandler(context_PreRequestHandlerExecute);
    }
    private void context_PreRequestHandlerExecute(object sender, EventArgs e)
    {
        //See next code block for implementation of this handler        
    }
    public void Dispose()
    {
    }
}
为页面 Init 事件注册事件处理程序
当请求处理到达“Pre request handler execute”阶段时,处理程序实例已准备就绪。处理程序可以是任何实现 IHttpHandler 的内容,它可以是网页或一些其他请求处理程序。因此,我们需要过滤掉定向到页面的 HTTP 请求。为此,我们检查请求处理程序的类型,并确保它是一个 HTTP 页面。(稍后,我们可以添加一些其他附加逻辑,用于根据页面类型添加控件)。一旦我们确定处理程序是 Page 对象,我们就需要在其 init 事件中注册另一个事件处理程序,以将控件插入到正确的位置。在页面执行周期中,init 事件是页面控件已初始化的位置。这就是我们定位“Form”控件并添加其他代码以放置我们自己的页眉和页脚控件的地方,以便它可以在其余的页面执行阶段中使用。要定位 form 控件,我们只需迭代 Page 的 Controls 集合。form 控件应该位于 Page 的 Controls 集合的前几个元素中,因此遍历它们应该不是大问题。
    private void context_PreRequestHandlerExecute(object sender, EventArgs e)
    {
        //Get the Request handler from the Context
        HttpContext oContext = ((HttpApplication)sender).Context;
        //Check if the request handler is a Page object
        if (oContext.Handler is Page)
        { 
            Page objPage = (Page)oContext.Handler;
            //Register an event handler for the page init event            
            objPage.Init += new EventHandler(Page_Init);
        }        
    }
    private void Page_Init(object sender, EventArgs e)
    {
        //See next code block for implementation of this handler        
    }
实现页面 Init 事件处理程序的事件处理程序
在页面 Init 事件处理程序中,我们需要查找“Form”元素,实例化页眉和页脚控件(使用用户控件 .ascx 文件),并将控件放置在页面中。用于页眉和页脚的控件是使用 Visual Studio .NET 创建的用户控件。这包括一个 ascx 文件和一个代码隐藏文件。您可以在用户控件中放入其他服务器控件,例如登录按钮,并将处理登录按钮单击的代码放在用户控件的代码隐藏中。
    private void Page_Init(object sender, EventArgs e)
    {
        //Iterate through the control collection for the page
        foreach (Control objControl in ((Page)sender).Controls) 
        {
            if (objControl is HtmlForm) 
            {
                HtmlForm objForm = (HtmlForm)objControl;
                //Instantiate and add the control to the page 
                //using the .ASCX file
                objForm.Controls.AddAt(0, 
                    objForm.Page.LoadControl("Header.ascx"));
                objForm.Controls.Add(objForm.Page.LoadControl("Footer.ascx"));
                break;
            }
        }
    }
在 web.config 文件中注册模块
要注册 HTTP 模块,只需在 web.config 文件的 system.web 元素中添加以下配置段:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> <httpModules> <add name="HeaderFooterModule" type="MyNamespace.HeaderFooterModule,MyAssembly"/> </httpModules> </system.web> </configuration>
变奏
对于具有不同部分(例如管理部分和公共页面部分)的站点,我们可能希望使用不同的页眉和页脚设计。在这种情况下,一种可能的方案是让这些部分中的页面实现不同的标记接口。标记接口基本上是一个没有定义任何方法的接口。如果您只是想识别页面是否属于某个特定类别,它特别有用。例如,我们可以将上面的 Page_Init 处理程序实现替换为以下代码,以便它检查页面类型并确定要使用的适当的页眉和页脚 ascx 文件:
private void Page_Init(object sender, EventArgs e)
{
  //Iterate through the control collection for the page
  foreach (Control objControl in ((Page)sender).Controls) 
  {
    if (objControl is HtmlForm) 
    {
      HtmlForm objForm = (HtmlForm)objControl;
      if (sender is IAdminPage) 
      {
        //Instantiate and add the admin control to the page using 
        //the .ASCX file
        objForm.Controls.AddAt(0, 
          objForm.Page.LoadControl("AdminHeader.ascx"));
        objForm.Controls.Add(objForm.Page.LoadControl("AdminFooter.ascx"));
      }
      else
      {
        //Instantiate and add the regular control to the page 
        //using the .ASCX file
        objForm.Controls.AddAt(0,objForm.Page.LoadControl("Header.ascx"));
        objForm.Controls.Add(objForm.Page.LoadControl("Footer.ascx"));
      }    
      break;
    }
  }
}
对于管理页面的代码隐藏类
public interface IAdminPage {
}
public class HeaderFooterModule : Page, IAdminPage{
    ...
}
关于以编程方式向页面添加控件的特别说明
需要注意的是,使用这种方法,您不能直接在 ASP.NET 页面中编写代码片段。虽然这可能引起一些人的担忧,但我认为这并不是一个大问题,因为我相信大多数代码片段都可以被控件替代。
结论
通过这种使用 HTTP 模块添加页眉和页脚控件的方法,我们可以实现以下目标:
- 在不修改 ASP.NET 页面的任何内容的情况下,为所有页面添加统一的页眉和页脚。
- 可以为不同的部分拥有不同套的页眉和页脚。所有页面只需要实现适当的标记接口。
- 能够修改模块以添加额外的逻辑(例如基于用户凭据...)来实例化和插入不同的页眉/页脚控件。
