HttpApplication
触发事件来通知你的程序有事发生
,
以此来负责请求流转
.
这作为
HttpApplication.Init()
函数的一部分发生
(
用
Reflector
查看
System.Web.HttpApplication.InitInternal()
方法和
HttpApplication.ResumeSteps()
方法来了解更多详情
),
连续设置并启动一系列事件
,
包括执行所有的处理器
(handler).
这些事件处理器映射到
global.asax
中自动生成的哪些事件中
,
同时它们也映射到所有附加的
HttpModule(
它们本质上是
HttpApplication
对外发布的额外的事件接收器
(sink)).
HttpModule
和
HttpHandler
两者都是根据
Web.config
中对应的配置被动态载入并附加到事件处理链中
.HttpModule
实际上是事件处理器
,
附加到特殊的
HttpApplication
事件上
,
然而
HttpHandler
是用来处理
”
应用级请求处理
”
的终点
.
HttpModule
和
HttpHandler
两者都是在
HttpApplication.Init()
函数调用的一部分中被载入并附加到调用链上
.
图
6
显示了不同的事件
,
它们是何时发生的以及它们影响管道的哪一部分
.
图
6-
事件在
ASP.NET http
管道中流转的过程
.HttpApplication
对象的事件驱动请求在管道中流转
.Http Module
可以拦截这些事件并覆盖或者扩展现有的功能
.
HttpContext, HttpModules
和
HttpHandlers
httpApplication
它本身对发送给应用程序的数据一无所知
-
它只是一个通过事件来通讯的消息对象
.
它触发事件并通过
HttpContext
对象来向被调用函数传递消息
.
实际的当前请求的状态数据由前面提到的
HttpContext
对象维护
.
它提供了所有请求专有的数据并从进入管道开始到结束一直跟随请求
.
图
7
显示了
ASP.NET
管道中的流程
.
注意上下文对象
(
即
HttpContext),
这个从请求开始到结束一直都是你
”
朋友
”
的对象
,
可以在一个事件处理函数中保存信息并在以后的事件处理函数中取出
.
一旦管道被启动
,HttpApplication
开始象图六那样一个个的触发事件
.
每个事件处理器被触发
,
如果事件被挂接
,
这些处理器将执行它们自己的任务
.
这个处理的主要任务是最终调用挂接到此特定请求的
HttpHandler.
处理器
(handler)
是
ASP.NET
请求的核心处理机制
,
通常也是所有应用程序级别的代码被执行的地方
.
记住
ASP.NET
页面和
Web
服务框架都是作为
HttpHandler
实现
,
这里也是处理请求的的核心之处
.
模块
(module)
趋向于成为一个传递给处理器
(handler)
的上下文的预处理或后处理器
.ASP.NET
中典型的默认处理器包括预处理的认证
,
缓存以及后处理中各种不同的编码机制
.
有很多关于
HttpHandler
和
HttpModule
的可用信息
,
所以为了保持这篇文章在一个合理的长度
,
我将提供一个关于处理器的概要介绍
.
HttpModule
当请求在管道中传递时
,HttpApplicaion
对象中一系列的事件被触发
.
我们已经看到这些事件在
Global.asax
中作为事件被发布
.
这种方法是特定于应用程序的
,
可能并不总是你想要的
.
如果你要建立一个通用的可用被插入任何
Web
应用程序的
HttpApplication
事件钩子
,
你可用使用
HttpModule,
这是可复用的
,
不需要特定语应用程序代码的
,
只需要
web.config
中的一个条目
.
模块本质上是过滤器
(fliter)-
功能上类似于
ISAPI
过滤器
,
但是它工作在
ASP.NET
请求级别上
.
模块允许为每个通过
HttpApplication
对象的请求挂接事件
.
这些模块作为外部程序集中的类存贮
.,
在
web.config
文件中被配置
,
在应用程序启动时被载入
.
通过实现特定的接口和方法
,
模块被挂接到
HttpApplication
事件链上
.
多个
HttpModule
可用被挂接在相同的事件上
,
事件处理的顺序取决于它们在
Web.config
中声明的顺序
.
下面是在
Web.config
中处理器定义
.
<configuration
>
<system.web
>
<httpModules
>
<add
name
=
"BasicAuthModule"
type
="HttpHandlers.BasicAuth,WebStore"
/>
</httpModules
>
</system.web
>
</configuration
>
注意你需要指定完整的类型名和不带
dll
扩展名的程序集名
.
模块允许你查看每个收到的
Web
请求并基于被触发的事件执行一个动作
.
模块在修改请求和响应数据方面做的非常优秀
,
可用为特定的程序提供自定义认证或者为发生在
ASP.NET
中的每个请求增加其他预处理
/
后处理功能
.
许多
ASP.NET
的功能
,
像认证和会话
(Session)
引擎都是作为
HttpModule
来实现的
.
虽然
HttpModule
看上去很像
ISAPI
过滤器
,
它们都检查每个通过
ASP.NET
应用的请求
,
但是它们只检查映射到单个特定的
ASP.NET
应用或虚拟目录的请求
,
也就是只能检查映射到
ASP.NET
的请求
.
这样你可以检查所有
ASPX
页面或者其他任何映射到
ASP.NET
的扩展名
.
你不能检查标准的
.HTM
或者图片文件
,
除非你显式的映射这些扩展名到
ASP.NET ISAPI dll
上
,
就像图
1
中展示的那样
.
一个常见的此类应用可能是使用模块来过滤特定目录中的
JPG
图像内容并在最上层通过
GDI+
来绘制
’
样品
’
字样
.
实现一个
HTTP
模块是非常简单的
:
你必须实现之包含两个函数
(Init()
和
Dispose())
的
IHttpModule
接口
.
传进来的事件参数中包含指向
HTTPApplication
对象的引用
,
这给了你访问
HttpContext
对象的能力
.
在这些方法上你可以挂接到
HttpApplication
事件上
.
例如
,
如果你想挂接
AuthenticateRequest
事件到一个模块上
,
你只需像列表
5
中展示的那样做
列表5:基础的HTTP模块是非常容易实现的
public
class BasicAuthCustomModule : IHttpModule
{
public
void Init(HttpApplication application)
{
// *** Hook up any HttpApplication events
application.AuthenticateRequest +=
new EventHandler(
this.OnAuthenticateRequest);
}
public
void Dispose() { }
public
void OnAuthenticateRequest(
object source, EventArgs eventArgs)
{
HttpApplication app = (HttpApplication) source;
HttpContext Context = HttpContext.Current;
…
do what you have to
do… }
}
记住你的模块访问了
HttpContext
对象
,
从这里可以访问到其他
ASP.NET
管道中固有的对象
,
如请求
(Request)
和响应
(Response),
这样你还可以接收用户输入的信息等等
.
但是记住有些东西可能是不能访问的
,
它们只有在处理链的后段才能被访问
.
你可以在
Init()
方法中挂接多个事件
,
这样你可以在一个模块中实现多个不同的功能
.
然而
,
将不同的逻辑分到单独的类中可能会更清楚的将模块进行模块化
(
译注
:
这里的模块化和前面的模块没有什么关系
)
在很多情况下你实现的功能可能需要你挂接多个事件
-
例如一个日志过滤器可能在
BeginRequest
事件中记录请求开始时间
,
然后在
EndRequest
事件中将请求结束写入到日志中
.
注意一个
HttoModule
和
HttpApplication
事件中的重点
:Response.End()
或
HttpApplication.CompleteRequest()
会在
HttpApplication
和
Module
的事件链中
”
抄近道
”.
看
”
注意
Response.End()”
来获得更多信息
.
注意
Response.End()
当创建
HttpModule
或者在
Global.asax
中实现事件钩子的时候
,
当你调用
Response.End
或
Appplication.CompleteRequest
的时候要特别注意
.
这两个函数都结束当前请求并停止触发在
HTTP
管道中后续的事件
,
然后发生将控制返回到
Web
服务器中
.
当你在处理链的后面有诸如记录日志或对内容进行操作的行为时
,
因为他们没有被触发
,
有可能使你上当
.
例如
,sample
中
logging
的例子就会失败
,
因为如果调用
Response.End()
的话
,EndRequest
事件并不会被触发
.
HttpHandlers
模块是相当底层的,而且对每个来到ASP.NET应用程序的请求都会被触发.Http处理器更加的专注并处理映射到这个处理器上的请求.
Http处理器需要实现的东西非常简单,但是通过访问HttpContext对象它可以变得非常强大.Http处理器通过实现一个非常简单的IHttpHandler接口(或是它的异步版本,IHttpAsyncHandler),这个接口甚至只含有一个方法-ProcessRequest()-和一个属性IsReusable.关键部分是ProcessRequest(),这个函数获取一个HttpContext对象的实例作为参数.这个函数负责从头到尾处理Web请求.
单独的,简单的函数?太简单了,对吧?好的,简单的接口,但并不弱小!记住WebForm和WebService都是作为Http处理器实现的,所以在这个看上去简单的接口中包装了很强大的能力.关键是这样一个事实,当一个请求来到Http处理器时,所有的ASP.NET的内部对象都被准备和设置好来处理请求了.主要的是HttpContext对象,提供所有相关的请求功能来接收输入并输出回Web服务器.
对一个HTTP处理其来说所有的动作都在这个单独的ProcessRequest()函数的调用中发生.这像下面所展示的这样简单:
public void ProcessRequest(HttpContext context)
{
context.Response.Write("Hello World");
}
也可以像一个可以从HTML模板渲染出复杂表单的WebForm页面引擎那么完整,复杂.通过这个简单,但是强大的接口要做什么,完全取决于你的决定.
因为Context对象对你是可用的,你可用访问Request,Response,Session和Cache对象,所以你拥有所有ASP.NET请求的关键特性,可以获得用户提交的内容并返回你产生的内容给客户端.记住HttpContext对象-它是你在整个ASP.NET请求的生命周期中的”朋友”.
处理器的关键操作应该是将输出写入Response对象或者更具体一点,是Response对象的OutputStream.这个输出是实际上被送回到客户端的.在幕后,ISAPIWorkerRequest管理着将输出流返回到ISAPI ecb的过程.WriteClient方法是实际产生IIS输出的方法.
图7-ASP.NET请求管道通过一系列事件接口来转发请求,提供了更大的灵活性.Application当请求到来并通过管道时作为一个载入Web应用并触发事件的宿主容器.每个请求都沿着配置的Http过滤器和模块的路径走(译注:原文为Http Filters And Modules,应该是指Http Module和Http Handler).过滤器可以检查每个通过管道的请求,Handler允许实现应用程序逻辑或者像Web Form和WebService这样的应用层接口.为了向应用提供输入输出,Context对象在这个处理过程中提供了特定于请求的的信息.
WebForm使用一系列在框架中非常高层的接口来实现一个Http处理器,但是实际上WebForm的Render()方法简单的以使用一个HtmlTextWriter对象将它的最终结果输出到context.Response.OutputStream告终.所以非常梦幻的,终究即使是向WebForm这样高级的工具也只是在Request和Response对象之上进行了抽象而已.
到了这里你可能会疑惑在Http handler中你到底需要处理什么.既然WebForm提供了简单可用的Http Handler实现,那么为什么需要考虑更底层的东西而放弃这扩展性呢?
WebForm对于产生复杂的HTML页面来说是非常强大的,业务层逻辑需要图形布局工具和基于模块的页面.但是WebForm引擎做了一系列overhead intensive的任务.如果你想要做的是从系统中读入一个文件并通过代码将其返回的话,不通过WebForm框架直接返回文件会更有效率.如果你要做的是类似从数据库中读出图片的工作,并不需要使用页面框架-你不需要模板而且确定不需要Web页面并从中捕捉用户事件.
没有理由需要建立一个页面对象和Session并捕捉页面级别的事件-所有这些需要执行对你的任务没有帮助的额外的代码.
所以自定义处理器更加有效率.处理器也可用来做WebForm做不到的事情,例如不需要在硬盘上有物理文件就可用处理请求的能力,也被称为虚拟Url.要做到这个,确认你在图1中展示的应用扩展对话框中关掉了”检查文件存在”选项.
这对于内容提供商来说非常常见,象动态图片处理,XML服务,URL重定向服务提供了vanity Urls,下载管理以及其他,这些都不需要WebForm引擎.
异步HTTP Handler
在这篇文章中我大部分都在讨论同步处理,但是ASP.NET运行时也可以通过异步HTTP handler来支持异步操作.这些处理器自动的将处理”卸载”到独立的线程池的线程中并释放主ASP.NET线程,使ASP.NET线程可以处理其他的请求.不幸的是在1.x版的.NET中,”卸载”后的处理还是在同一个线程池中,所以这个特性之增加了一点点的性能.为了创建真正的异步行为,你必须创建你自己的线程并在回调处理中自己管理他们.
当前版本的ASP.NET 2.0 Beta 2在IhttpHandlerAsync(译注:此处应该是指IHttpAsyncHandler,疑为作者笔误)接口和Page类两方面做了一些对异步处理的改进,提供了更好的性能,但是在最终发布版本中这些是否会保留.