上篇提到请求进入到System.Web后,创建完HttpApplication对象后会执行一堆的管道事件,然后可以通过HttpModule来对其进行扩展,那么这篇文章就来介绍下如何定义我们自己的module来实现对项目的一些扩展。

  先看一下IHttpModule的接口源码,只有Dispose()和Init(HttpApplication context)这两个方法,从方法名称我们就可以知道Dispose方法是用来释放资源的,而Init方法自然就是用来初始化的,一般仅用于给期望的HttpApplication事件注册方法。

  1. using System;
  2. using System.Security.Permissions;
  3. namespace System.Web
  4. {
  5. /// <summary>
  6. /// Provides module initialization and disposal events to the implementing class.
  7. /// </summary>
  8. [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
  9. public interface IHttpModule
  10. {
  11. /// <summary>
  12. /// Initializes a module and prepares it to handle requests.
  13. /// </summary>
  14. /// <param name="context">
  15. /// An <see cref="T:System.Web.HttpApplication" /> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application
  16. /// </param>
  17. void Init(HttpApplication context);
  18. /// <summary>
  19. /// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule" />.
  20. /// </summary>
  21. void Dispose();
  22. }
  23. }

  Init方法拥有HttpApplication的参数,那么我们先来实现一个自己定义的HttpModule。实现自定义的httpmodule需要实现IHttpModule接口。

  1. namespace Jesen.Web.Core
  2. {
  3. /// <summary>
  4. /// 自定义实现HttpModule,实现IHttpModule接口
  5. /// </summary>
  6. public class MyHttpModule : IHttpModule
  7. {
  8. public void Dispose()
  9. {
  10.  
  11. }
  12.  
  13. public void Init(HttpApplication context)
  14. {
  15. //在此处添加管道事件的处理
  16. context.BeginRequest += new EventHandler(Context_BeginRequest);
  17. context.EndRequest += new EventHandler(Context_EndRequest);
  18. }
  19.  
  20. private void Context_BeginRequest(object sender, EventArgs e)
  21. {
  22. HttpApplication context = sender as HttpApplication;
  23.  
  24. context.Response.Write("<h1 style='color:#00f'>来自MyHttpModule的处理开始</h1>");
  25. }
  26.  
  27. private void Context_EndRequest(object sender, EventArgs e)
  28. {
  29. HttpApplication context = sender as HttpApplication;
  30.  
  31. context.Response.Write("<h1 style='color:#00f'>来自MyHttpModule的处理结束</h1>");
  32. }
  33. }
  34. }

  实现完IHttpModule接口后我们还需要对自定义的HttpModule进行注册,注册的方式是通过web.config来配置。

  VS2013及以后/IIS7.0之后的集成模式的配置位置:

  1. <system.webServer>
  2. <modules runAllManagedModulesForAllRequests="false">
  3. <!--runAllManagedModulesForAllRequests处理静态文件的请求-->
  4. <remove name="FormsAuthentication" />
  5. <remove name="WindowsAuthentication" />
  6. <remove name="PassportAuthentication" />
  7. <remove name="RoleManager" />
  8. <remove name="FileAuthorization" />
  9. <remove name="UrlAuthorization" />
  10. <add name="MyHttpModule" type="Jesen.Web.Core.MyHttpModule,Jesen.Web.Core"/>
  11. </system.webServer>

  如果是VS2013以前/IIS7.0之前和经典模式的配置位置:

  1. <system.web>
  2. <compilation debug="true" targetFramework="4.5" />
  3. <httpRuntime targetFramework="4.5" />
  4. <!--<httpModules>
  5. <remove name="FormsAuthentication" />
  6. <add name="MyHttpModule" type="Jesen.Web.Core.MyHttpModule,Jesen.Web.Core" />
  7. <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
  8. </httpModules>
  9. </system.web>

  接着我们运行程序,浏览器访问结果如下,浏览器输出我们上面自定义的HttpModule处理事件的代码。

  其实module对所有请求都起作用,在每次接收到请求的时候,都会有一堆的Module被执行,因为.Net框架已经在全局配置文件里面配好了一些必要的module,该配置文件地址:C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\web.config,我们可以在自己的项目中的web.config文件中去移除一些不需要的module,例如上面的 <remove name="FormsAuthentication" />移除了Form表单验证。全局web.config配置文件对httpModule的配置如下:

  至此我们可以总结一下module的作用,1、可以用来做权限认证;2、可以做Url转发;3、可以做性能监控;4、可以用来做反爬虫,检测同一个IP频繁的请求。当然,module不适合单独为某些请求而服务,因为它是针对所有请求的。

  接下来我们来看下asp.net中的另一类事件,我们把代码改了一下,在我们自定义的Module里面我们定义了一个事件MyHttpModuleEvent,我们依然按照上面注册module的方法在web.config文件中注册好MyHttpModule。不同的是我们在项目启动文件Global.asax.cs文件中添加了 以web.config注册模块的名称_Module中的事件的一个方法。运行项目之后,我们发现该方法MyHttpModule_MyHttpModuleEvent被调用了。

  1. /// <summary>
  2. /// 自定义实现HttpModule,实现IHttpModule接口
  3. /// </summary>
  4. public class MyHttpModule : IHttpModule
  5. {
  6. public event EventHandler MyHttpModuleEvent;
  7.  
  8. public void Dispose()
  9. {
  10.  
  11. }
  12.  
  13. public void Init(HttpApplication context)
  14. {
  15. //在此处添加管道事件的处理
  16. context.BeginRequest += new EventHandler(Context_BeginRequest);
  17. context.EndRequest += new EventHandler(Context_EndRequest);
  18. }
  19.  
  20. private void Context_BeginRequest(object sender, EventArgs e)
  21. {
  22. HttpApplication application = sender as HttpApplication;
  23. HttpContext context = application.Context;//Url重写
  24. if (context.Request.Url.AbsolutePath.Equals("/Pipe/Some", StringComparison.OrdinalIgnoreCase))
  25. context.RewritePath("/Pipe/Another");
  26.  
  27. if (MyHttpModuleEvent!= null)
  28. MyHttpModuleEvent.Invoke(this, e);
  29. }
  30.  
  31. private void Context_EndRequest(object sender, EventArgs e)
  32. {
  33. HttpApplication application = sender as HttpApplication;
  34. HttpContext context = application.Context;
  35. }
  36. }
  1. protected void MyHttpModule_MyHttpModuleEvent(object sender, EventArgs e)
  2. {
  3. Response.Write("<h3>来自Global.asax 的 MyHttpModule_MyHttpModuleEvent</h2>");
  4. }

那么为什么这个方法会被调用,这个其实是.Net框架约定俗成的。约定命名方式:HttpModule注册名称_事件名称,而这个事件的执行时机是由我们在自定义的module中确定的。

  最后我们来看下Global.asax.cs文件中的一些方法。

  1. /// <summary>
         /// 网站启动时,响应第一次请求的执行的
  2. /// 以后再以不执行了
  3. /// 挺合适做一些初始化
  4. /// </summary>
  5. protected void Application_Start()
  6. {
  7. AreaRegistration.RegisterAllAreas();//注册区域路由
  8. GlobalConfiguration.Configure(WebApiConfig.Register);//注册webapi路由
  9. FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);//注册全局过滤器
  10. RouteConfig.RegisterRoutes(RouteTable.Routes);//注册路由
  11. BundleConfig.RegisterBundles(BundleTable.Bundles);
  12. }
  13. protected void Application_End(object sender, EventArgs e)
  14. {
  15. //在应用程序关闭时运行的代码
  16. logger.Info("Application_End");
  17. }
  18.  
  19. /// <summary>
  20. /// 请求出现异常,都可以处理
  21. /// 也可以完成全局异常处理
  22. /// filter只能处理控制器里面的异常
  23. /// </summary>
  24. /// <param name="sender"></param>
  25. /// <param name="e"></param>
  26. protected void Application_Error(object sender, EventArgs e)
  27. {
  28. // 在出现未处理的错误时运行的代码
  29. var error = Server.GetLastError();
  30.  
  31. logger.Info("Application_Error");
  32. Response.Write("出错");
  33. Server.ClearError();
  34.  
  35. }
  36.  
  37. protected void Session_Start(object sender, EventArgs e)
  38. {
  39. // 在新会话启动时运行的代码
  40. logger.Info("Session_Start");
  41. }
  42. protected void Session_End(object sender, EventArgs e)
  43. {
  44. // 在会话结束时运行的代码。
  45. // 注意: 只有在 Web.config 文件中的 sessionstate 模式设置为
  46. // InProc(默认内存里) 时,才会引发 Session_End 事件。如果会话模式设置为 StateServer
  47. // 或 SQLServer,则不会引发该事件。
  48. logger.Info("Session_End");
  49. }

  从该文件中,我们可以看到Session_Start和Session_End两个方法,它们的调用也是.Net框架约定的,因为我们通过反射查看SessionStateModule的源码,可以看到其定义了Start和End两个事件。

  1. public event EventHandler Start
  2. {
  3. add
  4. {
  5. this._sessionStartEventHandler = (EventHandler)Delegate.Combine(this._sessionStartEventHandler, value);
  6. }
  7. remove
  8. {
  9. this._sessionStartEventHandler = (EventHandler)Delegate.Remove(this._sessionStartEventHandler, value);
  10. }
  11. }
  12. public event EventHandler End
  13. {
  14. add
  15. {
  16. SessionOnEndTarget onEndTarget = this._onEndTarget;
  17. lock (onEndTarget)
  18. {
  19. if (this._store != null && this._onEndTarget.SessionEndEventHandlerCount == )
  20. {
  21. this._supportSessionExpiry = this._store.SetItemExpireCallback(new SessionStateItemExpireCallback(this._onEndTarget.RaiseSessionOnEnd));
  22. }
  23. SessionOnEndTarget expr_4E = this._onEndTarget;
  24. int sessionEndEventHandlerCount = expr_4E.SessionEndEventHandlerCount + ;
  25. expr_4E.SessionEndEventHandlerCount = sessionEndEventHandlerCount;
  26. }
  27. }
  28. remove
  29. {
  30. SessionOnEndTarget onEndTarget = this._onEndTarget;
  31. lock (onEndTarget)
  32. {
  33. SessionOnEndTarget expr_17 = this._onEndTarget;
  34. int sessionEndEventHandlerCount = expr_17.SessionEndEventHandlerCount - ;
  35. expr_17.SessionEndEventHandlerCount = sessionEndEventHandlerCount;
  36. if (this._store != null && this._onEndTarget.SessionEndEventHandlerCount == )
  37. {
  38. this._store.SetItemExpireCallback(null);
  39. this._supportSessionExpiry = false;
  40. }
  41. }
  42. }
  43. }

  需要注意的是在每处理一个Http请求时,应用程序事件都会触发一遍,但是Application_Start和 Application_End 例外,它仅在第一个资源文件被访问时被触发。Http Module无法注册和响应Session事件,对于Session_Start 和 Session_End,只能通过Glabal.asax来处理。

扩展 IHttpModule的更多相关文章

  1. 一种基于自定义代码的asp.net网站首页根据IP自动跳转指定页面的方法!

    一种基于自定义代码的asp.net网站首页根据IP自动跳转指定页面的方法! 对于大中型网站,为了增强用户体验,往往需要根据不同城市站点的用户推送或展现相应个性化的内容,如对于一些大型门户网站的新闻会有 ...

  2. Asp.Net 管道事件注册/HttpApplication事件注册

    一.HttpApplication简介 在HttpRuntime创建了HttpContext对象之后,HttpRuntime将随后创建一个用于处理请求的对象,这个对象的类型为HttpApplicati ...

  3. 一种基于自定义代码的asp.net网站访问IP过滤方法!

    对于一些企业内部核心系统,特别是外网访问的时候,为了信息安全,可能需要对外部访问的IP地址作限制,虽然IIS中也提供了根据IP地址或IP地址段进行限制或允许,但并没有提供根据IP地址所在的城市进行限制 ...

  4. 你真的熟悉ASP.NET MVC的整个生命周期吗?

    一.介绍 我们做开发的,尤其是做微软技术栈的,有一个方向是跳不过去的,那就是MVC开发.我相信大家,做ASP.NET MVC 开发有的有很长时间,当然,也有刚进入这个行业的.无论如何,如果有人问你,你 ...

  5. 通过IHttpModule,IHttpHandler扩展IIS

    IIS对Http Request的处理流程 当Windows Server收到从浏览器发送过来的http请求,处理流程如下(引用自官方文档): 最终请求会被w3wp.exe处理,处理过程如下: 左边蓝 ...

  6. 17+个ASP.NET MVC扩展点【附源码】

    1.自定义一个HttpModule,并将其中的方法添加到HttpApplication相应的事件中!即:创建一个实现了IHttpmodule接口的类,并将配置WebConfig.  在自定义的Http ...

  7. Asp.net Mvc模块化开发之分区扩展框架

    对于一个企业级项目开发,模块化是非常重要的. 默认Mvc框架的AreaRegistration对模块化开发真的支持很好吗?真的有很多复杂系统在使用默认的分区开发的吗?我相信大部分asp.net的技术团 ...

  8. IHttpModule在webconfig中的注册

    在asp.net中,提供了两种方式用来解决获取由asp.net服务器创建和维护的HttpApplication对象,方便注册HttpApplication对象的事件处理.这两种方式为:IHtpModu ...

  9. 【IHttpHandler】IHttpModule实现URL重写

    1.用自定义IHttpModule实现URL重写 一般来说,要显示一些动态数据总是采用带参数的方式,比如制作一个UserInfo.aspx的动态页面用于显示系统的UserInfo这个用户信息表的数据, ...

随机推荐

  1. BZOJ_5369_[Pkusc2018]最大前缀和_状压DP

    BZOJ_5369_[Pkusc2018]最大前缀和_状压DP Description 小C是一个算法竞赛爱好者,有一天小C遇到了一个非常难的问题:求一个序列的最大子段和. 但是小C并不会做这个题,于 ...

  2. CF上的3道小题(1)

    CF上的3道小题 终于调完了啊.... T1:CF702E Analysis of Pathes in Functional Graph 题意:你获得了一个n个点有向图,每个点只有一条出边.第i个点的 ...

  3. python3.6 + selenium2.53.1 查询数据库并将返回的内容中每一行的内容转换成class对象

    环境: win10 python3.6 selenium2.53.1 准备工作:先安装pymysql python2.x链接数据库使用MySQLdb,而python3.x链接数据库使用pymysql ...

  4. windows下 zookeeper dubbo 安装+配置+demo 详细图文教程

    Java集群优化——dubbo+zookeeper构建 互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,Dubbo是一个分布式服务框架,在这 ...

  5. bzoj 4184: shallot【线性基+时间线段树】

    学到了线段树新姿势! 先离线读入,根据时间建一棵线段树,每个节点上开一个vector存这个区间内存在的数(使用map来记录每个数出现的一段时间),然后在线段树上dfs,到叶子节点就计算答案. 注意!! ...

  6. 在ios Xcode10下小白都能解决library not found for -libstdc++.6.0.9

    写在前面 library not found for -libstdc++.6.0.9,今天做项目的时候碰到这个问题,解决的过程中遇到了目录路径不对的问题(不在通常的/Applications/Xco ...

  7. pytest的参数化

    参数化有两种方式: 1. @pytest.mark.parametrize 2.利用conftest.py里的 pytest_generate_tests 1中的例子如下: @pytest.mark. ...

  8. DP Codeforces Round #260 (Div. 1) A. Boredom

    题目传送门 /* 题意:选择a[k]然后a[k]-1和a[k]+1的全部删除,得到点数a[k],问最大点数 DP:状态转移方程:dp[i] = max (dp[i-1], dp[i-2] + (ll) ...

  9. Errors running builder 'JavaScript Validator'错误处理

    MyEclipse2014编辑代码时,只要保存就会报出如下错误信息: Errors occurred during the build. Errors running builder 'JavaScr ...

  10. Acitivty四种启动模式

    Acitivty的四种启动模式 在清单文件中声明 Activity 时,您可以使用 <activity> 元素的 launchMode 属性指定 Activity 应该如何与任务关联. l ...