??????? ASP.NET中一个有用的特性是HTTP管道的扩展性,所谓HTTP管道指的是从客户端到服务器端所经过的路径。这个月我将给大家展示一下HTTP Modules(模块)。我们可以用HTTP Modules扩展ASP.NET应用程序---也就是为我们的应用程序添加预处理和善后处理,然后由新的应用程序来处理每个到达的请求。举例来说,如果你想为你的应用程序添加自定义的认证,最有用的技术就是截取到达的客户端的请求,然后在自定义的HTTP module,下面我们就以HTTP管道开始我们的介绍。
HTTP管道
????? 要搞懂ASPNET中的HTTP module,你需要清楚HTTP管道如何工作。当一个HTTP请求通过80端口(HTTP常用端口,HTTPS和验证的sockets端口常用443端口)到达服务器,而在真正由你的应用程序处理之前,该请求会通过组成HTTP管道的一系列的过程。
????? Microsoft® Internet Information Services (IIS) 是一系列环节当中的第一环。虽然ASPNET有自己的对象模型、进程隔离机制、基于.NET Framework类的会话状态管理机制,IIS仍然被用来把请求传递到ASPNET运行时。IIS将ASPNET映射到ASPNET_ISAPI.DLL,它是一个由ASPNET提供的ISAPI扩展。ASPNET_ISAPI.DLL的功能是将请求传递到ASPNET工作者进程ASPNET_WP.EXE。这部分工作完成之后,该请求就被包装到HttpContext类的一个实例当中,接着通过管道经过许多ASPNET类。HttpContext 类包括很多成员,比如响应、请求,也包括你可能想知道的于请求有关的安全信息。
???????接下来该请求被传送到一个HttpApplication实例。这个步骤对于获得应用程序范围内的方法、数据和事件是很有用处的。由于这个类(HttpApplication)对于设置HTTPmoduls是很关键的,我将深入的来介绍一下HttpApplication。
?????? HttpApplication对象接受到请求之后,将它传递过一个或几个HttpModule对象。有许多系统级别的HTTP模块提供从状态管理认证到输出缓存的服务。截取请求的模块的数量是由机器级别的machine.config和对应应用同程序的web.config文件来设定。在ASP中提供预处理和善后处理比较经典的做法是在ISAPI过滤器中完成。这就表明,ASP HTTP模块编写时更直接更简单。
?????? 这一系列环节中最后一个环节是HttpHandler。如果你用过一段时间的ASPNET,你应该对System.Web.UI.Page 类比较熟悉。Page类是HttpHander的一个实例,它实现了IHttpHandler接口。实现IHttpHandler接口的类可以通过这个接口的ProcessRequest方法勾入(HOOK)HTTP管道和服务请求。
Figure 1 HTTP Request
每个请求到来时,如果发现请求的URI指向一个ASP.NET扩展名的文件,ASP.NET会在web.config文件当中寻找与这个扩展名相关的实现了IhttpHandler接口的类。如果你在你的web.config文件当中没有做任何改变,当浏览到一个ASPX文件时,ASP.NET就会根据System.Web.UI.Page创建一个处理程序。你可以通过设置web.config文件来将单独的HTTP处理程序映射到各自相应的URI。图1表示了一个HTTP请求通过HTTP管道时经过的过程。与HttpModule相关的类包括:BeginRequest, AuthenticateRequest, AuthorizeRequest等。ASP.NET HTTP Modules
一个HTTP module其实就是一个实现了System.Web.IhttpModule接口的类。
public interface IHttpModule
{
? void Dispose();
? void Init(HttpApplication context);
}
当一个HttpModule挂接到管道时(通过在web.config文件中的设定的入口),ASP.NET运行时调用此模块的Init和Dispose方法。当模块将自己附加到HttpApplication对象时调用Init方法,而当模块取消与HttpApplication 的联系时调用Dispose方法。Init和Dispose方法表示了模块挂接到HttpApplication所暴露的一系列事件。这些事件包括请求开始、请求结束、请求认证等等。注意一下传递给Init方法的HttpApplication类型的参数。一般来说,Init方法接管HttpApplication对象并将事件处理程序映射到相应的事件。
图2自定义的HttpModule
// This module, named HttpModules.CS will be compiled
//? into an assembly named HttpModules.dll
using System;
using System.Web;
namespace HttpModuleExamples {
?? public class CustomHttpModule : IHttpModule {
????? // IHttpModule members
????? public void Init(HttpApplication httpApp) {
???????? httpApp.BeginRequest +=???
?????????? new EventHandler(this.OnBeginRequest);???
???????? httpApp.EndRequest +=
?????????? new EventHandler(this.OnEndRequest);
????? }
????? public void Dispose() {
???????? // Usually, nothing has to happen here...
????? }
????? // event handlers??
????? public void OnBeginRequest(object o, EventArgs ea) {
???????? HttpApplication httpApp = (HttpApplication) o;??
???????? HttpContext ctx = HttpContext.Current;
???????? ctx.Response.Write("Beginning Request
");
????? }
????? public void OnEndRequest(object o, EventArgs ea) {
???????? HttpApplication httpApp = (HttpApplication) o;??
???????? HttpContext ctx = HttpContext.Current;
???????? ctx.Response.Write("Ending Request
");
????? }
?? }
}
图2给出了一些C#代码,这些代码定义了一个附加到一个应用程序的BeginRequest和EndRequest事件的HttpModule,它为每个请求提供简单的预处理(请求处理之前)和善后处理(请求处理之后)。这些代码被编译到一个程序集并部署到bin文件夹。要想把这个处理程序安装到应用程序处理的一系列环节当中,我们只需象下面的一样在web.config文件当中的httpModule部分声明一下。
下面的代码展示的是一个在处理环节中使用了HttpModule的普通的ASPX文件:
ASP.NET现在开始通过CustomHttpModule的请求开始的OnBeginRequest方法和请求结束时的OnEndRequest方法来跟踪所有的请求。当你浏览到这个页面时你会看到像图3中的一个简单的ASPX页面。
图3?一个简单的ASPX页面
在HttpModule中你并不是仅仅可以截获BeginRequest和EndRequest事件。图4给出了你可以跟踪的并且可以放到HttpModule处理的HttpApplication事件。捕获这些事件只不过需要为你想处理的事件建立一个事件处理程序。图5给出了在HttpModule中截获AuthenticateRequest事件的C#代码。
图4
Event | Occurs |
AcquireRequestState | When ASP.NET acquires the current state (for example, session state) associated with the current request |
AuthenticateRequest | When a security module has established the identity of the user |
AuthorizeRequest | When a security module has verified user authorization |
BeginRequest | When the first event in the HTTP pipeline chain of execution responds to a request |
Disposed | When ASP.NET completes the chain of execution when responding to a request |
EndRequest | When the last event in the HTTP pipeline chain of execution responds to a request |
Error | When an unhandled exception is thrown |
PostRequestHandlerExecute | When the ASP.NET handler (page, XML Web Service) finishes execution |
PreRequestHandlerExecute | Just before ASP.NET begins executing a handler such as a page or XML Web Service |
PreSendRequestContent | Just before ASP.NET sends content to the client |
PreSendRequestHeaders | Just before ASP.NET sends HTTP headers to the client |
ReleaseRequestState | After ASP.NET finishes executing all request handlers; also causes state modules to save the current state data |
ResolveRequestCache | When ASP.NET completes an authorization event to let the caching modules serve requests from the cache, bypassing execution of the handler (the page or XML Web Service, for example) |
UpdateRequestCache | When ASP.NET finishes executing a handler in order to let caching modules store responses that will be used to serve subsequent requests from the cache |
?
图5 截获AuthenticateRequest事件
using System; using System.Web; namespace HttpModuleExamples { public class CustomAuthentication : IHttpModule { // IHttpModule members public void Init(HttpApplication httpApp) { httpApp.BeginRequest += new EventHandler(this.OnAuthenticateRequest); } public void Dispose() { // Usually, nothing has to happen here... } // event handlers public void OnAuthenticateRequest(object o, EventArgs ea) { // Do any custom authentication here—perhaps manage // custom credentials. } } }
提前结束请求
截获HTTP请求最经常的原因是当出现错误时提前结束请求。例如:如果您自己来处理认证,当结果是错误,没有通过认证时,你需要停止此请求。如果你编写了一个SOAP服务器,当一个非SOAP请求到达时你可能需要终端这个请求。HttpApplication类中有一个叫做CompleteRequest的方法,它用来完成请求。你可以调用CompleteRequest并且设置context(上下文)对象的StatusCode和StatusDescription属性,来通知客户端。图6给出了截获一个请求并且当请求是不安全的时就结束它。
图6?提前结束一个Http请求
public class TestSecureConnection : IHttpModule { // IHttpMoule members public string ModuleName { ... } public void Init(HttpApplication httpApp) { httpApp.BeginRequest += new EventHandler(this.OnBeginRequest); } public void Dispose() { ... } public void OnBeginRequest(object o, EventArgs ea) { HttpApplication httpApp = (HttpApplication) o; HttpContext ctx = (HttpContext) ea.ExtendedInfo; if(!ctx.Request.IsSecureConnection) { httpApp.CompleteRequest(); ctx.Response.StatusCode = 403; ctx.Response.StatusDescription = "Use SSL, please."; } } }
图7 系统提供的HttpModules
Class | Description |
DefaultAuthenticationModule | Insures the presence of an Authentication object in the context |
FileAuthorizationModule | Verifies that the remote user has permissions in Windows NT to access the file requested |
FormsAuthenticationModule | Turns on ASP.NET forms authentication |
PassportAuthenticationModule | Provides a wrapper around Passport authentication services |
SessionStateModule | Provides session state services for an application |
UrlAuthorizationModule | Provides URL-based authorization services for allowing or denying access to specified resources |
WindowsAuthenticationModule | Turns on Windows/IIS authentication for the application |
系统提供的模块
很多ASP.NET的性质是通过使用HttpModule技术来增加的,比如说:包括输出缓存、会话状态、Windows验证、窗体验证、Passport验证、URL验证和文件验证。图7给出了这些性质以及实现这些性质的模块。
每个预定义的HttpModule都是在machine.config文件中。图8给出了这些设置。
图?8 预定义的HttpModules注册
附到一个应用程序上的一系列的HttpModule表示成对IhttpModule引用的集合,叫做HttpModuleCollection,这些集合可以作为HttpApplication类的Modules属性来访问。在运行时,集合里面包括所有的machine.config中的系统提供的模块和web.config中定义的模块。HttpModuleCollection是一系列对IhttpModule的引用,这些模块或者以它们的名字作为关键字或者ordinal作为关键字,正如图9表示的。图10给出了Web页面当中列出来的模块。
当应用程序启动时,一般来说所有的HttpModule都由ASP.NET控制。如果你想亲自管理这些模块,你可以使用HttpModuleCollection来获得一个对任意模块的引用并调用Init或者Dispose方法。或者你可以手工的装载一个HttpModule程序集而且为你的应用程序增加基于特定的自定义的规则的模块。
图9 判断使用的是哪个模块
public void ShowModules(Object o, EventArgs E) {
? HttpApplication httpApp =
?? HttpContext.Current.ApplicationInstance;
? HttpModuleCollection httpModuleColl =
?? httpApp.Modules;
? Response.Write("
");
? String[] rgstrModuleNames;
? rgstrModuleNames = httpModuleColl.AllKeys;
? foreach(String strModuleName in rgstrModuleNames) {
??? Response.Write(strModuleName);
??? Response.Write("
");
? }
? Response.Write("
");
}
<%@ Page Language="C#"
??? src="UseHttpModules.cs"
??? Inherits="UseHttpModulesPage"
??? trace='true'%>
?
图10附加的HttpModules
?
HttpModules and Global.ASAX
ASP.NET应用程序可能包含一个名为GLOBAL.ASAX的文件(也叫做ASP.NET应用程序文件)。GLOBAL.ASAX位于你的ASP.NET应用程序的根目录下面。当应用程序在运行时被加载,ASP.NET提取GLOBAL.ASAX文件并产生一个从HttpApplication派生的运行时对象。GLOBAL.ASAX是可选的,但是当它存在的话,它包含由ASP.NET和HTTP模块产生的与应用程序级别的对应的代码。
?