WebSurfer's Home

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

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

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