Community Server专题四:HttpHandler

server

HttpHandler实现了ISAPI Extention的功能,他处理请求(Request)的信息和发送响应(Response)。HttpHandler功能的实现通过实现IHttpHandler接口来达到。

看图先:

在ASP.NET 管道处理的末端是HTTP Hander,其实每个Asp.net的Page都实现了IHttpHander,在VS.net中的对象察看器中你可以证实这一点

具体的类是这样定义的:public class Page : TemplateControl, IhttpHandler。

接口IHttpHandler的定义如下:

interface IHttpHandler

{

void ProcessRequest(HttpContext ctx);

bool IsReuseable { get; }

}

接口中ProcessRequest是添加自己的代码进行相应处理的地方。IsReuseable属性指明该HttpHandler的实现类是否需要缓存。

在CS中有很多继承IHttpHandler接口的类,我取出有代表性而又容易理解的一个做分析:找到CommunityServerComponents项目Components目录下的Redirect.cs文件,内容如下:

//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.Web;

namespace CommunityServer.Components
{
    /**//// <summary>
    /// Summary description for Redirect.
    /// </summary>
    public class Redirect : IHttpHandler
    {
        public Redirect()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        public void ProcessRequest(HttpContext context)
        {
            string url = context.Request.QueryString["u"];
            if(!Globals.IsNullorEmpty(url))
            {
                context.Response.Redirect(url);
               
            }
            else
            {
                context.Response.Redirect(Globals.GetSiteUrls().Home);
            }

            context.Response.End();
        }

        public bool IsReusable
        {
            get { return false; }
        }
    }
}

这里的Redirect功能是在web请求满足HttpHandler配置文件中预设条件下自动拦截执行的,在web.config中<httpHandlers>节点下可以看到

<add verb="GET" path="Utility/redirect.aspx" type="CommunityServer.Components.Redirect, CommunityServer.Components" />

对该类的配置

· verb可以是"GET"或"POST",表示对GET或POST的请求进行处理。"*"表示对所有请求进行处理,这里是对GET请求进行处理。

· path指明对相应的文件进行处理,"*.aspx"表示对发给所有ASPX页面的请求进行处理,这里单独对redirect.aspx页面进行处理。可以指明路径,如"blogs"。表明只对blogs目录下的redirect.aspx文件请求进行处理。

· type属性中,逗号前的字符串指明HttpHandler的实现类的类名,后面的字符串指明Dll文件的名称。

实际处理是怎么样的呢?其实redirect.aspx页面在CS项目中并不存在,CS把对redirect.aspx的请求处理交给了CommunityServer.Components.dll程序集中Redirect类进行处理。处理的过程是执行

public void ProcessRequest(HttpContext context)

方法(Redirect类下的ProcessRequest方法是对当前请求的上下文Context中Url是否包含“u”参数,如果有并且参数值不为null就调用Response.Redirect方法跳转到“u”参数值所执行的页面,如果没有参数或者参数值为空就跳转到CS的首页)。

另外在CS中对RSS和Trackback的处理都使用了httpHandler的处理方式。

前面提到,所有页面的基类Page都实现了HttpHandler接口,因此每个asp.net的页面都可以看成是一个HttpHandler处理类,只是配置部分在machine.config中

<httpHandlers>
            <add verb="*" path="*.vjsproj" type="System.Web.HttpForbiddenHandler"/><add verb="*" path="*.java" type="System.Web.HttpForbiddenHandler"/><add verb="*" path="*.jsl" type="System.Web.HttpForbiddenHandler"/><add verb="*" path="trace.axd" type="System.Web.Handlers.TraceHandler"/>
            <add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
            <add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory"/>
            <add verb="*" path="*.asmx" type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" validate="false"/>
            <add verb="*" path="*.rem" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="false"/>
            <add verb="*" path="*.soap" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="false"/>
            <add verb="*" path="*.asax" type="System.Web.HttpForbiddenHandler"/>
            <add verb="*" path="*.ascx" type="System.Web.HttpForbiddenHandler"/>
            <add verb="GET,HEAD" path="*.dll.config" type="System.Web.StaticFileHandler"/>
            <add verb="GET,HEAD" path="*.exe.config" type="System.Web.StaticFileHandler"/>
            <add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler"/>
            <add verb="*" path="*.cs" type="System.Web.HttpForbiddenHandler"/>
            <add verb="*" path="*.csproj" type="System.Web.HttpForbiddenHandler"/>
            <add verb="*" path="*.vb" type="System.Web.HttpForbiddenHandler"/>
            <add verb="*" path="*.vbproj" type="System.Web.HttpForbiddenHandler"/>
            <add verb="*" path="*.webinfo" type="System.Web.HttpForbiddenHandler"/>
            <add verb="*" path="*.asp" type="System.Web.HttpForbiddenHandler"/>
            <add verb="*" path="*.licx" type="System.Web.HttpForbiddenHandler"/>
            <add verb="*" path="*.resx" type="System.Web.HttpForbiddenHandler"/>
            <add verb="*" path="*.resources" type="System.Web.HttpForbiddenHandler"/>
            <add verb="GET,HEAD" path="*" type="System.Web.StaticFileHandler"/>
            <add verb="*" path="*" type="System.Web.HttpMethodNotAllowedHandler"/>
        </httpHandlers> 

借助一个工具:Reflector,看看Page类下的HttpHandler处理方法ProcessRequest都做了什么

[EditorBrowsable(EditorBrowsableState.Never)]
public void ProcessRequest(HttpContext context)
{
      this.SetIntrinsics(context);
      this.ProcessRequest();
}
private void SetIntrinsics(HttpContext context)
{
      this._context = context;
      this._request = context.Request;
      this._response = context.Response;
      this._application = context.Application;
      this._cache = context.Cache;
      if ((this._clientTarget != null) && (this._clientTarget.Length > 0))
      {
            this._request.ClientTarget = this._clientTarget;
      }
      base.HookUpAutomaticHandlers();
}
private void ProcessRequest()
{
      Thread thread1 = Thread.CurrentThread;
      CultureInfo info1 = thread1.CurrentCulture;
      CultureInfo info2 = thread1.CurrentUICulture;
      this.FrameworkInitialize();
      try
      {
            try
            {
                  if (this.IsTransacted)
                  {
                        this.ProcessRequestTransacted();
                  }
                  else
                  {
                        this.ProcessRequestMain();
                  }
                  this.ProcessRequestEndTrace();
            }
            finally
            {
                  this.ProcessRequestCleanup();
                  InternalSecurityPermissions.ControlThread.Assert();
                  thread1.CurrentCulture = info1;
                  thread1.CurrentUICulture = info2;
            }
      }
      catch
      {
            throw;
      }
}

在Page类的ProcessRequest方法先是把上下文Context内容赋值到当前Page中的一些属性里,然后调用System.Web.UI.TemplateControl中的HookUpAutomaticHandlers()

internal void HookUpAutomaticHandlers()
{
      if (this.SupportAutoEvents)
      {
            SimpleBitVector32 vector1 = new SimpleBitVector32(this.AutoHandlers);
            InternalSecurityPermissions.Reflection.Assert();
            if (!vector1[1])
            {
                  vector1[1] = true;
                  this.GetDelegateInformation("Page_Init", ref vector1, 2, 4);
                  this.GetDelegateInformation("Page_Load", ref vector1, 8, 0x10);
                  this.GetDelegateInformation("Page_DataBind", ref vector1, 0x20, 0x40);
                  this.GetDelegateInformation("Page_PreRender", ref vector1, 0x80, 0x100);
                  this.GetDelegateInformation("Page_Unload", ref vector1, 0x200, 0x400);
                  this.GetDelegateInformation("Page_Error", ref vector1, 0x800, 0x1000);
                  this.GetDelegateInformation("Page_AbortTransaction", ref vector1, 0x2000, 0x4000);
                  this.GetDelegateInformation("OnTransactionAbort", ref vector1, 0x8000, 0x10000);
                  this.GetDelegateInformation("Page_CommitTransaction", ref vector1, 0x20000, 0x40000);
                  this.GetDelegateInformation("OnTransactionCommit", ref vector1, 0x80000, 0x100000);
                  this.AutoHandlers = vector1.Data;
            }
            if (vector1[2])
            {
                  base.Init += this.GetDelegateFromMethodName("Page_Init", vector1[4]);
            }
            if (vector1[8])
            {
                  base.Load += this.GetDelegateFromMethodName("Page_Load", vector1[0x10]);
            }
            if (vector1[0x20])
            {
                  base.DataBinding += this.GetDelegateFromMethodName("Page_DataBind", vector1[0x40]);
            }
            if (vector1[0x80])
            {
                  base.PreRender += this.GetDelegateFromMethodName("Page_PreRender", vector1[0x100]);
            }
            if (vector1[0x200])
            {
                  base.Unload += this.GetDelegateFromMethodName("Page_Unload", vector1[0x400]);
            }
            if (vector1[0x800])
            {
                  this.Error += this.GetDelegateFromMethodName("Page_Error", vector1[0x1000]);
            }
            if (vector1[0x2000])
            {
                  this.AbortTransaction += this.GetDelegateFromMethodName("Page_AbortTransaction", vector1[0x4000]);
            }
            else if (vector1[0x8000])
            {
                  this.AbortTransaction += this.GetDelegateFromMethodName("OnTransactionAbort", vector1[0x10000]);
            }
            if (vector1[0x20000])
            {
                  this.CommitTransaction += this.GetDelegateFromMethodName("Page_CommitTransaction", vector1[0x40000]);
            }
            else if (vector1[0x80000])
            {
                  this.CommitTransaction += this.GetDelegateFromMethodName("OnTransactionCommit", vector1[0x100000]);
            }
      }
}

方法连接一些Handlers,通过委托加载相关的事件进行页面的初始化工作。

我不再往下分析,整个Page页面很庞大,有兴趣的朋友自己慢慢研究,说这些只是要明白一点,任何一个Page页面都是HttpHandler,页面处理是从这里开始。

在.Text的早期版本中(很久没有看.Text的代码了)安装时要配置IIS

在asp.net管道处理的级别上对一些扩展名称做映射,如*.html(其实.Text是做了*.*到ASP.NET映射,够狠!),该扩展名的文件原本直接由IIS提交给请求者而不会经过asp.net处理机制处理,因此asp.net管道中的httpHandler是不可能拦截到的,但是只要做如下操作

这时,IIS会把对*.html文件的请求也交由ASP.NET机制去处理,继承IHttpHandler就可以拦截它(.Text中实际继承的是IHttpHandlerFactory)。有了这些知识就不难理解为什么你在.Text系统下察看blog时请求的是html文件(其实请求的html文件根本就不存在),返回的信息确是动态页面的内容,这与大型CMS系统不同,并不是为了节约系统资源预先生成html。DotText之所以这样做牵涉到Permalink和Search Engine Friendly,有这方面兴趣的朋友可以在google找到很多相关资源。

虽然CS中的blog与.Text有密不可分的关系,但是CS中的blog已经没有采取早期.Text的Url扩展名为.html的访问机制,而是直接采用.aspx扩展名,也许更多考虑的是CS的部署问题,毕竟不是所有的CS使用者都会有可以自己配置IIS扩展名映射的权限,大多数虚拟主机也是不提供这样功能的。

HttpHandler还可以用来处理图片与下载的盗链问题,先在IIS中添加一些常用图片扩展名的映射到IIS中用Asp.net来处理对它的请求,这样就可以通过继承IHttpHandler的类和适当的配置来处理用户的请求,大致说一下过程:

在实现IHttpHandler接口的类中,ProcessRequest方法先用判断上次请求的URL(为什么是判断上次请求的URL,请看我上一篇专题中对IIS运行过程的讲解),如果这个URL是在你限定内那么就调用Response.WriteFile方法输出文件,如果不是,你可以自己采取一些措施,比如输出一个带有警告字样的图片等等。当然,使用HttpHandler处理图片与下载盗链问题是需要IIS控制权限的,而且需要浪费一些系统资源。

时间: 2024-08-22 14:16:44

Community Server专题四:HttpHandler的相关文章

Community Server专题一:概述Community Server_实用技巧

Community Server专题一:概述Community Server Community Server(CS)是一个非常优秀的Asp.net开源软件,目前官方发布的系统中包括三个部分:Asp.net Forums.DotText.Gallery.如果你是某个以CS构架网站的会员,你可以很容易的就拥有一个Blog.一个相册.还能在论坛上与他人一起进行讨论,这样就形成一个以User为中心的社区,这也就是起名为 Community Server的意义所在了. CS的构架很巧妙,三套原本不同的开

Community Server专题二:体系结构_实用技巧

Community Server专题二:体系结构 在进行CS细节分析的之前,有必要先了解CS工程(解决方案)的组成,以及组成CS工程中项目的结构,本文分为三个部分:1.工程结构 2.三层构架 3.数据库构架. 1:工程结构 =538) {this.width=538;}" border=0> CS工程主要分为4个部分 a:系统底层构架项目CommunityServerComponents.CommunityServerControls,提供给其他项目父类.接口.全局变量.CS系统设置.公用

Swift语法专题四——字符串与字符

Swift解读专题四--字符串与字符 一.引言         Swift中提供了String类型与Characters类型来处理字符串和字符数据,Swift中的String类型除了提供了许多方便开发者使用的方法外,还可以与Foundation框架的NSString类进行转换,使用起来十分方便. 二.String基础         在Swift中,使用双引号来定义字符串,开发者可以通过如下代码来创建一个字符串常量: let str = "Hello, playground" 可以通过

Community Server专题八:MemberRole之Membership深入篇

server 专题八的上篇大致讨论了MemberRole中的Membership实现,对于运用Membership进行web开发足够,但是对于想更深入了解Membership实现机理的朋友那是远远不够的,这个专题我们更深入一下了解Membership. 其实MemberRole是一个非常好的资源包,借住Reflector这个优秀的工具,你可以对其进行代码分析.它无论是在组建的构架.代码的设计.数据库表的建立.存储过程的使用等都是非常优秀的,你是程序员也好构架师也罢,其中可以学习的真的很多很多,我

RDS SQL Server - 专题分享 - 巧用执行计划缓存之索引缺失

title: RDS SQL Server - 专题分享 - 巧用执行计划缓存之索引缺失 author: 风移 摘要 执行计划缓存是MSSQL Server内存管理十分重要的部分,同样如何巧用执行计划缓存来解决我们平时遇到的一系列问题也是一个值得深入研究的专题.这篇文章是如何巧用执行计划缓存的开篇,分享如何使用执行计划缓存来分析索引缺失(Missing Indexes). 问题引入 缺失索引是SQL Server CPU使用率居高不下的第一大杀手,也是SQL Server数据库非常大的潜在风险点

Community Server专题三:HttpModule

server 从专题三开始分析Community Server的一些具体的技术实现,根据IIS对请求的处理流程,从HttpModule& HttpHandler切入话题,同时你也可以通过一系列的专题了解CS的运行过程,不只如此,所有的.Net 1.1 构架的Web App都是以同样的顺序执行的. 先了解一下IIS系统.它是一个程序,负责对网站的内容进行管理并且处理对客户的请求做出反应.当用户对一个页面提出请求时,IIS做如下反应(不考虑权限问题): 1.把对方请求的虚拟路径转换成物理路径 2.根

Community Server专题五:IHttpHandlerFactory

server 如果你了解Factory设计模式,这篇文章可以不用看,只要会配置IHttpHandlerFactory就可以,不了解也没有关系,通过下面的文章你可以学会如何使用IHttpHandlerFactory,同时还会明白Factory设计模式的广义定义 在前一篇专题中讲解了HttpHandler在CS中的运用以及一些相关的话题.其实实现HttpHandler功能还有另外一个选择就是Handler Factory(CS 中没有用到Handler Factory),这是通过继承IHttpHan

Community Server专题八:MemberRole之Membership

server MemberRole是一个在asp.net 1.1下实现用户管理.角色管理.用户特性信息存储(profile)等的一个组件,该组件被ASP.NET 2.0 Beta 2所采用,也就是ASP.NET 2.0 Beta 2中所说的Membership and Roles.如果你在asp.net 1.1下采用了MemberRole,那么你的web程序将会很容易的过渡到asp.net 2.0,另外多个采取MemberRole进行用户管理的web程序需要整合时也非常容易.我将分4个专题来分析

Community Server专题六:Delegates &amp; Events

server 对于CS的分析你可以能会从页面开始,其实那并不是一个很好的方法,因为CS采用了MasterPage和内建的Theme与Skins,页面一层嵌套一层,如果你对CS页面执行机制不了解,或者你是初学者,这个时候可能就会碰壁,接着就放弃了对CS更深入的了解.我希望我的专题能从CS的运行过程开始一步一步地讲解,同时把ASP.NET的运行机理也表述出来,因此学习了解CS的过程就是对ASP.NET深入了解得过程.当然,我个人的开发经验与水平也是有限的,如果在专题中表述有问题,或者有疑问可以直接在