WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

customErrors と requestFiltering

by WebSurfer 2010年10月12日 12:17

先の記事 アプリケーションレベルの例外処理 で書きました、(1) Button 6 クリックおよび (2) 存在しない静的ファイルを要求した場合は、web.config の customErrors 要素にカスタムエラーページを設定してもそれが表示されない(下の画像のような標準エラーページが表示される)件につき理由を調べてみました。

標準エラーページ

調査結果をまとめると以下のとおりです(自信度 99% ぐらい。残り 1% の不安要素は後述します)。

(1) Button 6 クリック(Global-NoCatch.asax へリダイレクト)

IIS7 で導入された 要求のフィルタリング で、特定の拡張子(asa, asax, ascx など)のファイルに対する要求がブロックされています。

Global-NoCatch.asax へリダイレクトした場合は以下のようになります。拡張子でチェックしているので Global.asax でも結果は同じです。

要求フィルタモジュール (RequestFilteringModule) が拡張子 asax をチェックして拒否。 → 静的ファイルハンドラー (StaticFile) によって 404.7 エラーとして処理される。 → デフォルト(httpErrors 設定なし)では標準エラーページを返す。

先に applicationHost.config の設定を変えて(fileExtension=".asax" allowed="false" を true にして)試したときも、やはり標準エラーページになったのは、asax が静的ファイルハンドラー (StaticFile) によって処理されたためと思われます。

注意:
2014/4/19 時点の最新のサンプルでは、Button 6 クリックでのリダイレクト先が Global-NoCatch.asax から NonexistentPage-NoCatch.aspx/xxx...(xxx... は x が 500 文字)に変わっています。(遅くとも 2011/4/23 には変わっていたはず) 結果、HTTP/1.1 400 Bad Request が返ってきます。詳しくは下の「2011/4/23 追記」と「2011/8/22 追記」を見てください。

(2) 存在しない静的ファイルを要求

統合パイプラインモードでも既存のハンドラーマッピングはすべて機能したままなので、静的ファイルは依然として IIS のネイティブの静的ファイルハンドラー (StaticFile) によって処理される。 → デフォルト(httpErrors 設定なし)では標準エラーページを返す。

以上から、要求フィルタリングで拒否設定されているファイルや存在しない静的ファイルを要求されて、カスタムエラーページを返には、TechNet のページ HTTP エラー <httpErrors> の中の「カスタムエラーページを追加する方法」で述べられている手順を取るほかなさそうです。

なお、開発サーバーで検証すると動作が異なる件は、今後二度と開発サーバーを検証に利用しないことにするということで、気にしないことにしました。(笑) 開発サーバーには要求フィルタリングはなく、例外はすべて ASP.NET で処置されるということなのかもしれません(想像です)。

------ 2011/4/23 追記(2014/4/19 一部訂正) ------

上に書いたとおり、MSDN ライブラリの英語版 Complete Example for Error Handlers にコメントしましたが、それを受けてコードが一部変更されています。(日本語の方は 2011/4/23 現在以前のままですが)

具体的には、Button6 クリックでのリダイレクト先を Global-NoCatch.asax から NonexistentPage-NoCatch.aspx/xxx...(xxx... は x が 500 文字)に変えています。

でも、依然としてダメです。DefaultRedirectErrorPage.aspx にはリダイレクトされません。サーバーは HTTP/1.1 400 Bad Request を返します。


------ 2011/8/22 追記(2014/4/19 一部訂正) ------

MSDN ライブラリの英語版 Complete Example for Error Handlers に書いたコメントはすでに消されていました。また、「日本語の方は 2011/4/23 現在以前のまま」と書きましたが、英語版と同様に修正されていました。

ただし、コードの内容は 2011/4/23 時点の英語版のままです。Button 6 の説明に "Click this button to create an HTTP 400 (invalid url) error. Application_Error will catch this but will not take any action on it, and ASP.NET will redirect to DefaultRedirectErrorPage.aspx." と書いてありますが、そうはなりません。

NonexistentPage-NoCatch.aspx/xxx...(xxx... は x が 500 文字)を要求すると、サーバーは HTTP/1.1 400 Bad Request を返します。web.config の defaultRedirect に指定した DefaultRedirectErrorPage.aspx にはリダイレクトされません。

(HTTP/1.1 400 Bad Request が返ってくると、IE に表示されるのはサーバーから帰ってきた html コードではなく IE が差し替えたものになるので注意してください。Firefox の場合はサーバーから帰ってきた html コードがそのまま表示されます)

ちなみに、以前のように Global-NoCatch.asax にリダイレクトすると、「HTTP エラー 404.7 - Not Found 要求フィルタ モジュールが、ファイル拡張子を拒否するように構成されています。」という標準エラーページが返ってきます。

Tags: ,

Exception Handling

アプリケーションレベルの例外処理

by WebSurfer 2010年10月3日 15:16

要求の処理中に Try/Catch ブロックで処置されなかった例外を、当該ページの Page_Error ハンドラおよび Global.asax の Application_Error ハンドラで処置し、例外を解釈してカスタムエラーページに適切なメッセージを表示するという話です。

以下の MSDN ライブラリに具体的な説明およびサンプルがあります。

【2016/12/12 訂正】.NET Framework 4 の記事のリンク先を英語版に変更しました。日本語版は、何かの手違いか、元の不完全なコード(Visual Studio 2008 の記事のコードと同じ)に戻ってしまったようです。

.NET Framework 4

How to: Handle Application-Level Errors

Complete Example for Error Handlers

Visual Studio 2008

方法 : アプリケーションレベルのエラーを処理する

エラー ハンドラーの完全なコード例

Visual Studio 2008 (.NET Framework 3.5) のサンプルコードには一部不備があるようで、自分が試した限りでは期待したとおりに動きませんでした。まず、その点を忘れないように書いておきます。

  1. Global.asax の Application_Error ですべての例外が処置されるので、web.config の customErrors 要素で defaultRedirect="HttpErrorPage.aspx" を設けても、それにリダイレクトされることはありません。なお、ここで「処置」とは Server.ClearError メソッドで前回の例外を削除することを意味します。
  2. Global.asax の Application_Error を修正して例外を処置しないようにすれば、HttpErrorPage.aspx にリダイレクトされるようになります。しかしながら、リダイレクト先のページでは Server.GetLastError で例外を取得することはできません(Transfer でないため)。
  3. web.config で customErrors mode="RemoteOnly" ... となっているため、開発環境でローカルのサーバにアクセスして試験する場合、カスタムエラーページは表示されません。(これは不具合というよりは、説明が不親切ということですが)

.NET Framework 4 のサンプルでは、上記の点は修正・改善されています。1, 2 については、例外が HttpException の場合 HttpErrorPage.aspx に Transfer することで解決しています。3 については、サンプルコードを mode="On" に変更しています。

.NET Framework 4 のサンプルの説明は上記の MSDN ライブラリのページにあります。それとダブルかもしれませんが、追加の説明を以下に書いておきます。

web.config: customErrors 要素の設定は以下のとおりです。mode="On" によって、ローカルでもカスタムエラーページが表示されるようにしています。

<customErrors mode="On" 
  defaultRedirect="DefaultRedirectErrorPage.aspx">
  <error statusCode="404" redirect="Http404ErrorPage.aspx"/>
</customErrors>

また、例外が処置されなかった場合に表示するカスタムエラーページの設定もされています。上の設定では、404 エラー (Not Found) の場合は Http404ErrorPage.aspx に、404 エラー以外の場合は DefaultRedirectErrorPage.aspx にリダイレクトされるはずです(「はず」と書いたのは、自分が試した限りでは期待通りに動かなかったからです。詳細は後述します)。

なお、error 要素で指定する特定の statusCode のエラーについては、defaultRedirect よりも、こちらが優先されるようです(MSDN ライブラリにはそのような記述は見つからず、自分手試した限りですが)。また、Transfer ではなく Redirect なので、遷移先のページでは Server.GetLastError メソッドによって発生した例外を取得できないことに注意してください。

Default.aspx: ボタンクリックで、例外をスロー (Button 1 ~ 3) したり、存在しないページへリダイレクト (Button 4 ~ 5) したり、アクセスが許可されてないファイルへリダイレクト (Button 6) したりします。

この中の Page_Error ハンドラで処置する例外(すなわちページレベルで処置する例外)は ArgumentOutOfRangeException(Button 2 で発生)のみです。InvalidOperationException(Button 1 で発生)の場合は GenericErrorPage.aspx に Transfer し、その他のすべての例外の場合は Global.asax に制御が移るようにコーディングされています(ただし、Button 6 が期待通りになりません。詳細は後述します)。

Global.asax: ページレベルで例外が処置されなかった場合、この中の Application_Error ハンドラに制御が飛んできます。Default.aspx からは、 Button 3 で Exception 例外が発生した場合のみ制御がここに飛んできます。(ちなみに、InvalidOperationException(Button 1 で発生)の場合は GenericErrorPage.aspx に Transfer されて処置され、ArgumentOutOfRangeException(Button 2 で発生)の場合はページレベルで処置されます)

Button 4 ~ 6 の場合は、存在しないページまたはアクセスが許可されてないファイルへのリダイレクトですから、HTTP 302 でブラウザが Location のページをサーバーに要求すると、サーバーではどのページも経由せず、直接 Global.asax の Application_Error ハンドラに制御が飛ぶはずです。(「はず」と書いたのは、Button 6 が期待通りにならないからです。詳細は後述します)

Application_Error ハンドラでは、例外の内、HttpException 以外をこの中で処置します。HttpException については、要求ページのファイル名に "NoCatch" が含まれる場合は何もせず return し、含まれない場合は HttpErrorPage.aspx に Transfer します。

GenericErrorPage.aspx: Default.aspx で InvalidOperationException がスローされた場合(Button 1 をクリックした場合)、このページに Transfer されてきて例外が処置されます。

HttpErrorPage.aspx: Global.asax で例外をチェックして、それが HttpException で、かつ、要求されたページのファイル名に "NoCatch" が含まれない場合はこのページに Transfer されてきて、HttpException が処置されます。(ファイル名に "NoCatch" が含まれる場合は、このページには Transfer されません・・・すなわち、例外は処置されません)

DefaultRedirectErrorPage.aspx: ページレベルでも Global.asax でも処置されなかった例外があった場合、このページにリダイレクトされます。ただし、web.config の customErrors 要素に error statusCode="404" の設定があるので、404 エラーの場合は Http404ErrorPage.aspx にリダイレクトされます。

Http404ErrorPage.aspx: ページレベルでも Global.asax でも処置されなかった例外があって、404 エラーの場合はこのページにリダイレクトされます。

Default.aspx を起動して、表示されるボタンをクリックして結果がどうなるか調べてみました。期待される結果は Default.aspx の表示に書いてあるとおりで(ソースのコメントは case 4 と 5 が間違っていますが)、開発サーバーを利用した場合は実際の結果も書いてあるとおりになりました。

しかしながら、IIS7 上で試してみたところ、Button 6 クリック(アクセスが許可されてないファイル Global-NoCatch.asax へリダイレクト)ではカスタムエラーページ (DefaultRedirectErrorPage.aspx) は表示されず、標準エラーページになってしまいます。標準エラーページのエラーメッセージは「HTTP エラー 404.7 - Not Found 要求フィルタ モジュールが、ファイル拡張子を拒否するように構成されています。」となっています。

ためしに、Global-NoCatch.asax を NonexistentPage.html(存在しない html ページ)に変更して試してみると、これもカスタムエラーページ (HttpErrorPage.aspx) は表示されず、標準エラーページ(HTTP エラー 404.0 - Not Found 探しているリソースは削除されたか、名前が変更されたか、または一時的に使用不可能になっています)になってしまいます。開発サーバーで試すと期待通り HttpErrorPage.aspx が表示されます。

IIS7 は統合パイプラインモードで検証しているので、html ファイルや .jpg ファイルなどの静的コンテンツファイルも ASP.NET にマップされており、カスタムエラーページが表示されるはずなのですが。

理由は、Global-NoCatch.asax や NonexistentPage.html を要求したときは、Global.asax の Application_Error ハンドラに制御が来る前に、標準エラーページが応答としてブラウザに返されてしまうからです。

何故、開発サーバーでは期待通り Global.asax の Application_Error ハンドラに制御が飛んでカスタムエラーページが返されるのに、IIS7 ではダメなのか、いろいろ調査しましたが不明です。

ググって調べているときに、「IIS 7. 0 でホストされている Web ページを参照しようとすると、エラー メッセージ:"HTTP エラーの 404.7-FILE_EXTENSION_DENIED"」という サポートオンライン のページを見つけたので、それに書いてあるように applicationHost.config の設定を変えて(fileExtension=".asax" allowed="false" を true にして)試してみましたが、ステータスコードが 404.0 に変わるだけで、やはり標準エラーページになってしまいます。

結局、今までのところ、Global-NoCatch.asax や NonexistentPage.html を要求されて、カスタムエラーページを返す方法は、TechNet のページ HTTP エラー <httpErrors>「カスタム エラー ページを追加する方法」以外に見つかりませんでした。この手順に従って、状態コード 404 で「このサイトで URL を実行」 の URL に Http404ErrorPage.aspx を指定したら、Global-NoCatch.asax や NonexistentPage.html の要求に対して、そのページが表示されるようになりました。

IIS7 の設定を変えて、開発サーバーで試したときと同じ結果にできないか、調査中です。ここまで調べただけで、もうイヤになってきているので、挫折する可能性が大ですが。(笑)

---------- 2010/10/12 追記 ----------

上に書きました、(1) Button 6 クリック(Global-NoCatch.asax へリダイレクト)および (2) 存在しない静的ファイルを要求した場合は、設定したカスタムエラーページが表示されない件につき理由を調べてみました。

ここに続けて書くと、ここの記事が長くなりすぎるので、customErrors と requestFiltering というタイトルで別に記事をポストしましたので、そちらを見てください。

Tags:

Exception Handling

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar