HTTP ハンドラによる動的コンテンツの提供

Scott Mitchell

April 2004
日本語版最終更新日 2006 年 7 月 26 日

適用対象
Microsoft ASP.NET
Microsoft Internet Information Services

要約:この記事では、3 つの実際のシナリオを紹介しながら、HTTP ハンドラにより、テンプレートを使用して URL ドリブン コンテンツを提供する方法を説明します。

この記事のソース コードのダウンロード

目次

はじめに
ファイル拡張子による要求の振り分け
HTTP ハンドラの作成、および構成
実際に使用されている HTTP ハンドラの分析
まとめ
関連書籍

はじめに

Microsoft Internet Information Services (IIS) Web サーバーに要求が到着すると、必ず、IIS が要求ファイルの拡張子を検査し、そのファイルをどのように処理するかを判断します。HTML ページ、画像、カスケード スタイル シート (CSS) ファイルなどの静的ファイルは、IIS がそのまま処理します。Microsoft ASP.NET Web ページ、または Web サービス (拡張子が .aspx、または .asmx のファイル) への要求は、ASP.NET エンジンに引き渡します。拡張子が .aspx のファイルへの要求は、従来の ASP エンジンに引き渡します。ASP.NET エンジン、および ASP エンジンは、要求されたリソース用のマークアップを生成します。ASP.NET ページ、および従来の ASP Web ページの場合、生成されるマークアップは HTML です。また Web サービスの場合は、SOAP 応答です。要求されたリソースのマークアップがエンジンによって正常に生成されると、マークアップは IIS に戻されます。IIS は、そのマークアップを、リソースを要求したクライアントに送信します。

このモデルでのコンテンツの提供 (IIS が直接的に提供するのは静的コンテンツのみとし、動的コンテンツの提供は別のエンジンに委任する) には、2 つの利点があります。

  1. これは、処理の分割に役立ちます。IIS は、静的コンテンツの提供の向上に集中し、動的コンテンツの提供に関する細部は、外部プログラムに任せることができます。つまり、IIS は、効率的に HTML ページ、および画像を提供することに集中でき、ASP.NET エンジンは、効率的に ASP.NET Web ページ、および Web サービスを提供することに集中できます。
  2. 新しい動的サーバーサイド テクノロジを、プラグインの形で IIS に追加できます。ASP.NET Web サービスの提供を、外部エンジンに依存するのではなく、IIS 自体で行うことを想像してみてください。この場合、ASP.NET の、または動的サーバーサイド テクノロジの新バージョンが登場するたびに、その新バージョンをサポートする、新バージョンの IIS を作成することが必要になってしまいます。そして、最新バージョンを使用するには、IIS のバージョン アップが必要になります。

コンテンツ提供をこのモデルで実行するには、IIS で、ファイル拡張子をプログラムにマップすることが必要です。この情報は、IIS メタベースに存在し、次のセクションで説明する、インターネット サービス マネージャで構成できます。要求が IIS に到着したときに、このマッピングを照会して、要求をどこに送るかを判断します。.aspx、.asmx、.ashx、.cs、.vb、.config などの拡張子は、すべて既定で構成されており、ASP.NET エンジンに送られます。

要求が IIS から ASP.NET エンジンに送られた場合、ASP.NET エンジンも同様のステップを実行して、要求されたファイルを生成する適切な方法を判断します。具体的には、ASP.NET エンジンは、要求されたファイルの拡張子を検査し、その拡張子に関連付けられている HTTP ハンドラ を起動します。このハンドラが、要求されたファイルのマークアップの生成を実行します。

注意 技術的には、ASP.NET エンジンは、HTTP ハンドラ、または HTTP ハンドラ ファクトリのどちらかを起動します。 HTTP ハンドラ ファクトリは、HTTP ハンドラのインスタンスを戻すクラスです。

HTTP ハンドラは、特定の種類の Web コンテンツについて、そのコンテンツをどのように生成するかを認識しているクラスです。たとえば、ASP.NET Web ページを提供するための .NET Framework の HTTP ハンドラ クラスや、Web サービスを提供するためのものなどがそれぞれに存在します。IIS が動的コンテンツの提供を外部プログラムに依存するのと同様に、ASP.NET エンジンは、コンテンツの種類ごとに、そのコンテンツの提供をそれぞれ別のクラスに依存します。

IIS と同様に、ASP.NET エンジンをプラグイン可能にすることにより、前に説明した利点が ASP.NET にももたらされます。このモデルで興味深いのは、開発者が、新しい HTTP ハンドラ クラスを作成し、それを ASP.NET エンジンにプラグインできるという点です。この記事では、カスタム HTTP ハンドラを作成し、それらを ASP.NET Web アプリケーションで使用する方法について詳しく説明します。まず、ASP.NET エンジンが、要求をサービスする HTTP ハンドラをどのように判断するかの詳細を確認します。次に、独自の HTTP ハンドラ クラスをわずかなコードで簡単に作成する方法を説明します。最後に、現在の Web アプリケーションで使用を開始できるように、HTTP ハンドラの実際の使用例を説明します。

ファイル拡張子による要求の振り分け

「はじめに」で説明したように、IIS および ASP.NET エンジンはどちらも、要求のファイル拡張子に基づいて、着信要求を外部プログラム、またはクラスに渡します。これを行うには、IIS、および ASP.NET のどちらにも、ファイル拡張子を外部プログラムにマップする、ある種の一覧表が必要です。IIS は、この情報をメタベースに保管します。これは、インターネット サービス マネージャから編集可能です。図 1 に、IIS アプリケーションの [アプリケーションの構成] ダイアログ ボックスの画面を示します。各拡張子を、特定の実行可能プログラムのパスにマップしています。図 1 には、ASP.NET エンジンにマップされているいくつかのファイル拡張子 (.asax、.ascx、.ashx、.asmx など) が表示されています。

Dd297788.httphandlers_fig01(ja-jp,MSDN.10).gif

図 1. 構成されたファイルの拡張子


具体的には、IIS は、ASP.NET 関連の拡張子を、\WINDOWS_DIR\Microsoft.NET\Framework\VERSION\aspnet_isapi.dll にマップします。ASP.NET がファイル拡張子を HTTP ハンドラにマップするのと同様に、IIS は、ファイル拡張子を ISAPI 拡張にマップします。(ISAPI 拡張は、着信 Web 要求を処理するアンマネージのコンパイル済みクラスです。これが、要求されたリソースのコンテンツ生成を行います。) しかし、ASP.NET エンジンは、.NET Framework のマネージ クラスのセットです。aspnet_isapi.dll は、アンマネージの領域 (IIS) とマネージの領域 (ASP.NET エンジン) の橋渡しの役割を果たします。

IIS 固有マッピングをカスタマイズする方法なども含めて、IIS が着信要求をどのように処理するかについての詳細は、Michele Leroux Bustamante の記事 Inside IIS and ASP.NET (英語) を参照してください。

IIS が、ファイル拡張子と ISAPI 拡張の一覧表をメタベースに保管するのに対して、ASP.NET での一覧表は、XML 形式の構成ファイルに保管されます。machine.config ファイル (\WINDOWS_DIR\Microsoft.NET\Framework\VERSION\CONFIG\ にあります) は、Web サーバー全体の既定のマッピングを含みます。また、Web.config ファイルは、各 Web アプリケーション専用のマッピングを指定するのに使用できます。

machine.config ファイル、および Web.config ファイルのどちらでも、マッピングは、<httpHandlers> エレメントに保管されています。各マッピングは、次のような構文の、1 つの <add> エレメントで表されます。

                  
<add verb="verb list" path="extension | path"
type="HTTP handler type" />

                

verb 属性は、GET、または POST など、特定の種類の HTTP 要求のみを、HTTP ハンドラが提供するように制限するために使用できます。すべての動詞を含めるには、* を使用します。path 属性には、*.scott など、HTTP ハンドラにマップする拡張子を指定します。また、特定の URL パスを指定することもできます。最後の type 属性には、このコンテンツの生成を行う HTTP ハンドラのタイプを指定します。

以下は、machine.config ファイル内の既定の HTTP ハンドラ割り当ての一部です。

                  
<httpHandlers>
   <add verb="*" path="*.aspx"
     type="System.Web.UI.PageHandlerFactory" />
   <add verb="*" path="*.asmx"
type="System.Web.Services.Protocols.WebServiceHandlerFactory,
System.Web.Services, Version=1.0.5000.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" validate="false" />
   <add verb="*" path="*.ascx"
     type="System.Web.HttpForbiddenHandler" />
   <add verb="*" path="*.config"
      type="System.Web.HttpForbiddenHandler" />
   <add verb="*" path="*.cs"
     type="System.Web.HttpForbiddenHandler" />
   <add verb="*" path="*.vb"
      type="System.Web.HttpForbiddenHandler" />
   ...
</httpHandlers>

                

最初の <add> エレメントは、すべてのASP.NET Web ページ (*.aspx) への要求を HTTP ハンドラ ファクトリ PageHandlerFactory マップしています。2 つ目は、すべての Web サービス (.asmx) への要求を WebServiceHandlerFactory クラスにマップしています。その後の 4 つの <add> エレメントは、それぞれの拡張子を HttpForbiddenHandler HTTP ハンドラにマップしています。このため、ユーザーが、アプリケーションの Web.config ファイルなどの .config ファイルの表示を試みると、この HTTP ハンドラが呼び出されます。HttpForbiddenHandler は、単純に、図 2 に示すような、その種類のファイルは提供できないことを示すメッセージを発行するだけです。

注意 機密ファイルへの直接的なアクセスは、machine.config ファイル、または Web.config ファイルで、その拡張子を HttpForbiddenHandler HTTP ハンドラにマップすることにより防御できます。たとえば、Web ホスティング企業では、IIS で、.mdb ファイル (Microsoft Access データベース ファイル) への要求を ASP.NET エンジンに送るように構成します。次に、ASP.NET エンジンで、すべての .mdb ファイルを HttpForbiddenHandler にマップします。これで、ユーザーが Web アクセス可能な場所に Access データベース ファイルを置いたとしても、不正なユーザーがそれらをダウンロードすることを防止できます。詳細については、筆者の記事 Protecting Files with ASP.NET (英語) を参照してください。

Dd297788.httphandlers_fig02(ja-jp,MSDN.10).gif

図 2. web.config の表示の制限


machine.config ファイルは、Web サーバー上のすべての Web アプリケーションの既定のマッピングを指定します。しかし、マッピングは、Web.config ファイルを使用することにより、Web アプリケーション単位でカスタマイズできます。HTTP ハンドラを Web アプリケーションに追加するには、<httpHandlers> エレメントに <add> エレメントを追加します。<httpHandlers> エレメントは、以下のように、<system.web> エレメントの子として追加する必要があります。

                  
<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <system.web>
    <httpHandlers>
        <add verb="verb list" path="extension | path" type="type" />
    </httpHandlers>

    ...
  </system.web>
</configuration>

                

Web アプリケーションで特定の HTTP ハンドラを除去するには、以下のように、<remove> エレメントを使用します。

                  
	<httpHandlers>
        <remove verb="verb list" path="extension | path" />
    </httpHandlers>

                

ASP.NET エンジンでファイル拡張子と HTTP ハンドラのマッピングをカスタマイズする場合は、machine.config ファイル、または Web.config ファイルで設定するファイル拡張子は、IIS メタベースで aspnet_isapi.dll にマップされている必要があることを理解しておくことが重要です。ASP.NET エンジンが要求を適切な HTTP ハンドラに送るには、最初に、IIS から要求を受け取る必要があります。IIS は、要求されたファイルの拡張子が IIS メタベースで aspnet_isapi.dll にマップされている場合にのみ、要求を ASP.NET エンジンに送ります。カスタム HTTP ハンドラを作成する際には、常に、この点に注意してください。この記事の後の部分では、IIS メタベースへの追加が必要となる例を示します。そこでは、.gif、および .jpg の拡張子を aspnet_isapi.dll ISAPI 拡張にマップします。

ここまでは、IIS が着信要求をどのように ISAPI 拡張にマップするか、および ASP.NET エンジンが着信要求をどのように HTTP ハンドラ (または HTTP ハンドラ ファクトリ) にマップするかを説明しました。これで、独自の HTTP ハンドラ クラスの作成方法の説明に入る前の準備は完了です。

HTTP ハンドラの作成、および構成

HTTP ハンドラを ASP.NET Web アプリケーションに追加するのに必要なステップは 2 つです。最初に、HTTP ハンドラを作成します。これには、System.Web.IHttpHandler インターフェイスを実装するクラスの作成が必要です。2 つ目に、ASP.NET Web アプリケーションが HTTP ハンドラを使用するように構成する必要があります。前のセクションで、Web アプリケーションが HTTP ハンドラを使用するように構成する方法は説明しました。<httpHandlers> セクションをアプリケーションの Web.config ファイル、または Web サーバーの machine.config ファイルに追加します。アプリケーションが HTTP ハンドラを使用するように構成する方法は既に確認したので、ここからは、HTTP ハンドラの作成を中心に説明します。

IHttpHandler インターフェイスは、1 つのメソッド ProcessRequest(HttpContext)、および 1 つのプロパティ IsReusable を定義します。ProcessRequest(HttpContext) メソッドは、要求に関する情報を含む System.Web.HttpContext インスタンスを受け取ります。ProcessRequest(HttpContext) メソッドは、その要求の詳細に基づいて、正しいマークアップを出力します。

ProcessRequest(HttpContext) メソッドに渡される HttpContext クラスが提供する重要なプロパティの多くは、System.Web.UI.Page クラスが提供するものと同じです。Request、および Response プロパティは、着信要求、および送信要求を処理することを可能にします。Session、および Application プロパティは、セッション、およびアプリケーションの状態を処理するために使用できます。Cache プロパティは、アプリケーションのデータ キャッシュへのアクセスを提供します。User プロパティは、要求を行っているユーザーに関する情報を含みます。

注意 HttpContext クラスと Page クラスの類似性は、偶然ではありません。Page クラスは、HTTP ハンドラそのものであり、IHttpHandler を実装します。Page クラスの ProcessRequest(HttpContext) メソッドの開始時には、Page クラスの RequestResponseServer、および他の内在オブジェクトに、渡された HttpContext の対応するプロパティが割り当てられます。

IHttpHandlerIsReusable プロパティは、HTTP ハンドラが再使用されるかを示す Boolean プロパティです。IsReusable プロパティは、HTTP ハンドラの 1 つのインスタンスを、同じ種類のファイルへの別の要求に使用できるかどうか、つまり各要求が、別個の HTTP ハンドラ クラスのインスタンスを必要とするかどうかを示します。正式な資料には、IsReusable の使用の最適な使用法についての情報はあまりありません。マイクロソフトの ASP.NET チームの開発者である Dmitry Robsman による This ASP.NET Forums post (英語) では、「各要求で新しいインスタンスを必要としない場合、ハンドラは再使用可能である。メモリ割り当てはコストが小さいので、1 回の初期化コストが大きい場合のみ、ハンドラを再使用可能とする必要がある」と説明されています。また、Dmitry は、Page クラス (これは HTTP ハンドラです) は、再使用可能でないことも指摘しています。この情報を参照すれば、作成する HTTP ハンドラが、IsReusable で false を戻すことが正しいか判断できるでしょう。

Simple HTTP ハンドラの作成

HTTP ハンドラ クラスの作成を解説するために、要求情報と、現在時刻を表示するだけの、単純な Simple HTTP ハンドラを作成しましょう。まず、自分で選択した言語で、新しいクラス ライブラリ プロジェクトを作成します (ここでは C# を使用し、プロジェクトの名前は skmHttpHandlers とします)。既定の Class.cs (または Class.vb) ファイルで新しいプロジェクトが作成されます。このファイルに、次のようなコードを追加します (名前空間は違っても構いません)。

                  
using System;
using System.Web;

namespace skmHttpHandlers
{
   public class SimpleHandler : IHttpHandler
   {
      public void ProcessRequest(HttpContext context)
      {
         // TODO:  SimpleHandler.ProcessRequest 実装を追加します。
      }

      public bool IsReusable
      {
         get
         {
            // TODO:  SimpleHandler.IsReusable の
            // get する実装を追加します。
            return false;
         }
      }
   }
}

                

このコードは、IHttpHandler を実装する SimpleHandler という名前のクラスを定義しています。SimpleHandler クラスには、1 つのメソッド ProcessRequest(HttpContext)、および 1 つのプロパティ IsReusable があります。IsReusable プロパティはこのままにしておくことができるので (これは、既定で false を戻します)、作成する必要があるのは、ProcessRequest(HttpContext) メソッドのコードのみです。

この HTTP ハンドラに、現在時刻、および要求の詳細を表示させるには、ProcessRequest(HttpContext) メソッドに次のコードを追加します。

                  
public void ProcessRequest(HttpContext context)
{
   context.Response.Write("<html><body><h1>The current time is ");
   context.Response.Write(DateTime.Now.ToLongTimeString());
   context.Response.Write("</h1><p><b>Request Details:</b><br /><ul>");
   context.Response.Write("<li>Requested URL: ");
   context.Response.Write(context.Request.Url.ToString());
   context.Response.Write("</li><li>HTTP Verb: ");
   context.Response.Write(context.Request.HttpMethod);
   context.Response.Write("</li><li>Browser Information: ");
   context.Response.Write(context.Request.Browser.ToString());
   context.Response.Write("</li></ul></body></html>");
}

                

このコードは、要求を行っている Web ブラウザに戻す HTML マークアップそのものを生成し、一連の Response.Write() ステートメントを使用して、コンテンツを出力していることに注意してください。ProcessRequest(HttpContext) メソッドの目的は、ページのマークアップを Response オブジェクトの出力ストリームに書き込むことです。これを実現する簡単な方法が、Response.Write() の使用です。(「画像の保護」セクションでは、画像ファイルのバイナリ コンテンツを直接 Respose オブジェクトの OutputStream プロパティに書き込む方法を説明します)。

Web コントロールの処理

HTML マークアップを生成する HTTP ハンドラでは、Response.Write() ステートメントの使用だけでは不十分な場合もあります。代わりに、HTML マークアップを出力するのに Web コントロールを使用することがあります。ASP.NET Web ページで Web コントロールを使用するときのように簡単で、わかりやすいものではありませんが、HTTP ハンドラでも Web コントロールを使用できます。採用できる技法は、2 つあります。

  1. HTTP ハンドラの出力のテンプレート として機能する別の ASP.NET Web ページを作成します。HTTP ハンドラは、テンプレートと動的コンテンツを連携させて表示を行います。
  2. HTTP ハンドラ内のプログラムで Web コントロールを作成し、RenderControl() メソッドを使用して、その Web コントロールの HTML マークアップを生成します。

最初の方法は、コードとコンテンツを明確に分離できるので、最適な方法と言えます。つまり、HTTP ハンドラが生成す HTML マークアップは、HTTP ハンドラの Response.Write() ステートメントではなく、テンプレートの ASP.NET Web ページを修正するだけで変更でます。しかし、この手法を適切に実現するには、少し作業が必要になります。「HTTP ハンドラ ファクトリによる URL ドリブン コンテンツの表示」 セクションでは、この技法を使用する HTTP ハンドラについて説明します。

2 つ目の方法では、HTTP ハンドラの ProcessRequest() メソッドで生成されるようにする Web コントロール クラスのインスタンスをプログラムで作成する必要があります。この技法は、別のコントロールでネストされる Web コントロールを追加する場合に、Web コントロールの階層を自分で手動で作成する必要があるため、少し複雑になります。コントロール階層を作成した後、コントロール階層の HTML を RenderControl() メソッドを使用して生成します。

次のコードは、HTTP ハンドラでプログラムで Web コントロールを生成する方法を示しています。

                  
public void ProcessRequest(HttpContext context)
{
   // Panel をルートとし、
   // 2 つの Label、および LiteralControl を子とするコントロール階層を作成します。
   Panel p = new Panel();

   Label lbl1 = new Label();
   lbl1.Text = "Hello, World!";
   lbl1.Font.Bold = true;

   Label lbl2 = new Label();
   lbl2.Text = "How are you?";
   lbl2.Font.Italic = true;

   p.Controls.Add(lbl1);
   p.Controls.Add(new LiteralControl(" - "));
   p.Controls.Add(lbl2);

   // Panel コントロールを生成します。
   StringWriter sw = new StringWriter();
   HtmlTextWriter writer = new HtmlTextWriter(sw);
   p.RenderControl(writer);

   // 生成された HTML を出力します。
   context.Response.Write(sw.ToString());
}

                

(このコードを機能させるためには、System.IOSystem.WebSystem.Web.UI、および System.Web.UI.WebControls の名前空間を、Imports、または using のステートメントを使用してインクルードする必要があります。)

手動でのコントロール階層の作成、および生成は、Web コントロールをドラッグ アンド ドロップで追加したり、宣言的 Web コントロール構文を使用したりする場合よりも、明らかに難しくなります。ASP.NET Web ページの HTML 部分を変更してページを表示すると、HTML 部分が、前の例と同じように、コントロール階層をプログラムで作成するクラスに変換されることに注意してください。

Simple HTTP ハンドラを使用するための ASP.NET Web アプリケーションの構成

HTTP ハンドラ クラスを作成できれば、後は、ASP.NET Web アプリケーションが特定のファイル拡張子でそのハンドラを使用するように構成するだけです。HTTP ハンドラのクラス ライブラリ プロジェクト用に、新規の Microsoft Visual Studio .NET ソリューションを作成している場合、これの最も簡単な方法としては、そのソリューションに新規の ASP.NET Web アプリケーション プロジェクトを追加します。さらに、HTTP ハンドラ プロジェクトを、ASP.NET Web アプリケーションの References フォルダに追加します。(References フォルダを右クリックし、[Add Reference] を選択します。[Add Reference] ダイアログ ボックスで、[Projects] タブを選択し、前のセクションで作成したクラス ライブラリ プロジェクトを選択します。) Visual Studio .NET を使用しない場合は、HTTP ハンドラのアセンブリを、手動で、ASP.NET Web アプリケーションの /bin ディレクトリにコピーする必要があります。

Web アプリケーションが HTTP ハンドラを使用するように構成するには、ファイル拡張子 (または、特定のパス) を HTTP ハンドラにマップする必要がありました。.simple のように、独自の拡張子を付けることもできますが、そうするには、IIS でも .simple 拡張子を ASP.NET エンジンの ISAPI 拡張 (aspnet_isapi.dll) にマップするように構成する必要があります。共有 Web サーバーでサイトをホストする場合には、Web ホスティング企業が、カスタム マッピングを IIS メタベースに追加することを許可しないこともあります。ただし、.ashx 拡張子は、Web サーバーに .NET Framework がインストールされていれば、自動的に追加され、ASP.NET エンジンの ISAPI 拡張にマップされています。このため、この拡張子は、IIS メタベースへのアクセス、または修正の許可がない場合に、カスタム HTTP ハンドラで使用できます。

最初のこの例では、.ashx 拡張子を使用して、Web アプリケーションがカスタム HTTP ハンドラを使用するように構成します。これは、次の <httpHandlers> セクションを Web アプリケーションの Web.config ファイルに追加することで実現します。

                  
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <httpHandlers>
       <!-- Simple Handler -->
       <add verb="*" path="*.ashx"
         type="skmHttpHandlers.SimpleHandler, skmHttpHandlers" />
    </httpHandlers>
  </system.web>
</configuration>

                

この <add> エレメントは、すべての HTTP 動詞について、拡張子が .ashx であるすべてのファイルへの着信要求が、skmHttpHandlers.SimpleHandler HTTP ハンドラで処理されることを示しています。type 属性の値には、使用する HTTP ハンドラ クラスのタイプ (namespace.className) と、カンマで区切って、HTTP ハンドラ クラスが存在するアセンブリ名を指定します。

この <httpHandlers> セクションを Web アプリケーションの Web.config ファイルに追加すると、拡張子が .ashx であるすべてのパスが、図 3 に示すページを表示するようになります。

Dd297788.httphandlers_fig03(ja-jp,MSDN.10).gif

図 3. Simple HTTP ハンドラ


図 3 は、Web アプリケーションの HelloWorld.ashx ファイルを表示したときのブラウザのスクリーンショットです。このファイル HelloWorld.ashx は、実際には存在しないことに注意してください。次のような処理が行われています。

  1. HelloWorld.ashx への要求が IIS に到着します。
  2. IIS は、拡張子が .ashx であることを認識し、要求を ASP.NET エンジンに送ります。
  3. ASP.NET エンジンは、Web.config ファイルを照会し、.ashx ファイルは、SimpleHandler HTTP ハンドラで処理する必要があることを認識します。このため、ASP.NET エンジンは、このクラスのインスタンスを作成し、現在の HttpContext を渡して ProcessRequest() メソッドを呼び出します。
  4. SimpleHandler HTTP ハンドラは、現在時刻、および要求の詳細を Response オブジェクトの出力ストリームに出力します。
  5. ASP.NET エンジンは、HTTP ハンドラで生成された HTML マークアップを IIS に戻します。
  6. IIS は、生成された HTML マークアップを HelloWorld.ashx を要求したブラウザに戻します。
  7. ブラウザは、図 3 に示すように、生成された HTML を表示します。

この 7 つのステップは、ASPisNeat.ashx、myfile.ashx など、拡張子が .ashx であるどのようなファイルを要求しても、同じように実行されます。

実際に使用されている HTTP ハンドラの分析

ここまでは、HTTP ハンドラの作成、およびそのハンドラを使用するための Web アプリケーションの構成に必要なステップを説明してきました。ここからは、実際の ASP.NET Web アプリケーションで使用を開始できるように、実践的な HTTP ハンドラを説明します。(ここで紹介する HTTP ハンドラの考えの大部分は、HTTP ハンドラの実際の使用例を募集した筆者のブログ (英語) を基にしています。)

ここからは、次の 3 つの HTTP ハンドラについて説明します。

  • CodeFormatHandler コード ファイル (.cs ファイル、および .vb ファイル) を Visual Studio .NET と同様の形式で書式設定する HTTP ハンドラ。(このハンドラは、要求がローカル ホストから行われている場合にのみコード ファイルを表示します。)
  • ImageHandler GIF、および JPEG の画像を提供する HTTP ハンドラ。このハンドラは、画像に透かしを入れます。さらに、画像が "持ち出し" されていないか (つまり、別の Web サイトからその画像にリンクしていないか) をチェックします。
  • EmployeeHandlerFactory 従業員データベースから特定の従業員に関する情報を表示する別の ASP.NET Web ページを処理する HTTP ハンドラ ファクトリ

この 3 つのハンドラのサンプルすべての完全なソースは、使用をデモンストレーションする ASP.NET Web アプリケーションのソース コードも併せて、この記事のダウンロードから入手可能です。

HTTP ハンドラによるコードの書式設定

ASP.NET Web ページの分離コード クラスのソース コードをすぐに確認したいのに、Visual Studio .NET を起動し、関連のプロジェクトを開くような時間がないことはありませんか。場合によっては、既に、Visual Studio .NET をいくつか開いており、さらに、Microsoft Outlook、Microsoft Word、.NET Framework ドキュメンテーション、SQL Enterprise Manager、および Web ブラウザも同時に起動しているかもしれません。このようなときに、さらに Visual Studio .NET を開くことはあまりしたくありません。

サーバーに存在する分離コード クラスにアクセスし、そのソースを表示できれば、それが理想的です。つまり、WebForm1.aspx ページのコードを確認する場合は、単純に、ブラウザで http://localhost/WebHost1.aspx.cs にアクセスできればよいのです。しかし、これを試みると、"This type of page is not served" (図 2 を参照) となります。これは、既定で、.cs、および .vb の拡張子が HttpForbiddenHandler HTTP ハンドラにマップされているためです。分離コード クラスに、接続文字列や、不特定のユーザーに表示したくない機密情報が含まれる可能性を考えれば、この動作は適切です。また、ASP.NET Web アプリケーションを開発サーバーから、本番サーバーに移動するときには、分離 コード クラスのファイルはコピーせず、.aspx ファイル、および /bin ディレクトリ内の必要なアセンブリのみをコピーするのが最善とされています。

しかし、開発サーバーでは、分離コードのソース コードをブラウザで表示したい場合があります。1 つの方法として、.cs、および .vb のファイルの HttpForbiddenHandler HTTP ハンドラへのマッピングを除去することができます。(これは、machine.config ファイルを修正することで開発サーバー全体に適用できます。また、Web.config ファイルの <httpHandlers> セクション内で <remove> エレメントを使用すれば、アプリケーション単位に適用できます。) このようにした場合、ソース コードは、プレーン テキストとして書式設定されずに表示されます。(図 4 を参照)。

Dd297788.httphandlers_fig04(ja-jp,MSDN.10).gif

図 4. HTTP ハンドラによるコードの表示


これは確かに機能しますが、ソース コードの表示としては最適ではありません。しかし、無料の .NET ライブラリを使用して、コードを HTML で書式設定することが可能です。たとえば、squishyWARE による squishySyntaxHighlighter (英語) を参照してください。 squishySyntaxHighlighter は、わずかなコードで、Visual Basic.NET コード、C# コード、または XML コンテンツを含む文字列を受け取り、その渡された内容を、Visual Studio .NET がコード、および XML コンテンツを表示するのと同様の方法で表示する HTML の文字列を戻します。これを実現するために、HTTP ハンドラを使用します。これは、特定の種類のコンテンツを表示するという、HTTP ハンドラの目的そのものです。この場合は、分離コード クラスの書式設定された表示を提供します。

以下のコードは、CodeFormatHandler HTTP ハンドラの ProcessRequest() メソッドを示しています。このコードの中で使用されている SyntaxHighlighter クラスは、squishySyntaxHighlighter によって提供されているクラスです。コードを書式設定するには、SyntaxHighlighter クラスのインスタンスを、GetHighlighter() static メソッドを使用して作成します。このとき、コードをどのように書式設定するか (Visual Basic.NET、C#、または XML) を指定します。次に、作成したインスタンスの Highlight(contents) メソッドで、文字列入力 (contents) を受け取り、内容の HTML で書式設定された表示を戻します。このメソッドの最後では、書式設定した内容を、Response.Write() を使用して出力しています。

                  
public void ProcessRequest(HttpContext context)
{
   string output = string.Empty;

   // ファイルの内容を取得します。
   StreamReader sr = File.OpenText(context.Request.PhysicalPath);
   string contents = sr.ReadToEnd();
   sr.Close();

   // 拡張子の基づいてファイルを書式設定する方法を判断します。
   string extension = Path.GetExtension(
     context.Request.PhysicalPath).ToLower();
   SyntaxHighlighter highlighter;

   if (extension == ".vb")
   {
      highlighter = SyntaxHighlighter.GetHighlighter(
        SyntaxType.VisualBasic );
      output = highlighter.Highlight( contents );
   }
   else if (extension == ".cs")
   {
      highlighter = SyntaxHighlighter.GetHighlighter(
        SyntaxType.CSharp );
      output = highlighter.Highlight( contents );
   }
   else // 未知の拡張子
   {
      output = contents;
   }

   // 書式設定した内容を出力します。
   context.Response.Write("<html><body>");
   context.Response.Write(output);
   context.Response.Write("</body></html>");
}

                

図 5 は、図 4 と同じコードを、CodeFormatHandler を使用して書式設定したときのスクリーンショットです。

Dd297788.httphandlers_fig05(ja-jp,MSDN.10).gif

図 5: HTTP ハンドラによって書式設定されたコード


注意 ダウンロードに含まれる CodeFormatHandler HTTP ハンドラの ProcessRequest() メソッドは、より堅牢になっており、ページへのローカルホストによるアクセスのみに、分離コード クラスのソース コードの表示を許可します。

HTTP ハンドラを Web アプリケーションで構成するには、単純に、アプリケーションの Web.config ファイルの <httpHandlers> セクションに <add> エレメンを追加します (Web サーバー全体でハンドラを使用する場合には machine.config ファイルに追加します)。次のように設定することで、.cs、および .vb のどちらの拡張子も、CodeFormatHandler (HttpForbiddenHandler ではなく) に送られます。

                  
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <httpHandlers>
       <!-- Code Format handler -->
       <add verb="*" path="*.cs"
         type="skmHttpHandlers.CodeFormatHandler,
         skmHttpHandlers" />
       <add verb="*" path="*.vb"
         type="skmHttpHandlers.CodeFormatHandler,
         skmHttpHandlers" />
    </httpHandlers>

    ...
  </system.web>
</configuration>

                

画像の保護

Web では、コンテンツの半分はオリジナルだが、残りの半分は盗用したものであると言われることもあります。コンピュータでは、他人の作品から、その複製を作成することが簡単にできます。たとえば、プロの写真家が、自身の Web サイトに画像をいくつか掲載するとします。こうした場合、画像が、他の人によって単純に保存され、あたかもその人の作品かのように別の Web サイトに掲載されるのは防ぎたいと考えます。他のサイトでの自分の画像の使用を許可する場合でも、そのサイトで、自分側の Web サーバーを指す <img> タグが追加されるのは避け、画像自体がそのサイトに保存されるようにする必要があります。(たとえば、www.bandwidthThief.com サイトの Web ページに、<img> タグを <img src="http://yourSite.com/BigImage.jpg"> という指定で配置する場合などです。これは、避けた方がよいとされています。ユーザーは、www.bandwidthThief.com の Web サイトを参照しているのにもかかわらず、yourSite.com の Web サーバーから画像を提供するので、yourSite.com 側の帯域幅に負荷がかかることになるためです。)

画像を保護するために、次の 2 つを行う HTTP ハンドラを作成します。

  1. 要求された画像が、別のサイトからリンクされていないことを確認する。
  2. 画像に透かしを入れる。

画像が要求されたときに、ほとんどのブラウザは、referrer (参照元) HTTP ヘッダーで、画像を含んでいる Web ページの URL を送信します。このため、この HTTP ハンドラで行う処理は、referrer HTTP ヘッダーのホストと、画像 URL のホストが同じであることをチェックします。同じでない場合は、帯域幅の不正使用が行われていることになります。この場合は、オリジナルの画像 (または、透かしの入った画像) を戻すのではなく、"この画像は www.YourSite.com で参照できます" などの代替画像を戻します。

注意 ブラウザによっては、画像を要求するときには、referrer HTTP ヘッダーを送信しない場合があります。また、この機能を無効に設定することが可能な場合もあります。このため、この技法は、完全ではありません。しかし、大部分の Web ユーザーについては機能するので、不正な Web サイトの設計者に、自分側の Web サーバーの画像への直接リンクをやめさせる効果は期待できます。

透かしを入れるには、System.Drawing 名前空間のクラスを使用して、画像の中央にテキスト メッセージを追加します。.NET Framework は、System.Drawing 名前空間に、実行時に画像を作成、および修正するのに使用できるクラスを多数含んでいます。このクラスの説明は、この記事の内容を超えていますが、詳細についての入り口としては、Chris Garrett の GDI+ and System.Drawing Articles (英語) をお勧めします。

ImageHandler HTTP ハンドラの作成

以下のコードは、ImageHandler HTTP ハンドラの ProcessRequest() メソッドを示しています。

                  
public void ProcessRequest(HttpContext context)
{
   if (context.Request.UrlReferrer == null ||
      context.Request.UrlReferrer.Host.Length == 0 ||
      context.Request.UrlReferrer.Host.CompareTo(
        context.Request.Url.Host.ToString()) == 0)
   {
      // 画像のバイナリ データを取得します。
      Bitmap bmap = new Bitmap(context.Request.PhysicalPath);

      // 透かしを入れる必要があるか判断します。
      if (ImageConfiguration.GetConfig().AddWatermark)
      {
         // ビットマップのインスタンスから Graphics オブジェクトを作成します。
         Graphics gphx = Graphics.FromImage(bmap);

         // フォントを作成します。
         Font fontWatermark = new Font("Verdana", 8, FontStyle.Italic);

         // テキストが、
         // 縦、および横の両方で
         // 中心となるように指定します。
         StringFormat stringFormat = new StringFormat();
         stringFormat.Alignment = StringAlignment.Center;
         stringFormat.LineAlignment = StringAlignment.Center;

         // 透かしを入れます...
         gphx.DrawString(ImageConfiguration.GetConfig().WatermarkText,
                        fontWatermark, Brushes.Beige,
                        new Rectangle(10, 10, bmap.Width - 10,
                          bmap.Height - 10),
                        stringFormat);

         gphx.Dispose();
      }


      // 送信するファイルの種類を判断します。
      switch (
        Path.GetExtension(context.Request.PhysicalPath).ToLower())
      {
         case ".gif":
            context.Response.ContentType = "image/gif";
            bmap.Save(context.Response.OutputStream, ImageFormat.Gif);
            break;

         case ".jpg":
            context.Response.ContentType = "image/jpeg";
            bmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
            break;
      }

      bmap.Dispose();
   }
   else
   {
      string imgPath =
        context.Server.MapPath(
        ImageConfiguration.GetConfig().ForbiddenFilePath);
      Bitmap bmap = new Bitmap(imgPath);

      // 送信するファイルの種類を判断します。
      switch (Path.GetExtension(imgPath))
      {
         case ".gif":
            context.Response.ContentType = "image/gif";
            bmap.Save(context.Response.OutputStream, ImageFormat.Gif);
            break;

         case ".jpg":
            context.Response.ContentType = "image/jpeg";
            bmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
            break;
      }

      bmap.Dispose();
   }
}

                

ImageHandler は、HTTP ハンドラが使用する構成情報を含む ImageConfiguration クラスと連携して機能します。ImageConfiguration クラスには、該当する Web.config セクションをデシリアライズする static GetConfig() メソッドによってデータが設定されます。ImageConfiguration には、次の 3 つのプロパティがあります。

  • FobiddenFilePath 画像が別の Web サイトから要求されている場合に、要求された画像の代わりに表示するファイルへのパス。
  • AddWatermark すべての画像に透かしを入れるかどうかを示す Boolean。
  • WatermarkText "Copyright Scott Mitchell" など、透かしとして表示するテキスト。

これらの設定は、次のように、Web アプリケーションの Web.config ファイルの <ImageHandler> セクションで指定されます。

                  
<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <configSections>
     <section name="ImageHandler"
       type=
         "skmHttpHandlers.Config.ImageConfigSerializerSectionHandler,
         skmHttpHandlers" />
  </configSections>

  <ImageHandler>
    <forbiddenFilePath>
       ~/images/STOP-STEALING-MY-BANDWIDTH.gif
    </forbiddenFilePath>
    <addWatermark>true</addWatermark>
    <watermarkText>Copyright Scott Mitchell</watermarkText>
  </ImageHandler>

  <system.web>
    ...
  </system.web>
</configuration>

                

この設定は、画像が外部のサイトから要求されたことが検出された場合には、エンド ユーザーに、~/images/STOP-STEALING-MY-BANDWIDTH.gif の画像を表示することを指定しています。 さらに、すべての画像に "Copyright Scott Mitchell" というテキストの透かしを入れることを指定しています。

ProcessRequest() メソッドは、最初に、referrer HTTP ヘッダーの Host と、画像の URL の Host が一致しているかを判断することで、この画像がリモート ホストから要求されているものであるかどうかをチェックします。ホストが一致しない場合、画像は別の Web サーバーから要求されていることになるので、ユーザーを、ImageConfiguration クラスの ForbiddenFilePath プロパティで指定されている画像にリダイレクトします。ホストが一致する場合、コードは、AddWatermark プロパティが true であるかをチェックし、true である場合は、指定された WatermarkText を使用して画像に透かしを入れます。

Web アプリケーションが ImageHandler HTTP ハンドラを使用するための構成

ASP.NET Web で ImageHandler HTTP ハンドラを使用するには、最初に、ImageConfiguration のプロパティを、Web.config の <ImageHandler> セクションに追加します。次に、<add> エレメントを含め、.gif、および .jpg の拡張子をこの HTTP ハンドラに関連付けます。<ImageHandler> セクションについては前に説明したので、以下には、<httpHandlers> セクションのみを示します。次に示すように、<add> エレメントは、2 つ追加する必要があります。1 つは、.gif ファイルとハンドラのマッピング、もう 1 つは、.jpg ファイルのマッピングです。

                  
<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  ... <ImageHandler> セクション...

  <system.web>
    <httpHandlers>
        <!-- ImageHandler ハンドラ -->
        <add verb="*" path="*.jpg"
         type="skmHttpHandlers.ImageHandler, skmHttpHandlers" />
        <add verb="*" path="*.gif"
         type="skmHttpHandlers.ImageHandler, skmHttpHandlers" />
    </httpHandlers>

    ...
  </system.web>
</configuration>

                

Web.config ファイルに <httpHandlers> セクションを追加したら、さらに、IIS が、.gif ファイル、および .jpg ファイルへの要求をすべて、aspnet_isapi.dll ISAPI 拡張に送るように構成する必要があります。これを行わない場合、GIF ファイル、または JPEG ファイルへの要求が到着したときに、IIS 自体がその要求を処理してしまいます。GIF、または JPEG への要求が着信したときに、その要求が ASP.NET エンジンに送られるように、IIS メタベースにこのマッピングを追加する必要があります。要求を送られた ASP.NET エンジンは、その要求を、さらに ImageHandler HTTP ハンドラに送ります。

これで、HTTP ハンドラの作成、およびそのハンドラを使用するための Web アプリケーション (および IIS) の構成に関する説明は完了です。このハンドラの動作を確認してみましょう。図 6 は、筆者のペット Sam の 2 つの画像を表示する Web ページです。画像には、指定したテキストでの透かしが入っていることに注意してください。

Dd297788.httphandlers_fig06(ja-jp,MSDN.10).gif

図 6. 透かしの入った画像


図 7 は、別のサーバーから画像へのアクセスを試みる Web ページです。この例では、Web ページの HTML に、<img src="http://mitchellsvr/HttpHandlerTest/images/SamSitting.jpg"> と指定した <img> タグが含まれています。(mitchellsvr は、筆者のコンピュータの名前です。) ページが http://localhost/HttpHandlerTest/WebForm1.aspx から要求される場合、ブラウザは、 referrer HTTP ヘッダーとして、http://localhost/HttpHandlerTest/WebForm1.aspx を送信します。一方、要求される画像の URL は http://mitchellsvr/HttpHandlerTest/images/SamSitting.jpg です。ImageHandler HTTP ハンドラは、referrer の Host と、画像 URL の Host が異なることを検知し、STOP-STEALING-MY-BANDWIDTH.gif の画像を表示します。

Dd297788.httphandlers_fig07(ja-jp,MSDN.10).gif

図 7. 画像の要求の制限


HTTP ハンドラ ファクトリによる URL ドリブン コンテンツの表示

Web 開発者であれば、おそらく、パラメータに基づいてさまざまなデータを表示する単一ページを作成したことがあるでしょう。たとえば、クエリ ストリングによって渡された従業員 ID に基づいて、その従業員の情報を表示するといった Web ページです。/DisplayEmployee.aspx?EmpID=EmployeeID という URL で Web ページを提供することもできますが、/employees/name.info のような、より覚えやすい URL を使用して情報を提供することもできます。(この代わりの URL では、Jisun Lee という従業員の情報を表示する場合には、/employees/JisunLee.info にアクセスします。)

URL を読みやすく、覚えやすくする技法は、いくつかあります。1 つが、URL 書き換えを使用する方法です。 筆者の前の記事 ASP.NET での URL 書き換え では、 HTTP モジュール、および HTTP ハンドラを使用して URL 書き換えを実行する方法を説明しています。URL 書き換えは、存在しない URL への Web 要求をインターセプトし、その要求を実在する URL への要求として送り直すプロセスです。URL 書き換えは、URL を短く、覚えやすくするために一般的に使用されています たとえば、ある電子商取引の Web サイトに、/ListProductsByCategory.aspx というページがあるとします。このページは、特定のカテゴリの商品をすべてリストします。商品のカテゴリは、クエリ ストリングのパラメータによって指定されます。つまり、販売するある種の部品をすべてリストする場合には、/ListProductsByCategory.aspx?CatID=PN-1221 というようになります。URL 書き換えでは、/Products/Widgets.aspx というような実際には存在しない "偽" の URL を定義できます。/Products/Widget.aspx への要求が ASP.NET エンジンに到着すると、URL 書き換えによって、ユーザーは、透過的に /ListProductsByCategory.aspx?CatID=PN-1221 にリダイレクトされます。しかし、ユーザーのブラウザのアドレス欄には、/Products/Widgets.aspx が表示されます。ASP.NET エンジンでの URL 書き換えは、通常、HttpContext クラスの RewritePath(newURL) メソッドを使用して実現されます。これは、HTTP モジュール、および HTTP ハンドラのどちらでも使用できます。

2 つめの技法は、.info ファイルの表示方法を認識する HTTP ハンドラの作成です。この HTTP ハンドラでは、要求された URL を検査し、従業員の名前を抽出することで、その従業員の情報を表示します。従業員の名前を取得した後に、データベース テーブルを検索して要求されている従業員に関する情報を取得します。最後に、この情報を HTML マークアップとして表示するステップが必要となります。

ImageHandler の例で、HTTP ハンドラが、要求されたリソースの URL を検査する方法を説明したので、従業員の名前の抽出、およびその従業員に関するデータベース上の情報へのアクセスは、比較的わかりやすいでしょう。ここで課題となるのは、従業員情報の表示です。最も簡単なのは、HTTP ハンドラで、HTML 出力をハード コードする方法です。Response.Write() ステートメントを使用して HTML マークアップを正確に出力し、従業員の情報を必要な場所に挿入します。(これは、最初の HTTP ハンドラの例 SimpleHandler と同じ動作です。)

また、より優れた手法として、ASP.NET ページをテンプレートとして使用し、コードとコンテンツを分離する方法があります。これを実現するには、最初に、Web アプリケーションに ASP.NET Web ページを作成しておく必要があります。このページは、他の ASP.NET Web ページと同様に、HTML マークアップと、Web コントロールの両方で構成されます。この例では、従業員の名前、社会保障番号、および簡単な経歴を表示します。このため、ページには、これらの 3 つの項目に対応した 3 つのラベルを用意します。図 8 に、Visual Studio .NET の [デザイン] タブ に表示される ASP.NET Web ページ DisplayEmployee.aspx のスクリーンショットを示します。

Dd297788.httphandlers_fig08(ja-jp,MSDN.10).gif

図 8. 従業員情報のページ


次に、HTTP ハンドラ ファクトリ を作成する必要があります。HTTP ハンドラ ファクトリは、System.Web.IhttpHandlerFactory インターフェイスを実装するクラスであり、呼び出されたときに、IHttpHandler を実装するクラスのインスタンスを戻します。Web アプリケーションが HTTP ハンドラ ファクトリを使用するように構成する方法は、HTTP ハンドラの場合とまったく同じです。Web アプリケーションで、拡張子と HTTP ハンドラ ファクトリをマップすれば、ASP.NET エンジンは、その拡張子への要求が到着したときに、HTTP ハンドラ ファクトリに IHttpHandler インスタンスを求めるようになります。HTTP ハンドラ ファクトリは、そのインスタンスを ASP.NET エンジンに提供し、ASP.NET エンジンは、そのインスタンスの ProcessRequest() メソッドを呼び出します。ASP.NET エンジンは、HTTP ハンドラ ファクトリの GetHandler() メソッドを呼び出すことにより IHttpHandler インスタンスを要求します。

この例では、GetHandler() メソッドは、2 つの処理を行う必要があります。1 つは、要求されている従業員の名前の判断、もう 1 つは、その従業員の情報の取得です。その後で、前に作成した ASP.NET Web ページ (DisplayEmployee.aspx) を提供する HTTP ハンドラを戻します。しかし、ASP.NET Web ページを提供する HTTP ハンドラを作成する必要はありません。これは、.NET Framework に既に存在しており、System.Web.UI.PageParser クラスの GetCompiledPageInstance() メソッドを呼び出すことで取得できます。このメソッドは、3 つの入力を受け取ります。要求された Web ページの仮想パス、要求された Web ページの物理パス、およびこの Web ページの要求で使用された HttpContext です。GetCompiledPageInstance() がどのように動作するかを詳細に理解する必要はありません。ただし、このメソッドは、ASP.NET の HTML 部分をクラスにコンパイルし、必要な場合には、このクラスのインスタンスを戻すことに注意してください。(この自動生成クラスは、System.Web.UI.Page クラスから派生する分離コード クラスから派生します。Page クラスは、IHttpHandler を実装するので、GetCompiledPageInstance() から戻されるクラスは、HTTP ハンドラです。)

最後の課題は、HTTP ハンドラ ファクトリから、ASP.NET Web ページ テンプレートに従業員情報を渡す方法です。HttpContext オブジェクトには、Items プロパティがあります。これは、同じ HttpContext を共有するリソース間で情報を渡すために使用できます。このため、従業員のデータは、ここに保管します。

最後のステップは、ASP.NET Web ページ テンプレート (DisplayEmployee.aspx) に戻すことです。テンプレートの分離コード クラスでは、従業員情報を HttpContextItems プロパティから取得し、その従業員データを、ページの HTML 部分にあるそれぞれの Label Web コントロールに割り当てる必要があります。

このコードのダウンロードに含まれているデモンストレーションでは、従業員のクラス表現 (Employee) 、および名前に基づいて従業員を生成するクラス (EmployeeFactory) を提供する EmployeeBOL プロジェクトで一連のクラスを提供しています。HTTP ハンドラ ファクトリの GetHandler() メソッドを以下に示します。最初に、従業員の名前を判断し、その後、EmployeeFactory.GetEmployeeByName() によって戻される Employee オブジェクトを HttpContextItems コレクションに追加していることに注意してください。最後に、DisplayEmployee.aspx ASP.NET Web ページ テンプレートを物理ファイル パスとして PageParser.GetCompiledInstance() に渡し、そこから戻される IHttpHandler インスタンスを戻しています。

                  
public class EmployeeHandlerFactory : IHttpHandlerFactory
{
   ...

   public IHttpHandler GetHandler(HttpContext context,
     string requestType, string url, string pathTranslated)
   {
      // 従業員の名前を判断します。
      string empName =
        Path.GetFileNameWithoutExtension(
        context.Request.PhysicalPath);

      // Employee オブジェクトを Items プロパティに追加します。
      context.Items.Add("Employee Info",
        EmployeeFactory.GetEmployeeByName(empName));

      // DisplayEmployee.aspx HTTP ハンドラを取得します。
      return PageParser.GetCompiledPageInstance(url,
        context.Server.MapPath("DisplayEmployee.aspx"), context);
   }
}

                

DisplayEmployee.aspx ASP.NET Web ページ テンプレートの分離コード クラスでは、HttpContextItems プロパティから Employee クラスのインスタンスにアクセスし、Label Web コントロールの Text プロパティに、対応する Employee プロパティを代入します。

                  
public class DisplayEmployee : System.Web.UI.Page
{
   // ページの HTML 部分に置く 3 つの Label Web コントロール
   protected System.Web.UI.WebControls.Label lblName;
   protected System.Web.UI.WebControls.Label lblSSN;
   protected System.Web.UI.WebControls.Label lblBio;

   private void Page_Load(object sender, System.EventArgs e)
   {
      // Employee の情報を context からロードします。
      Employee emp = (Employee) Context.Items["Employee Info"];

      if (emp != null)
      {
         // Employee のプロパティを、Label コントロールに代入します。
         lblName.Text = emp.Name;
         lblSSN.Text = emp.SSN;
         lblBio.Text = emp.Biography;
      }
   }
}

                

Web アプリケーションが HTTP ハンドラ ファクトリを使用するように構成するには、HTTP ハンドラの場合と同様に、<httpHandlers> セクションに <add> エレメントを追加します。

                  
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <httpHandlers>
      <!-- EmployeeHandlerFactory -->
      <add verb="*" path="*.info"
         type="skmHttpHandlers.EmployeeHandlerFactory,
         skmHttpHandlers" />
    </httpHandlers>
  </system.web>
</configuration>

                

この HTTP ハンドラ ファクトリは、.info 拡張子を使用するので、.info 拡張子を IIS で aspnet_isapi.dll ISAPI 拡張にマップする必要もあります。図 9 は、employee HTTP ハンドラ ファクトリを実行したときのスクリーンショットです。

Dd297788.httphandlers_fig09(ja-jp,MSDN.10).gif

図 9. 従業員情報のハンドラの使用


テンプレートを使用するこのような方法の利点は、従業員情報のページを変更する場合に、DisplayEmployee.aspx ページの編集のみで済むことです。HTTP ハンドラ ファクトリを変更する必要はありません。また、HTTP ハンドラ ファクトリのアセンブリをリコンパイルする必要もありません。

注意 .Text (英語) (Scott Watermasysk によって作成された C# のオープンソースのブログ エンジン) は、HTTP ハンドラ ファクトリを使用して、URL ドリブン コンテンツを提供しています。.Text のテンプレート システムは、この記事で説明したものよりもかなり綿密に作成されています。 単一のマスタ テンプレート ページ DTP.aspx があり、これが、すべての要求のテンプレートとして使用されています。しかし、このテンプレートは、行われる要求の種類に基づいてカスタマイズすることが可能です。たとえば、/blog/posts/123.aspx というような URL が要求された場合、.Text は、URL パス (/posts/) から、ユーザーが特定のポストを表示しようとしていると判断することができます。このため、マスタ テンプレート ページは、単一のブログ ポストを表示する専用のユーザー コントロールのセットをロードすることにより (実行時に) カスタマイズされます。一方、 /blog/archive/2004/04.aspx への要求が到着した場合、.Text は、パス (/archive/2004/XX.aspx) から、特定の月 (この例では 2004 年 4 月) のすべてのポスト を表示しようとしていると判断することができます。このため、マスタ テンプレート ページには、1 か月分のエントリを表示するのに適したユーザー コントロールがロードされるようにします。

まとめ

IIS における ISAPI 拡張と同様に、HTTP ハンドラは、ASP.NET エンジンと、Web コンテンツの表示の間に抽象的なレベルを提供します。ここまでで説明したように、HTTP ハンドラは、指定された拡張子に基づき、特定の種類の要求のマークアップを生成します。HTTP ハンドラは、IHttpHandler インターフェイスを実装し、ProcessRequest() メソッドですべての処理を提供します。

この記事では、HTTP ハンドラの実際のシナリオを 3 つ説明しました。コードを書式設定するハンドラ、画像を保護するハンドラ、およびテンプレートを使用して URL ドリブン コンテンツを提供する HTTP ハンドラ ファクトリです。この他にも、HTTP ハンドラは、次のような目的に使用できます。

  • 外部 CSS ファイルに変数を追加する (詳細については、Rory Blythブログ (英語) を参照してください)。
  • 画像のサムネールのリストを表示する (Andrew Connell がこのブログ コメント (英語) で賛同している考え)。
  • 余白およびコメントの除去や、変数の名前変更などにより外部 JavaScript ファイルを最適化する。

HTTP ハンドラの使用について優れたアイディアがあれば、筆者のブログ REQUEST: Ideas for a Useful HTTP Handler Demo (英語) へのコメントをお待ちしております。

プログラムを楽しんでください。

関連書籍

著者紹介

Scott Mitchell は 4GuysFromRolla.com の創設者であり、過去 6 年間にわたりマイクロソフトの Web テクノロジに携わり、5 冊の書籍を著しています。現在、フリーランスのコンサルタント、トレーナー、およびライターとして活動しています。彼の連絡先は mitchell@4guysfromrolla.com です。また、ブログは http://ScottOnWriting.NET (英語) から参照できます。


Page view tracker