アーカイブ

Archive for 2007年6月

ASP.NET の仕組み

2007年6月18日 1件のコメント

■ASP.NET の仕組み

#この記事はVSUGのメールニュースに連載したコラムをまとめ、説明用の図を追加したものです。

1.処理の開始と HttpContext

1

ASP.NET を含む Web アプリケーションでは、ブラウザから Web サーバにリクエストが投げられることによって一回の処理が開始され、Web サーバがブラウザにHTMLを送り返すことで処理が終了します。Web アプリケーションはこのリクエスト単位の処理を何回も繰り返すことによって、一つのシステムとして動作しているのです。
そしてASP.NET では、Web サーバとして必ず IIS が動作しています。このため、ブラウザから投げられたリクエストをサーバ上でまず IIS が受け取ります。IIS は要求されているURL の拡張子を確認し、その拡張子がマッピングされている処理にリクエストを渡します。
IIS 上では .aspx や .ashx、.asmx といった拡張子がASP.NETにマッピングされていますので、それらのURLに対するリクエストがASP.NETによって処理されることになります。

ASP.NET がリクエストを受け取ったときに最初に生成されるのが HttpContextオブジェクトです。HttpContext オブジェクトにはブラウザからのリクエストの内容が 格納されるだけでなく、ユーザ情報やキャッシュ、アプリケーション、セッションといったオブジェクト、そしてブラウザに送るレスポンスの内容も格納されます。各ページの処理で利用する Application、Cache、Request、Response、Session といった組み込みオブジェクトは、実際には HttpContext オブジェクトの中身を参照していると考えられます。
通常は HttpContext オブジェクトの存在を意識することなく、ページに組み込まれたオブジェクトを利用するわけですが、明示的に HttpContext オブジェクトを利用することで便利に使える場合もあります。その一つがページの処理中から呼び出すクラス内で現在のリクエストの情報を利用するような場合です。この場合ページから呼び出すメソッドの引数に必要な情報を渡すということもできます。しかし呼び出されたクラス内で HttpContext の静的プロパティである Currentプロパティを利用し、現在のContext の内容を直接参照すれば余分なコードを記述する必要はなくなります。
また Server.Transfer でページ遷移を行う場合も HttpContext オブジェクトを利用できます。Server.Transfer を利用したページ遷移では1つのリクエストの処理の中でサーバ上でページの切り替えが行われます。このため生成されるHttpContextオブジェクトは1つだけなので、最初のページから遷移先のページにデータを渡したいときに HttpContext オブジェクトの Items プロパティを介してデータを渡すことができるのです。
この Items プロパティの中身は  IDictionary となっていて、文字列のキーと object 型の値が格納できます。これはキーを指定すればどんなデータでも格納することができるということです。HttpContext オブジェクトはリクエストに対する処理の最初から最後まで存在していますので、Itemsプロパティをどんなデータでも入れられるグローバル変数のように利用することが可能になっています。

2.リクエストに共通する処理(IHttpModule)

 

リクエストがASP.NETに渡され、HttpContextオブジェクトが生成されると、その後以下の順序で処理が行われます。 2

  1. ユーザ情報をHttpContextに格納(認証)
  2. リクエスト先の実行権限を確認(承認)
  3. キャッシュによるリクエストの解決
  4. セッション情報をHttpContextに格納
  5. 各ページ毎の処理
  6. セッション情報の保存
  7. キャッシュへの情報の格納

まずユーザの情報がHttpContextに格納されます。匿名ユーザが許可されている場合やForm認証でまだユーザがログインしていない場合でもその状態にあったユーザ情報が設定されます。そして、そのユーザがリクエストしているページにアクセスしてよいかどうかが確認されます。もしページ全体がキャッシュされている場合はこの時点でキャッシュが返されます。次にセッション等のサーバ上に保存されている情報がHttpContextに格納されたあとで、各ページの処理が開始されます。ページ毎の処理については後述します。ページの処理が終わると、HttpContextに格納されているセッション等の情報の保存が行われ、また、必要であればページの内容がキャッシュに格納されます。

これらの処理はHttpApplicationクラスのイベントとして呼び出されます。
HttpApplicationクラスのドキュメントを見ると、どういったイベントがあって、それらがどういう順序で発生するかが記述されていますので確認しておくとよいでしょう。
http://msdn2.microsoft.com/ja-JP/library/system.web.httpapplication.aspx)
また、これらの処理は実際にはHttpModuleとして実装されています。C:\Windows\Microsoft.NET\Framework\v2.0.50727\CONFIGフォルダに格納されているweb.configファイルをみると、httpModulesセクションに以下のクラスが登録されています。

  • System.Web.Caching.OutputCacheModule
  • System.Web.SessionState.SessionStateModule
  • System.Web.Security.WindowsAuthenticationModule
  • System.Web.Security.FormsAuthenticationModule
  • System.Web.Security.PassportAuthenticationModule
  • System.Web.Security.RoleManagerModule
  • System.Web.Security.UrlAuthorizationModule
  • System.Web.Security.FileAuthorizationModule
  • System.Web.Security.AnonymousIdentificationModule
  • System.Web.Profile.ProfileModule
  • System.Web.Mobile.ErrorHandlerModule
  • System.ServiceModel.Activation.HttpModule

クラスの名前からだいたいその役割は読み取れると思います。これらのモジュールはIHttpModuleインターフェースを実装しており、その内部にはどのイベントが発生したときにどういった処理を行うかというコードが実装されています。

HttpModuleは独自に実装して追加することも可能です。たとえばリクエストのログを保存するモジュールや、リクエスト中のURLを書き換えるモジュールを作成し、追加することができます。
これらの処理はglobal.asaxファイルの中のApplicationイベントとして実装することもできます。ですが、いくつものWebアプリケーションで同じ処理を利用したいのであれば、モジュールとしてdllを作成しておくと、web.configに設定を追加するだけでどんなWebアプリケーションでも利用することができるようになるので便利です。

3.処理の切り分け(IHttpHandler)

 

ASP.NETはWebアプリケーション(.aspx)へのリクエストもWebサービス(.asmx)へのリクエストも同じように受け付けます。3
IHttpModuleによる処理はそれらすべてのリクエストに対して行われます。では、.aspxと.asmxはどこで切り分けられるのでしょうか。
実は各ページの処理を行う時点で、拡張子毎に異なるクラスが呼び出されます。どの拡張子に対してどのクラスが呼び出されるかは、C:\Windows\Microsoft.NET\Framework\v2.0.50727\CONFIGフォルダに格納されているweb.configファイルのhttpHandlersセクションに登録されています。たとえば、.aspxに対するリクエストではSystem.Web.UI.PageHandlerFactoryクラスが呼び出され、.asmxに対するリクエストではSystem.Web.Services.Protocols.WebServiceHandlerFactoryクラスが呼び出されます。
これらのクラスはIHttpHandlerインターフェースを実装しているか、またはIHttpHandlerインターフェースを実装しているクラスをよびだすIHttpHandlerFactoryインターフェースを実装しています。このIHttpHandlerインターフェースがASP.NETの仕組みでは重要な役割を担っており、IHttpHandlerインターフェースが持つProcessRequestメソッドが呼び出されることで各ページの処理が開始されるのです。Pageクラス(.aspxファイル)もその仕組みの中で動作していることは、PageクラスがIHttpHandlerを継承していることからもわかります。
このような仕組みを理解していると、IHttpHandlerインターフェースを実装するクラスを作成し、web.configに登録することで独自のリクエスト処理機能を組み込んでASP.NETを拡張することが可能になります。実際にマイクロソフトのサイトの中では.mspxという拡張子が利用されていますが、これはIHttpHandlerインターフェースを利用した拡張例と考えられます。

IHttpHandlerインターフェースを実装するクラスの作成は難しくありません。
IsReusableプロパティでtrueを返すようにして、あとはProcessRequestメソッドでブラウザに返すHTMLを組み立てるだけです。ProcessRequestメソッドはHttpContextオブジェクトを引数として受け取っていますから、HttpContextオブジェクトのResponseプロパティに組み立てたHTMLを書き込めば実装完了ということになります。
このIHttpHandlerの応用例としてはRSSデータを提供する.rssページの作成といったことが考えられます。この場合、まずIISが.rssページへのリクエストを受け取った際にASP.NETに処理が渡されるよう、IISの設定を行っておく必要があります。そのうえで、web.configのhttpHandlersセクションに.rssページを登録する、といった作業も必要になります。

ちなみに、web.configのhttpHandlersセクションで目をひくクラスにSystem.Web.HttpForbiddenHandlerクラスがあります。*.asaxや*.cs、*.vb、*.mdf、*.mdb、*.csproj、*.vbproj等多くの拡張子がこのクラスに関連づけられています。
これらの拡張子はソースファイルやデータファイルなどVisualStudio上で主に使われるファイルで、ブラウザに返されては困るものばかりです。つまり、もしこれらの拡張子に対してリクエストがあったとしても、そのリクエストに対して処理を行わないようにするのがHttpForbiddenHandlerクラスの役割となっています。
サーバ上にブラウザから直接リクエストされたくないファイルを置く必要がある場合にはこのクラスを利用することができます。

4.コードの分離とコンパイル

 

.NET Framework上で動くプログラムはすべてコードで記述され、それがコンパイルされてはじめて実行できるものになります。4ASP.NETであってもこの仕組みは同じです。ASP.NETのページファイルに記述されているのは
HTMLに似たタグばかりですが、実はこれらのタグは自動的にソースコードに変換され、それがコンパイルされて実行されます。

実際にどのようなソースコードが生成されるのかを確かめてみましょう。
確かめるために特別な作業は必要ありません。Webアプリケーションを実行すると、次のフォルダの下に自動的に名前が付けられたフォルダ/ファイルが作成されます。

C:\Windows\Microsoft.NET\Framework\バージョン\Temporary ASP.NET Files\アプリケーション名

デバッグを実行した場合にはここにコンパイルされる前のソースコードも保存されますので、そのファイルを直接開いて内容を確認することができます。最初はHelloWorldレベルの簡単なアプリケーションを作成してみて、そこで保存されるソースコードを見てみるとわかりやすいでしょう。
ASP.NET 2.0では1つのページファイルから1つのソースコードファイルが生成されますが、その中には実際は2つのクラスが記述されています。この2つのクラスには継承による親子関係があります。親のクラスはpartialクラスとなっていてコードファイルのクラスと同じ名前がつけられます。partialクラスはコンパイル時に1つのソースコードとして統合されて扱われます。こういった仕組みによってコードファイルとページファイルが関連づけられて動作するようになっているのです。
コードによるプログラムの動作が含まれるのが親のクラス、コントロールの見映えやプロパティを設定しているのが子のクラスという役割分担が行われています。

5.コントロールツリー

 

ページファイルから生成されるソースコードの中にはあるコントロールが別のコントロールに含まれているといった関係も定義されています。 5
このコントロール間の関係をコントロールツリーと呼びます。
コントロールツリーをソースコードを追いかけて理解するのは大変ですので、視覚的に表示させてみましょう。そのためには、ページファイルのページディレクティブ("<%@ Page"から始まる行)に「Trace="true"」という記述を追加してページを実行します。実行中のページにトレース情報が表示され、その中に「コントロールのツリー」という項目があるのに気づくと思います。
「コントロールのツリー」を見ると、ページの内部では__Pageを起点に、LiteralControlやHtmlHead、HtmlTitle、そしてページに追加されている様々なコントロールが階層構造(ツリー)を形作っていることを視覚的に理解することができます。
ページファイルに記述されている通常のHTMLタグ(runat="server"の属性が定義されていないもの)はLiteralControlとしてコントロールツリーに組み込まれます。また、GridViewのような複雑なコントロールが実際にどのようなコントロールの組み合わせで実現されているかといったこともコントロールツリーを見ると確認することができます。

コントロールツリーは、コードからコントロールを動的に追加したいといった場合や、細かいコントロールの動きを制御したい、といった場合には必須の知識です。GridViewの内部のコントロールのように表面にはあらわれてこないコントロールもツリーをたどることでプログラムから直接操作することが可能です。常日頃からどのようなコントロールツリーが構成されているのか意識するようにしておくとよいでしょう。

6.ページ(aspxファイル)の動作

 

ページファイルに含まれるコントロールを操作するにはイベントの発生順序を理解する必要があります。ASP.NETではイベントがあらかじめ決められた順序で発生することに6よって、コントロールの状態が変化していきます。このためイベントの発生順序を理解していないと、適切なタイミングでコントロールを操作することができません。
このイベントの発生順序ですが、aspxファイルのトレースを表示すると、「トレース情報」という項目にページ上で発生したイベントとそのイベントの開始/終了時の時間が記載されます。
ページの初期表示時とPostBack時では発生するイベントが少しことなりますが、初期表示時はいくつかのイベントが省かれているだけですので、ここではPostBack時のイベントについてだいたいの流れを確認してみましょう。

まず、初期化が行われます。ここではイベントとイベント発生時の実行メソッドを結びつける等のページの処理のための準備が行われます。
次にブラウザから戻ってきたViewStateの情報とブラウザからPostされた情報を読み込みます。
そのあとでaspxファイルに定義されているコントロールを読み込み、コントロールツリーを生成します。コントロールツリーを生成する中で、ViewStateやPostされた情報から各コントロールのプロパティに値が設定されます。
同じコントロールに対してViewStateの値とPostされた値が異なっている場合、値の変更によるイベントが発生します。GridViewのSelectedIndexCahngeといったイベントはここで発生します。
次にポストバックの元となったイベントの処理が行われます。ボタンクリックといったイベントはここで処理されます。
最後にコントロールがHTMLに変換される処理であるレンダリングの準備が行われ、ブラウザに返すHTMLが作成されます。

この最後のレンダリングという処理ではじめてHTMLが組み立てられることになります。
ASP.NETで利用するコントロールは、自分自身をどのようなHTMLに変換すればよいかを知っています。レンダリングの時点でコントロールの持つプロパティの状態にあわせたHTMLをコントロール自身が生成してくれるのです。このためASP.NETのプログラムというのは、必要なHTMLが生成されるようにレンダリングの時点までにコントロールのプロパティを適切に設定してあげる作業だということもできます。
コントロールは上記で述べてきたようなイベントが発生するなかでその状態を変化させていますから、開発者はコントロールを操作するために適切なイベントを選択しなければいけません。
ASP.NETでは、Pageで発生するイベントに対応して個々のコントロール上でのイベントも発生するという仕組みになっています。トレース情報を活用してイベントの発生タイミングを確認しながら、処理を行うのに適切なイベントを決定していく、といった作業が必要になることもあると思います。

7.まとめ

 

ASP.NETには十分使いやすい開発環境が整っていますので、ここまで述べてきたような仕組みを知らなくてもとりあえず動くものは作れてしまいます。 7
しかし、より効率的なプログラミングを行いたい、ASP.NETの持つ力をより引き出したい、といった場合にはASP.NETがどのような仕組みになっているのかを理解していることは重要です。
この記事はASP.NETの仕組みの全体像を理解していただこうと思いまとめたものです。これをきっかけに、ASP.NETの仕組みを深く理解してその力をより引き出そうと考える人が増えて欲しいと願っています。

カテゴリー:.NET, ASP.NET 備忘録