はじめに
ASP.NETアプリケーションで未処理の例外が発生すると、既定では訪問者に「ランタイムエラー」ページまたは例外の詳細ページが表示されます(どちらのページが表示されるかは、Webサイトの設定および訪問者がローカルホストから表示しているかどうかによります)。理想的なことを言えば、このような難解なエラーページは開発者だけに表示され、通常のユーザーにはユーザーフレンドリなカスタムエラーページが表示されるべきでしょう。前回の記事「未処理の例外への適切な対処法」では、未処理の例外への応答としてユーザーフレンドリなカスタムWebエラーページを表示するASP.NET Webアプリケーションの設定方法を紹介しました。
ただし、前回の記事で紹介した方法では、未処理の例外が発生したときに内部的にResponse.Redirect()
を使ってユーザーをカスタムエラーページにリダイレクトしています。ユーザーをリダイレクトすると、要求のコンテキストは失われます(リダイレクトによってブラウザがエラーページに新しい要求を送るからです)。そのため、ユーザーフレンドリなカスタムエラーページでは、発生したエラーについての情報を得ることができません。もっと詳しいエラーメッセージを表示するためには、こうした情報が必要になることもあります。
未処理の例外がASP.NETランタイムに通知されると、アプリケーションレベルのError
イベントがトリガされます。このイベントのためのイベントハンドラを作成することで、エラーの詳細にアクセスしたり、エラーを記録したり、開発者に通知したり、Server.Transfer()
を使ってユーザーをカスタムエラーページに送ったりすることができます(コンテキストを維持して、カスタムエラーページから未処理の例外の詳細にアクセスできるようにします)。
この記事では、Error
イベントのイベントハンドラを作成する方法を説明し、エラーの詳細を記録するためのフリーなオープンソースコンポーネントを紹介します。
Errorイベントを調べる
HttpApplication
クラスは、すべてのASP.NET Webアプリケーションに共通のメソッド、プロパティ、イベントを表現するものです。このクラスのイベントの一つに、Errorイベントがあります。未処理の例外がASP.NETランタイムに通知されるたびに、このイベントがトリガされます。例えば、あるASP.NET Webページに、データベースに接続してDELETEステートメントを実行するコードがあるものとします。このページに訪問者があったときにデータベースがオフライン状態になっていると、SqlException
が発生します。コード内にエラー処理ロジックがなければ、この例外はASP.NETページの分離コードクラスからASP.NETランタイムに通知され、その時点でError
イベントが発生します。
未処理の例外が発生した場合、通常は次のいずれかを行います。
- エンドユーザーにユーザーフレンドリなカスタムメッセージを表示する
- そのエラーを「処理」する
エラーを「処理」するには、まずアプリケーションのError
イベントのイベントハンドラを作成する必要があります。イベントハンドラは「Global.asax」で作成できます。あるいはHTTPモジュールで処理できます。イベントハンドラでは、Server.GetLastError()
メソッドを使って未処理の例外の詳細を取得できます。エラーの詳細が得られたら、その情報を記録したり、開発者に通知したり、ユーザーフレンドリなカスタムエラーページに制御を渡したりできます。
Global.asaxでErrorイベントハンドラを作成する
「Global.asax」は、Webアプリケーションのルートディレクトリに追加できるオプションのファイルで、このファイルを使用してアプリケーションレベルおよびセッションレベルのイベントを処理することができます。アプリケーションのError
イベントのイベントハンドラを作成するには、まず「Global.asax」ファイルを自分のプロジェクトに追加します。ソリューションエクスプローラで目的のプロジェクトを右クリックし、[Add New Item]を選択し、ダイアログボックスから[Global Application Class]を選択します(図1のスクリーンショットを参照)。
これにより、メインアプリケーションレベルのイベント(Error
、Start
、End
)に関するイベントハンドラと、セッションレベルのイベント(Start
、End
)に関するイベントハンドラを含むダミーの「Global.asax」ファイルが作成されます。いま問題にしているのはError
イベントだけなので、残りのイベントハンドラは削除してかまいません。
Error
イベントハンドラでは、次のメソッドを呼び出すことにより、発生した例外に関する情報にアクセスできます。
HttpContext.Current.Server.GetLastError()
このメソッドから返されるException
オブジェクトには、ランタイムに通知されたエラー(そのError
イベントをトリガしたもの)に関する情報が入っています。その例外がASP.NETページによるものであれば、元の例外がHttpUnhandledException
内にラップされています。ASP.NETページレベルの例外には、後ほど紹介するInnerException
プロパティを通じてアクセスできます。
未処理の例外が発生したときには、電子メールで開発者に例外の詳細を知らせると共に、前回の記事「未処理の例外への適切な対処法」で説明したように、ユーザーをカスタムエラーページにリダイレクトするものとしましょう。これを行うためには、「Web.config」内で<customErrors>
を適切に設定し、それから「Global.asax」内のError
イベントハンドラに次のコードを追加します。
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs) 'Get exception details Dim ex As Exception = HttpContext.Current.Server.GetLastError() If TypeOf ex Is HttpUnhandledException AndAlso _ ex.InnerException IsNot Nothing Then ex = ex.InnerException End If If ex IsNot Nothing Then Try 'Email the administrator with information 'that an error has occurred '!!! UPDATE THIS VALUE TO YOUR EMAIL ADDRESS Const ToAndFromAddress As String = "'email address'" '(1) Create the MailMessage instance Dim mm As New System.Net.Mail.MailMessage(ToAndFromAddress, _ ToAndFromAddress) '(2) Assign the MailMessage's properties mm.Subject = "An unhandled exception occurred!" mm.Body = String.Format( _ "An unhandled exception occurred:{0}Message: {1}{0}{0} Stack Trace:{0}{2}", _ System.Environment.NewLine, ex.Message, ex.StackTrace) mm.IsBodyHtml = False '(3) Create the SmtpClient object Dim smtp As New System.Net.Mail.SmtpClient '(4) Send the MailMessage (will use the Web.config settings) smtp.Send(mm) Catch 'Whoops, some problem sending email! 'Just send the user onto CustomErrorPage.aspx... End Try End If End Sub
イベントハンドラの最初の数行に特に注意を払ってください。発生した例外にアクセスし(HttpContext.Current.Server.GetLastError()
)、必要に応じてInnerException
プロパティを調べています。それから、「Sending Email in ASP.NET 2.0」に掲載したコードを使って、例外の詳細を指定の電子メールアドレスに送ります(このコードで使用しているSMTPリレーサーバーの詳細は「Web.config」にあります)。
ユーザーがデータ駆動型のページを訪れ、データベースに接続する際に問題があったします。このとき、ユーザーには設定されているエラーページが表示されますが、開発者には例外の詳細を記した電子メールが送られます。