利用 IHttpModule 自定义 HTTP 处理模块

本文内容

  • 引入
  • IHttpModule 概述
  • 创建自定义 HTTP 模块的步骤
  • 演示创建自定义 HTTP 模块
  • HTTP 模块的工作方式
  • HTTP 模块与 Global.asax 文件
  • 参考资料

引入

本文在 VS 2008 和 IIS 6 环境下概述如何利用 IHttpModule 自定义 HTTP 模块。

当我们在 VS 2008 里新建一个 Web 应用程序项目后,会在 Web.config 文件看到如下一个配置:

<httpModules>
  <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>

我们知道 VS 2008 已经可以开发 Ajax 程序,异步刷新界面,如 ScriptManager 控件、UpdatePanel 控件,那么 System.Web.Handlers.ScriptModule 类就是管理用于 ASP.NET 中 AJAX 功能的 HTTP 模块。

更进一步,看它 C# 声明:

[AspNetHostingPermissionAttribute(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermissionAttribute(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public class ScriptModule : IHttpModule

该类继承了 IHttpModule 接口。代码如下所示。看看它的事件,你多少体会出如若让 Web 应用程序支持 Ajax 功能,发出异步请求,需要做什么,至少能够检查传入和传出的请求。

//
// ScriptModule.cs
//
// Author:
//   Igor Zelmanovich <[email protected]>
//
// (C) 2007 Mainsoft, Inc.  http://www.mainsoft.com
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
 
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.Script.Services;
 
namespace System.Web.Handlers
{
    public class ScriptModule : IHttpModule
    {
        protected virtual void Init (HttpApplication context) {
            context.PreSendRequestHeaders += new EventHandler (PreSendRequestHeaders);
            context.PostAcquireRequestState += new EventHandler (PostAcquireRequestState);
            context.AuthenticateRequest += new EventHandler (AuthenticateRequest);
        }
 
        void AuthenticateRequest (object sender, EventArgs e) {
            // The AuthenticateRequest event is raised after the identity of the current user has been 
            // established. The handler for this event sets the SkipAuthorization property of the HttpContext 
            // for the current request. This property is checked in the authorization module to see 
            // if it has to omit authorization checking for the requested url. Usually an HttpModule 
            // use this property to allow anonymous access to some resources (for example, 
            // the Login Page if we’re using forms authentication). In our scenario, 
            // the ScriptModule sets the SkipAuthorization to true if the requested url is 
            // scriptresource.axd or if the authorization module is enabled and the request is a rest 
            // request to the authorization web service.
        }
 
        void PostAcquireRequestState (object sender, EventArgs e) {
            // The PostAcquireRequestState event is raised after the session data has been obtained. 
            // If the request is for a class that implements System.Web.UI.Page and it is a rest 
            // method call, the WebServiceData class (that was explained in a previous post) is used 
            // to call the requested method from the Page. After the method has been called, 
            // the CompleteRequest method is called, bypassing all pipeline events and executing 
            // the EndRequest method. This allows MS AJAX to be able to call a method on a page 
            // instead of having to create a web service to call a method.
            HttpApplication app = (HttpApplication) sender;
            HttpContext context = app.Context;
            if (context == null)
                return;
            
            HttpRequest request = context.Request;
            string contentType = request.ContentType;
            IHttpHandler currentHandler = context.CurrentHandler;
            if (currentHandler == null)
                return;
#if TARGET_J2EE
            if (!(currentHandler is Page) && currentHandler is IServiceProvider) {
                pageType = (Type) ((IServiceProvider) currentHandler).GetService (typeof (Type));
                if (pageType == null)
                    return;
            }
#endif
            Type pageType = currentHandler.GetType ();
            if (typeof (Page).IsAssignableFrom (pageType) && !String.IsNullOrEmpty (contentType) && contentType.StartsWith ("application/json", StringComparison.OrdinalIgnoreCase)) {
                IHttpHandler h = RestHandler.GetHandler (context, pageType, request.FilePath);
                h.ProcessRequest (context);
                app.CompleteRequest ();
            }
        }
 
        void PreSendRequestHeaders (object sender, EventArgs e) {
            HttpApplication app = (HttpApplication) sender;
            HttpContext context = app.Context;
            if (context.Request.Headers ["X-MicrosoftAjax"] == "Delta=true") {
                Page p = context.CurrentHandler as Page;
#if TARGET_J2EE
                if (p == null && context.CurrentHandler is IServiceProvider)
                    p = (Page) ((IServiceProvider) context.CurrentHandler).GetService (typeof (Page));
#endif
                if (p == null)
                    return;
                ScriptManager sm = ScriptManager.GetCurrent (p);
                if (sm == null)
                    return;
                if (context.Response.StatusCode == 302) {
                    context.Response.StatusCode = 200;
                    context.Response.ClearContent ();
                    if (context.Error == null || sm.AllowCustomErrorsRedirect)
                        ScriptManager.WriteCallbackRedirect (context.Response.Output, context.Response.RedirectLocation);
                    else
                        sm.WriteCallbackException (context.Response.Output, context.Error, false);
                }
                else if (context.Error != null) {
                    context.Response.StatusCode = 200;
                    context.Response.ClearContent ();
                    sm.WriteCallbackException (context.Response.Output, context.Error, true);
                }
            }
        }
 
        protected virtual void Dispose () {
        }
 
        #region IHttpModule Members
 
        void IHttpModule.Dispose () {
            Dispose ();
        }
 
        void IHttpModule.Init (HttpApplication context) {
            Init (context);
        }
 
        #endregion
    }
}

IHttpModule 概述

HTTP 模块是一个在每次针对应用程序发出请求时调用的程序集。HTTP 模块作为 ASP.NET 请求管道的一部分调用,它们能够在整个请求过程中访问生命周期事件。HTTP 模块使您可以检查传入和传出的请求并根据该请求进行操作。

HTTP 模块通常具有以下用途:

  • 安全 因为您可以检查传入的请求,所以 HTTP 模块可以在调用请求页、XML Web services 或处理程序之前执行自定义的身份验证或其他安全检查。
  • 统计信息和日志记录 因为 HTTP 模块是在每次请求时调用的,所以,您可以将请求统计信息和日志信息收集到一个集中的模块中,而不是收集到各页中。
  • 自定义的页眉或页脚 因为您可以修改传出响应,所以可以在每一个页面或 XML Web services 响应中插入内容,如自定义的标头信息。

创建自定义 HTTP 模块的步骤

编写 HTTP 模块的一般过程如下:

  • 创建一个实现 IHttpModule 接口的类。
  • 实现 Init 方法。该方法初始化模块,并创建所需的任何应用程序事件。例如,如果希望为响应附加一些内容,可以创建 EndRequest 事件。如果希望执行自定义身份验证逻辑,可以创建 AuthenticateRequest 事件。
  • 为已创建的事件编写代码。
  • 若模块需要释放,还可以实现 Dispose 方法。
  • 在 Web.config 文件中注册模块。

演示创建自定义 HTTP 模块

将一个字符串追加到响应的开头和末尾。在对其扩展名已分配给 ASP.NET 的文件进行任何请求的过程中,该模块都将自动运行。

  • 新建 Web 项目。
  • 新建名为 HelloWorldModule 的类,该类继承 IHttpModule,如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
 
namespace HttpModuleDemo
{
    public class HelloWorldModule : IHttpModule
    {
        public HelloWorldModule()
        {
        }
 
        public String ModuleName
        {
            get { return "HelloWorldModule"; }
        }
 
        // In the Init function, register for HttpApplication 
        // events by adding your handlers.
        public void Init(HttpApplication application)
        {
            application.BeginRequest +=
                (new EventHandler(this.Application_BeginRequest));
            application.EndRequest +=
                (new EventHandler(this.Application_EndRequest));
        }
 
        private void Application_BeginRequest(Object source,
             EventArgs e)
        {
            // Create HttpApplication and HttpContext objects to access
            // request and response properties.
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;
            string filePath = context.Request.FilePath;
            string fileExtension =
                VirtualPathUtility.GetExtension(filePath);
            if (fileExtension.Equals(".aspx"))
            {
                context.Response.Write("<h1><font color=red>" +
                    "HelloWorldModule: Beginning of Request" +
                    "</font></h1><hr>");
            }
        }
 
        private void Application_EndRequest(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;
            string filePath = context.Request.FilePath;
            string fileExtension =
                VirtualPathUtility.GetExtension(filePath);
            if (fileExtension.Equals(".aspx"))
            {
                context.Response.Write("<hr><h1><font color=red>" +
                    "HelloWorldModule: End of Request</font></h1>");
            }
        }
 
        public void Dispose() { }
    }
}
  • 在 Web.config 文件注册该模块,如下所示:
<httpModules>

  <add name="HelloWorldModule" type="HttpModuleDemo.HelloWorldModule"/>
</httpModules>
  • 新建一个页面,并用浏览器查看该页面,页面会显示如下信息。

HTTP 模块的工作方式

模块必须注册才能从请求管道接收通知。注册 HTTP 模块的最常用方法是在应用程序的 Web.config 文件中进行注册。

当 ASP.NET 创建表示您的应用程序的 HttpApplication 类的实例时,将创建已注册的任何模块的实例。在创建模块时,将调用它的 Init 方法,并且模块会自行初始化。

当这些应用程序事件被引发时,将调用模块中的相应方法。该方法可以执行所需的任何逻辑。如检查身份验证或记录请求信息。在事件处理过程中,模块能访问当前请求的 Context 属性。这使得可以将请求重定向到其他页、修改请求或者执行任何其他请求操作。例如,若模块检查身份验证,则在凭据不正确的情况下,模块可能重定向到登录页或错误页。否则,当模块的事件处理程序完成运行时,ASP.NET 会调用管道中的下一个进程。这可能是另一个模块,也可能是用于该请求的 HTTP 处理程序(如 .aspx 文件)。

HTTP 模块与 Global.asax 文件

之所以把它们放在一起,是因为在某种程度上,它们功能有相似之处。

可以在应用程序的 Global.asax 文件中实现模块的许多功能,这使你可以响应应用程序事件。但是,

  • 模块相对于 Global.asax 文件具有如下优点:模块可以进行封装。创建一次后在其他应用程序中使用。通过将它们添加到全局程序集缓存,并将其注册到 Machine.config 文件中,可以跨应用程序重新使用它们。
  • 使用 Global.asax 文件的好处在于可以处理其他已注册事件,如 Session_Start 和 Session_End。此外,Global.asax 文件还允许您实例化可在整个应用程序中使用的全局对象。

当您必须创建依赖应用程序事件的代码,并且符合以下条件时,都应该使用模块:

  • 希望在其他应用程序中重用该模块。
  • 希望避免将复杂代码放在 Global.asax 文件中。
  • 该模块应用于管道中的所有请求(仅限 IIS 7.0 集成模式)。

当您必须创建依赖应用程序事件的代码并且不需要跨应用程序重用代码时,应该将代码添加到 Global.asax 文件中。当必须订阅对于模块不可用的事件(如 Session_Start)时,也可以使用 Global.asax 文件。

参考资料

MSDN HTTP 处理程序和 HTTP 模块概述 http://msdn.microsoft.com/zh-cn/library/bb398986(v=VS.90).aspx

MSDN IHttpModule http://msdn.microsoft.com/zh-cn/library/system.web.ihttpmodule(v=VS.90).aspx

下载 Demo

时间: 2024-08-08 09:40:18

利用 IHttpModule 自定义 HTTP 处理模块的相关文章

团队项目利用Msbuild自定义Task实现增量发布

最近一直在做自动部署工具,主要利用到了Msbuild的自定义Task,通过Task我们可以自定义编译.部署过程减少人工直接干预.Msbuild的详细用法,可以去园子里搜一下,有很多的基础教程,这里就不赘述了,还是集中说一下增量发布的问题. 增量主要涉及到三部分内容,程序.配置和静态文件(例如CSS.JS等),程序的增量比较简单,通过版本对比或者TFS的修改记录便可以查询出被修改过的程序集.配置文件增量大致有两种,全增量和部分增量.全增量也很简单,直接把修改过的配置文件复制到发布包就OK了:部分增

利用Theme自定义Activity进入退出动画

有没有觉得Activity的默认动画太快了或者太难看了.. 我原来使用Activity.overridePendingTransition来自定义Activity的进入动画,却发现没法定义退出的动画.结果就发现了强大的Theme和Style,之后还需要好好研究一下. 具体是这样子的: 在AndroidManifest里面,对于application和activity标签可以定义theme属性.如果对Application定义了某一个属性,那么会对所有的activity产生影响,当然你可以在act

利用委托自定义事件

事件,这个大家都非常熟悉的名词,代码里几乎离不开它.但是我们平时都是用现成的事件,如果满足不了我们的需求怎么办?那就只能咱自己写了,那么问题就来了,如何自定义事件呢? 在这之前,我们就必须先了解事件与委托的干系是什么,只有弄清楚事物的本质,我们才能掌握住事物的灵魂. 我们先看看最常用的一个事件:Form_Load() public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs

如何发布一个自定义Node.js模块到NPM(详细步骤)

咱们闲话不多说,直接开始! 由于我从没有使用过MAC,所以我不保证本文中介绍的操作与MAC一致. 文章开始我先假定各位已经在window全局安装了Node.js,下面开始进行详细步骤介绍: 本文本着,以极少的文字说明以及极少的代码书写为原则来给大家演示! 文章中上传的模块不具备任何意义! 一.封装node.js模块时的必须项 1.创建package.json 每一个完整封装的node模块,必须含有一个参数明确的package.json文件! 以下为package.json的最精简配置: { "n

【教程】【FLEX】#003 自定义事件、模块间通讯

本篇笔记,主要阐明 事件是如何创建 和 如何使用自定义事件达到模块之间通讯 的效果. 句子解释: 什么叫做模块之间的通讯呢?? 简单点说,就是两个模块之间可以互相传数据. A模块 可以接收到 B模块的数据,这个就叫做通讯. 所以A模块注册了一个事件, B模块触发了这个事件,A接收到. 那么 这个就是A.B模块进行通讯了. 一.自定义事件的创建(代码例子在文章底部) 1.创建一个ActionScript类,继承Event 2.声明属性 2.1  保存数据的属性(事件保存数据的对象,可多个) 2.2

python---爬虫相关性能(各个异步模块的使用,和自定义异步IO模块)

一:线程池,进程池等相关文章了解 python---基础知识回顾(十)进程和线程(py2中自定义线程池和py3中的线程池使用) python---基础知识回顾(十)进程和线程(协程gevent:线程在I/O请求上的优化) python---异步IO(asyncio)协程 二:异步I/O模块的了解(同协程:线程在I/O请求上的优化) (一)asyncio模块的使用 1.使用简单实例 import asyncio,time async def func1(num): print(num,"befor

利用Dockerfile自定义镜像-图解轻松学Docker&K8S

>>> 点我开始视频学习 <<< 你好,我是老齐,本节咱们来学习使用docker file配置文件,构建属于自己的镜像.回到咱们的控制台,在这首先来看一下.上一节课我们学习了如何从远程仓库来安装tomcat 镜像.对于这个tomcat来说,只要运行一个非常简单的命令. Docker run -p 8000 tomcat 马上一个全新的tomcat就会给我们完成自动部署,但这里也衍生出来一个问题,作为当前的tomcat他并不是一个有效的应用,因为我们并没有在上面发布任何属

Spring+SpringMVC+Mybatis 利用AOP自定义注解实现可配置日志快照记录

目的: 需要对一些事物的操作进行日志记录,如果在service内进行记录,大量的代码重复,并且维护比较麻烦.所以采用AOP的方式对service进行拦截.使用自定义注解的目的则是判断是否需要记录日志和传递额外的信息. 方式 本次解决方案十分感谢博主-跳刀的兔子的博文 本文绝大部分参考与本文,略有不同,所以做一些整理,博主的文章更详细一些. 1.首先新建自定义注解 @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @

利用WordPress自定义字段为文章添加下载按钮

树经常要上传一些资源来给大家分享,但是每次都要手动加链接,关联图片,十分麻烦,于是就利用了一下wordpress的自定义字段功能来自动添加下载按钮下面就来说说怎么实现这个功能吧~其实方法很简单,利用的就是下面这一段代码 <?php if(get_post_meta($post->ID, "download", $single = true) != ""){ ?> <div id="download"> <a h