.NET Framework による IIS 7 のモジュールおよびハンドラーの開発
公開日: 2008 年 2 月 23 日 (作業者: chriskno (英語))
更新日: 2009 年 5 月 12 日 (作業者: chriskno (英語))
概要
この記事では、.NET Framework を基盤として IIS 7 Web サーバー機能を開発する方法の概要を示します。次のような内容について説明します。
- IIS 7 モジュールと IIS 7 ハンドラーのどちらを開発するかを決定する方法
- Visual Studio、Visual C# Express、または .NET Framework に備わるコマンド ライン ツールを使用して、開発環境をセットアップする方法
- 最初のプロジェクトを作成する方法
- 単純なモジュールおよびハンドラーを開発する方法
- 単純なモジュールおよびハンドラーを IIS 7 サーバーに展開する方法
IIS 7 のマネージ モジュールおよびマネージ ハンドラーを実際に確認し、アプリケーションで使用するためにダウンロードするには、「HttpRedirection モジュールを使用して要求をアプリケーションにリダイレクトする (英語)」、「DirectoryListingModule を使用して IIS Web サイトの見やすいディレクトリ一覧を表示する (英語)」、「IconHandler を使用して ASP.NET アプリケーションで見栄えのよいファイル アイコンを表示する (英語)」、および「IIS と ASP.NET によるホット リンクの阻止 (英語)」を参照してください。
この記事で説明しているモジュールとハンドラーのソース コード プロジェクトをダウンロードすることもできます。
はじめに: ASP.NET による IIS 7 機能の開発
IIS 7 以前のリリースの IIS では、Web サーバーの機能を構築するための主な拡張機能 API として、ISAPI と呼ばれる C API が提供されていました。新たに設計し直された IIS 7 では、すべての付属機能の基盤となる新しい C++ API が用意されており、Web サーバーの完全なランタイム拡張機能を実現できます。
これに加えて、IIS 7 では、ASP.NET 2.0 との密接な統合によって、Web サーバー拡張 .NET API を初めて完全にサポートしています。これにより、開発者は、使い慣れた ASP.NET 2.0 API を使用して構築した新しい Web サーバー機能によって、IIS 7 を拡張できるようになります。同様に、IIS 7 で既存の ASP.NET 2.0 モジュールおよびハンドラーを使用し、ASP.NET 統合を活用して、コードを新たに記述することなくアプリケーションの機能を強化できます。IIS 7 における ASP.NET 統合の詳細については、こちらをクリックしてください。
使用ツール: 開発環境の決定
IIS 7 モジュールおよびハンドラーを作成するには、.NET アセンブリの開発およびコンパイルが可能な環境ならばどれでも使用できます。一般的には、次のような選択肢があります。
- Visual Studio 2008
- 無料でダウンロードできる Visual C# 2008 Express Edition (英語) (または Visual Basic 2008 Express などのその他の Express ツール)。
- .NET Framework ランタイムに含まれている C# コマンド ライン コンパイラ (csc.exe) (その他の言語の場合は、SDK をダウンロードする必要があります) および使い慣れたソース エディター。
この記事のサンプルでは C# を使用していますが、IIS 7 コンポーネントはサポートされているその他の .NET 言語 (マネージ C++ を除く) でも作成できます。この記事では、前述の 3 つの環境で IIS 7 拡張機能コンポーネントを開発する方法を示します。
注: IIS 7 は既存の ASP.NET API を .NET 拡張機能として活用しており、Windows XP® および Windows Server® 2003 上で、.NET Framework 2.0 を使用して、IIS 7 .NET モジュールおよびハンドラーを作成できます。ただし、新しい IIS 7 の機能をサポートするために追加された新しい ASP.NET API を使用する予定の場合は、コードをコンパイルするには、Windows Vista® 上で開発を行うか、Windows Vista に含まれているバージョンの System.Web.dll または最新リリースの .Net Framework 3.5 を入手する必要があります。詳細については、こちらの記事 (英語) を参照してください。
IIS 7 を拡張する 2 つの方法: モジュールとハンドラー
すべての IIS 7 Web サーバー機能は、モジュールとハンドラーの 2 つのカテゴリに分類されます。
モジュールは、以前のバージョンの IIS の ISAPI フィルターに似ており、あらゆる要求の処理に関与して、何らかの方法で要求の変更または追加を行います。たとえば、IIS 7 の付属モジュールとして、要求の認証ステータスを操作する認証モジュール、送信応答を圧縮する圧縮モジュール、要求に関する情報を要求ログに記録するログ記録モジュールなどがあります。
モジュールは、ASP.NET の System.Web.IHttpModule インターフェイスを実装する .NET クラスであり、System.Web 名前空間の API を使用して ASP.NET の要求処理に関与します。
ハンドラーは、以前のバージョンの IIS の ISAPI 拡張機能に似ており、特定のコンテンツの種類について、要求を処理し、応答を生成します。モジュールとハンドラーとの主な違いは、ハンドラーは通常、特定の要求パスや拡張子にマップされ、そのパスや拡張子に対応する特定のサーバー リソースの処理をサポートする点です。IIS 7 で提供されるハンドラーの例としては、ASP スクリプトを処理する ASP、静的ファイルを提供する静的ファイル ハンドラー、ASPX ページを処理する ASP.NET のページ ハンドラーなどがあります。
ハンドラーは、ASP.NET の System.Web.IHttpHandler または System.Web.IAsyncHttpHandler インターフェイスを実装する .NET クラスであり、System.Web 名前空間の API を使用して、サポートする特定のコンテンツについて HTTP 応答を生成します。
IIS 7 の機能の開発を計画する際は、その機能によって、特定の URL や拡張子について要求を処理するのか、それとも任意の規則に則ってすべてまたは一部の要求を処理するのかを、最初に検討する必要があります。前者の場合はハンドラーを、後者の場合はモジュールを作成します。
この記事では、単純なモジュールやハンドラーの作成方法、プロジェクトの作成やコンパイルの共通手順、およびサーバーへの展開の具体的な手順を紹介します。
注: モジュールを作成する場合はハンドラーを作成する必要はありません。逆の場合も同じです。
最初の手順: Visual Studio プロジェクトの作成
モジュールまたはハンドラーを作成するには、モジュール クラスまたはハンドラー クラスを含む .NET アセンブリ (DLL) を作成する必要があります。Visual Studio または Visual Studio Express ツールを使用している場合は、最初の手順として、クラス ライブラリ プロジェクトを作成します (独自のテキスト エディターやコマンド ライン コンパイラを使用している場合は、プロジェクトのコンパイル(英語)の説明に進み、Visual Studio を使用せずにプロジェクトをコンパイルする方法の手順を参照してください)。
'[ファイル] メニューの [新規作成]、[プロジェクト] の順にクリックします。[新しいプロジェクト] ダイアログ ボックス (次の図) で、プロジェクトの種類として [Visual C#] を選択し、右側の [Visual Studio にインストールされたテンプレート] の一覧で [クラス ライブラリ] をクリックします)。
ASP.NET および IIS 7 のモジュールおよびハンドラーの開発に使用する API を持つ System.Web.dll アセンブリへの参照を追加します。右側のソリューション エクスプローラーのツリー ビューで、プロジェクト ノードの下にある [参照設定] ノードを右クリックして [参照の追加] をクリックし、[.NET] タブで System.Web アセンブリ、Version 2.0 (次の図) をクリックします。
注: IIS 7 固有の ASP.NET API を利用する予定がない場合は、Windows XP および Windows Server 2003 で System.Web アセンブリ Version 2.0 を使用できます。このアセンブリを参照してコンパイルされたモジュールやハンドラーは、Windows Vista および Windows Server 2008 上の IIS 7 で問題なく展開および操作することができます。モジュールで IIS 7 固有の ASP.NET API を使用する場合は、Windows Vista または Windows Server 2008 で開発作業を行うか、.NET Framework 3.5 から System.Web.dll アセンブリを入手する必要があります。IIS 7 固有の API には、HttpServerUtility.TransferRequest、HttpResponse.Headers コレクション、HttpApplication.LogRequest イベントなどがあります。
コードの記述: 単純なモジュールの作成
最初のタスクは単純なモジュールの作成です。この記事の後半で、単純なハンドラーも作成します。ハンドラーの作成(英語)については、後のトピックに進んでください。
モジュールを作成するには、System.Web.IHttpModule インターフェイスを実装するクラスを定義します。
プロジェクト システムによって生成された "class1.cs" ファイルを削除し、MyModule という名前の新しい C# クラスを追加します。これには、右側のツリー ビューで MyIIS7Project プロジェクトを右クリックし、[追加]、[新しい項目] の順にクリックし、[クラス] を選択し、[ファイル名] フィールドに「MyModule.cs」と入力します。
System.Web 名前空間をインポートして、この名前空間の型に容易にアクセスできるようにします。
MyModule クラスで IHttpModule インターフェイスを実装し、インターフェイスのメンバーの Dispose() および Init() を定義します。これは、IHttpModule インターフェイスを右クリックして、[インターフェイスの実装] をクリックとすると、簡単に実行できます。
※図には記述されていませんが、クラスは 以下のように public として宣言してください。
public class MyModule : IHttpModule
Dispose() メソッドは、モジュールがアンロードされるときにアンマネージ リソースを特定してクリーンアップし、モジュール インスタンスがガベージ コレクターによって最終処理が行われる前にリソースを解放できるようにします。通常、このメソッドは空のままにしておくことができます。
Init(HttpApplication context) メソッドは重要なメソッドです。このメソッドの役割は、モジュールの初期化の実行と、HttpApplication クラスで使用可能な 1 つまたは複数の要求処理イベントへのモジュールの関連付けです。要求処理時に、サブスクライブしている各イベントについてモジュールが呼び出され、そのサービスを実行できるようになります。これを行うには、以下の手順を実行します。
モジュール クラスのメソッドを、HttpApplication インスタンスで提供されるイベントに関連付けることによって、1 つまたは複数の要求処理イベントをサブスクライブします。このメソッドは、System.EventHandler デリゲートのシグネチャに従っている必要があります。OnPreExecuteRequestHandler という名前の新しいメソッドを定義し、そのメソッドを HttpApplication.PreRequestRequestHandlerExecute イベントに関連付けます。このイベントは、サーバーが要求の要求ハンドラーを呼び出す直前に発生します。
public void Init(HttpApplication context) { context.PreRequestHandlerExecute += newEventHandler(OnPreRequestHandlerExecute) }
これで、各要求について、PreRequestHandlerExecute イベントを受信するようモジュールを設定しました。他にも受信したいイベントがある場合は、この手順を繰り返してください。
これで、モジュールに処理を実行させることができるようになりました。では、モジュールで使用可能な ASP.NET API の使用方法を紹介します。要求に Referrer ヘッダーが指定されているかどうかを確認し、指定されている場合は、この要求を拒否します。あまり高度な方法ではありませんが、他の Web サイトからの自分の Web サイトへのリンクを防ぐことができます。この処理は、各要求についてハンドラーが実行される直前に呼び出される 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!"); } }
注: HttpApplication インスタンスは、source 引数によってモジュールに提供されます。キャストが必要です。HttpContext オブジェクトや、要求を表す HttpRequest オブジェクトなど、要求オブジェクト モデルの残りの部分には HttpApplication インスタンスからアクセスすることができます。
前述のコードは、Referrer ヘッダーが指定されているかどうかを確認し、指定されている場合は、"403 禁止" エラー コードを出力して要求を拒否します。
コードの記述: 単純なハンドラーの作成
次のタスクは単純なハンドラーの作成です。この記事では、単純なモジュールも作成しています。モジュールの作成(英語)については、前のトピックをお読みください。
ハンドラーを作成するには、System.Web.IHttpHandler インターフェイスを実装するクラスを定義します (ページで非同期処理を実行する場合は、System.Web.IHttpAsyncHandler を実装することもできます)。これには、次の手順を実行します。
まだ作業をしていない場合は、プロジェクト システムによって生成された "class1.cs" ファイルを削除し、MyHandler という名前の新しい C# クラスを追加します。これには、右側のツリー ビューで MyIIS7Project プロジェクトを右クリックし、[追加]、[新しい項目] の順にクリックし、[クラス] を選択し、[ファイル名] フィールドに「MyHandler.cs」と入力します。
System.Web 名前空間をインポートして、この名前空間の型に容易にアクセスできるようにします。
MyHandler クラスで IHttpHandler インターフェイスを実装し、インターフェイスのメンバーの IsReusable および ProcessRequest() (英語) を定義します。これは、IHttpHandler インターフェイスを右クリックして、[インターフェイスの実装] をクリックすると、簡単に実行できます。
※図には記述されていませんが、クラスは 以下のように public として宣言してください。
public class MyHandler : 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 を要求すると協定世界時を取得できるようにするのが目標です。実装は次のようになります。
{ 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 キーを押すか、プロジェクトを右クリックして [ビルド] をクリックすることによって、ツールから直接プロジェクトをコンパイルできます。
<ProjectDirectory>\bin\debug フォルダーに .DLL アセンブリと .PDB シンボル ファイルが生成されます。.PDB シンボル ファイルは、サーバー上のアセンブリをデバッグしたり、プロジェクトのデバッグ段階で例外にソース コード行を含めたりするのに使用できます。
アセンブリを運用サーバーにアップロードする場合は、ソリューションの構成を [Release] に変更します。これを行うには、ソリューション ノードを右クリックして [構成マネージャ] をクリックし、構成の種類を [Release] に変更します。次に、Release バージョンのアセンブリ (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 7 モジュールおよびハンドラーの展開 (近日公開)」の記事を参照してください。
以下の手順では、モジュールおよびハンドラーを IIS 7 サーバー上の既存のアプリケーションに展開することを想定しています。アプリケーションを作成していない場合は、通常、%systemdrive%\inetpub\wwwroot にある "Default Web Site" のルート アプリケーションを使用してください。次の例では、Default Web Site にある "myiis7project" という名前のアプリケーションにモジュールおよびハンドラーを展開します。
モジュールおよびハンドラーを展開するには、ASP.NET アプリケーションで使用可能な実装を含むアセンブリを作成します。
前にコンパイルした MyIIS7Project.dll アセンブリを、アプリケーションのルートにある /BIN ディレクトリにコピーします。このディレクトリが存在しない場合は作成します。
モジュールとハンドラーがアプリケーションに読み込まれるように構成します。[スタート] ボタンをクリックし、[検索の開始] ボックスに「inetmgr.exe」と入力し、Enter キーを押して、IIS 7 管理ツールを開きます。このツールの左側のツリー ビューでサーバー ノードをダブルクリックし、[サイト] ノードを展開し、モジュールやハンドラーを追加したいサイトまたはアプリケーションをダブルクリックします。
[モジュール] 機能アイコンをクリックし、操作ウィンドウで [マネージ モジュールの追加] をクリックし、表示されたダイアログ ボックスで、モジュール名 (任意) と完全修飾モジュール型「MyIIS7Modules.MyModule」を入力します。このツールでは、bin 内のアセンブリが自動的に読み込まれ、IHttpModule インターフェイスを実装する型が検出されるので、ドロップダウン ボックスから型を選択することもできます。[OK] をクリックして、モジュールを追加します。
もう一度、サイト/アプリケーション ノードをダブルクリックし、[ハンドラー マッピング] 機能アイコンをクリックして、ハンドラーを追加します。次に、操作ウィンドウで [マネージ ハンドラーの追加] をクリックし、表示されたダイアログ ボックスで、パスとして「time.tm」を、型として「MyIIS7Modules.MyHandler」を、名前 (任意) として「MyHandler」を指定します。ここでも同様に、型がドロップダウン ボックスに表示されます。これは、管理ツールによって、アセンブリ内でこの型が自動的に検出されるからです。[OK] をクリックして、ハンドラーを追加します。
前述の操作によって生成されるアプリケーション構成によって、MyModule モジュールがアプリケーションに読み込まれるように構成され (これにより、すべての要求について実行可能になる)、要求を処理する MyHandler ハンドラーがアプリケーション内のURL である time.tm にマップされます。
この構成では、モジュールおよびハンドラーは、IIS 7 統合モード アプリケーション内でのみ実行できます。モジュールやハンドラーを IIS 7 および以前のバージョンの IIS のクラシック モード アプリケーションでも実行するには、モジュールやハンドラーのクラシック ASP.NET 構成も追加する必要があります。さらに、IIS 7 および以前のバージョンの IIS でクラシック モードで実行する場合、ハンドラーの要件として、IIS スクリプトマップで、.tm 拡張子を ASP.NET にマップするスクリプト マップを作成する必要があります。また、この場合、モジュールは ASP.NET にマップされた拡張子に対する要求についてのみ実行されます。詳細については、「IIS 7 モジュールおよびハンドラーの展開 (近日公開)」を参照してください。
モジュールやハンドラーを追加するには、IIS 7 コマンド ライン ツール AppCmd.exe を使用したり、スクリプトやマネージ コードから IIS 7 構成を操作したりすることができるほか、web.config ファイルに直接構成を記述することもできます。これらのオプションの詳細については、「IIS 7 モジュールおよびハンドラーの展開 (近日公開)」を参照してください。
モジュールとハンドラーのテスト
モジュール/ハンドラーの展開および構成が完了したので、次にこれらのテストを行います。
アプリケーションの "time.tm" への要求を実行して、ハンドラーをテストします。成功した場合、サーバーの現在時刻が表示されます。この例では、Default Web Site の myiis7project アプリケーションにハンドラーを展開したので、アプリケーションに対する要求の URL は、https://localhost/myiis7project/time.tm のようになります。
ハンドラーがこのアプリケーションに適切に展開されていれば、現在のサーバー時刻が表示されます。
https://localhost/myiis7project/time.tm?utc=true への要求も実行して、時刻を UTC で表示してみてください。モジュールをテストします。アプリケーション内で、/time.tm URL にリンクする page.html という簡単な HTML ページを作成します。
**page.html
<html> <body> <a href="time.tm" mce_href="time.tm">View current server time</a> </body> </html>
次に、https://localhost/myiis7project/page.html への要求を実行してリンク先の表示を試みます。このリンクをクリックするとエラーが表示されます。
先ほどと同じ URL を要求しているのに、正常に時刻が表示されないのはなぜでしょうか。これは、このモジュールでは、アプリケーションへの要求で Referrer ヘッダーが指定されている場合、この要求を拒否するように構成されているからです。したがって、直接 URL を要求した場合にはアクセスできますが、別のページからリンクをたどった場合は、モジュールによって要求が拒否されます。
まとめ
この記事では、使い慣れた ASP.NET API を使用して IIS 7 モジュールおよびハンドラーを開発し、アプリケーションに展開するための基本的な手順を説明しました。また、開発環境の選択肢や、モジュールとハンドラーのどちらを作成するかを決定する方法についても説明しました。この記事で紹介した情報を活用して、IIS 7 アプリケーションの機能を強化するモジュールおよびハンドラーを作成してみてください。
この記事で説明しているモジュールおよびハンドラーのソース コード プロジェクトをダウンロードできます。また、ASP.NET のメンバーシップ プロバイダーを使用した基本認証を実現するモジュールの例が「.NET を使用したモジュールの開発」で紹介されています。
IIS 7 のマネージ モジュールおよびマネージ ハンドラーによって、アプリケーションの付加価値を向上する方法の例を参考にしたり、アプリケーションで使用するためにこれらのモジュールおよびハンドラーをダウンロードするには、「HttpRedirection モジュールを使用して要求をアプリケーションにリダイレクトする (英語)」、「DirectoryListingModule を使用して IIS Web サイトの見やすいディレクトリ一覧を表示する (英語)」、「IconHandler を使用して ASP.NET アプリケーションで見栄えのよいファイル アイコンを表示する (英語)」、および「IIS と ASP.NET によるホット リンクの阻止 (英語)」を参照してください。