介绍
IIS 7.0 及更高版本允许通过两种方式开发的模块扩展服务器:
- 使用托管代码和 ASP.NET 服务器扩展性 API
- 使用本机代码和 IIS 本机服务器扩展性 API
与以前版本的 IIS 不同,大多数服务器扩展性方案不需要本机(C++)代码开发,并且可以使用托管代码和 ASP.NET API 来适应。 使用 ASP.NET 扩展服务器,可以显著缩短开发时间,并利用 ASP.NET 和 .NET Framework 的丰富功能。 若要详细了解如何使用 ASP.NET 扩展 IIS,请参阅 使用 .NET 开发 IIS 模块。
IIS 还提供一个 (C++) 本机核心服务器 API,它取代了以前的 IIS 版本中的 ISAPI 筛选器和扩展 API。 如果你有需要本机代码开发的特定要求,或者想要转换现有的本机 ISAPI 组件,请利用此 API 生成服务器组件。 新的本机服务器 API 使用直观的对象模型实现面向对象的开发,可更好地控制请求处理,并使用更简单的设计模式来帮助编写可靠的代码。
本演练将探讨以下任务:
- 使用原生 C++ 服务器 API 开发原生模块
- 在服务器上部署本机模块
若要编译模块,必须安装包含 IIS 头文件的平台 SDK。 此处提供了最新的 Windows Vista 平台 SDK。
若要将平台 SDK 与 Visual Studio 2005 配合使用,必须注册 SDK。 安装 SDK 后,通过 “开始 > 程序 > ”执行此操作Microsoft Windows SDK > Visual Studio 注册 > 将 Windows SDK 目录注册到 Visual Studio。
此模块的源代码在 Visual Studio IIS7 原生模块示例中提供。
开发原生模块
在此任务中,我们将使用新的本地(C++)服务器 API 来研究本地模块的开发。 原生模块是包含以下项的 Windows DLL:
- RegisterModule 导出的函数。 此函数负责创建模块工厂,并为一个或多个服务器事件注册模块。
- 从 CHttpModule 基类继承的模块类的实现。 此类提供模块的主要功能。
- 实现 IHttpModuleFactory 接口的模块工厂类。 该类负责创建模块的实例。
注释
在某些情况下,还可以实现 IGlobalModule 接口,以便扩展一些与请求处理无关的服务器功能。 这是一个高级主题,本次演练不包括对此的介绍。
本机模块具有以下生命周期:
服务器工作进程启动时,它将加载包含模块的 DLL,并调用其导出的 RegisterModule 函数。 在此函数中,你将:
a。 创建模块工厂。
b. 为模块实现的请求管道事件注册模块工厂。请求到达时,服务器:
a。 创建一个你的模块类的实例,使用你提供的工厂。
b. 针对注册的每个请求事件对模块实例调用相应的事件处理程序方法。
选项c. 在请求处理结束时释放模块的实例。
现在,生成它。
该模块的完整源代码在 Visual Studio IIS7 本机模块示例中提供。 以下步骤是开发模块最重要的步骤,不包括支持代码和错误处理。
实现服务器加载模块 DLL 时调用的 RegisterModule 函数。 其签名和本机 API 的其余部分在 httpserv.h 头文件中定义,该文件是平台 SDK 的一部分(如果没有平台 SDK,请参阅有关获取它的 简介 ):
main.cpp:
HRESULT
__stdcall
RegisterModule(
DWORD dwServerVersion,
IHttpModuleRegistrationInfo * pModuleInfo,
IHttpServer * pHttpServer
)
{
// step 1: save the IHttpServer and the module context id for future use
g_pModuleContext = pModuleInfo->GetId();
g_pHttpServer = pHttpServer;
// step 2: create the module factory
pFactory = new CMyHttpModuleFactory();
// step 3: register for server events
hr = pModuleInfo->SetRequestNotifications( pFactory,
RQ_ACQUIRE_REQUEST_STATE,
0 );
}
The RegisterModule
在 RegisterModule 中需要完成三项基本任务:
保存全局状态
我们将存储全局服务器实例,模块上下文 ID 供以后用于全局变量。 虽然此示例不使用此信息,但许多模块发现在请求处理期间保存和使用会很有用。 IHttpServer 接口提供对许多服务器函数(例如打开文件)和访问缓存的访问权限。 模块上下文 ID 用于将自定义模块状态与多个服务器对象(例如请求和应用程序)相关联。
创建模块工厂
在本演练的后面部分,我们将实现工厂类 CMyHttpModuleFactory。 此实例化类负责为每个请求创建我们的模块实例。
为所需请求处理事件注册模块工厂
注册是通过 SetRequestNotificatons 方法完成的,该方法指示服务器:使用指定的工厂为每个请求创建模块实例;并且,为每个指定的请求处理阶段调用相应的事件处理程序。
在这种情况下,我们只对RQ_ACQUIRE_REQUEST_STATE阶段感兴趣。 构成请求处理管道的阶段的完整列表在 httpserv.h 中定义:
#define RQ_BEGIN_REQUEST 0x00000001 // request is beginning
#define RQ_AUTHENTICATE_REQUEST 0x00000002 // request is being authenticated
#define RQ_AUTHORIZE_REQUEST 0x00000004 // request is being authorized
#define RQ_RESOLVE_REQUEST_CACHE 0x00000008 // satisfy request from cache
#define RQ_MAP_REQUEST_HANDLER 0x00000010 // map handler for request
#define RQ_ACQUIRE_REQUEST_STATE 0x00000020 // acquire request state
#define RQ_PRE_EXECUTE_REQUEST_HANDLER 0x00000040 // pre-execute handler
#define RQ_EXECUTE_REQUEST_HANDLER 0x00000080 // execute handler
#define RQ_RELEASE_REQUEST_STATE 0x00000100 // release request state
#define RQ_UPDATE_REQUEST_CACHE 0x00000200 // update cache
#define RQ_LOG_REQUEST 0x00000400 // log request
#define RQ_END_REQUEST 0x00000800 // end request
此外,还可以订阅由于其他模块执行的操作(例如刷新对客户端的响应)在请求处理期间可能发生的多个非确定性事件:
#define RQ_CUSTOM_NOTIFICATION 0x10000000 // custom notification
#define RQ_SEND_RESPONSE 0x20000000 // send response
#define RQ_READ_ENTITY 0x40000000 // read entity
#define RQ_MAP_PATH 0x80000000 // map a url to a physical path
为了使 RegisterModule 实现可供服务器访问,必须导出它。 使用一个包含 EXPORTS 关键字以导出 RegisterModule 函数的 DEF 文件。
接下来,实现模块工厂类:
mymodulefactory.h:
class CMyHttpModuleFactory : public IHttpModuleFactory
{
public:
virtual HRESULT GetHttpModule(
OUT CHttpModule **ppModule,
IN IModuleAllocator *
)
{
}
virtual void Terminate()
{
}
};
模块工厂实现 IHttpModuleFactory 接口,并用于在每个请求上创建模块的实例。
服务器在每个请求的开头调用 GetHttpModule 方法,以获取要用于此请求的模块实例。 该实现只返回我们接下来实现的模块类 CMyHttpModule 的新实例。 正如我们不久看到的,这使我们能够轻松地存储请求状态,而无需担心线程安全,因为服务器始终为每个请求创建和使用模块的新实例。
更高级的工厂实现可能决定使用单一实例模式,而不是每次创建新实例,或使用提供的 IModuleAllocator 接口在请求池中分配模块内存。 本演练中未讨论这些高级模式。
当工作进程关闭以执行模块的最终清理时,服务器将调用 Terminate 方法。 如果在 RegisterModule 中初始化任何全局状态,请在此方法中实现其清理。
实现 Module 类
此类负责在一个或多个服务器事件期间为模块提供主要功能。
myhttpmodule.h:
class CMyHttpModule : public CHttpModule
{
public:
REQUEST_NOTIFICATION_STATUS
OnAcquireRequestState(
IN IHttpContext * pHttpContext,
IN OUT IHttpEventProvider * pProvider
);
};
模块类继承自 CHttpModule 基类,该基类定义前面讨论的每个服务器事件的事件处理程序方法。 当请求处理管道执行每个事件时,它会对已注册该事件的每个模块实例调用关联的事件处理程序方法。
每个事件处理程序方法具有以下签名:
REQUEST_NOTIFICATION_STATUS
OnEvent(
IN IHttpContext * pHttpContext,
IN OUT IHttpEventProvider * pProvider
);
IHttpContext 接口提供对请求上下文对象的访问,该对象可用于执行请求处理任务,例如检查请求和操作响应。
IHttpEventProvider 接口被更具体的接口取代,每个接口为提供模块特定功能的事件而设计。 例如, OnAuthenticateRequest 事件处理程序接收 IAuthenticationProvider 接口,该接口允许模块设置经过身份验证的用户。
每个事件处理程序方法的返回是REQUEST_NOTIFICATION_STATUS枚举的值之一。 如果模块已成功执行任务,则必须返回RQ_NOTIFICATION_CONTINUE;管道应继续执行。
如果发生故障,并且想要中止请求处理并出现错误,则必须设置错误状态并返回RQ_NOTIFICATION_FINISH_REQUEST。 RQ_NOTIFICATION_PENDING返回允许您异步执行工作,并释放正在处理请求的线程,以便可以将该线程重新用于其他请求。 本文未讨论异步执行。
我们的模块类重写了 OnAcquireRequestState 事件处理程序方法。 为了在任何管道阶段提供功能,模块类必须重写相应的事件处理方法。 如果在 RegisterModule 中注册某个事件,但不重写模块类上的相应事件处理程序方法,则模块会在运行时失败,并在调试模式下编译时触发调试断言。 请小心,并确保重写方法的方法签名与所重写的 CHttpModule 类的基类方法完全等效。
编译模块
请记住,需要平台 SDK 才能进行编译。 请参阅简介,以了解有关获取它以及启用 Visual Studio 引用它的更多信息。
部署本机模块
编译模块后,必须在服务器上部署它。 编译模块,然后将 IIS7NativeModule.dll(以及 IIS7NativeModule.pdb 调试符号文件(如果需要)复制到运行 IIS 的计算机上的任意位置。
与可以直接添加到应用程序的托管模块不同,本机模块需要首先安装在服务器上。 这需要管理权限。
若要安装本机模块,有几种选项:
-
使用APPCMD.EXE命令行工具
APPCMD 使模块安装变得简单。 转到“启动>程序>附件”,右键单击命令行提示,然后选择“以管理员身份运行”。 在命令行窗口中,执行以下操作:
%systemroot%\system32\inetsrv\appcmd.exe install module /name:MyModule /image:[FULL\_PATH\_TO\_DLL]
其中 [FULL_PATH_TO_DLL] 是刚生成的包含模块的已编译 DLL 的完整路径。 -
使用 IIS 管理工具
这使你能够使用 GUI 添加模块。 转到“开始>运行”,键入 inetmgr,然后按 Enter。 连接到 localhost,找到“模块”任务,然后双击将其打开。 然后,单击右侧窗格中的 “添加本机模块 ”任务。 -
手动安装模块
通过将模块添加到 <applicationHost.config 配置文件中的 system.webServer>/<globalModules> 配置部分手动安装该模块,并在同一文件中的 system.webServer</>modules< 配置部分中添加对模块>的引用,以便启用它。 建议使用前两个选项之一来安装模块,而不是直接编辑配置。
任务已完成-我们已经完成了配置新的本机模块。
总结
在本演练中,你学习了如何使用新的本机(C++)扩展性 API 开发和部署自定义本机模块。 请参阅 Native-Code 开发概述 ,了解有关本机 (C++) 服务器 API 的详细信息。
若要了解如何使用托管代码和 .NET 框架扩展 IIS,请参阅 使用 .NET 开发 IIS 模块。 若要详细了解如何管理 IIS 模块,请参阅 模块概述白皮书。