次の方法で共有


未処理の例外を処理する (VB)

作成者: Scott Mitchell

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

運用環境の Web アプリケーションでランタイム エラーが発生した場合は、開発者に通知し、後で診断できるようにエラーをログに記録することが重要です。 このチュートリアルでは、ASP.NET がランタイム エラーを処理する方法の概要を説明し、未処理の例外が ASP.NET ランタイムに到達するたびにカスタム コードを実行する 1 つの方法について説明します。

はじめに

ASP.NET アプリケーションで未処理の例外が発生すると、ASP.NET ランタイムにバブルアップされ、イベントが Error 発生し、適切なエラー ページが表示されます。 エラー ページには、3 つの異なる種類があります。ランタイム エラーの死の黄色い画面 (YSOD) です。例外の詳細 YSOD;およびカスタム エラー ページ。 前の チュートリアル では、リモート ユーザー用のカスタム エラー ページと、ローカルにアクセスするユーザーの例外の詳細 YSOD を使用するようにアプリケーションを構成しました。

サイトの外観に一致するわかりやすいカスタム エラー ページの使用は、既定のランタイム エラー YSOD より優先されますが、カスタム エラー ページの表示は、包括的なエラー処理ソリューションの一部にすぎません。 運用環境のアプリケーションでエラーが発生した場合、例外の原因を特定して対処できるように、開発者にエラーの通知を受け取る必要があります。 さらに、後でエラーを調べて診断できるように、エラーの詳細をログに記録する必要があります。

このチュートリアルでは、未処理の例外の詳細にアクセスして、ログに記録して開発者に通知できるようにする方法について説明します。 このチュートリアルに続く 2 つのチュートリアルでは、少し構成した後に、ランタイム エラーを開発者に自動的に通知し、その詳細をログに記録するエラー ログ ライブラリについて説明します。

注意

このチュートリアルで説明する情報は、未処理の例外を一意またはカスタマイズされた方法で処理する必要がある場合に最も役立ちます。 例外をログに記録して開発者に通知するだけで済む場合は、エラー ログ ライブラリを使用します。 次の 2 つのチュートリアルでは、このような 2 つのライブラリの概要について説明します。

イベントが発生したときにコードをError実行する

イベントは、オブジェクトに、関心のあることが発生したことを通知し、別のオブジェクトが応答でコードを実行するためのメカニズムを提供します。 ASP.NET 開発者として、あなたはイベントの観点から考えることに慣れている。 訪問者が特定の Button をクリックしたときにコードを実行する場合は、その Button Click のイベントのイベント ハンドラーを作成し、そこにコードを配置します。 ASP.NET ランタイムは、ハンドルされない例外が発生するたびにイベントを発生Errorさせると、エラーの詳細をログに記録するためのコードがイベント ハンドラーに格納されます。 しかし、イベントのイベント ハンドラー Error を作成するにはどうすればよいですか?

イベントはError、要求の有効期間中に HttpApplication HTTP パイプラインの特定のステージで発生する クラス内の多くのイベントの 1 つです。 たとえば、 HttpApplication クラスの BeginRequest イベント は、すべての要求の開始時に発生します。その AuthenticateRequest イベント は、セキュリティ モジュールが要求者を識別したときに発生します。 これらのイベントにより、ページ開発者は HttpApplication 、要求の有効期間内のさまざまな時点でカスタム ロジックを実行する手段が得られます。

イベントのイベント ハンドラーは HttpApplication 、 という名前 Global.asaxの特別なファイルに配置できます。 Web サイトでこのファイルを作成するには、 という名前 Global.asaxのグローバル アプリケーション クラス テンプレートを使用して、Web サイトのルートに新しい項目を追加します。

Web サイトでこのファイルを作成するために Global.asax という名前のグローバル アプリケーション クラス テンプレートを使用して、Web サイトのルートに新しい項目を追加するスクリーンショット。

図 1: Web アプリケーションに追加 Global.asax する
(クリックするとフルサイズの画像が表示されます)

Visual Studio によって作成される Global.asax ファイルの内容と構造は、Web アプリケーション プロジェクト (WAP) と Web サイト プロジェクト (WSP) のどちらを使用しているかによって若干異なります。 WAP では、 Global.asax は 2 つの別個のファイルとして実装されます。 Global.asaxGlobal.asax.vb ファイルにはGlobal.asax、ファイルを参照.vbする@Applicationディレクティブ以外に何も含めず、対象のイベント ハンドラーはファイルでGlobal.asax.vb定義されます。 WSP の場合、1 つのファイル () のみが作成され、 Global.asaxイベント ハンドラーは ブロックで <script runat="server"> 定義されます。

Global.asax Visual Studio のグローバル アプリケーション クラス テンプレートによって WAP で作成されたファイルには、および Application_Errorという名前Application_BeginRequestApplication_AuthenticateRequestのイベント ハンドラーが含まれています。これは、それぞれ イベント 、AuthenticateRequest、および ErrorHttpApplicationイベント BeginRequestハンドラーです。 、および Session_Endという名前Application_EndApplication_StartSession_Startのイベント ハンドラーもあります。これは、Web アプリケーションの起動時、新しいセッションの開始時、アプリケーションの終了時、およびセッションの終了時に発生するイベント ハンドラーです。 Global.asax Visual Studio によって WSP で作成されたファイルには、、Application_StartSession_StartApplication_Endおよび Session_End イベント ハンドラーだけがApplication_Error含まれています。

注意

ASP.NET アプリケーションをデプロイするときは、そのファイルを Global.asax 運用環境にコピーする必要があります。 WAP で作成されたファイルは Global.asax.vb 、このコードがプロジェクトのアセンブリにコンパイルされるため、運用環境にコピーする必要はありません。

Visual Studio のグローバル アプリケーション クラス テンプレートによって作成されたイベント ハンドラーは、すべてではありません。 イベント ハンドラー に という名前を付けることで、任意 HttpApplication のイベントのイベント ハンドラー Application_EventNameを追加できます。 たとえば、次のコードをファイルに追加して、Global.asaxイベントのイベント ハンドラーをAuthorizeRequest作成できます。

Sub Application_AuthorizeRequest(ByVal sender As Object, ByVal e As EventArgs)
    ' Event handler code
End Sub

同様に、グローバル アプリケーション クラス テンプレートによって作成された、不要なイベント ハンドラーを削除することもできます。 このチュートリアルでは、イベントのイベント ハンドラー Error のみが必要です。ファイルから他のイベント ハンドラーを自由に Global.asax 削除できます。

注意

HTTP モジュールは、イベントの イベント ハンドラーを定義する別の方法を HttpApplication 提供します。 HTTP モジュールは、Web アプリケーション プロジェクト内に直接配置したり、別のクラス ライブラリに分離したりできるクラス ファイルとして作成されます。 これらはクラス ライブラリに分離できるため、HTTP モジュールは、イベント ハンドラーを作成するためのより柔軟で再利用可能なモデルを HttpApplication 提供します。 Global.asaxファイルは、それが存在する Web アプリケーションに固有ですが、HTTP モジュールはアセンブリにコンパイルできます。その時点で、HTTP モジュールを Web サイトに追加することは、アセンブリを フォルダーにBinドロップして にモジュールWeb.configを登録するのと同じくらい簡単です。 このチュートリアルでは、HTTP モジュールの作成と使用については説明しませんが、次の 2 つのチュートリアルで使用される 2 つのエラー ログ ライブラリは HTTP モジュールとして実装されています。 HTTP モジュールの利点の詳細については、「HTTP モジュールとハンドラーを使用してプラグ可能な ASP.NET コンポーネントを作成する」を参照してください。

未処理の例外に関する情報の取得

この時点で、イベント ハンドラーを含む Global.asax ファイルがあります Application_Error 。 このイベント ハンドラーを実行するときは、開発者にエラーを通知し、その詳細をログに記録する必要があります。 これらのタスクを実行するには、まず、発生した例外の詳細を確認する必要があります。 Server オブジェクトの GetLastError メソッド を使用して、イベントが発生する原因となった未処理の例外の詳細を Error 取得します。

Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
    ' Get the error details
    Dim lastErrorWrapper As HttpException = _
        CType(Server.GetLastError(), HttpException)
End Sub

メソッドはGetLastError、 型Exceptionのオブジェクトを返します。これは、.NET Framework内のすべての例外の基本型です。 しかし、上記のコードでは、 によって GetLastError 返された Exception オブジェクトを オブジェクトに HttpException キャストしています。 ASP.NET リソースの Error 処理中に例外がスローされたためにイベントが発生する場合、スローされた例外は 内で HttpExceptionラップされます。 Error イベントを発生させた実際の例外を取得するには、 プロパティを InnerException 使用します。 存在しないページの Error 要求など、HTTP ベースの例外が原因でイベントが発生した場合は、 HttpException がスローされますが、内部例外はありません。

次のコードでは、 をGetLastErrormessage使用して、イベントをトリガーした例外に関する情報をError取得し、 という名前lastErrorWrapperの変数に をHttpException格納します。 次に、発生元の例外の型、メッセージ、スタック トレースを 3 つの文字列変数に格納し、 がイベントをトリガーしたError実際の例外 (HTTP ベースの例外の場合) か、または要求の処理中にスローされた例外のラッパーであるかどうかを確認lastErrorWrapperします。

Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
    ' Get the error details
    Dim lastErrorWrapper As HttpException = _
        CType(Server.GetLastError(), HttpException)

    Dim lastError As Exception = lastErrorWrapper
    If lastErrorWrapper.InnerException IsNot Nothing Then
        lastError = lastErrorWrapper.InnerException
    End If

    Dim lastErrorTypeName As String = lastError.GetType().ToString()
    Dim lastErrorMessage As String = lastError.Message
    Dim lastErrorStackTrace As String = lastError.StackTrace
End Sub

この時点で、例外の詳細をデータベース テーブルに記録するコードを記述するために必要なすべての情報があります。 目的のエラーの詳細 (型、メッセージ、スタック トレースなど) ごとに列を含むデータベース テーブルを作成し、要求されたページの URL や現在ログオンしているユーザーの名前などの便利な情報を作成できます。 Application_Errorイベント ハンドラーでは、データベースに接続し、テーブルにレコードを挿入します。 同様に、電子メールでエラーを開発者に警告するコードを追加することもできます。

次の 2 つのチュートリアルで確認したエラー ログ ライブラリは、このような機能をすぐに使用できるため、このエラー ログと通知を自分で作成する必要はありません。 ただし、イベントが Error 発生していること、およびイベント ハンドラーを使用してエラーの Application_Error 詳細をログに記録し、開発者に通知できることを示すために、エラーが発生したときに開発者に通知するコードを追加しましょう。

未処理の例外が発生したときに開発者に通知する

運用環境で未処理の例外が発生した場合は、エラーを評価し、実行する必要があるアクションを決定できるように、開発チームに警告することが重要です。 たとえば、データベースへの接続でエラーが発生した場合は、接続文字列チェック倍にする必要があります。また、Web ホスティング会社でサポート チケットを開く必要があります。 プログラミング エラーが原因で例外が発生した場合は、将来このようなエラーを防ぐために、追加のコードまたは検証ロジックを追加する必要があります。

名前空間の .NET Framework クラスをSystem.Net.Mail使用すると、電子メールを簡単に送信できます。 クラスはMailMessage電子メール メッセージを表し、 などのToBodyFromSubjectプロパティがあります。Attachments SmtpClassは、指定した SMTP サーバーをMailMessage使用してオブジェクトを送信するために使用されます。SMTP サーバーの設定は、 の Web.config file要素で<system.net>プログラムまたは宣言によって指定できます。 ASP.NET アプリケーションで電子メール メッセージを送信する方法の詳細については、「ASP.NET Web ページ サイトからEmailを送信する」および「System.Net.Mail FAQ」をチェック。

注意

要素 <system.net> には、電子メールの送信時に SmtpClient クラスによって使用される SMTP サーバー設定が含まれます。 Web ホスティング会社には、アプリケーションからメールを送信するために使用できる SMTP サーバーがある可能性があります。 Web アプリケーションで使用する必要がある SMTP サーバー設定については、Web ホストのサポート セクションを参照してください。

次のコードをイベント ハンドラーに Application_Error 追加して、エラーが発生したときに開発者に電子メールを送信します。

Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
    ' Get the error details
    Dim lastErrorWrapper As HttpException = _
        CType(Server.GetLastError(), HttpException)
    
    Dim lastError As Exception = lastErrorWrapper
    If lastErrorWrapper.InnerException IsNot Nothing Then
        lastError = lastErrorWrapper.InnerException
    End If

    Dim lastErrorTypeName As String = lastError.GetType().ToString()
    Dim lastErrorMessage As String = lastError.Message
    Dim lastErrorStackTrace As String = lastError.StackTrace

    Const ToAddress As String = "support@example.com"
    Const FromAddress As String = "support@example.com"
    Const Subject As String = "An Error Has Occurred!"

    ' Create the MailMessage object
    Dim mm As New MailMessage(FromAddress, ToAddress)
    mm.Subject = Subject
    mm.IsBodyHtml = True
    mm.Priority = MailPriority.High
  mm.Body = string.Format( _
"<html>" & vbCrLf & _
"  <body>" & vbCrLf & _
"  <h1>An Error Has Occurred!</h1>" & vbCrLf & _
"  <table cellpadding=""5"" cellspacing=""0"" border=""1"">" & vbCrLf & _
"  <tr>" & vbCrLf & _
"  <tdtext-align: right;font-weight: bold"">URL:</td>" & vbCrLf & _
"  <td>{0}</td>" & vbCrLf & _
"  </tr>" & vbCrLf & _
"  <tr>" & vbCrLf & _
"  <tdtext-align: right;font-weight: bold"">User:</td>" & vbCrLf & _
"  <td>{1}</td>" & vbCrLf & _
"  </tr>" & vbCrLf & _
"  <tr>" & vbCrLf & _
"  <tdtext-align: right;font-weight: bold"">Exception Type:</td>" & vbCrLf & _
"  <td>{2}</td>" & vbCrLf & _
"  </tr>" & vbCrLf & _
"  <tr>" & vbCrLf & _
"  <tdtext-align: right;font-weight: bold"">Message:</td>" & vbCrLf & _
"  <td>{3}</td>" & vbCrLf & _
"  </tr>" & vbCrLf & _
"  <tr>" & vbCrLf & _
"  <tdtext-align: right;font-weight: bold"">Stack Trace:</td>" & vbCrLf & _
"  <td>{4}</td>" & vbCrLf & _
"  </tr> " & vbCrLf & _
"  </table>" & vbCrLf & _
"  </body>" & vbCrLf & _
"</html>", _
  Request.RawUrl, _
  User.Identity.Name, _
  lastErrorTypeName, _
  lastErrorMessage, _
  lastErrorStackTrace.Replace(Environment.NewLine, "<br />"))

    'Attach the Yellow Screen of Death for this error
    Dim YSODmarkup As String = lastErrorWrapper.GetHtmlErrorMessage()
    If Not String.IsNullOrEmpty(YSODmarkup) Then
        Dim YSOD As Attachment = _
            Attachment.CreateAttachmentFromString(YSODmarkup, "YSOD.htm")
        mm.Attachments.Add(YSOD)
    End If

    ' Send the email
    Dim smtp As New SmtpClient()
    smtp.Send(mm)
End Sub

上記のコードは非常に長い間、その大部分は開発者に送信された電子メールに表示される HTML を作成します。 コードは、メソッド (lastErrorWrapper) によって返される をGetLastErrorHttpException参照することから始まります。 要求によって発生した実際の例外は を介して lastErrorWrapper.InnerException 取得され、変数 lastErrorに割り当てられます。 型、メッセージ、およびスタック トレース情報は、 から lastError 取得され、3 つの文字列変数に格納されます。

次に、 という名前mmMailMessageオブジェクトが作成されます。 電子メール本文は HTML 形式で、要求されたページの URL、現在ログオンしているユーザーの名前、例外に関する情報 (型、メッセージ、スタック トレース) が表示されます。 クラスの良い点 HttpException の 1 つは、 GetHtmlErrorMessage メソッドを呼び出すことで、例外の詳細の黄色い画面 (YSOD) の作成に使用される HTML を生成できることです。 このメソッドは、例外の詳細 YSOD マークアップを取得し、添付ファイルとして電子メールに追加するために使用されます。 注意が必要です。イベントをトリガーした Error 例外が HTTP ベースの例外 (存在しないページの要求など) GetHtmlErrorMessage である場合、メソッドは を返 nullします。

最後の手順では、 を送信します MailMessage。 これを行うには、新しい SmtpClient メソッドを作成し、そのメソッドを Send 呼び出します。

注意

Web アプリケーションでこのコードを使用する前に、 定数と 定数のToAddress値を、support@example.comエラー通知メールの送信先とFromAddress送信元のメール アドレスに変更する必要があります。 また、 の セクションWeb.configで SMTP サーバー設定を<system.net>指定する必要があります。 使用する SMTP サーバーの設定を決定するには、Web ホスト プロバイダーに問い合わせてください。

このコードを配置すると、エラーが発生するたびに、開発者はエラーを要約し、YSOD を含む電子メール メッセージを送信します。 前のチュートリアルでは、Genre.aspxにアクセスし、 などのGenre.aspx?ID=fooクエリ文字列を使用して無効なID値を渡すことで、ランタイム エラーを示しました。 ファイルが配置されたページにアクセスすると、前のチュートリアルと Global.asax 同じユーザー エクスペリエンスが生成されます。開発環境では、引き続き例外の詳細の黄色い画面が表示されますが、運用環境ではカスタム エラー ページが表示されます。 この既存の動作に加えて、開発者には電子メールが送信されます。

図 2 は、 にアクセスしたときに受信したメールを Genre.aspx?ID=foo示しています。 電子メール本文は例外情報を要約し YSOD.htm 、添付ファイルには例外の詳細 YSOD に表示されるコンテンツが表示されます ( 図 3 を参照)。

例外情報と共に受信したメールのスクリーンショット。

図 2: 未処理の例外が発生するたびに、開発者にEmail通知が送信される
(クリックするとフルサイズの画像が表示されます)

未処理の例外がある場合に開発者が受信した電子メール通知のスクリーンショット。

図 3: Email通知には、例外の詳細 YSOD が添付ファイルとして含まれています
(クリックするとフルサイズの画像が表示されます)

カスタム エラー ページの使用について

このチュートリアルでは、 と イベント ハンドラーをApplication_Error使用Global.asaxして、ハンドルされない例外が発生したときにコードを実行する方法について説明しました。 具体的には、このイベント ハンドラーを使用して、開発者にエラーを通知しました。それを拡張して、エラーの詳細をデータベースに記録することもできます。 イベント ハンドラーが Application_Error 存在しても、エンド ユーザーのエクスペリエンスには影響しません。 エラーの詳細 YSOD、ランタイム エラー YSOD、カスタム エラー ページなど、構成済みのエラー ページが引き続き表示されます。

カスタム エラー ページを使用するときに、ファイルとApplication_Errorイベントが必要かどうかGlobal.asax疑問に思うのが自然です。 エラーが発生すると、ユーザーにカスタム エラー ページが表示されるため、開発者に通知するコードを配置し、エラーの詳細をカスタム エラー ページの分離コード クラスに記録できないのはなぜですか? カスタム エラー ページの分離コード クラスにコードを追加することはできますが、前のチュートリアルで説明した手法を使用するときにイベントをトリガーした Error 例外の詳細にはアクセスできません。 GetLastErrorカスタム エラー ページから メソッドを呼び出すと、 が返されますNothing

この動作の理由は、カスタム エラー ページにリダイレクトによって到達するためです。 ハンドルされない例外が ASP.NET ランタイムに達すると、ASP.NET エンジンはそのErrorイベント (イベント ハンドラーをApplication_Error実行) を発生させ、 を発行Response.Redirect(customErrorPageUrl)してユーザーをカスタム エラー ページにリダイレクトします。 メソッドは Response.Redirect HTTP 302 状態コードを使用してクライアントに応答を送信し、新しい URL (カスタム エラー ページ) を要求するようにブラウザーに指示します。 その後、ブラウザーはこの新しいページを自動的に要求します。 ブラウザーのアドレス バーがカスタム エラー ページ URL に変わるため、エラーが発生したページとは別にカスタム エラー ページが要求されたことを確認できます ( 図 4 を参照)。

エラーが発生したときにカスタム エラー ページ U R L にリダイレクトされるブラウザーのスクリーンショット。

図 4: エラーが発生すると、ブラウザーはカスタム エラー ページ URL にリダイレクトされます
(クリックするとフルサイズの画像が表示されます)

最終的な効果は、サーバーが HTTP 302 リダイレクトで応答したときに、ハンドルされない例外が発生した要求が終了することです。 カスタム エラー ページに対する後続の要求は、まったく新しい要求です。この時点で、ASP.NET エンジンはエラー情報を破棄し、さらに、前の要求の未処理の例外をカスタム エラー ページの新しい要求に関連付ける方法はありません。 これが、カスタム エラー ページから呼び出されたときに を返すnull理由GetLastErrorです。

ただし、エラーの原因となったのと同じ要求中にカスタム エラー ページを実行することもできます。 メソッドは Server.Transfer(url) 、指定した URL に実行を転送し、同じ要求内で処理します。 イベント ハンドラー内のコードを Application_Error カスタム エラー ページの分離コード クラスに移動し、 を次のコードに Global.asax 置き換えます。

Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
    ' Get the error details
    Dim lastErrorWrapper As HttpException = _
        CType(Server.GetLastError(), HttpException)

    If lastErrorWrapper.GetHttpCode() = 404 Then
        Server.Transfer("~/ErrorPages/404.aspx")
    Else
        Server.Transfer("~/ErrorPages/Oops.aspx")
    End If
End Sub

ハンドルされない例外が発生すると、 Application_Error イベント ハンドラーは HTTP 状態コードに基づいて適切なカスタム エラー ページに制御を転送します。 制御が転送されたため、カスタム エラー ページは を介して Server.GetLastError 未処理の例外情報にアクセスでき、開発者にエラーを通知してその詳細をログに記録できます。 この呼び出しにより Server.Transfer 、ASP.NET エンジンがユーザーをカスタム エラー ページにリダイレクトできなくなります。 代わりに、カスタム エラー ページのコンテンツが、エラーを生成したページへの応答として返されます。

まとめ

ASP.NET Web アプリケーションでハンドルされない例外が発生すると、ASP.NET ランタイムによってイベントが Error 発生し、構成されたエラー ページが表示されます。 Error イベントのイベント ハンドラーを作成することで、開発者にエラーを通知したり、詳細をログに記録したり、他の方法で処理したりできます。 イベントのイベント ハンドラー HttpApplicationErrorを作成するには、次の 2 つの方法があります。ファイル内 Global.asax または HTTP モジュールから。 このチュートリアルでは、電子メール メッセージを Error 使用して開発者にエラーを Global.asax 通知するイベント ハンドラーをファイルに作成する方法について説明しました。

イベント ハンドラーの Error 作成は、一意またはカスタマイズされた方法で未処理の例外を処理する必要がある場合に便利です。 ただし、例外をログに記録したり、開発者に通知したりするために独自 Error のイベント ハンドラーを作成することは、数分でセットアップできる無料で使いやすいエラー ログ ライブラリが既に存在するため、時間の最も効率的な使用ではありません。 次の 2 つのチュートリアルでは、このような 2 つのライブラリを調べます。

プログラミングに満足!

もっと読む

このチュートリアルで説明するトピックの詳細については、次のリソースを参照してください。