はじめに
.NETアプリケーションでは、不正な処理(無効なキャスト、null値の参照、オフラインのデータベースへの接続など)を実行すると、「例外」というものが発生します。コード内でTry/Catchブロックを使用すると、例外をキャッチして直接処理できます。ASP.NETアプリケーションの場合、コード内で処理されていない例外はASP.NETのランタイムに通知され、HttpUnhandledExceptionが発生します。既定では、未処理の例外が発生した場合は「ランタイムエラー」というテキストがページに表示され、例外の詳細を確認する方法についての開発者向けの説明が示されます(図1を参照)。この「ランタイムエラー」ページは、外部のサイト訪問者に表示されるページです。開発者自身がローカルホストでサイトを表示しているときには、未処理の例外が発生すると、発生した例外の種類と詳細が既定のエラーページで表示されます。
エンドユーザーがこの「ランタイムエラー」ページを見たら、不安と戸惑いを覚えるのは確実です。ごく普通のコンピュータユーザーが「ランタイム」の意味を知っているとは思えないからです。すべてのユーザーに伝わるのは、何かとんでもない事態が発生したということです。ユーザーは、データやこれまでの作業が失われたのではないか、エラーの原因は自分の操作にあったのではないかと、恐れをなすかもしれません。皮肉なことに、未処理の例外が発生したことの当事者である開発者は蚊帳の外です。エンドユーザーがエラーの詳細(どのページでエラーが発生したかや、その直前にユーザーが行った操作など)をメールで知らせてくれるまで、エラーの発生を把握できないのです。
幸い、ASP.NETには、これら2つの問題の解決策が用意されています。問題発生をもっと穏便に伝えるページに自動的にリダイレクトするよう、ASP.NETアプリケーションを設定できるのです。このユーザーフレンドリなカスタムのエラーページでは、「ランタイム」などの専門用語は使わずに済みますし、Webサイト全体とページの見た目を統一できます。さらには、未処理の例外が発生したことをログに記録して開発者に通知するための方法もあります。この記事では、未処理の例外が発生したときにユーザーフレンドリなエラーページを表示する方法を説明します。そのような例外が発生したときにログに記録してサイト管理者に通知する方法については、別の記事(『Processing Unhandled Exceptions』)で説明する予定です。
未処理の例外が発生したときにユーザーフレンドリなエラーページを表示する
未処理の例外がASP.NETのランタイムに通知されると、アプリケーションの<customErrors>
設定が参照されます。この設定は、アプリケーションのWeb.configファイルで指定されています。「ランタイムエラー」ページ、例外の詳細を説明するページ、ユーザーフレンドリなカスタムのエラーページのうちどれがユーザーに表示されるかは、この設定に応じて決まります。具体的には、<customErrors>
設定のmode
プロパティを使用します。このプロパティには、次の3つの値のいずれかを指定します。
- On
- Off
- remoteOnly
既定の「ランタイムエラー」ページではなくユーザーフレンドリなエラーページを表示するには、<customErrors>
設定のdefaultRedirect
プロパティに、表示するエラーページのURLを指定します。ユーザーフレンドリなエラーページは、静的なHTMLページとASP.NETページのどちらでもかまいません。また、絶対URL(http://www.someserver.com/SomePage.htmのような形式のURL)とWebサイトからの相対URLのいずれでも指定できます。相対URLでは、~
という文字を使用して、Webアプリケーションのルートをファイルパスのベースとして指定できます(例えば~/ErrorPage.aspx
)。
具体例を見てみましょう。次のように<customErrors>
を設定した場合、未処理の例外が発生すると、すべてのユーザーに対して、ユーザーフレンドリなエラーページ「GeneralServerError.aspx」が表示されます。
<customErrors mode="On" defaultRedirect="~/GeneralServerError.aspx" />
mode
をremoteOnly
に変更した場合は、例外が発生すると、リモートユーザーに対してのみ「GeneralServerError.aspx」が表示されます。ローカルホストで表示しているユーザーに対しては、例外の詳細が表示されます。
未処理の例外がASP.NETのランタイムに通知されると、ランタイムは、<customErrors>
設定を確認したうえで、defaultRedirect
に対するResponse.Redirect()
を内部的に発行します。これを受けて、HTTPのステータスコード302がブラウザに送信されます。指定のURL(この例ではGeneralServerError.aspx)を要求せよという意味のコードです。また、ランタイムは、aspxerrorpath
というクエリ文字列パラメータを所定のURLに付加します。これは、未処理の例外が発生したときにユーザーが表示していたURLを表す文字列です。ユーザーフレンドリなエラーページ(GeneralServerError.aspx)では、問題が発生したことを伝えるメッセージをユーザーに対して表示できます。
<customErrors>
の設定の使用例を示すASP.NET 2.0のWebアプリケーションのサンプルをダウンロードできます。エラーに応じて異なるページにユーザーをリダイレクトする
ユーザーフレンドリなエラーページを<customErrors>
設定のdefaultRedirect
プロパティで指定した場合は、ランタイムに通知された未処理の例外の種類にかかわらず、常にそのWebページがユーザーに表示されます。内部サーバー例外(ステータスコード500)の場合も、HTTP関連の例外(ステータスコード404=ページが見つからないなど)の場合も同じです。<customErrors>
設定では、発生した例外のHTTPステータスコードに応じて異なるWebページをユーザーに表示するというカスタマイズが可能です。つまり、404エラーの場合と内部サーバー例外の場合とで、表示するページを変えることができます。
<customErrors>
設定をカスタマイズするには、<error>
要素を追加して、どのHTTPステータスコードの例外が発生した場合にどのページをユーザーに表示するかを指定します。<error>
要素は、1つも指定しなくても、多数指定してもかまいません。発生した例外のステータスコードが、一連の<error>
要素で指定されていない場合には、defaultRedirect
プロパティで指定したページがユーザーに表示されます。
例えば、次のように<customErrors>
を設定した場合は、存在しないページを要求したときには「FourOhFour.aspx」が表示され、その他の未処理の例外のときには「GeneralServerError.aspx」が表示されます。
<customErrors mode="On" defaultRedirect="~/GeneralServerError.aspx"> <error statusCode="404" redirect="~/FourOhFour.aspx" /> </customErrors>
表示する404ページでは、要求されたページ(存在しないページ)をaspxerrorpath
クエリ文字列フィールドで判断できます。例えば、存在しないページwww.server.com/NoSuchPage.aspx
を表示しようとしたユーザーがASP.NETランタイムによってリダイレクトされる先は、www.server.com/FourOhFour.aspx?aspxerrorpath=/NoSuchPage.aspx
となります。さらには、ユーザーが別のページ上のリンクをクリックすることでこの存在しないWebページを要求したのかどうかを判断することも可能です。その場合は、移動元のページに「リンク切れ」があり、修正が必要だということです。別のページのリンクから存在しないページにたどり着いたのかどうかは、ASP.NET 2.0の場合はRequest.UrlReferrer
プロパティ、ASP.NET 1.xの場合はRequest.ServerVariables("HTTP_REFERER")
変数で判断できます。
1つ注意してほしいのですが、<customErrors>
設定は、ASP.NETリソースの要求時に未処理の例外が発生した場合にのみ、ASP.NETランタイムによって適用されるものです。それ以外の場合には適用されません。例えば、WebサーバーとしてIISを使用している場合は、HTMLページなどの静的リソースに対する要求はIISによって処理され、ASP.NETエンジンには渡されません。従って、存在しないHTMLページをユーザーが表示しようとすると、ステータスコード404の応答がIISから返され、<customErrors>
で指定したページへのリダイレクトは行われません。<customErrors>
で指定したのと同じエラーページを表示するには、IIS内でカスタムの404ページを作成するか、またはHTMLページをASP.NETエンジンにマッピングします。
<customErrors>
設定が適用されます。別の拡張法としては、無効なURLと正しいURLの対応表をデータベーステーブルの形で用意するという方法も考えられます。404エラーページ内でデータベース検索を実行し、要求されたページ(存在しないページ)に対応する正しいURLがそのテーブルに登録されているかどうかを確認します。そして、登録されている場合は、ユーザーを正しいURLに自動的にリダイレクトするという方法です。この機能が役立つのは、例えば次のような場合です。よそのWebサイトから自分のWebサイトの特定のページにリンクが張られているのに、リンク元にスペルミスがあって、リンク切れになってしまっているとしましょう。先方のWebサイトのリンクを修正してもらうのが理想的ですが、他人が問題を修正してくれるのを待つという受け身の姿勢ではなく、スペルミスのURLと正しいページを対応付けるという能動的な姿勢で望むことが可能です。そうすれば、先方のWebサイトの切れたリンクから自分のサイトにやって来たユーザーを、正しいページに自動的にリダイレクトできます。
まとめ
未処理の例外が発生したときにASP.NETのランタイムが行う処理は3種類に分かれます。すなわち、汎用的な「ランタイムエラー」ページを表示するか、例外の詳細を表示するか、ユーザーフレンドリなカスタムのエラーページを表示するかのいずれかです。既定では、リモートユーザーには「ランタイムエラー」ページが表示され、ローカルホストから表示しているユーザー(通常は開発者)には例外の詳細ページが表示されます。しかし、既定の「ランタイムエラー」ページの代わりにカスタムのエラーページを表示することは簡単です。これらの設定は、Web.configの<customErrors>
要素でカスタマイズできます。
<customErrors>
の設定によってユーザーフレンドリなカスタムのエラーページを表示するという方法には、1つ問題点があります。発生した例外について取得できる詳細情報が、その例外がどのページで発生したかという情報だけであるという点です(aspxerrorpath
クエリ文字列フィールドで取得可能)。カスタムのエラーページで例外の詳細をログに記録したり、発生した例外に応じてエンドユーザーに表示するメッセージをカスタマイズしたりできれば申し分ありません。これらの問題点の対処法については、別の記事『Processing Unhandled Exceptions』で説明します。また、未処理の例外をログに記録して開発者に通知する処理を自動化できる、フリーなオープンソースライブラリも紹介します。
それでは、ハッピープログラミング!