1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > ASP.NET-自定义HttpModule与HttpHandler

ASP.NET-自定义HttpModule与HttpHandler

时间:2019-05-12 19:40:33

相关推荐

ASP.NET-自定义HttpModule与HttpHandler

在之前的是如何在IIS下工作的这篇文章中介绍了与IIS配合工作的机制,在http请求经过一系列处理后,最后到达管道中,这时,就是Http Modules和HttpHandler出场的时候了。

再来摆出管道工作时序图来一看:

HttpModule

HttpModule是类似于过滤器的作用,可以没有,也可以有任意个,每一个都可以订阅管道事件中的任意个事件,在每个订阅的事件中可自定义功能实现。

HttpModule是实现IHttpModule接口的类。接口如下:

publicinterfaceIHttpModule{//摘要://处置由实现System.Web.IHttpModule的模块使用的资源(内存除外)。voidDispose();////摘要://初始化模块,并使其为处理请求做好准备。////参数://context://一个System.Web.HttpApplication,它提供对应用程序内所有应用程序对象的公用的方法、属性和事件的访问voidInit(HttpApplicationcontext);}

下面实现一个HttpModule,并订阅管道中的一系列事件,订阅事件就是在Init方法中绑定EventHandler的过程:

代码有点长,因为我把每一个事件都订阅了,这样一来可以清楚的看出哪些事件执行了,这些事件执行的先后顺序是什么。代码如下:

publicclassMyModule:IHttpModule{#regionIHttpModuleMemberspublicvoidDispose(){//此处放置清除代码。}publicvoidInit(HttpApplicationcontext){//下面是如何处理LogRequest事件并为其//提供自定义日志记录实现的示例context.LogRequest+=newEventHandler(OnLogRequest);context.BeginRequest+=newEventHandler(context_BeginRequest);context.AuthenticateRequest+=newEventHandler(context_AuthenticateRequest);context.AcquireRequestState+=newEventHandler(context_AcquireRequestState);context.AuthorizeRequest+=newEventHandler(context_AuthorizeRequest);context.Disposed+=newEventHandler(context_Disposed);context.Error+=newEventHandler(context_Error);context.EndRequest+=newEventHandler(context_EndRequest);context.MapRequestHandler+=newEventHandler(context_MapRequestHandler);context.PostAcquireRequestState+=newEventHandler(context_PostAcquireRequestState);context.PostAuthenticateRequest+=newEventHandler(context_PostAuthenticateRequest);context.PostAuthorizeRequest+=newEventHandler(context_PostAuthorizeRequest);context.PostLogRequest+=newEventHandler(context_PostLogRequest);context.PostReleaseRequestState+=newEventHandler(context_PostReleaseRequestState);context.PostRequestHandlerExecute+=newEventHandler(context_PostRequestHandlerExecute);context.PostResolveRequestCache+=newEventHandler(context_PostResolveRequestCache);context.PostUpdateRequestCache+=newEventHandler(context_PostUpdateRequestCache);context.ReleaseRequestState+=newEventHandler(context_ReleaseRequestState);context.RequestCompleted+=newEventHandler(context_RequestCompleted);context.ResolveRequestCache+=newEventHandler(context_ResolveRequestCache);context.UpdateRequestCache+=newEventHandler(context_UpdateRequestCache);context.PreRequestHandlerExecute+=newEventHandler(context_PreRequestHandlerExecute);context.PreSendRequestContent+=newEventHandler(context_PreSendRequestContent);context.PreSendRequestHeaders+=newEventHandler(context_PreSendRequestHeaders);context.PostMapRequestHandler+=newEventHandler(context_PostMapRequestHandler);}voidcontext_Error(objectsender,EventArgse){WriteLog("Error");//HttpContext.Current.Response.Write("Error<br/>");}voidcontext_UpdateRequestCache(objectsender,EventArgse){WriteLog("UpdateRequestCache");//HttpContext.Current.Response.Write("UpdateRequestCache<br/>");}voidcontext_ResolveRequestCache(objectsender,EventArgse){WriteLog("ResolveRequestCache");//HttpContext.Current.Response.Write("ResolveRequestCache<br/>");}voidcontext_RequestCompleted(objectsender,EventArgse){WriteLog("RequestCompleted");//HttpContext.Current.Response.Write("RequestCompleted<br/>");}voidcontext_ReleaseRequestState(objectsender,EventArgse){WriteLog("ReleaseRequestState");//HttpContext.Current.Response.Write("ReleaseRequestState<br/>");}voidcontext_PostUpdateRequestCache(objectsender,EventArgse){WriteLog("PostUpdateRequestCache");//HttpContext.Current.Response.Write("PostUpdateRequestCache<br/>");}voidcontext_PostResolveRequestCache(objectsender,EventArgse){WriteLog("PostResolveRequestCache");//HttpContext.Current.Response.Write("PostResolveRequestCache<br/>");}voidcontext_PostRequestHandlerExecute(objectsender,EventArgse){WriteLog("PostRequestHandlerExecute");//HttpContext.Current.Response.Write("PostRequestHandlerExecute<br/>");}voidcontext_PostReleaseRequestState(objectsender,EventArgse){WriteLog("PostReleaseRequestState");//HttpContext.Current.Response.Write("PostReleaseRequestState<br/>");}voidcontext_PostLogRequest(objectsender,EventArgse){WriteLog("PostLogRequest");//HttpContext.Current.Response.Write("PostLogRequest<br/>");}voidcontext_PostAuthorizeRequest(objectsender,EventArgse){WriteLog("PostAuthorizeRequest");//HttpContext.Current.Response.Write("PostAuthorizeRequest<br/>");}voidcontext_PostAuthenticateRequest(objectsender,EventArgse){WriteLog("PostAuthenticateRequest");//HttpContext.Current.Response.Write("PostAuthenticateRequest<br/>");}voidcontext_PostAcquireRequestState(objectsender,EventArgse){WriteLog("PostAcquireRequestState");//HttpContext.Current.Response.Write("PostAcquireRequestState<br/>");}voidcontext_MapRequestHandler(objectsender,EventArgse){WriteLog("MapRequestHandler");//HttpContext.Current.Response.Write("MapRequestHandler<br/>");}voidcontext_Disposed(objectsender,EventArgse){WriteLog("Disposed");//HttpContext.Current.Response.Write("Disposed<br/>");}voidcontext_AuthorizeRequest(objectsender,EventArgse){WriteLog("AuthorizeRequest");//HttpContext.Current.Response.Write("AuthorizeRequest<br/>");}voidcontext_AcquireRequestState(objectsender,EventArgse){WriteLog("AcquireRequestState");//HttpContext.Current.Response.Write("AcquireRequestState<br/>");}voidcontext_PreSendRequestHeaders(objectsender,EventArgse){WriteLog("PreSendRequestHeaders");//HttpContext.Current.Response.Write("PreSendRequestHeaders<br/>");}voidcontext_PreSendRequestContent(objectsender,EventArgse){WriteLog("PreSendRequestContent");//HttpContext.Current.Response.Write("PreSendRequestContent<br/>");}voidcontext_PreRequestHandlerExecute(objectsender,EventArgse){WriteLog("PreRequestHandlerExecute");//HttpContext.Current.Response.Write("PreRequestHandlerExecute<br/>");}voidcontext_EndRequest(objectsender,EventArgse){WriteLog("EndRequest");//HttpContext.Current.Response.Write("EndRequest<br/>");}voidcontext_BeginRequest(objectsender,EventArgse){WriteLog("*******************************************************************************");HttpApplicationapp=senderasHttpApplication;WriteLog(app.Context.Request.Path);WriteLog("BeginRequest");//HttpContext.Current.Response.Write("BeginRequest<br/>");}voidcontext_AuthenticateRequest(objectsender,EventArgse){WriteLog("AuthenticateRequest");//HttpContext.Current.Response.Write("AuthenticateRequest<br/>");}#endregionpublicvoidOnLogRequest(Objectsource,EventArgse){//可以在此处放置自定义日志记录逻辑WriteLog("OnLogRequest");//HttpContext.Current.Response.Write("OnLogRequest<br/>");}publicvoidcontext_PostMapRequestHandler(objectsender,EventArgse){WriteLog("PostMapRequestHandler");//HttpContext.Current.Response.Write("PostMapRequestHandler<br/>");}publicvoidWriteLog(stringmessage){stringpath=@"d:\aspnet\httpmodule.txt";StreamWriterwriter=null;if(!File.Exists(path)){writer=File.CreateText(path);}else{FileInfoinfo=newFileInfo(path);writer=info.AppendText();}writer.WriteLine(message);writer.Flush();writer.Close();}}

订阅的事件实现中,将事件名称保存到我本地D盘的一个文本文件中。

代码实现完毕了,下一步就是要代码起作用了,很简单,只需要在web.config中简单配置就可以了。配置中注意IIS7集成模式和IIS7经典模式(包括IIS6)的区别,配置如下:

<!--IIS6或者IIS7经典模式--><system.web><httpModules><addname="mycustommodule"type="fengzheng.MyModule,handler_modules"/></httpModules></system.web><!--IIS7集成模式--><system.webServer><modules><addname="mycustommodule"type="fengzheng.MyModule,handler_modules"/></modules></system.webServer>

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

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

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

publicinterfaceIHttpHandler{//摘要://获取一个值,该值指示其他请求是否可以使用System.Web.IHttpHandler实例。////返回结果://如果System.Web.IHttpHandler实例可再次使用,则为true;否则为false。boolIsReusable{get;}//摘要://通过实现System.Web.IHttpHandler接口的自定义HttpHandler启用HTTPWeb请求的处理。////参数://context://System.Web.HttpContext对象,它提供对用于为HTTP请求提供服务的内部服务器对象(如Request、Response、Session//和Server)的引用。voidProcessRequest(HttpContextcontext);}

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

publicclassMyIISHandler:IHttpHandler{///<summary>///您将需要在网站的Web.config文件中配置此处理程序///并向IIS注册它,然后才能使用它。有关详细信息,///请参见下面的链接:/?linkid=8101007///</summary>#regionIHttpHandlerMemberspublicboolIsReusable{//如果无法为其他请求重用托管处理程序,则返回false。//如果按请求保留某些状态信息,则通常这将为false。get{returntrue;}}publicvoidProcessRequest(HttpContextcontext){//在此处写入您的处理程序实现。WriteLog("请求一个asox页面");}#endregion}

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

<!--IIS6或者IIS7经典模式--><system.web><httpHandlers><addname="mycustomhandler"path="*.asox"verb="*"type="fengzheng.MyIISHandler,handler_modules"/></httpHandlers></system.web><!--IIS7集成模式--><system.webServer><handlers><addname="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:用名字空间、类名称和程序集名称的组合形式指定处理程序或处理程序工厂的实际类型。运行时首先搜索bin目录中的DLL,接着在GAC中搜索。

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

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

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

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

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

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

publicinterfaceIHttpHandlerFactory{//摘要://返回实现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对象。IHttpHandlerGetHandler(HttpContextcontext,stringrequestType,stringurl,stringpathTranslated);////摘要://使工厂可以重用现有的处理程序实例。////参数://handler://要重用的System.Web.IHttpHandler对象。voidReleaseHandler(IHttpHandlerhandler);}

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

publicclassMyHttpHandlerFactory:IHttpHandlerFactory{publicIHttpHandlerGetHandler(HttpContextcontext,stringrequestType,stringurl,stringpathTranslated){//写日志WriteLog(string.Format("requestType:{0}|url:{1}|pathTranslated:{2}",requestType,url,pathTranslated));//生成一个IHttpHandlerTypet=typeof(MyIISHandler);IHttpHandlerhandler=Activator.CreateInstance(t)asIHttpHandler;returnhandler;}publicvoidReleaseHandler(IHttpHandlerhandler){}}

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

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

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

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

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

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

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

具体的可以参考这篇文章

部署网站注意事项:

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

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

<?xmlversion="1.0"encoding="utf-8"?><!--有关如何配置应用程序的详细信息,请访问/fwlink/?LinkId=169433--><configuration><system.web><compilationdebug="true"targetFramework="4.5"/><httpRuntimetargetFramework="4.5"/></system.web></configuration>按照经典模式部署,配置文件应该如下:<?xmlversion="1.0"encoding="utf-8"?><!--有关如何配置应用程序的详细信息,请访问/fwlink/?LinkId=169433--><configuration><system.web><compilationdebug="true"targetFramework="4.5"/><httpRuntimetargetFramework="4.5"/><httpModules><addname="mycustommodule"type="fengzheng.MyModule,handler_modules"/></httpModules><httpHandlers><addname="mycustomhandler"path="*.asox"verb="*"type="fengzheng.MyIISHandler,handler_modules"/></httpHandlers></system.web></configuration>

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

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

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

按照集成模式部署,配置文件应该如下,将配置信息放到system.webServer节点之下:<?xmlversion="1.0"encoding="utf-8"?><!--有关如何配置应用程序的详细信息,请访问/fwlink/?LinkId=169433--><configuration><system.web><compilationdebug="true"targetFramework="4.5"/><httpRuntimetargetFramework="4.5"/></system.web><system.webServer><modules><addname="mycustommodule"type="fengzheng.MyModule,handler_modules"/></modules><handlers><addname="mycustomhandler"path="*"verb="*"type="fengzheng.MyIISHandler,handler_modules"/></handlers></system.webServer></configuration>

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。