大学IT网 - 最懂大学生的IT学习网站! QQ资料交流群:367606806
当前位置:大学IT网 > ASP.NET技巧 > ASP.NET-自定义HttpModule与HttpHand

ASP.NET-自定义HttpModule与HttpHand(2)

关键词:ASP.NET自定义HttpModuleHttpHan  阅读(1887) 赞(17)

[摘要]本文是对ASP.NET-自定义HttpModule与HttpHandler的讲解,对学习ASP.NET编程技术有所帮助,与大家分享。

如此一来,一个HttpModule及其配置工作就完成了,接下来,发布网站到IIS或者直接在VS中运行,随便访问项目中的一个文件(任何文件类型都可以),我的项目中有一个WebForm2.aspx的页面,我在浏览器中访问这个页面,发现页面是空白的,因为页面中我什么都没写,上面的Module实现中,我把输出全部放到本地D盘的一个文本文件中了,ok,打开那个文本文件。如图:

image

我们看到输出内容,第2行是访问的页面地址,下面依次为订阅的事件输出,我们清楚的看到了事件的执行顺序。

BeginRequest          		#发出信号表示创建任何给定的新请求。 此事件始终被引发,并且始终是请求处理期间发生的第一个事件
AuthenticateRequest         #发出信号表示配置的身份验证机制已对当前请求进行了身份验证。 订阅 AuthenticateRequest 事件可确保在处理附加模块或事件处理程序之前对请求进行身份验证
PostAuthenticateRequest     #预订 PostAuthenticateRequest 事件的功能可以访问由 PostAuthenticateRequest 处理的任何数据
AuthorizeRequest			#发出信号表示 ASP.NET 已对当前请求进行了授权。 订阅 AuthorizeRequest 事件可确保在处理附加的模块或事件处理程序之前对请求进行身份验证和授权
PostAuthorizeRequest		#发出信号表示 ASP.NET 已对当前请求进行了授权。 订阅 PostAuthorizeRequest 事件可确保在处理附加的模块或处理程序之前对请求进行身份验证和授权
ResolveRequestCache			#引发这个事件来决定是否可以使用从输出缓冲返回的内容来结束请求。这依赖于Web应用程序的输出缓冲时怎样设置的
PostResolveRequestCache		#在 ASP.NET 跳过当前事件处理程序的执行并允许缓存模块满足来自缓存的请求时发生
MapRequestHandler			#ASP.NET 基础结构使用 MapRequestHandler 事件来确定用于当前请求的请求处理程序
PostMapRequestHandler		#在 ASP.NET 已将当前请求映射到相应的事件处理程序时发生
AcquireRequestState			#当 ASP.NET 获取与当前请求关联的当前状态(如会话状态)时发生
PostAcquireRequestState		#预订 AcquireRequestState 事件的功能可以访问由 PostAcquireRequestState 处理的任何数据
PreRequestHandlerExecute	#在ASP.NET开始执行HTTP请求的处理程序之前引发这个事件。在这个事件之后,ASP.NET 把该请求转发给适当的HTTP处理程序
PostRequestHandlerExecute   #在 ASP.NET 事件处理程序(例如,某页或某个 XML Web service)执行完毕时发生
ReleaseRequestState			#在 ASP.NET 执行完所有请求事件处理程序后发生。 该事件将使状态模块保存当前状态数据
PostReleaseRequestState		#在 ASP.NET 已完成所有请求事件处理程序的执行并且请求状态数据已存储时发生
UpdateRequestCache			#当 ASP.NET 执行完事件处理程序以使缓存模块存储将用于从缓存为后续请求提供服务的响应时发生
PostUpdateRequestCache		#在 ASP.NET 完成缓存模块的更新并存储了用于从缓存中为后续请求提供服务的响应后,发生此事件
OnLogRequest				#恰好在 ASP.NET 为当前请求执行任何记录之前发生,即使发生错误,也会引发 LogRequest 事件
PostLogRequest				#在 ASP.NET 处理完 LogRequest 事件的所有事件处理程序后发生
EndRequest					#在 ASP.NET 响应请求时作为 HTTP 执行管线链中的最后一个事件发生
PreSendRequestContent		#恰好在 ASP.NET 向客户端发送内容之前发生,可能发生多次
PreSendRequestHeaders		#恰好在 ASP.NET 向客户端发送 HTTP 标头之前发生
RequestCompleted			#在任何托管模块和处理程序执行后,它使模块清理资源

访问一个页面的过程中,依次触发了23个事件,而HttpModule可订阅的事件个数为25个,观察发现,Error和Disposed这两个事件没有触发。Error事件在发生错误的情况下执行,而Disposed事件,当我们关闭刚才打开的页面,再到文本文件里查看,发现Disposed事件出现了,所以Disposed在会话结束后触发。

由于HttpModule的个数可以有多个,我们可以按照上面的方式定义HttpModule实现类,然后再web.config中增加配置项,就可以实现多个HttpModule同时订阅管道事件了。

介绍完HttpModule,那么HttpHandler又是什么呢,它又在什么什么时候执行呢?接下来看一下HttpHandler。

HttpHandler

HttpHandler是HTTP请求的处理中心,真正地对客户端请求的服务器页面做出编译和执行,并将处理过后的信息附加在HTTP请求信息流中再次返回到HttpModule中。
HttpHandler与HttpModule不同,一旦定义了自己的HttpHandler类,那么它对系统的HttpHandler的关系将是“覆盖”关系。

HttpHandler是实IHttpHandler接口的类,IHttpHandler接口定义如下:

    public interface IHttpHandler
    {
        // 摘要: 
        //     获取一个值,该值指示其他请求是否可以使用 System.Web.IHttpHandler 实例。
        //
        // 返回结果: 
        //     如果 System.Web.IHttpHandler 实例可再次使用,则为 true;否则为 false。
        bool IsReusable { get; }

        // 摘要: 
        //     通过实现 System.Web.IHttpHandler 接口的自定义 HttpHandler 启用 HTTP Web 请求的处理。
        //
        // 参数: 
        //   context:
        //     System.Web.HttpContext 对象,它提供对用于为 HTTP 请求提供服务的内部服务器对象(如 Request、Response、Session
        //     和 Server)的引用。
        void ProcessRequest(HttpContext context);
    }

接口中只有一个属性和一个方法,所以实现一个HttpHandler也很简单,下面实现一个简单的HttpHandler,代码如下:

public class MyIISHandler : IHttpHandler
    {
        /// <summary>
        /// 您将需要在网站的 Web.config 文件中配置此处理程序 
        /// 并向 IIS 注册它,然后才能使用它。有关详细信息,
        /// 请参见下面的链接: http://go.microsoft.com/?linkid=8101007
        /// </summary>
        #region IHttpHandler Members

        public bool IsReusable
        {
            // 如果无法为其他请求重用托管处理程序,则返回 false。
            // 如果按请求保留某些状态信息,则通常这将为 false。
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            //在此处写入您的处理程序实现。
            WriteLog("请求一个asox页面");
        }

        #endregion
    }

上面我实现了一个很简单的HttpHandler类,在ProcessRequest方法中,调用上面的HttpModule类中写文本文件的方法,在文本文件中写入“请求一个asox页面”,没错,是一个asox页面,我自己定义的文件格式,下面我会在web.config中添加配置项:

<!--IIS6或者IIS7经典模式-->
  <system.web>
    <httpHandlers>
      <add name="mycustomhandler" path="*.asox" verb="*" type="fengzheng.MyIISHandler,handler_modules"/>
    </httpHandlers>
  </system.web>
 
<!--IIS7集成模式-->
  <system.webServer>
    <handlers>
       <add name="mycustomhandler" path="*.asox" verb="*" type="fengzheng.MyIISHandler,handler_modules"/>
    </handlers>
  </system.webServer>

配置项属性的解释:

path:指定了需要调用处理程序的路径和文件名(可以包含通配符)。“*”、“*.aspx”、“booklist.aspx”、“test1.aspx,test2.aspx”、“*.asox”、“*.txt”。

verb:指定了处理程序支持的HTTP动作。*-支持所有的HTTP动作;“GET”-支持Get操作;“POST”-支持Post操作;“GET, POST”-支持两种操作。

type:用名字空间、类名称和程序集名称的组合形式指定处理程序或处理程序工厂的实际类型。ASP.NET运行时首先搜索bin目录中的DLL,接着在GAC中搜索。

接着,发布站点到IIS。打开IIS,找到当前站点的“处理程序映射”,会发现多了刚刚配置的HttpHandler,如图:

2014-04-15_181203

没错,关于对*.asox这种类型的文件,就可以映射到上面创建的HttpHandler来进行处理,观察其它条目发现,像*.aspx、*.ashx的处理程序是System.Web.UI.PageHandlerFactory和System.Web.UI.SimpleHandlerFactory这样的工厂类型。没错,可以指定处理程序为一个HttpHandler,也可以指定为一个抽象工厂类型。先不说工厂类型的事儿,访问一下网站中的asox页面,看一下文本文件的记录情况。

image

起作用了,在HttpModule输出的一堆信息中,夹杂着HttpHandler的输出,当然这仅限于访问asox类型的页面,因为我只对路径为*.asox的文件格式做了设置,修改下配置文件,例如将path=”*.asox”改为path=”*.aspx”,那么ASP.NET对*.aspx页面原有的解析机制将被我们设置的处理程序所覆盖。

回到上面的输出内容,我们观察HttpHandler输出内容所在的位置,位于PreRequestHandlerExecute和PostRequestHandlerExecute这两个事件之间,这与HttpApplication类中的管道事件的创建过程有关。

前面说到了,处理处理程序可以指定为一个工厂类型,下面,我就创建一个工厂类型的处理程序。

这里所说的工厂类型的处理程序,就是实现了IHttpHandlerFactory接口的类,IHttpHandlerFactory接口定义如下:

public interface IHttpHandlerFactory
    {
        // 摘要: 
        //     返回实现 System.Web.IHttpHandler 接口的类的实例。
        //
        // 参数: 
        //   context:
        //     System.Web.HttpContext 类的实例,它提供对用于为 HTTP 请求提供服务的内部服务器对象(如 Request、Response、Session
        //     和 Server)的引用。
        //
        //   requestType:
        //     客户端使用的 HTTP 数据传输方法(GET 或 POST)。
        //
        //   url:
        //     所请求资源的 System.Web.HttpRequest.RawUrl。
        //
        //   pathTranslated:
        //     所请求资源的 System.Web.HttpRequest.PhysicalApplicationPath。
        //
        // 返回结果: 
        //     处理请求的新的 System.Web.IHttpHandler 对象。
        IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated);
        //
        // 摘要: 
        //     使工厂可以重用现有的处理程序实例。
        //
        // 参数: 
        //   handler:
        //     要重用的 System.Web.IHttpHandler 对象。
        void ReleaseHandler(IHttpHandler handler);
    }

同样很简单,也是只有两个接口方法,下面是实现这个接口的工厂,代码如下:

public class MyHttpHandlerFactory:IHttpHandlerFactory
{
        public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
        {
			//写日志
            WriteLog(string.Format("requestType:{0}|url:{1}|pathTranslated:{2}", requestType, url, pathTranslated));
            
			//生成一个IHttpHandler
			Type t = typeof(MyIISHandler);
            IHttpHandler handler =  Activator.CreateInstance(t) as IHttpHandler;
            return handler;
        }

        public void ReleaseHandler(IHttpHandler handler)
        {
        }
}

方法中,返回了前面创建的那个HttpHander类,依然调用记录文本文件的方法输出内容,方便观察执行的实际和具体内容。配置文件改改为这个工厂类。

<add name="mycustomFactoryHandler" path="*.asox" verb="*" type="fengzheng.MyHttpHandlerFactory,handler_modules"/>

配置完毕后,访问网站中的asox页面,打开文本文件,内容如下:

image

我们发现,工厂类中构造IHttpHandler接口的方法发生在HttpModule的MapRequestHandler之后,这同样与HttpApplication类中构造管道事件有关。

说了这么多,那么,HttpModule和HttpHandler究竟能干什么呢?

HttpModule很常用的一个作用就是Url重写,URLRewriter就是基于HttpModule实现的。

另外,有通过HttpHandler对图片加水印,防止盗链的。

具体的可以参考这篇文章

部署网站注意事项:

网站采用.net 4.0集成模式部署,集成模式是一种统一的请求处理管道,它将ASP.NET请求管道与IIS核心管道组合在一起,这种模式能够提供更好的性能,能够实现配置和治理的模块化,而且增加了使用托管代码模块扩展IIS时的灵活性。IIS经典模式与集成模式的区别

集成模式和经典模式的配置文件稍有不同,部署时需要注意针对不同的部署模式,修改配置文件。在vs2013中新建的web应用程序,默认的web.config内容如下:

<?xml version="1.0" encoding="utf-8"?>
<!--
  有关如何配置 ASP.NET 应用程序的详细信息,请访问
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
</configuration>
  • 按照经典模式部署,配置文件应该如下:
<?xml version="1.0" encoding="utf-8"?>
<!--
  有关如何配置 ASP.NET 应用程序的详细信息,请访问
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <httpModules>
      <add name="mycustommodule" type="fengzheng.MyModule,handler_modules"/>
    </httpModules>
    <httpHandlers>
      <add name="mycustomhandler" path="*.asox" verb="*" type="fengzheng.MyIISHandler,handler_modules"/>
    </httpHandlers>
  </system.web>
</configuration>

经典模式经测试总是出现如下错误,500.21 - 模块无法识别:

HTTP 错误 500.21 - Internal Server Error
处理程序“PageHandlerFactory-ISAPI-4.0_64bit”在其模块列表中有一个错误模块“IsapiModule”

至于错误原因:目前还不是很清楚。

  • 按照集成模式部署,配置文件应该如下,将配置信息放到system.webServer节点之下:
<?xml version="1.0" encoding="utf-8"?>
<!--
  有关如何配置 ASP.NET 应用程序的详细信息,请访问
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <system.webServer>
    <modules>
      <add name="mycustommodule" type="fengzheng.MyModule,handler_modules"/>
    </modules>
    <handlers>
      <add name="mycustomhandler" path="*" verb="*" type="fengzheng.MyIISHandler,handler_modules"/>
    </handlers>
  </system.webServer>
</configuration>
«上一页12下一页»


相关评论