选择HttpHandler还是HttpModule?
  HttpHandler和HttpModule之间有什么差别
  之所以有这个疑问,是因为在这二类对象中都可以访问Request, Response对象,都能处理请求。   我原以为在博客 用Asp.net写自己的服务框架 中做了那么多的演示应该把它们的使用方法说清楚了, 然而有些人看了我的那些示例,仍然不知道该如何选择它们,为了实现同一个目标,我既用了HttpHandler,也有用HttpModule。 现在看来,我当时设计的那些示例并不是讲清楚HttpHandler和HttpModule之间有什么差别, 而是在演示如何利用HttpHandler和HttpModule设计一个服务框架。   很庆幸那篇博客内容没有走题,今天只好再来写一篇了。   本文约定:
  1. HttpHandler泛指所有实现IHttpHandler接口的类型。
  2. HttpModule泛指所有实现IHttpModule接口的类型。
  因此,本文将不会特别区分这些类型与接口。   理解ASP.NET管线   HttpHandler和HttpModule,它们都与ASP.NET管线有关,所以我想理解这二类对象必须要理解ASP.NET管线的工作方式。   下图反映了ASP.NET管线的处理流程:
ASP.NET管线处理流程   这是一张时序图,我们应该从二个角度来理解它:
  1. 有哪些调用动作。
  2. 有哪些参与者。   每个调用动作,都反映了ASP.NET管线的处理阶段,它们会引发相应的事件(除GetHandler,ProcessRequest之外), HttpModule则会订阅这些事件,参与到管线处理过程。 这些阶段中,有些阶段还引发了二个事件,完整的管线事件可参考MSDN文档:   图片中还反映了ASP.NET的三种主要的参与者:
  1. HttpModule
  2. HttpHandlerFactory
  3. HttpHandler   有没有有想过:这三种参与者中,每种有多少个参与对象呢?   为了清楚地回答这个问题,我准备了下面的表格: 管线参与者 每次请求中参与者数量
HttpModule >= 0
HttpHandlerFactory 1
HttpHandler 1   为什么要引入HttpHandlerFactory呢? 请看我的博客 细说 HttpHandler 的映射过程,今天就不重复这块内容了。   除开HttpHandlerFactory,我们可以发现:在ASP.NET管线中,HttpHandler应该只有一个,而HttpModule是可选的。
  进而,我们是不是可以这样理解:HttpHandler才是处理请求的主角(不可缺少),HttpModule是配角(可以没有)?   理解HttpApplication   前面我们一直在说ASP.NET管线,那么,谁在控制管线过程?
  答案是:HttpApplication对象。
  1. HttpApplication细分它的处理过程,在不同阶段引发不同的事件,使得HttpModule通过订阅事件的方式加入到请求的处理过程中。
  2. 在请求的处理过程中,HttpApplication对象主要扮演着控制处理流程的推进作用。
  3. HttpApplication会在固定的阶段获取一个IHttpHandler实例,然后将请求的响应过程交给具体的IHttpHandler来实现。   HttpApplication如何产生,如何工作?
  1. HttpApplication对象会被重用,当HttpRuntime不能从HttpApplicationFactory获取空闲的实例时,才会创建。
  2. HttpRuntime会将每个请求交给一个HttpApplication对象来处理。
  3. HttpApplication对象在初始化时负责加载全部的HttpModule。
  4. 每个HttpApplication对象会控制属于它的管线过程(前面已解释)。   HttpApplication是个非常重要的类型,它的许多功能都属于框架的基础部分,不需要我们调用, 因此,我们平时不会用到它。   我不想让博客走题,下面来看看今天的主角吧。   理解HttpHandler   前面说到HttpRuntime会将请求交给HttpApplication来处理, 此时你有没有想过这样一个问题:为什么HttpApplication不直接处理请求,而是要再交给一个HttpHandler对象来处理呢?   答案是:每个请求的内容可能并不相同,它们存在多样性,因此ASP.NET采用了抽象工厂模式来处理这些请求。 ASP.NET在web.config的架构中,允许我们指定某些请求映射到一个HttpHandlerFactory,例如:
<!--适用于IIS6的配置-->
<system.web>
<httpHandlers>
<add path="*.cspx" verb="*" type="MyMVC.AjaxHandlerFactory, MyMVC" validate="true" />
<add path="*.aspx" verb="*" type="MyMVC.MvcPageHandlerFactory, MyMVC" validate="true" />
<add path="/mvc/*" verb="*" type="MyMVC.MvcPageHandlerFactory, MyMVC" validate="true" />
</httpHandlers>
</system.web>
<!--适用于IIS7的配置(集成模式)-->
<system.webServer>
<handlers>
<add name="AjaxHandlerFactory" verb="*" path="*.cspx" type="MyMVC.AjaxHandlerFactory, MyMVC" preCondition="integratedMode" />
<add name="MvcPageHandlerFactory" verb="*" path="*.aspx" type="MyMVC.MvcPageHandlerFactory, MyMVC" preCondition="integratedMode" />
<add name="MvcPageHandlerFactory2" verb="*" path="/mvc/*" type="MyMVC.MvcPageHandlerFactory, MyMVC" preCondition="integratedMode" />
</handlers>
</system.webServer>   当某个请求与一个规则匹配后,ASP.NET会调用匹配的HttpHandlerFactory的GetHandler方法来获取一个HttpHandler实例, 最后由一个HttpHandler实例来处理当前请求。   HttpApplication是如何将请求交给HttpHandler实例来处理的呢?
  为了理解这个过程,我们要来看一下IHttpHandler接口的定义:
// 这个接口用于同步调用
// 异步版本的接口用法请参考:http://www.cnblogs.com/fish-li/archive/2011/11/20/2256385.html
public interface IHttpHandler
{
// 获取一个值,该值指示其他请求是否可以使用 IHttpHandler 实例。
bool IsReusable { get; }
// 通过实现 IHttpHandler 接口的自定义 HttpHandler 启用 HTTP Web 请求的处理。
void ProcessRequest(HttpContext context);
}   HttpApplication在将某个请求交给HttpHandler实例来处理时,是通过接口来调用的(ProcessRequest方法)。   与HttpHandler的相关话题:
  1. 异步 HttpHandler:细说ASP.NET的各种异步操作。
  2. 如何重用HttpHandler:细说 HttpHandler 的映射过程。   HttpHanlder的典型应用
<%@ WebHandler Language="C#" Class="Login" %>
using System;
using System.Web;
public class Login : IHttpHandler {
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/plain";
string username = context.Request.Form["name"];
string password = context.Request.Form["password"];
if( password == "aaaa" ) {
System.Web.Security.FormsAuthentication.SetAuthCookie(username, false);
context.Response.Write("OK");
}
else {
context.Response.Write("用户名或密码不正确。");
}
}   通常我去这样创建一个ashx文件(HttpHanlder)响应某种特殊的请求。
  所以,我们应该这样理解HttpHanlder:一个HttpHanlder用于响应一类特定的请求。   我们经常用到的HttpHanlder有哪些?   1. aspx页面。
  2. asmx服务文件。
  3. ashx文件(一般处理程序)。
  4. 实现IHttpHandler接口的自定义类型。   我们通常使用HttpHanlder做什么? HttpHanlder类型 实现目标
aspx页面 响应请求,输出HTML结果
asmx服务文件 响应服务调用
ashx文件(一般处理程序) 实现简单的AJAX响应
实现IHttpHandler接口的自定义类 响应什么扩展名的请求??   理解HttpModule   设计HttpHanlder的目的很明确:生成响应结果。
  那么,设计HttpModule又是为什么呢?   前面说过,一个HttpHanlder用于处理一类特定的请求,每个aspx, ashx都可以认为是一类请求。 有时候我们发现所有页面可能都需要某些相同的检查功能(如身份检查), 假如只能使用HttpHanlder,那我们就要让所有页面都去调用那些相同的检查功能。 谁愿意做这些重复的事情? 或许有些人会回答,可以自己实现一个基类,把检查功能放在基类中去调用。 然而,这种做法只能解决重复调用问题,它会让代码失去灵活性(扩展性), 试想一下:如果需要再加入新的检查功能,或者用新的检查方法替换原有的检查逻辑时怎么办? 只能是修改基类了吧?   设计HttpModule的目的正是为了提供一个灵活的方法解决这种功能重用问题。 它采用事件(观察者)的设计模式,将某些HttpHanlder都需要的功能抽取出来, 形成不同的观察者类型,这些观察者类型可以编译成类库形式,供多个网站项目共用。 为了让ASP.NET管线更灵活,ASP.NET允许我们在web.config中自由配置需要的HttpModule,例如:
<!--适用于IIS6的配置-->
<system.web>
<httpModules>
<add name="SetOutputCacheModule" type="MyMVC.SetOutputCacheModule, MyMVC"/>
</httpModules>
</system.web>
<!--适用于IIS7的配置(集成模式)-->
<system.webServer>
<modules>
<add name="SetOutputCacheModule" type="MyMVC.SetOutputCacheModule, MyMVC" preCondition="integratedMode" />
</modules>
</system.webServer>   配置只是告诉ASP.NET:这些HttpModule需要运行起来。 有没有想过这些HttpModule到底是如何进入管线运行起来的呢? 前面我只是说了HttpModule会订阅这些事件,那么事件又是在哪里订阅的呢? 还是来看一下IHttpModule接口的定义吧:
// 这个接口用于同步调用
// 异步用法请参考:http://www.cnblogs.com/fish-li/archive/2011/11/20/2256385.html
public interface IHttpModule
{
// 初始化模块,并使其为处理请求做好准备。
void Init(HttpApplication app);
void Dispose();
}   注意这个关键的Init方法,它传入一个HttpApplication类型的参数,有了HttpApplication对象,HttpModule就可以订阅HttpApplication的所有事件了。 请看下面的示例代码:
public class TestModule : IHttpModule
{
public void Dispose() {}
public void Init(HttpApplication app)
{
app.PostAcquireRequestState += new EventHandler(app_PostAcquireRequestState);
app.PreRequestHandlerExecute += new EventHandler(app_PreRequestHandlerExecute);
}
void app_PreRequestHandlerExecute(object sender, EventArgs e)
{
throw new NotImplementedException();
}
void app_PostAcquireRequestState(object sender, EventArgs e)
{
throw new NotImplementedException();
}
}   HttpModule的典型应用
public class SetOutputCacheModule : IHttpModule
{
public void Init(HttpApplication app)
{
app.PreRequestHandlerExecute += new EventHandler(app_PreRequestHandlerExecute);
}
void app_PreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
Dictionary<string, OutputCacheSetting> settings = ConfigManager.Settings;
if( settings == null )
throw new ConfigurationErrorsException("SetOutputCacheModule加载配置文件失败。");
// 实现方法:
// 查找配置参数,如果找到匹配的请求,就设置OutputCache
OutputCacheSetting setting = null;
if( settings.TryGetValue(app.Request.FilePath, out setting) ) {
setting.SetResponseCache(app.Context);
}
}   这个Module用于给一些在配置文件中指出要缓存的请求设置输出缓存,示例代码已在上篇博客 不修改代码就能优化ASP.NET网站性能的一些方法 介绍过了。 其实设置输出缓存的最根本手段还是调用Response.Cache的一些公开方法,修改输出响应头。   我们用HttpModule做什么事情?   1. 修改某些请求(例如前面的示例修改了响应头)。
  2. 检查检查请求(例如身份认证检查)。   HttpModule能处理哪些请求呢?   1. 默认是全部进入ASP.NET的请求。
  2. 如果只需要处理部分请求,那么请自行判断(参考前面的示例)。   三大对象的总结   前面我分别介绍了HttpApplication,HttpHanlder和HttpModule,这里再把三者的关系重新梳理一遍。   在请求的处理过程中,HttpApplication对象主要扮演着控制管线处理流程的作用,它负责推进整个处理流程, 除了在不同阶段引发不同的事件外(供HttpModule使用),HttpApplication对象还会根据当前请求寻找一个合适的IHttpApplicationFactory实例, 并最终得到一个IHttpHandler的实例用于处理请求。   设计这三种类型的目的在于:
  1. HttpApplication控制处理流程,在不同阶段引发不同的事件。
  2. 由于请求的多样性,每个请求会由一个HttpHandler对象来处理。
  3. 对于一些通用性的功能,尤其是与响应内容无关的,设计成HttpModule是最合适的。   案例演示   Q:我有一些html文件,需要做身份认证检查(判断Session),我该如何实现?   想好了就来看看我的解决方案: /// <summary>
/// 用于响应HTML文件的处理器
/// </summary>
public class HtmlHttpHandler : IHttpHandler, IRequiresSessionState, IReadOnlySessionState
{
public void ProcessRequest(HttpContext context)
{
// 获取要请求的文件名
string filePath = context.Server.MapPath(context.Request.FilePath);
// 用Fiddler查看响应头,如果看到有这个头,就表示是由这段代码处理的。
context.Response.AddHeader("SeesionExist", (context.Session != null ? "yes": "no"));
// 在这里,你可以访问context.Session
// 设置响应内容标头
context.Response.ContentType = "text/html";
// 输出文件内容
context.Response.TransmitFile(filePath);
}   Q:我需要压缩所有的ASP.NET请求的响应结果,该怎么实现?   想好了就来看看我的解决方案: /// <summary>
/// 为请求支持gzip压缩输出的DEMO
/// </summary>
public class GzipModule : IHttpModule
{
public void Init(HttpApplication app)
{
app.BeginRequest += new EventHandler(app_BeginRequest);
}
void app_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
// 判断浏览器是否支持GZIP压缩
string flag = app.Request.Headers["Accept-Encoding"];
if( string.IsNullOrEmpty(flag) == false && flag.ToLower().IndexOf("gzip") >= 0 ) {
// 设置GZIP压缩
app.Response.Filter = new GZipStream(app.Response.Filter, CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "gzip");
}
}   如何选择?   在结束这篇博客之前,再问问各位读者:现在知道何时选择HttpHandler还是HttpModule了吗?   如果还没有看明白,那我就最后告诉你一个识别方法:
  1. 如果要响应一类请求,那么就选择HttpHandler。
  2. 如果要修改或者检查所有请求(总之就是不生成响应结果),那就选择HttpModule。   最后给各位留下一个题目,下面这些ASP.NET提供的功能,它们是采用了哪个方式实现的?
  1. Session
  2. 身份认证
  3. URL授权检查
  3. 通过trace.axd查看跟踪信息
  4. OutputCache
  5. 禁止下载config文件
  6. 禁止查看下载源代码文件
  注意:本文的主题是:选择HttpHandler还是HttpModule,所以请不要扯远了。

  

HttpHandler,HttpApplication, HttpModule的更多相关文章

  1. ASP.NET三剑客 HttpApplication HttpModule HttpHandler 解析

    我们都知道,ASP.Net运行时环境中处理请求是通过一系列对象来完成的,包含HttpApplication,HttpModule, HttpHandler.之所以将这三个对象称之为ASP.NET三剑客 ...

  2. 关于HttpModule和HttpHandler以及HttpApplication

    HttpRuntime打交道的是http协议跟IIS层面的东西,HttpApplication则具体到应用程序这一级别(也就是一个网站,这个跟web.config关系是基本一一对应的,像Module跟 ...

  3. 【IHttpHandler】HttpModule,HttpHandler,HttpHandlerFactory简单使用

    这三个对象我们在开发Asp.net程序时经常会用到,似乎很熟悉,但有时候又不太确定.本文通过一个简单的例子来直观的比较一下这三个对象的使用. HttpModule:Http模块,可以在页面处理前后.应 ...

  4. HttpModule,HttpHandler,HttpHandlerFactory

    HttpModule:Http模块,可以在页面处理前后.应用程序初始化.出错等时候加入自己的事件处理程序. HttpHandler:Http处理程序,处理页面请求 HttpHandlerFactory ...

  5. C#强化系列:HttpModule,HttpHandler,HttpHandlerFactory简单使用

    这三个对象我们在开发Asp.net程序时经常会用到,似乎很熟悉,但有时候又不太确定.本文通过一个简单的例子来直观的比较一下这三个对象的使用.HttpModule:Http模块,可以在页面处理前后.应用 ...

  6. HttpHandler(处理程序) 和 HttpModule(托管模块)

    本文参见:http://www.tracefact.net/Asp-Net/Introduction-to-Http-Handler.aspx 前言:前几天看到一个DTcms网站,里面有个伪静态技术, ...

  7. 用反射封装HttpHandler,实现通过action方法名调用方法

    using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Sy ...

  8. httphandler和httpmodule的区别

    ASP.Net处理Http Request时,使用Pipeline(管道)方式,由各个HttpModule对请求进行处理,然后到达 HttpHandler,HttpHandler处理完之后,仍经过Pi ...

  9. HttpModule & HttpHandler

    ASP.NET 处理请求的过程 inetinfo.exe:www 服务进程,IIS 服务 和 ASPNET_ISAPI.dll 都寄存在此进程中. ASPNET_ISAPI.dll:处理 .aspx ...

随机推荐

  1. 使用Maven开发一个简单的SpringData

    1:创建Maven项目 2:添加依赖(修改pom.xml为以下代码) <project xmlns="http://maven.apache.org/POM/4.0.0" x ...

  2. 开发监测keepalived裂脑的脚本

    检测思路:在备节点上执行脚本,如果可以ping通主节点并且备节点有VIP就报警,让人员介入检查是否裂脑. 在LB02备节点上开发脚本并执行: [root@lb02 ~]# cat /server/sc ...

  3. GNU汇编程序框架

    汇编的作用:1.对芯片进行初始化 2. 和C混合编程提升C的运行效率 .section .data < 初始化的数据> .section .bss <未初始化的数据> .sec ...

  4. (转) Redis哨兵的详解

    1 哨兵的作用 哨兵是redis集群架构中非常重要的一个组件,主要功能如下: 1. 集群监控:负责监控redis master和slave进程是否正常工作 2. 消息通知:如果某个redis实例有故障 ...

  5. 11、python中的函数(基础)

    一.什么是函数? 在数学中,x2+2x2+3=10这样的叫方程. 而ax2+bx2+c=d这样的才叫函数.数学的函数中,abcd等待输入的未知量叫自变量,它需要我们自己去输入,而x这种待求得未知量叫因 ...

  6. [2016-04-19 15:46:03 - IceHoloReader1.0] Installation error: INSTALL_FAILED_CONFLICTING_PROVIDER [20

    [2016-04-19 15:46:03 - IceHoloReader1.0] Installation error: INSTALL_FAILED_CONFLICTING_PROVIDER [20 ...

  7. Docker背后的内核知识(一)

    Docker背后的内核知识 当谈论Docker时,常常会聊到Docker的实现方式.很多开发者都知道,Docker容器本质上是宿主机上的进程.Docker通过namespace实现了资源隔离.通过cg ...

  8. Spring表达式语言:SpEL

    1.Spring表达式语言(简称:SpEL):是一个支持运行时查询和操作对象图的强大的表达式语言. 2.语法类似于EL:SpEL使用#{...}作为定界符,所有在大括号内的字符都被认为是SpEL. 3 ...

  9. 关于前后端日期处理 开发注意事项 jquery.tmpl()函数的使用

    1当后端将日期传到前段的时候 我们通常会需要将日期转为制定格式 除了平常我们使用的前段插件将日期转好 spring @datetimeFormat 注解 这些形式外 我们还可以在实体里通过get方法进 ...

  10. C#入门篇6-4:字符串操作 string分割字符串效率比较

    //分割字符串效率比较 public static void Fund() { //1.用string.Split方法 //a.字节数组: //625毫秒/百万次 string str1 = &quo ...