asp.net处理事件

时间:2023-04-17 16:25:50

从来不用也从来不研究这事件.但为了写那种CGI式的接口不得已研究一下.

环境  W10 VS2017

测试方法:写一个实现IHttpModule接口的类,在Init方法中加载所有事件然后打出日志,看看事件执行顺序.

模块接口类需要在IIS中打开"模块"->添加"模块"后使用.或者在webconfig里添加模块节点也行.

事件执行结果如下:

  • 0在 ASP.NET 响应请求时作为 HTTP 执行管线链中的第一个事件发生。BeginRequest
  • 1 当安全模块建立用户标识时发生 AuthenticateRequest
  • 2 当安全模块已建立用户标识时发生。PostAuthenticateRequest
  • 3 当安全模块已验证用户授权时发生。AuthorizeRequest
  • 4 在当前请求的用户已获授权时发生。PostAuthorizeRequest
  • 5 在 ASP.NET 完成授权事件以使缓存模块从缓存中为请求提供服务后发生,从而绕过事件处理程序(例如某个页或 XML Web services)的执行。ResolveRequestCache
  • 6 在 ASP.NET 跳过当前事件处理程序的执行并允许缓存模块满足来自缓存的请求时发生。PostResolveRequestCache
  • 7 在选择了用来响应请求的处理程序时发生 MapRequestHandler
  • 8 在 ASP.NET 已将当前请求映射到相应的事件处理程序时发生。PostMapRequestHandler
  • 9 当 ASP.NET 获取与当前请求关联的当前状态(如会话状态)时发生。AcquireRequestState
  • 10 在已获得与当前请求关联的请求状态(例如会话状态)时发生。PostAcquireRequestState
  • 11 恰好在 ASP.NET 开始执行事件处理程序(例如,某页或某个 XML Web services)前发生。PreRequestHandlerExecute
  • 12 当引发未经处理的异常时发生 Error
  • 13 在 ASP.NET 事件处理程序(例如,某页或某个 XML Web service)执行完毕时发生。PostRequestHandlerExecute
  • 14 在 ASP.NET 执行完所有请求事件处理程序后发生。该事件将使状态模块保存当前状态数据。ReleaseRequestState
  • 15 在 ASP.NET 已完成所有请求事件处理程序的执行并且请求状态数据已存储时发生。PostReleaseRequestState
  • 16 当 ASP.NET 执行完事件处理程序以使缓存模块存储将用于从缓存为后续请求提供服务的响应时发生。UpdateRequestCache
  • 17 在 ASP.NET 完成缓存模块的更新并存储了用于从缓存中为后续请求提供服务的响应后,发生此事件。PostUpdateRequestCache
  • 18 恰好在 ASP.NET 为当前请求执行任何记录之前发生。LogRequest
  • 19 在 ASP.NET 处理完 System.Web.HttpApplication.LogRequest 事件的所有事件处理程序后发生。PostLogRequest
  • 20在 ASP.NET 响应请求时作为 HTTP 执行管线链中的最后一个事件发生。EndRequest
  • 21 恰好在 ASP.NET 向客户端发送 HTTP 标头之前发生。PreSendRequestHeaders
  • 22 恰好在 ASP.NET 向客户端发送内容之前发生。PreSendRequestContent

一共有25个事件,日志显示了23个.有2个未打出.

  Disposed // 在释放应用程序时发生.   ----也可以自己调用Dispose()方法来引发这个事件,最好在EndRequest事件里调用,调用之后应用程序对象就没有了.

  RequestCompleted// 当托管对象与已经释放的请求相关联时发生。  ----这个不知道怎么触发

为了让一个类的方法变成接口可以这样:(参考博客http://www.cnblogs.com/fish-li/archive/2011/09/05/2168073.html)

  1. 用IHttpModule模块: 在模块中动态调用这个类的方法.简单的讲就是根据URL地址找到类名和方法名,然后用"反射"调用它们,并且将上下文对象传给这方法.
  2. 用IHttpHandler处理: 方法同上.

  根据参考博客提出的建议,使用IHttpHandler方法好.这个问题我看了几遍博客才有些明白.

------疑惑在于一直想不清楚IHttpModule和IHttpHandler区别到底在哪里.为什么要提供这两接口.现在看来,需要从这管道处理事件的设计角度看,能找到原因.------

管道处理方式设计:

  既然设计处理事件,也就是说将一次HTTP请求的处理过程硬性规定为这些步骤,每个步骤用注册事件的方式来执行自定义操作.这些步骤制定的是否合理,需要问设计者.这些事件也会调整,以前版本ASP.NET并没有25个事件,好像是19个.这样看来,没有合理,只有不断调优.之前写贪吃蛇游戏,采用的是类似的方法,在蛇每移动一下这个时刻,分解为几个步骤,判断蛇的状态,然后执行相应代码.这样做有个好处就是容易扩展功能,比如要新加一个状态,只要找到合适的步骤,将方法注册到这步骤上就行了.当然,前提是这些步骤设计得"合理".

IHttpModule与IHttpHandler时机

   IHttpModule的作用是注册管道事件,"所有请求"都会被执行这个类里注册过的事件(这个可能是错的).而IHttpHandler是一个处理程序,它属于管道处理的其中一个环节.经过打印日志发现:    

在第11个事件之后,执行IHttpHandler  // (11 恰好在 ASP.NET 开始执行事件处理程序(例如,某页或某个 XML Web services)前发生。)PreRequestHandlerExecute

另外在第7个事件之后,执行IHttpHandlerFactory(这个用于指定使用哪个IHttpHandler,详见参考博客)  //  7 在选择了用来响应请求的处理程序时发生 MapRequestHandler

HttpApplication类管理事件处理

  HttpApplication类管理这些事件的运行,它有一个方法CompleteRequest();  /使 ASP.NET 跳过 HTTP 执行管线链中的所有事件和筛选并直接执行 EndRequest 事件

 如果在第一个事件,BeginRequest事件中调用这个方法.那么程序打印日志如下:

  • 0在 ASP.NET 响应请求时作为 HTTP 执行管线链中的第一个事件发生。BeginRequest
  • 18 恰好在 ASP.NET 为当前请求执行任何记录之前发生。LogRequest
  • 19 在 ASP.NET 处理完 System.Web.HttpApplication.LogRequest 事件的所有事件处理程序后发生。PostLogRequest
  • 20在 ASP.NET 响应请求时作为 HTTP 执行管线链中的最后一个事件发生。EndRequest
  • 21 恰好在 ASP.NET 向客户端发送 HTTP 标头之前发生。PreSendRequestHeaders
  • 22 恰好在 ASP.NET 向客户端发送内容之前发生。PreSendRequestContent

  第1个和最后5个事件触发了,而IHttpHandler 需要在第11个事件后触发,所以没机会,就没执行.这说明IHttpHandler是管道过程中的其中一个环节.

  管道事件可以跳过而不执行,那么如果是充分利用ASP.NET管道事件机制的系统,它的很多代码都在注册在各事件步骤,那么随意跳过肯定不行.因为不能保证其它模块注册的事件有机会执行.

如果完全不使用这些事件也不使用这些模块,只是"为了让一个类的方法变成接口",那么,事件可以一个也不注册.模块可以不加载.但处理程序IHttpHandler一定要的.

模块和处理程序映射设置(关于IIS模块详见:      https://docs.microsoft.com/en-us/iis/get-started/introduction-to-iis/introduction-to-iis-architecture          )

  在IIS中新建一个网站后,(集成模式.NET4.0).已经加载了很多模块和处理程序映射了.可以删除不需要的模块(如果是继承的,删除不掉,需要在IIS节点上打开"模块",然后选中模块,点解除锁定.再到站点中就可以删除了)

  重要的模块:

  StaticFileModule(默认,本机,继承)

    // 用于处理静态文件,html,js,css.等等 如果不加的话,静态文件访问不了.

  DirectoryListingModule(默认,本机,继承)

    // 目录浏览功能,如果不加将,允许目录浏览权限的地方看不到目录列表.现在几乎没人浏览网站下的目录.除非是个FTP

  DefaultDocumentModule(默认,本机,继承)

    // 默认文档,例如http://www.xx/home 这样的URL会显示默认的index.htm(如果设定这个默认文档的话).如果不加,则没这功能.必须输入全的 http://www.xx/home/index.html

  AnonymousAuthenticationModule(默认,本机,继承)

    // 匿名验证.如果不加,网页打不开,提示没权限401错误.并且会触发第2个管道事件(当安全模块已建立用户标识时发生 AuthenticateRequest)后,马上触发 18-22事件.效果如同调用了 CompleteRequest()

  重要的处理程序映射:

  StaticFile 请求路径: *  模块: StaticFileModule,DefaultDocumentModule,DirectoryListingModule 权限:读取,脚本 请求限制:读取

    // 如果没有这个映射:静态文件访问不了.提示找不到404

模块和处理程序的关系:

  究竟处理程序和模块间有没关系?

  A. 处理程序映射:(在IIS功能,"处理程序映射"双击打开)可以添加几种

    1."添加托管处理程序",这种会设定"请求路径","类型",请求路径可以是*,*.ext,*.ashx,还可有目录如/api/*.总之是一个路径规则,"类型"就是这个处理程序的类名(带命名空间),请求限制还可设定如何调用这个处理程序,一般不用勾选映射选项.设定好之后,凡是符合这种路径规则的请求,都会执行这个处理程序.程序类必是实现了IHttpHandler接口的.

    2."添加模块映射",这种会设定"请求路径",模块,在模块下拉列表中只有本机安装好的模块,如果要自己模块则需要在IIS根上双击模块->配置本机模块->注册这个地方添加.然后在站点的"模块"功能中添加这个本机模块后,下拉列表里才有.(我使用C#写的实现IHttpModule接口的类编译成DLL后,注册.发现没有效果.不知道是什么原因).

  以上两种设定,感觉如下,IHttpModule和IHttpHandler都能处理请求,并且可以让IHttpHandler处理程序负责某一类路径规则的所有请求(1),也可以为另一种路径规则的请求指定用哪些模块处理(2).

  那么也就是说模块也不再是针对所有请求的了,那么这个模块中注册的事件,也可以只是针对某一类请求的.(这可能是个误解,可能本机模块和托管模块有很大区别)

  B.模块:(在IIS功能,"模块")

    添加指托管模块(实现IHttpModule接口的),有一个选项"仅针对向ASP.NET应用程序或托管处理程序发出的请求调用".选中和不选中时有区别.如果选了,那么在请求静态文件时,发现模块并不执行,而请求某个处理程序时,模块才执行.如果没有选择,那么处理静态文件时,它也执行了.这表明模块注册的事件是针对所有请求都有效的.

    这个选项在于解决一个明显问题,就是"如果说模块是针对所有请求的"那么请求静态文件时,这个模块的功能也会执行,但是大多数情况下,静态文件就是直接发向客户端了,并不需要去执行模块里的事件.不需要经过管道处理.

  I.上述情况或许表明,托管模块或者处理程序在实际上都是针对某种请求而设计,并不需要一种接管所有请求的模块或者处理程序.至少静态文件和动态文件的处理是不同的,StaticFile这个模块映射使用的路径是*,表明它接管所有请求,并且使用DefaultDocumentModule,DirectoryListingModule,StaticFileModule这三个模块处理这些请求.那么其它路径规则应该是在它之前执行,如果都找不到才丢给它当做静态文件处理(猜测的).

  II.小结:

    托管模块IHttpModule注册的事件是针对所有请求的,但在添加时,可以选择只针对托管请求(或者说是指明了处理程序的请求).它的作用在于注意各种事件,并不具体处理请求和返回结果.(虽然也可以)

    处理程序IHttpHandler是若干事件中的一个步骤,是在11步之后执行的.它的重要工作是返回一个处理结果.并不需要关心自己在处理事件中的位置

  III.如果完全不使用这套处理管道机制,那么也就没必要用ASP.NET了.可以用System.Net.HttpListener这个类写一个监听程序然后挂上自己的C#程序.

为了让一个类的方法变成接口,可以:

  定制一个网站,只加载必要的模块和处理程序映射,然后将自己的类库项目,再弄一个web.config(这个可以方便配置模块与映射,而不需要在IIS控制台上配置)放到这网站目录中,执行程序放bin.web.config放根目录 ,这样就可行.

------------------------------------随时修正和补充-----------------------------------