介绍
在Internet时代的开端客户端的需求非常有限文件就可以满足他们的需求但是随着时间的流逝客户端需求的扩充超越文件或静态文件所包含的功能
开发者需要扩充或扩展Web服务器的功能Web服务器厂商设计了不同的解决方案但是都遵循同一个主题向Web服务器插入某些组件所有的Web服务器补充技术都允许开发者建立并插入组件以增强Web服务器的功能微软公司提出了ISAPI(Internet服务器API)网景公司提出了NSAPI(网景服务器API)等等
ISAPI是一种重要的技术它允许我们增强与ISAPI兼容的Web服务器(IIS就是一种与ISAPI兼容的Web服务器)的能力我们使用下面的组件达到这个目的
· ISAPI扩展
· ISAPI过滤器
ISAPI扩展是使用Win动态链接库来实现的你可以把ISAPI扩展看作是一个普通的应用程序ISAPI扩展的处理目标是http请求这意味着你必须调用它们才能激活它们 你可以认为ISAPI过滤器仅仅就是一个过滤器而已客户端每次向服务器发出请求的时候请求要经过过滤器客户端不需要在请求中指定过滤器只需要简单地把请求发送给Web服务器接着Web服务器把请求传递给相关的过滤器接下来过滤器可能修改请求执行某些登录操作等等
由于这些组件的复杂性实现它们非常困难开发者不得不使用C/C++来开发这些组件但是对于很多人来说使用C/C++进行开发简直就是痛苦的代名词
那么ASPNET提供什么东西来实现这些功能呢?ASPNET提供的是HttpHandler(HTTP处理程序)和HttpModule(HTTP模块)
在深入了解这些组件的详细信息之前了解一下http请求经过HTTP模块和HTTP处理程序的时候的处理流程是有价值的
建立示例应用程序
我建立了下面一些的C#项目以演示应用程序的不同组件
· NewHandler (HTTP处理程序)
· Webapp (演示HTTP处理程序)
· SecurityModules (HTTP模块)
· Webapp (演示HTTP模块)
这些应用程序的安装步骤
· 解开attached zip文件中的所以代码
· 建立两个虚拟目录webapp和webapp把这两个目录指向Webapp和Webapp应用程序的实际物理目录
· 把NewHandler项目中的Newhandlerdll文件复制到webapp应用程序的bin目录
· 把SecurityModules项目中的SecurityModulesdll文件复制到webapp应用程序的bin目录中
ASPNET请求的处理过程
ASPNET请求处理过程是基于管道模型的在模型中ASPNET把http请求传递给管道中的所有模块每个模块都接收http请求并有完全控制权限模块可以用任何自认为适合的方式来处理请求一旦请求经过了所有HTTP模块就最终被HTTP处理程序处理HTTP处理程序对请求进行一些处理并且结果将再次经过管道中的HTTP模块
请注意在http请求的处理过程中只能调用一个HTTP处理程序然而可以调用多个HTTP模块
Http处理程序
HTTP处理程序是实现了SystemWebIHttpHandler接口的NET组件任何实现了IHttpHandler接口的类都可以用于处理输入的HTTP请求HTTP处理程序与ISAPI扩展有些类似HTTP处理程序和ISAPI扩展的差别在于在URL中可以使用HTTP处理程序的文件名称直接调用它们与ISAPI扩展类似
HTTP处理程序实现了下列方法
方法名称描述ProcessRequest 这个方法实际上是http处理程序的核心我们调用这个方法来处理http请求IsReusable 我们调用这个属性来决定http处理程序的实例是否可以用于处理相同其它类型的请求HTTP处理程序可以返回true或false来表明它们是否可以重复使用
你可以使用nfig或者nfig文件把这些类映射到http请求上映射完成以后当接收到相应请求的时候ASPNET会实例化http处理程序我们将解释如何在nfig和/或nfig文件中定义所有这些细节信息
ASPNET还通过IHttpHandlerFactory接口支持http处理程序的扩展ASPNET提供了把http请求路由到实现IHttpHandlerFactory接口的类的对象上的能力此外ASPNET还利用了Factory设计模式这种模式为建立一组相关对象而不提供具体类的功能提供了接口简单的说你可以把用于建立依赖传递进来的参数建立的http处理程序对象的类看作是factory(工厂)我们不用指定需要实例化的特定的http处理程序http处理程序工厂处理这种事务这样做的优点在于如果未来实现IHttpHandler接口的对象的实现方法发生了改变只要接口仍然相同客户端就不会受到影响
下面是IHttpHandlerFactory接口中的方法列表方法名称描述GetHandler 这个方法负责建立适当的处理程序并把它的指针返回到调用代码(ASPNET运行时)这个方法返回的处理程序对象应该实现了IHttpHandler接口ReleaseHandler 这个方法负责在请求处理完成后释放http处理程序Factory 实现决定了它的操作Factory 实现可以是实际摧毁实例也可以把它放入缓沖池供以后使用
在配置文件中注册HTTP处理程序和HTTP处理程序工厂
ASPNET在下面的配置文件中维护自己的配置信息
· nfig
· nfig
nfig文件包含应用于计算机上安装的所有Web应用程序的配置设置信息
nfig文件对于每个Web应用程序来说是特定的每个Web应用程序都有自己的nfig文件Web应用程序的任何子目录也可能包含自己的nfig文件这使得它们能够覆盖父目录的设置信息
为了给我们的Web应用程序添加HTTP处理程序你可以使用<httpHandlers>和<add>节点实际上处理程序都带有<add>节点列举在<httpHandlers>和</httpHandlers>节点之间下面是添加HTTP处理程序的一个普通的例子
<httpHandlers>
<add verb=supported http verbs path=path type=namespaceclassname assemblyname />
<httpHandlers>
在上面的XML中
· Verb属性指定了处理程序支持的HTTP动作如果某个处理程序支持所有的HTTP动作请使用*否则使用逗号分隔的列表列出支持的动作因此如果你的处理程序只支持HTTP GET和POST那么verb属性就应该是GET POST
· Path属性指定了需要调用处理程序的路径和文件名(可以包含通配符)例如如果你希望自己的处理程序只有在testxyz文件被请求的时候才被调用那么path属性就包含testxyz如果你希望含有xyz后缀的所有文件都调用处理程序path属性应该包含*xyz
· Type属性用名字空间类名称和部件名称的组合形式指定处理程序或处理程序工厂的实际类型ASPNET运行时首先搜索应用程序的bin目录中的部件DLL接着在全局部件缓沖(GAC)中搜索
ASPNET运行时对HTTP处理程序的使用方式
无论你是否相信ASPNET都使用HTTP请求实现了大量的自己的功能ASPNET使用处理程序来处理aspx asmx soap和其它ASPNET文件
下面是nfig文件中的一个片段
<httpHandlers>
<add verb=* path=traceaxd type=SystemWebHandlersTraceHandler/>
<add verb=* path=*aspx type=SystemWebUIPageHandlerFactory/>
<add verb=* path=*ashx type=SystemWebUISimpleHandlerFactory/>
<add verb=* path=nfig type=SystemWebHttpForbiddenHandler/>
<add verb=GETHEAD path=* type=SystemWebStaticFileHandler/>
</httpHandlers>
在上面的配置信息中你可以看到对aspx文件的所有请求都由SystemWebUIPageHandlerFactory类来处理与此类似nfig文件和其它文件(它们不能被客户端直接访问)的所有请求都由SystemWebHttpForbiddenHandler类处理你可能已经猜到当访问这些文件的时候该类简单地给客户端返回一个错误信息
执行HTTP处理程序
现在你将看到如何实现一个HTTP处理程序那么我们的新处理程序要做什么任务呢?前面我提到处理程序大多数用于给Web服务器添加新功能因此我将建立一个处理程序来处理新的文件类型——扩展名为seconds的文件我们建立了这个处理程序并在我们的Web应用程序的nfig文件中注册之后所有对seconds文件的请求都将由这个新处理程序来处理
你可能正在考虑这个处理程序的使用方法如果你希望引入一种新的服务器脚本语言或动态服务器文件(例如aspaspx)该怎么办呢?你可以为它编写一个自己的处理程序类似地如果你希望在IIS上运行Java小程序JSP和其它一些服务器端Java组件应该怎么办呢?一种方法是安装某些ISAPI扩展(例如Allaire或Macromedia Jrun)你也可以编写自己的HTTP处理程序尽管这对于第三方厂商(例如Allaire和Macromedia)来说是很复杂的事务但是它却是个很有吸引力的选择因为它们的HTTP处理能够能够访问ASPNET运行时暴露的所有新功能
实现我们的HTTP处理程序包含以下步骤
编写一个实现IHttpHandler接口的类
在nfig或nfig文件中注册这个处理程序
在Internet服务管理器中把文件扩展(seconds)映射到ASPNET ISAPI扩展DLL(aspnet_isapidll)上
第一步
在Visual StudioNET中建立一个新的C#类库项目并把它命名为MyHandlerVisual StudioNET将自动地给项目添加一个叫做Classcs的类把它改名为NewHandler在代码窗口中打开这个类并把类的名称和构造函数的名称改成NewHandler
下面是NewHandler类的代码
using System;
using SystemWeb;
namespace MyHandler
{
public class NewHandler : IHttpHandler
{
public NewHandler()
{
// TODO: 此处添加构造逻辑
}
#region Implementation of IHttpHandler
public void ProcessRequest(SystemWebHttpContext context)
{
HttpResponse objResponse = contextResponse ;
objResponseWrite(<html><body><h>Hello Seconds Reader ) ;
objResponseWrite(</body></html>) ;
}
public bool IsReusable
{
get
{
return true;
}
}
#endregion
}
}
你在ProcessRequest方法中可以看到该HTTP处理程序通过SystemWebHttpContext对象访问了所有作为参数传递给它的ASPNET内部对象实现ProcessRequest方法只需要简单地从context对象中提取HttpResponse对象并把发送一些HTML给客户端类似地IsReusable返回true表明这个处理程序可以被重复用作处理其它的HTTP请求
我们编译上面的代码并把它放到webapp虚拟目录的bin目录之中
第二步
在nfig文件中通过添加下面的文本来注册这个处理程序
<httpHandlers>
<add verb=* path=*seconds type=MyHandlerNewHandlerMyHandler/>
</httpHandlers>
第三步
由于我们已经建立了用于处理新扩展文件的处理程序了我们还需要把这个扩展名告诉IIS并把它映射到ASPNET如果你不执行这个步骤而试图访问Helloseconds文件IIS将简单地返回该文件而不是把它传递给ASPNET运行时其结果是该HTTP处理程序不会被调用
运行Internet服务管理器右键点击默认Web站点选择属性移动到Home目录选项页并点击配置按钮应用程序配置对话框弹出来了点击添加按钮并在可执行字段输入aspnet_isapidll文件路径在扩展字段输入seconds其它字段不用处理该对话框如下所示
点击确认按钮关闭应用程序配置和默认Web站点属性对话框
现在我们运行Internet Explorer并输入url看到的页面如下
HTTP处理程序中的对话状态
维护对话状态是Web应用程序执行的最通常的事务HTTP处理程序也需要访问这些对话状态但是HTTP处理程序的默认设置是没有激活对话状态的为了读取和/或写入状态数据需要HTTP处理程序实现下面的接口之一
· IRequiresSessionState
· IReadOnlySessionState
当HTTP处理程序需要读写对话数据的时候它必须实现IRequiresSessionState接口如果它只读取对话数据实现IReadOnlySessionState接口就可以了
这两个接口都是标记接口并没有包含任何方法因此如果你希望激活NewHandler处理程序的对话状态要像下面的代码一样声明NewHandler类
public class NewHandler : IHttpHandler IRequiresSessionState
HTTP模块
HTTP模块是实现了SystemWebIhttpModule接口的NET组件这些组件通过在某些事件中注册自身把自己插入ASPNET请求处理管道当这些事件发生的时候ASPNET调用对请求有兴趣的HTTP模块这样该模块就能处理请求了
HTTP模块实现了IhttpModule接口的下面一些方法
方法名称描述Init 这个方法允许HTTP模块向HttpApplication 对象中的事件注册自己的事件处理程序
Dispose 这个方法给予HTTP模块在对象被垃圾收集之前执行清理的机会
HTTP模块可以向SystemWebHttpApplication对象暴露的下面一些方法注册
事件名称描述AcquireRequestState 当ASPNET运行时准备好接收当前HTTP请求的对话状态的时候引发这个事件
AuthenticateRequest 当ASPNET 运行时准备验证用户身份的时候引发这个事件
AuthorizeRequest 当ASPNET运行时准备授权用户访问资源的时候引发这个事件
BeginRequest 当ASPNET运行时接收到新的HTTP请求的时候引发这个事件
Disposed 当ASPNET完成HTTP请求的处理过程时引发这个事件
EndRequest 把响应内容发送到客户端之前引发这个事件
Error 在处理HTTP请求的过程中出现未处理异常的时候引发这个事件
PostRequestHandlerExecute 在HTTP处理程序结束执行的时候引发这个事件
PreRequestHandlerExecute 在ASPNET开始执行HTTP请求的处理程序之前引发这个事件在这个事件之后ASPNET 把该请求转发给适当的HTTP处理程序
PreSendRequestContent 在ASPNET把响应内容发送到客户端之前引发这个事件这个事件允许我们在内容到达客户端之前改变响应内容我们可以使用这个事件给页面输出添加用于所有页面的内容例如通用菜单头信息或脚信息
PreSendRequestHeaders 在ASPNET把HTTP响应头信息发送给客户端之前引发这个事件在头信息到达客户端之前这个事件允许我们改变它的内容我们可以使用这个事件在头信息中添加cookie和自定义数据
ReleaseRequestState 当ASPNET结束所搜有的请求处理程序执行的时候引发这个事件
ResolveRequestCache 我们引发这个事件来决定是否可以使用从输出缓沖返回的内容来结束请求这依赖于Web应用程序的输出缓沖时怎样设置的
UpdateRequestCache 当ASPNET完成了当前的HTTP请求的处理并且输出内容已经准备好添加给输出缓沖的时候引发这个事件这依赖于Web应用程序的输出缓沖是如何设置的
除了这些事件之外我们还可以使用四个事件我们可以通过实现Web应用程序的globalasax文件中一些方法来使用这些事件
这些事件是
· Application_OnStart
当第一个请求到达Web应用程序的时候引发这个事件
· Application_OnEnd
准备终止应用程序之前引发这个事件
· Session_OnStart
用户对话的第一个请求引发这个事件
· Session_OnEnd
放弃对话或者对话超期的时候引发这个事件