.NET Framework を使用した IIS 7.0 モジュールとハンドラーの開発
作成者: Mike Volodarsky
概要
この記事では、.NET Framework に基づく IIS 7.0 以降の Web サーバー機能の開発の概要について説明します。 この記事では、次の方法を示します。
- IIS モジュールと IIS ハンドラーのどちらを開発するかを決定する方法
- .NET Framework で提供される Visual Studio、Visual C# Express、またはコマンド ライン ツールを使用して開発環境を設定する方法
- 最初のプロジェクトを作成する方法
- 単純なモジュールとハンドラーを開発する方法
- 単純なモジュールとハンドラーを IIS サーバーに展開する方法
実際のマネージド IIS モジュールとハンドラーをいくつか参照し、自分のアプリケーション用にダウンロードするには、HttpRedirection モジュールを使用したアプリケーションへのリダイレクト要求、DirectoryListingModule を使用した IIS Web サイトの見栄えのよいディレクトリ一覧の取得、IconHandler を使用した ASP.NET アプリケーションの見栄えのよいファイル アイコンの表示に関するページにアクセスします。
概要: ASP.NET を使用した IIS 機能の開発
IIS 7.0 より前の IIS リリースでは、Web サーバー機能を構築するための主要な拡張 API として、ISAPI と呼ばれる C API が装備されていました。 IIS 7.0 以降は、Web サーバーの完全なランタイム拡張を可能にし、すべての既存の機能を基にしたまったく新しい C++ API を提供する目的で、最初から設計し直されています。
これに加えて、IIS は初めて、ASP.NET 2.0 との緊密な統合を利用して、Web サーバーを拡張するための完全に忠実な .NET API も提供します。 つまり、使い慣れた ASP.NET 2.0 API で構築された新しい Web サーバー機能を使用して IIS を拡張できるようになりました。 同様に、IIS で既存の ASP.NET 2.0 モジュールとハンドラーを使用し、ASP.NET 統合を利用して、新しいコードを記述することなくアプリケーションの機能を強化できます。 IIS での ASP.NET 統合の詳細については、「ASP.NET と IIS 7 の統合」を参照してください。
交換のツール: 開発環境の決定
IIS モジュールとハンドラーをビルドするには、.NET アセンブリを開発およびコンパイルできる任意の環境を使用します。 一般的なオプションの一部を次に示します。
- Visual Studio 2005。 または、Visual Studio 2008 の最新ベータ リリースをダウンロードすることもできます。
- Visual C# 2005 Express Edition 無料ダウンロード (または Visual Basic 2005 Express を含む他の Express ツール)。
- .NET Framework ランタイムに含まれる C# コマンド ライン コンパイラ (csc.exe) (他の言語の場合は SDK をダウンロードする必要があります) とお好みのソース エディター。
この記事のサンプルでは C# を使用しますが、サポートされている他の .NET 言語 (マネージド C++ を除く) で IIS コンポーネントを開発できます。 この記事では、上記の 3 つの環境すべてで IIS 拡張機能コンポーネントを開発する方法について説明します。
Note
IIS は既存の ASP.NET API を利用して .NET 拡張機能を実現するため、Windows XP® および Windows Server® 2003 で .NET Framework 2.0 を使用して IIS .NET モジュールとハンドラーを開発できます。 ただし、新しい IIS 機能をサポートするために追加された新しい ASP.NET API のいずれかを使用する場合は、Windows Vista® で開発するか、Windows Vista または .NET Framework 3.5 の最新リリースから System.Web.dll のバージョンを取得してコードをコンパイルする必要があります。
IIS を拡張する 2 つの方法: モジュール対ハンドラー
すべての IIS Web サーバー機能は、モジュールとハンドラーの 2 つのカテゴリに適合します。
モジュールは、以前の IIS バージョンの ISAPI フィルターと同様に、要求に何らかの方法で変更または追加を加えるために、すべての要求の要求処理に参加します。 IIS の既存のモジュールの例として、要求の認証状態を操作する認証モジュール、送信応答を圧縮する圧縮モジュール、要求に関する情報を要求ログに記録するログ モジュールなどがあります。
このモジュールは、ASP.NET System.Web.IHttpModule インターフェイスを実装し、System.Web 名前空間の API を使用して、ASP.NET の要求処理ステージの 1 つ以上に参加する .NET クラスです。
ハンドラーとは、以前の IIS バージョンの ISAPI 拡張機能と同様に、要求を処理し、特定のコンテンツ タイプの応答を生成する役割を担います。 モジュールとハンドラーの主な違いは、ハンドラーは通常、特定の要求パスまたは拡張機能にマップされ、そのパスまたは拡張機能が対応する特定のサーバー リソースの処理をサポートする点です。 IIS で提供されるハンドラーの例としては、ASP スクリプトを処理する ASP、静的ファイルを処理する静的ファイル ハンドラー、ASPX ページを処理する ASP.NET の PageHandler などがあります。
ハンドラーは、ASP.NET System.Web.IHttpHandler または System.Web.IHttpAsyncHandler インターフェイスを実装する .NET クラスであり、System.Web 名前空間の API を使用して、サポート対象の特定のコンテンツに対する http 応答を生成します。
IIS 機能の開発を計画する場合、まず考慮する必要があるのは、この機能が特定の URL または拡張への要求の処理を担当しているか、任意のルールに基づいてすべて、または一部の要求に適用されるかです。 前者の場合はハンドラーであり、後者の場合はモジュールである必要があります。
この記事では、モジュールとハンドラーの両方について、単純なモジュールと単純なハンドラーのビルド方法、プロジェクトの作成とコンパイルの一般的な手順、サーバーに配置するための具体的な手順について説明します。
Note
モジュールを開発するときはハンドラーを開発する必要はありません。逆も同様です。
はじめに: Visual Studio プロジェクトの作成
モジュールまたはハンドラーをビルドするには、モジュール クラスまたはハンドラー クラスを含む .NET アセンブリ (DLL) を生成する必要があります。 Visual Studio または Visual Studio Express ツールを使用している場合、最初の手順はクラス ライブラリ プロジェクトを作成することです。
[ファイル] メニューで、[新規作成] [プロジェクト] の順に選びます。 [新しいプロジェクト] ダイアログ (下) で、[Visual C#] プロジェクトの種類を選択し、Visual Studio にインストールされているテンプレートの右側の一覧で [クラス ライブラリ] を選択します。
ASP.NET および IIS モジュールとハンドラーの開発に使用される API を含む "System.Web.dll" アセンブリへの参照を追加する必要があります。 右側のソリューション エクスプローラー ツリー ビューの [プロジェクト] ノードの下にある [参照] ノードを右クリックし、[参照の追加] を選択し、[.NET] タブで System.Web アセンブリ バージョン 2.0 (下) を選択します。
Note
IIS 固有の ASP.NET API を利用する予定がない場合は、Windows XP および Windows Server 2003 で System.Web アセンブリ バージョン 2.0 を使用できます。 このアセンブリを参照してコンパイルされたモジュールとハンドラーは、Windows Vista および Windows Server 2008 上の IIS に問題なく展開および操作できます。 モジュールで IIS 固有の ASP.NET API をいくつか使用する場合は、Windows Vista、Windows Server 2008 で開発するか、.NET Framework 3.5 から System.Web.dll アセンブリを取得する必要があります。 IIS 固有の API には、HttpServerUtility.TransferRequest、HttpResponse.Headers コレクション、HttpApplication.LogRequest イベントなどがあります。
コードの記述: 単純なモジュールの構築
最初のタスクは、単純なモジュールをビルドすることです。 この記事の後半では、サンプル ハンドラーもビルドします。
モジュールを作成するには、System.Web.IHttpModule インターフェイスを実装するクラスを定義します。
プロジェクト システムによって生成された "class1.cs" ファイルを削除し、右側のツリー ビューで MyIIS7Project プロジェクトを右クリックし、[追加]、[新しい項目]、[クラス] の順に選択し、[名前] フィールドに「MyModule.cs」と入力して、MyModule という名前の新しい C# クラスを追加します。
System.Web 名前空間をインポートして、その中の型に簡単にアクセスできるようにします。
MyModule クラスが IHttpModule インターフェイスを実装するようにし、このインターフェイス メンバー Dispose() と Init() を定義します。 これをすぐに行うには、IHttpModule インターフェイスを右クリックし、[インターフェイスの実装] オプションを選択します。
Dispose() メソッドは、モジュールがアンロードされるときにアンマネージド リソースを確定的にクリーンアップすることを目的としているため、モジュール インスタンスがガベージ コレクターによって終了される前にリソースを解放できます。 ほとんどの場合、このメソッドは空白のままにすることができます。
Init(HttpApplication コンテキスト) メソッドが、目的の主要なメソッドです。 その役割は、モジュールの初期化を実行し、HttpApplication クラスで使用可能な 1 つ以上の要求処理イベントにモジュールを接続することです。 要求処理中に、サブスクライブした各イベントに対してモジュールが呼び出され、サービスの実行と実行が可能になります。 その手順を次に示します。
モジュール クラスのメソッドを、指定された HttpApplication インスタンス上のいずれかのイベントに接続して、1 つ以上の要求処理イベントをサブスクライブします。 このメソッドは、System.EventHandler デリゲート シグネチャに従う必要があります。 OnPreExecuteRequestHandler と呼ばれる新しいメソッドを定義し、HttpApplication.PreRequestRequestHandlerExecute イベントに接続します。このイベントは、サーバーが要求の要求ハンドラーを呼び出す直前に発生します。
public void Init(HttpApplication context) { context.PreRequestHandlerExecute += newEventHandler(OnPreRequestHandlerExecute) }
この時点で、各要求で PreRequestHandlerExecute イベントを受信するようにモジュールが設定されました。 これを、受信する他のすべてのイベントに対して繰り返すことができます。
ここでは、モジュールが使用できる ASP.NET API のいくつかを使用する方法を実証するように、実効性がある動作をモジュールで実行します。 要求で参照元ヘッダーが指定されているかどうかを確認し、指定されている場合、これを拒否します。これは、自分の Web サイトに対し、他の Web サイトから誰かがリンクを張るのを防ぐ方法です。 これは、すべての要求でハンドラーが実行される直前に呼び出される OnPreRequestHandlerExecute メソッドで行います。
public void OnPreRequestHandlerExecute ( Object source, EventArgs e) { HttpApplication app = (HttpApplication)source; HttpRequest request = app.Context.Request; if (!String.IsNullOrEmpty( request.Headers["Referer"] )) { throw new HttpException(403, "Uh-uh!"); } }
Note
HttpApplication インスタンスは source 引数を介してモジュールに提供され、キャストが必要です。 HttpContext オブジェクトなどの HttpApplication インスタンスと、要求を表す包含 HttpRequest オブジェクトから、要求オブジェクト モデルの残りの部分にアクセスできます。
上記のコードは、参照元ヘッダーが指定されているかどうかを確認し、指定されている場合は、403 Unauthorized エラー コードで要求を拒否します。
コードの記述: 単純なハンドラーの構築
次のタスクは、単純なハンドラーを構築することです。 この記事の前半では、サンプル モジュールを作成しました。モジュールの構築について読みたい場合は、戻ってください。
ハンドラーを作成するには、System.Web.IHttpHandler インターフェイスを実装するクラスを定義する必要があります (ページを非同期に実行する場合は、System.Web.IHttpAsyncHandler を実装することもできます)。 その手順を次に示します。
プロジェクト システムによって生成された "class1.cs" ファイルをまだ削除していない場合は削除し、右側のツリー ビューで MyIIS7Project プロジェクトを右クリックし、[追加]、[新しい項目]、[クラス] の順に選択し、[名前] フィールドに「MyHandler.cs」と入力して、MyHandler という名前の新しい C# クラスを追加します。
System.Web 名前空間をインポートして、その中の型に簡単にアクセスできるようにします。
MyHandler クラスに IHttpHandler インターフェイスを実装し、インターフェイス メンバー IsReusable と ProcessRequest() を定義します。 これをすぐに行うには、IHttpHandler インターフェイスを右クリックし、[インターフェイスの実装] オプションを選択します。
IsReusable () は、ハンドラー インスタンスを後続の要求に再利用できるかどうかを示します。 場合によっては、要求の処理後に、ハンドラーが別の要求を処理するために正しくない状態になることがあります。特に、前の要求に関するデータをメンバー変数に格納している場合です。 ランタイムは、再利用可能としてマークされている場合でも、ハンドラーの同じインスタンスを使用して 2 つの要求を同時に処理することはありません。 作成中のハンドラーが要求ごとの状態をメンバー変数に格納せず、その ProcessRequest 関数を必要な回数呼び出せるようにする場合は、このプロパティが true を返すようにして、再利用を許可します。
ProcessRequest () メソッドは、ハンドラーのメイン エントリ ポイントです。 その役割は、提供された HttpContext インスタンスから使用可能な HttpRequest インスタンスで指定された要求を処理し、HttpContext から使用できる HttpResponse インスタンスを使用して適切な応答を生成することです。 ProcessRequest() メソッドは、ExecuteRequestHandler 要求処理ステージ中にランタイムによって呼び出され、構成されたハンドラー マッピングに基づいて要求がハンドラーにマップされた場合にのみ呼び出されます。 これは、アプリケーションに対するすべての要求の通知を受け取るモジュールとは異なります。
最初に IsReusable プロパティを実装します。 このハンドラーは要求のメンバー状態を格納せず、異なる要求を持つ ProcessRequest() への複数の呼び出しをサポートできるため、true を返すことでハンドラーを再利用可能としてマークします。
public bool IsReusable { get { return true; }
最後に、ProcessRequest() メソッドを実装して、ハンドラーが実効性のある処理を行えるようにします。 開発手順をわかりやすくするために、このハンドラーはサーバー上の現在の時刻を返し、必要に応じてクエリ文字列にタイムゾーンを指定できるようにします。 ここでの目標は、
http://myserver/time.tm
などの URL を要求し、サーバー上の現在の時刻を取得できるようにすることです。 また、http://myserver/time.tm?utc=true
を要求し、時刻を世界標準時にできるようにします。 この実装を次に示します。public void ProcessRequest(HttpContext context) { DateTime dt; String useUtc = context.Request.QueryString["utc"]; if (!String.IsNullOrEmpty(useUtc) && useUtc.Equals("true")) { dt = DateTime.UtcNow; } else { dt = DateTime.Now; } context.Response.Write( String.Format( "<h1>{0}</h1>", dt.ToLongTimeString() ) ); }
HttpRequest.QueryString コレクションを使用して QueryString 変数を取得し、HttpResponse.Write メソッドを使用して現在の時刻を応答に書き込みます。 これは、ハンドラーで実行できるさまざまな処理のサンプルにすぎません。HttpRequest クラスは要求に関する多くの情報を提供し、HttpResponse クラスはクライアントに返される応答を整形するためのさまざまな方法を提供しています。
ハンドラーが完成しました。
コード完了: モジュールまたはハンドラーのコンパイル
モジュールとハンドラーが実装されたので、ASP.NET が実行時に読み込めるアセンブリにそられをコンパイルできます。 Visual Studio または Visual Studio Express を使用している場合は、Ctrl + Shift + B キーを押すか、プロジェクトを右クリックして [ビルド] を選択して、ツールから直接プロジェクトをコンパイルします。
.DLL アセンブリは、<ProjectDirectory> \bin\debug フォルダー内に .PDB シンボル ファイルと一緒に生成されます。このファイルは、プロジェクトのデバッグ段階での例外で、ソース コード行を含むサーバー上のアセンブリのデバッグに使用できます。
アセンブリを実稼働サーバーにアップロードする場合は、ソリューション ノードを右クリックして Configuration Manager を選択し、種類を [デバッグ] に変更して、ソリューション構成を "リリース" に変更してください。 アセンブリのリリース バージョンをアップロードします (PDB ファイルはそのまま残しておきます)。これにより、デバッグ情報がアセンブリから削除され、最適化されてコードが高速化されます。
Visual Studio を使用していない場合は、Framework ランタイムに含まれる C# コマンド ライン コンパイラを使用してプロジェクトをコンパイルします。 プロジェクトをコンパイルするには、コマンド ライン プロンプトを開きます (Windows Vista または Windows Server 2008 の場合は、[管理者として実行] オプションを使用してコマンド ライン プロンプトを実行してください)。
> %windir%\Microsoft.NET\Framework\v2.0.50727\csc.exe /t:library /out:MyIIS7Project.dll /debug \*.cs /r:System.Web.dll
これにより、MyIIS7Project.DLL ファイルと MyIIS7Project.PDB ファイルが生成されます。 アセンブリのリリース バージョンをビルドする場合は、/debug スイッチを省略し、アセンブリを最適化するために /o スイッチを含めます。
アセンブリのサーバーへのデプロイ
カスタム モジュールとハンドラーを実装したので、Web アプリケーションにデプロイします。 アプリケーションにモジュールまたはハンドラーをデプロイする方法は多数あり、ニーズに合わせてデプロイを調整するために使用できる構成オプションがいくつかあります。 最も基本的なデプロイ手順を以下に示します。 サーバー全体のモジュール/ハンドラーをデプロイする方法など、デプロイと構成オプションの詳細については、シリーズの次の IIS モジュールとハンドラーのデプロイ (近日公開予定) に関する記事を参照してください。
次の手順では、モジュールとハンドラーを IIS サーバー上の既存のアプリケーションにデプロイすることを前提としています。 アプリケーションを作成していない場合は、"既定の Web サイト" のルート アプリケーションを使用します。これは通常は %systemdrive%\inetpub\wwwroot
にあります。 次の例では、既定の Web サイトにある "myiis7project" というアプリケーションにモジュールとハンドラーをデプロイします。
モジュールとハンドラーをデプロイするには、まず、その実装を含むアセンブリを ASP.NET アプリケーションで使用できるようにします。
先ほどコンパイルした MyIIS7Project.dll アセンブリを、アプリケーションのルートにある /BIN ディレクトリにコピーします。 このディレクトリが存在しない場合は、作成します。
アプリケーションに読み込まれるモジュールとハンドラーを構成します。 [スタート] メニューから IIS7 管理ツールを開き、スタート/検索ボックスに「inetmgr.exe」と入力し、Enter キーを押します。 このツールで、左側のツリー ビューでサーバー ノードをダブルクリックし、[サイト] ノードを展開し、モジュールとハンドラーを追加するサイトまたはアプリケーションをダブルクリックします。
[モジュール] 機能アイコンを選択し、[マネージド モジュールの追加] アクションを選択し、結果のダイアログ ボックスにモジュール名 (任意) と完全修飾モジュールの種類「MyIIS7Modules.MyModule」を入力します。 ドロップダウン ボックスで型を選択することもできます。これは、ツールによってアセンブリが bin に自動的に読み込まれるので、IHttpModule インターフェイスを実装する型を検出します。 [OK] を押してモジュールを追加します。
サイト/アプリケーション ノードをもう一度ダブルクリックし、[ハンドラー マッピング] 機能アイコンを選択してハンドラーを追加します。 次に、[マネージド ハンドラーの追加] アクションを選択し、結果のダイアログ ボックスで、パスに "time.tm" を指定し、型に "MyIIS7Modules.MyHandler"、名前に "MyHandler" (任意) を指定します。 ここでも、型はドロップダウン ボックスに表示されることに注意してください。これは、アセンブリで管理ツールによってこの型が自動的に検出されるためです。 [OK] を押してハンドラーを追加します。
上記のアクションによって生成されたアプリケーション構成により、MyModule モジュールがアプリケーションに読み込まれるよう構成され (すべての要求に対して実行できるようになります)、MyHandler ハンドラーがアプリケーション内の time.tm URL に要求を処理するようにマップされます。
この構成により、モジュールとアプリケーションは IIS 統合モードのアプリケーションでのみ実行できます。 モジュールとハンドラーを IIS および以前のバージョンの IIS 上のクラシック モード アプリケーションでも実行する場合は、モジュールとハンドラーのクラシック ASP.NET 構成も追加する必要があります。 さらに、IIS または以前のバージョンの IIS でクラシック モードで実行する場合、ハンドラーでは、.tm 拡張子を IIS スクリプトマップの ASP.NET にマッピングするスクリプト マップを作成する必要があります。そうすると、モジュールは、ASP.NET にマップされた拡張子への要求に対してのみ実行されます。 詳細については、IIS モジュールとハンドラーのデプロイ (近日公開予定) に関する記事を参照してください。
また、IIS コマンド ライン ツールと AppCmd.exe を使用してモジュールとハンドラーを追加し、スクリプトまたはマネージド コードから IIS 構成を操作し、構成を web.config ファイルに直接配置できます。 これらの追加オプションについては、IIS モジュールとハンドラーのデプロイ (近日公開予定) に関する記事で詳しく説明します。
モジュールとハンドラーのテスト
モジュールまたはハンドラーをデプロイして構成しました。 次に、それらをテストします。
アプリケーションで "time.tm" への要求を行って、ハンドラーをテストします。 成功した場合は、サーバー上の現在の時刻が表示されます。 既定の Web サイトの myiis7project ハンドラーをデプロイするときに、アプリケーションに対して
http://localhost/myiis7project/time.tm
などの要求を行います。ハンドラーがこのアプリケーションに適切にデプロイされている場合は、サーバーに現在の時刻が表示されます。
また、時刻を UTC で表示するように
http://localhost/myiis7project/time.tm?utc=true
に要求してみます。モジュールをテストします。 /time.tm URL にリンクする page.html という単純な html ページをアプリケーションに作成します。
page.html
<html> <body> <a href="time.tm">View current server time</a> </body> </html>
次に、リンクを表示するように
http://localhost/myiis7project/page.html
への要求を行います。 このリンクをクリックすると、次のエラーが表示されます。前と同じ URL を要求すれば、時刻が正常に表示されたのに、そうしなかったのはなぜだと思われるでしょう。 これは、このモジュールが、参照元ヘッダーを指定するアプリケーションへの要求を拒否するように構成されているからであり、それをテストする目的です。このヘッダーは、ユーザーがブラウザーで URL を直接入力するのでなく、Web サイトに移動するリンクをクリックするたびに、ブラウザーによって追加されます。 そのため、URL を直接要求するとアクセスできましたが、別のページからのリンクに従うと、モジュールによって要求が拒否されました。
まとめ
この記事では、使い慣れた ASP.NET API を使用して IIS モジュールとハンドラーを開発し、アプリケーションに展開するための基本的な手順を説明しました。 また、開発環境に対する選択肢と、モジュールとハンドラーをビルドするタイミングを決定する方法についても説明しました。 この記事の情報を使用すると、IIS アプリケーションを強化するための最初のモジュールとハンドラーを構築できます。
また、ASP.NET のメンバーシップ プロバイダーに対して基本認証を有効にするモジュールの例は、「.NET を使用したモジュールの開発」で参照できます。
マネージド IIS モジュールとハンドラーがアプリケーションに価値を追加する方法の他の例を確認し、アプリケーションにそれらをダウンロードするには、HttpRedirection モジュールを使用したアプリケーションへのリダイレクト要求、DirectoryListingModule を使用した IIS Web サイトの見栄えのよいディレクトリ一覧の取得、IconHandler を使用した ASP.NET アプリケーションの見栄えのよいファイル アイコンの表示に関するページをご覧ください。