|
 图6:事件在ASP.NET HTTP管道里传输。HttpApplication对象的事件驱动请求在管道里传输。HTTPModules可以截获这些事件,可以覆盖或增强已存在的功能。
HttpContext,HttpModules和HttpHandlers
HttpApplication本身并不知晓发送给Web程序的数据。它仅仅是个消息邮递者,只负责事件之间的通信。它触发事件,然后通过传递HttpContext对象,把信息发送给被调用的方法。在之前我们提到,当前请求的数据是在HttpContext对象里保存。它提供了请求从开始到结束需要的所有数据。图7展示了ASP.NET的管道之间的传输流程。注意,从请求的开始至结束,上下文对象(Context)都是你的伙伴,你可以在一个事件方法里使用它保存数据,然后在之后的事件方法里获取这些数据。
 图7:ASP.NET管道在一系列事件接口之间传输请求,这提供了足够的灵活性。HttpApplication担当主容器,负责加载Web程序,当请求到来时触发事件以及在管道之间传输请求。经过已配置的HTTP过滤和模块时,每一个请求都将遵循一个公有的路径。过滤器可以检查穿梭在管道里的每一个请求,而处理器允许实现应用程序的逻辑或者应用程序级的接口像WebForms和WebServices一样。为了给程序提供输入和输出,上下文对象(Context)给请求提供了所需的信息,它贯穿了请求生命周期的始终。
ASP.NET管道一旦启动,HttpApplication将逐一触发事件,如图6展示的那样。每一个事件都将被触发,如果事件绑定了事件处理器,那么这些事件处理器将被调用,执行它们的任务。这个过程的主要目的是通过调用HttpHandler处理指定的请求。对于ASP.NET请求而言,HttpHandler是处理请求机制的核心,在这里任意的应用程序级的代码被执行。记住,ASP.NET的页面和Web Service都是HttpHandler的具体实现,在这里,所有请求处理的核心功能被实现。HTTPModule则倾向于在分发给事件处理器之前或者之后对内容进行处理。在ASP.NET里典型的默认操作有:鉴定(Authentication),处理前的缓存操作以及各种处理后的编码操作机制。
关于HTTPModule和HttpHandler,这里有很多有用的信息,但为了保持这篇文章合理的尺度,我将仅仅讲述关于它们一些简短的、整体的看法。
HttpModules
伴随着HttpApplication触发的一系列事件,请求将会在管道之间穿梭。你已经看到了这些发布的事件,在Global.asax里都有对应事件的处理方法。这个方法(步骤)是程序(ASP.NET应用程序)携带的,尽管这个并不总是你需要的。如果你想构建一套通用的HttpApplication事件处理程序,并以插件的形式添加到任意的Web程序里。那么你可以使用HTTPModule,它是可重复使用的,不需要添加任何实现代码就可以在其它程序里使用,而你所做的仅仅在web.config里注册。
模块本质上是过滤器,在功能上类似于ASP.NET请求级别的ISAPI过滤。对于每一个穿过ASP.NET的HttpApplication对象的请求,模块都允许在HttpApplication对象触发的事件处理方法里截获这些请求。这些模块以类的形式存储在外部程序集里,可以在web.config里配置,当程序启动的时候加载。通过实现指定的接口和方法,模块就可以被添加到HttpApplication的事件链上。多个HttpModules可以钩住相同的事件,事件发生的顺序是它们在web.config里声明(配置)的顺序。如下就是在web.config里,一个模块的声明。
<configuration> <system.web> <httpModules> <add name= BasicAuthModule type=HttpHandlers.BasicAuth,WebStore /> </httpModules> </system.web> </configuration>
注意,在这里你需要指定一个完整的类型名和一个不带扩展名的程序集的名字。
模块允许你查看每一个传入的Web请求,基于触发的事件基础上执行操作。模块是非常有用的,它可以修改请求,输出响应的内容以及提供自定义的身份验证,另外还可以在特定的程序里,针对ASP.NET的每一个请求提供响应前处理和响应后处理。许多ASP.NET的特征像身份验证,会话引擎都是作为HTTP模块实现的。
HttpModules感觉有点类似于ISAPI过滤器,是由于它们查看进入ASP.NET程序的每一个请求,但它们局限性于仅可以查看映射到某一个ASP.NET程序或者虚拟目录的请求,和映射到ASP.NET的请求。因此,你仅可以查看所有的ASPX页面或者任意其它自定义的已经映射到这个程序的扩展名(译注:作者可能漏掉了ASMX,ASHX等,这里的意思应该是所有的ASP.NET默认的扩展名)。但是,你不能查看标准的.HTM或者图像文件,除非你通过添加这些扩展名,明确的把它们映射到ASP.NET的ISAPI DLL,如图一中那样。对于模块,一个常见的用处是过滤一个指定的文件夹的JPG图片内容,然后使用GDI+在每一张返回的图片上方添加“样图”字样。
实现一个HTTP模块是非常简单的:你必须实现一个IHttpModule接口,它包含两个方法:Init()和Dispose()。传递的事件参数中包含着一个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管道对象如Response和Request,因此你可以获取输入的数据等等。但紧记,某些对象现在可能不能使用,只有到这条链的后面的环节才会有效,
在Init()方法里,你可以钩住多个事件,因此在一个模块里,可以管理多个不同功能的操作。但是,应该尽可能的把不同的逻辑代码放到不同的类里,这样可以确保这个模块是标准的组件。在许多情况下,你实现的功能可能需要钩住多个事件。举个例子,一个日志过滤器可能需要在BeginRequest里记录请求的开始时间,在EndRequest里记录请求完成的时间。
在HttpModules里使用HttpApplication的事件时,有一点是需要注意的,Response.End()和HttpApplication.CompleteRequest()方法会使ASP.NET跳过HttpApplication和模块的事件链。可以查看这两个方法的帮助文档获取更多的信息。
HttpHandlers
模块是相当低层次的,它针对每一个传入ASP.NET程序的请求触发。HTTP处理器则着重于处理一个指定的请求映射。通常一个页面扩展已经被映射到处理器了。
实现一个HTTP处理器所需要做的是非常基础的,但是通过访问HttpContext对象,就会有很多有用的功能。HTTP处理器通过一个简单IHttpHandler接口实现(或者它的异步版本IHttpAsyncHandler)。它仅仅有一个方法ProcessRequest()和一个属性IsReusable。这里的关键是ProcessRequest()会得到一个HttpContext对象的实例。这个单独的方法将从开始到结束负责处理一个Web请求。
单一的,简单的方法?可能太简单了,是吗?可是,一个简单的接口,但它的实现可能并不简单。记住,WebForms和WebServices都是作为HTTP处理器实现的。因此,在这个看似简单的接口里,过多的实现过程被隐藏了。关键是,事实上到现在,一个HTTP处理器可以访问所有为了开始处理请求而被组建和配置起来的ASP.NET的内置对象。关键是,HttpContext对象提供了所有与请求相关的功能,可以获取流入的数据和输出数据到Web服务器。
对于HTTP处理器而言,所有的操作都通过简单地调用ProcessRequest()方法执行。可以简单到如下的样子:
public void ProcessRequest(HttpContext context) { context.Response.Write(Hello World); }
一个完整的实现像WebForms页面引擎那样,可以根据HTML模版展现复杂的表单。所以,这里的关键点是你想用这个简单但功能强大的接口做什么。
因为对你而言,HttpContext对象是可以使用的,这样你就可以访问Request,Response,Session和Cache对象,因此你已经拥有了ASP.NET请求的所有特征,可以自己做主如何处理用户提交的信息,然后给客户端返回处理后产生的内容。记住,在一个ASP.NET请求的生命期内,上下文对象始终都是你的朋友。
处理器的关键操作通常是往Response对象里写输出数据,或者更确切的说,是往Response对象的OutputStream里写。这个就是真正返回到客户端的输出数据。在底层,由ISAPIWorkerRequest负责把OutputStream发回给ISAPI的ecb.WriteClient方法,因为ecb.WriteClient方法才是真正执行IIS产生输出数据的。
通过使用大量的处于高层的、基础的框架接口,WebForms实现了一个HTTP处理器。但最后,WebForm的Render()方法却简单的使用了一个HtmlTextWriter对象,把最终的输出发送给context.Response.OutputStream对象而结束。因此,相当的奇妙,最终甚至一个高层次的工具像WebForm,也仅仅是在Response和Request对象之上的一个高层次的抽象。
在这个时候,你可能想知道,是否需要从头实现一个完整的HTTP处理器?毕竟WebForms已经提供了一个易用的HTTP处理器的实现,因此为什么还要为这些大量底层的东西而烦恼,放弃这些已经提供的灵活性呢?
WebForms是非常棒的,使用它可以生成复杂的HTML页面和处理业务层的逻辑而这些都需要图形化的设计和模版化的页面。除此以外,WebForms引擎还可以完成更多的,需要丰富的表现层的任务。如果所有你想做的是:从系统读取文件,由执行的代码把它返回。这样的任务,如果避开使用Web Forms页面框架,代替的是直接处理文件然后返回,相信后者才是更高效的方法。如果你做的事情仅仅是像从数据库里读取图片一样,那么完全没有必要使用页面框架来处理,因为这里的确没有Web UI需要你俘获离开图片的事件。
在这里找不到,组建一个页面对象和会话对象以及俘获页面层上事件的理由。对于你的任务而言,这里需要做的仅仅是执行代码,而这些与你手头上的任务毫不相干。
因此,这种情况下使用处理器会更加高效。处理器可以完成使用WebForms不可能完成的事情。比如:需要处理这样的请求,它们没有必要在磁盘上存在对应的物理文件,这些被请求的路径通常称为虚拟的URL。为了使这样的请求正常的工作,你需要确保已经在应用程序的扩 上一页 [1] [2] [3] [4] [5] [6] 下一页
从底层了解ASP.NET体系结构_asp.net教程_www.it958.cn |