はじめに
ご存じのとおり、ASP.NET WebページはHTML部分とソースコード部分から成ります。このうち、HTML部分にはHTMLマークアップとWebコントロールが混在していて、誰かがASP.NET Webページを訪れると、そのWebコントロールからHTMLマークアップが生成され、静的HTMLマークアップと合わせて、そのページを要求した訪問者のもとに送られ、ブラウザに表示されます。
WebコントロールからどのようなHTMLが生成されるかは、ASP.NET Webページを要求したブラウザに依存します。その意味で、これをアダプティブ(適応的)レンダリングと呼びます。たとえば、訪問者がInternet Explorer 6.0を通じてページを要求したのであれば、生成されるマークアップはHTML 4.0準拠のマークアップです。仮にLabel
WebコントロールのForeColor
プロパティが赤(red)なら、生成されるマークアップは次のようになります。
<span id="controlID" style="color: red">Label Text</span>
しかし、同じ訪問者がNetscape 4.72を使ったとすれば、生成されるマークアップはHTML 3.2準拠となります。その場合、Label
Webコントロールのカラー指定は、<span>
のstyle
属性によらず、<font>
要素で行われます。
<span id="controlID"><font color="red">Label Text</font></span>
アダプティブなコントロールというのは確かにすぐれたアイデアですが、ASP.NETの既定の実装は必ずしもいいことばかりではありません。というのも、最近普及してきているブラウザ群(Mozilla、Firefox、Netscape、Operaなどの最近バージョン)に対しては、ASP.NETコントロールが既定でHTML 3.2(HTML 4.0でなく)準拠のHTMLを生成するからです。ただ、幸いなことに、個々のWebアプリケーション(あるいはWebサーバー全体)に適切な設定をすれば、これら新しい非Microsoftブラウザに対してもHTML 4.0準拠のHTMLが生成されるように指定できます。以下では、WebコントロールのHTMLを4.0準拠とするか3.2準拠とするか、ASP.NETがどう判断するのかを見ていくことにします。また、Netscape、Opera、Mozillaの最近バージョンに対しても4.0準拠のHTMLマークアップが生成されるよう、Webアプリケーションを設定する方法も見ておくことにします。
Webコントロールのレンダリングの仕組み
すべてのASP.NET Webページは、直接あるいは間接にSystem.Web.UI.Page
クラスから導かれます。このクラスには、ASP.NET Webページを表現するための基本的なメソッドとプロパティが用意されています。Webサーバーは、ASP.NET Webページへの要求を受信すると、それをASP.NETエンジンに引き渡します。ASP.NETエンジンは要求されたページのPage
クラスのインスタンスを作成し、このクラスのProcessRequest()
メソッドを呼び出します。
ProcessRequest()
メソッドが呼び出されるところから、このページのライフサイクルが始まります。具体的には、ページのコントロール階層がロードされ、表示状態が復元され、Load
イベントが起こり、Webコントロールイベント群が起こり、表示状態が保存され、ページのレンダリングが完成します。次の図は、ASP.NETページのライフサイクルを示しています。
本稿では、このライフサイクルの詳細はさして重要ではありませんが、詳しく学びたいという方には、「How ASP.NET Web Pages are Processed on the Web Server」と「The ASP.NET Page Object Model」のページをお勧めします。
ASP.NET Webページのレンダリングは、次の方法で行われます。まず、Page
クラスがSystem.Web.UI.HtmlTextWriter
クラスのインスタンスを作成し、そのコントロール階層に含まれるすべてのコントロールのRenderControl()
メソッドを再帰的に呼び出して、それにHtmlTextWriter
インスタンスを引き渡します。各コントロールは、それぞれのHTMLマークアップを1つの(つまり、同じ)HtmlTextWriter
インスタンスの末尾に付加していきます。すべてのコントロールのレンダリングが終わると、Page
クラスはそのHtmlTextWriter
インスタンスの内容(つまり、ページ全体のHTMLマークアップ)を返します。
HtmlTextWriterによるアダプティブレンダリング
System.Web.UI
名前空間には、Webコントロールレンダリングのためのクラスが2つ含まれています。1つはHtmlTextWriter
、もう1つはHtml32TextWriter
です。前者がHTML 4.0準拠のマークアップ、後者がHTML 3.2準拠のマークアップのレンダリング用です(Html32TextWriter
は、HtmlTextWriter
からの派生クラスです)。
Page
クラスはレンダリング段階に入ると、まず新しいHtmlTextWriter
インスタンスを作成します。HtmlTextWriter
とHtml32TextWriter
のどちらのクラスインスタンスを作成するかを判断しなければなりませんが、それにはBrowser
オブジェクトのTagWriter
プロパティを調べます(Browser
オブジェクトには、ASP.NET Webページを要求してきたブラウザを特定するための読み取り専用プロパティがいくつかあります)。Webブラウザの特定には、User-Agent文字列が使われます。
Webブラウザはページを要求するとき、HTTPヘッダにUser-Agent文字列を含めて送信します。これが、ブラウザの特定に使用されます。たとえば、Internet ExplorerならMozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; .NET CLR 1.1.4322)
、Mozilla FireFoxならMozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.6) Gecko/20040206 Firefox/0.8
というUser-Agent文字列を送信します。User-Agent文字列を送信しないブラウザや、「偽の」User-Agent文字列を送信するブラウザもあります。たとえば、Operaでは、IE6のUser-Agent文字列を送信させることができますし、Mozillaにも同様の拡張機能があります(ご使用のブラウザがどのようなUser-Agent文字列を送信するかに興味がある方は、User-Agentのデモを参照してください)。
ASP.NETエンジンはUser-Agent文字列を調べ、Browser
のプロパティの値を特定します。プロパティの1つはTagWriter
です。この値は、既定ではブラウザがInternet Explorer 4.0以上の場合のみHtmlTextWriter
となり、その他のブラウザではHtml32TextWriter
となります。しかし、これらの値は幸いなことに設定可能であり、Netscape、Mozilla、Operaの最近バージョンについても、Html32TextWriter
ではなくHtmlTextWriter
クラスを使用するよう指示できます。こうした設定は、「maching.config」ファイルか「Web.config」ファイルの<browserCaps>
セクションで行います(「maching.config」で設定を変更すると、そのWebサーバー上のすべてのWebアプリケーションの既定動作が変わります。アプリケーション単位の設定変更には、「Web.config」ファイルを使用してください)。
<browserCaps>要素群の検査
<browserCaps>
要素には、いくつかの<case>
要素が含まれています。どの<case>
要素にもmatch
属性があり、そこに何らかの正規表現が含まれています。ブラウザから送られてきたUser-Agent文字列とこの正規表現が一致すると、一致した<case>
要素内部のプロパティ群がBrowser
オブジェクトに適用されます。「machine.config」ファイルを見ると、既定では、TagWriter
プロパティがHtml32TextWriter
クラスに設定されていることがわかります。
<browserCaps> ... tagwriter=System.Web.UI.Html32TextWriter ... </browserCaps>
さらに下のほうを見ていくと、Internet ExplorerのUser-Agentに一致する<case>
要素も見つかります。そこのTagWriter
プロパティはHtmlTextWriter
クラスに設定されているはずです。
<browserCaps> ... <case match="^Mozilla[^(]*\(compatible; MSIE <snip>"> browser=IE ... tagwriter=System.Web.UI.HtmlTextWriter </case> </browserCaps>
非Microsoftブラウザでは、TagWriter
プロパティが既定値(Html32TextWriter
)のままです。
Netscape、Mozilla、Operaに対するHTML 4.0準拠のレンダリング設定
最近の非MicrosoftブラウザでもHTML 4.0準拠のマークアップが生成されるようにするには、「maching.config」ファイルや「Web.config」ファイルをどう設定すればよいでしょうか。その前に、なぜそのような手間をかける必要があるのかを考えてみましょう。赤いForeColor
を持つLabel
を表示するとして、ASP.NETがそのLabel
を<span id="controlID" style="color: red">Label Text</span>
としてレンダリングしようが、<span id="controlID"><font color="red">Label Text</font></span>
(Netscapeの場合)としてレンダリングしようが、結果的に同じものが表示されるのなら、いっこうにかまわないのではないでしょうか。
確かに、ForeColor
の場合はかまわないでしょう。しかし、Label
のBackColor
プロパティならどうでしょうか。HtmlTextWriter
クラスは、背景色の指定にbackground-color
というCSS設定を使用します。CSSのないHTML要素の場合は、<table>
を使うか、何らかの裏技でも使わなければ、背景色を設定する方法がありません。したがって、Html32TextWriter
クラスはBackColor
プロパティを無視します。4.0準拠のマークアップが望まれる理由は、もう1つあります。それは、HtmlTextWriter
なら<div>
要素のレンダリングができることです。Html32TextWriter
クラスは<div>
を<table>
としてレンダリングするので、その結果、状況によっては不都合が生じます。
以下に、「Web.config」ファイルに追加できる<browserCaps>
セクションの一例を示します。この追加により、非Microsoftブラウザを使っている場合でも、WebコントロールからHTML 4.0準拠のマークアップが生成されるようになります。この<browserCaps>
セクションでは、TagWriter
プロパティ以外にも、Browser
オブジェクトのプロパティがいくつか設定されていることに注意してください(この<browserCaps>
セクションはRob Eberhardtが作成したもので、ここから入手できます)。
<!-- 2003-12-03, Rob Eberhardt - http://slingfive.com/demos/browserCaps/ --> <browserCaps> <!-- GECKO Based Browsers (Netscape 6+, Mozilla/Firebird, ...) //--> <case match="^Mozilla/5\.0 \([^)]*\) (Gecko/[-\d]+)? (?'type'[^/\d]*) ([\d]*)/(?'version'(?'major'\d+)(?'minor'\.\d+)(?'letters'\w*)).*"> browser=Gecko type=${type} frames=true tables=true cookies=true javascript=true javaapplets=true ecmascriptversion=1.5 w3cdomversion=1.0 css1=true css2=true xml=true tagwriter=System.Web.UI.HtmlTextWriter <case match="rv:(?'version'(?'major'\d+)(?'minor'\.\d+)(?'letters'\w*))"> version=${version} majorversion=${major} minorversion=${minor} <case match="^b" with="${letters}"> beta=true </case> </case> </case> <!-- AppleWebKit Based Browsers (Safari...) //--> <case match="AppleWebKit/(?'version'(?'major'\d)(?'minor'\d+)(?'letters'\w*))"> browser=AppleWebKit version=${version} majorversion=${major} minorversion=0.${minor} frames=true tables=true cookies=true javascript=true javaapplets=true ecmascriptversion=1.5 w3cdomversion=1.0 css1=true css2=true xml=true tagwriter=System.Web.UI.HtmlTextWriter <case match="AppleWebKit/(?'version'(?'major'\d)(?'minor'\d+) (?'letters'\w*))( \(KHTML, like Gecko\) )?(?'type'[^/\d]*)/.*$"> type=${type} </case> </case> <!-- Konqueror //--> <case match = "Konqueror/(?'version'(?'major'\d+)(?'minor'\.\d+) (?'letters'));\w*(?'platform'[^\)]*)"> browser=Konqueror version=${version} majorversion=${major} minorversion=${minor} platform=${platform} type=Konqueror frames=true tables=true cookies=true javascript=true javaapplets=true ecmascriptversion=1.5 w3cdomversion=1.0 css1=true css2=true xml=true tagwriter=System.Web.UI.HtmlTextWriter </case> </browserCaps>
さらに先へ――クライアントサイドでの妥当性検査
Netscape、Opera、Mozillaの最近のバージョンを使っているユーザーに対してHTML 4.0準拠のマークアップが生成されるようにASP.NETを設定することは、最初の重要な一歩です。しかし残念ながら、この変更を加えても、妥当性検査コントロールはクライアントサイド妥当性検査スクリプトを生成しません。これには2、3の困った理由がありますが、それについてはまたの機会に書くことにします。
そのときまで、ハッピープログラミング!
クライアントサイド妥当性検査についての記事
私が以前に執筆した記事では、クライアントサイド妥当性検査の実装方法を解説するとともに、ダウンレベルのブラウザではなぜクライアントサイド妥当性検査が動作しないかという理由について述べています。興味のある方は、「Client-Side Validation in Downlevel Browsers」を参照してください。