CodeZine(コードジン)

特集ページ一覧

未処理の例外への適切な対処法

ユーザーフレンドリなエラーページの表示

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2006/11/01 00:00

ASP.NETアプリケーションで未処理の例外が発生した場合、既定では「ランタイムエラー」というテキストがページに表示され、一般のユーザに不安と戸惑いを与えます。そこで本稿では、未処理の例外が発生したときにユーザーフレンドリなエラーページを表示する方法を説明します。

はじめに

 .NETアプリケーションでは、不正な処理(無効なキャスト、null値の参照、オフラインのデータベースへの接続など)を実行すると、「例外」というものが発生します。コード内でTry/Catchブロックを使用すると、例外をキャッチして直接処理できます。ASP.NETアプリケーションの場合、コード内で処理されていない例外はASP.NETのランタイムに通知され、HttpUnhandledExceptionが発生します。既定では、未処理の例外が発生した場合は「ランタイムエラー」というテキストがページに表示され、例外の詳細を確認する方法についての開発者向けの説明が示されます(図1を参照)。この「ランタイムエラー」ページは、外部のサイト訪問者に表示されるページです。開発者自身がローカルホストでサイトを表示しているときには、未処理の例外が発生すると、発生した例外の種類と詳細が既定のエラーページで表示されます。

図1
図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" />

 moderemoteOnlyに変更した場合は、例外が発生すると、リモートユーザーに対してのみ「GeneralServerError.aspx」が表示されます。ローカルホストで表示しているユーザーに対しては、例外の詳細が表示されます。

 未処理の例外がASP.NETのランタイムに通知されると、ランタイムは、<customErrors>設定を確認したうえで、defaultRedirectに対するResponse.Redirect()を内部的に発行します。これを受けて、HTTPのステータスコード302がブラウザに送信されます。指定のURL(この例ではGeneralServerError.aspx)を要求せよという意味のコードです。また、ランタイムは、aspxerrorpathというクエリ文字列パラメータを所定のURLに付加します。これは、未処理の例外が発生したときにユーザーが表示していたURLを表す文字列です。ユーザーフレンドリなエラーページ(GeneralServerError.aspx)では、問題が発生したことを伝えるメッセージをユーザーに対して表示できます。

注1
 この記事の最初に紹介されているリンクから、<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エンジンにマッピングします

注2
 Visual Studio 2005に同梱されているASP.NETの開発WebサーバーをファイルシステムベースのWebサイトで使用している場合は、すべてのリソースがASP.NETエンジンにマッピングされています。従って、HTMLページを要求するときにも<customErrors>設定が適用されます。
404エラーページをさらに改良する
 「FourOhFour.aspx」のエラーページによって、存在しないページを表示しようとしたことをユーザーフレンドリなメッセージでエンドユーザーに表示できるようになりました。さらに少しコードを加えると、この404エラーページの利便性を大きく向上させることができます。例えば、存在しないWebページにユーザーがたどり着いた原因が、別のページ上のリンク切れにあった場合は、開発者にメールで通知して、リンク切れの修正を促すようにすると便利です。ASP.NETのWebページから電子メールを送信する方法の詳細については、『Sending Email in ASP.NET 2.0』または『Sending Email from an ASP.NET 1.x Web Page』を参照してください。
 別の拡張法としては、無効なURLと正しいURLの対応表をデータベーステーブルの形で用意するという方法も考えられます。404エラーページ内でデータベース検索を実行し、要求されたページ(存在しないページ)に対応する正しいURLがそのテーブルに登録されているかどうかを確認します。そして、登録されている場合は、ユーザーを正しいURLに自動的にリダイレクトするという方法です。この機能が役立つのは、例えば次のような場合です。よそのWebサイトから自分のWebサイトの特定のページにリンクが張られているのに、リンク元にスペルミスがあって、リンク切れになってしまっているとしましょう。先方のWebサイトのリンクを修正してもらうのが理想的ですが、他人が問題を修正してくれるのを待つという受け身の姿勢ではなく、スペルミスのURLと正しいページを対応付けるという能動的な姿勢で望むことが可能です。そうすれば、先方のWebサイトの切れたリンクから自分のサイトにやって来たユーザーを、正しいページに自動的にリダイレクトできます。

まとめ

 未処理の例外が発生したときにASP.NETのランタイムが行う処理は3種類に分かれます。すなわち、汎用的な「ランタイムエラー」ページを表示するか、例外の詳細を表示するか、ユーザーフレンドリなカスタムのエラーページを表示するかのいずれかです。既定では、リモートユーザーには「ランタイムエラー」ページが表示され、ローカルホストから表示しているユーザー(通常は開発者)には例外の詳細ページが表示されます。しかし、既定の「ランタイムエラー」ページの代わりにカスタムのエラーページを表示することは簡単です。これらの設定は、Web.configの<customErrors>要素でカスタマイズできます。

 <customErrors>の設定によってユーザーフレンドリなカスタムのエラーページを表示するという方法には、1つ問題点があります。発生した例外について取得できる詳細情報が、その例外がどのページで発生したかという情報だけであるという点です(aspxerrorpathクエリ文字列フィールドで取得可能)。カスタムのエラーページで例外の詳細をログに記録したり、発生した例外に応じてエンドユーザーに表示するメッセージをカスタマイズしたりできれば申し分ありません。これらの問題点の対処法については、別の記事『Processing Unhandled Exceptions』で説明します。また、未処理の例外をログに記録して開発者に通知する処理を自動化できる、フリーなオープンソースライブラリも紹介します。

 それでは、ハッピープログラミング!



  • LINEで送る
  • このエントリーをはてなブックマークに追加

バックナンバー

連載:japan.internet.com翻訳記事

もっと読む

著者プロフィール

あなたにオススメ

All contents copyright © 2005-2022 Shoeisha Co., Ltd. All rights reserved. ver.1.5