規則の例外 - 第 1 部

Rob Howard
Microsoft Corporation

July 23, 2001

事実を事実として認めるならば、Web アプリケーションを作成すると、たまにコード内でバグ (より偏見のない世界では、問題点と言います) に遭遇します。不幸なことに、テスタの地位が高い (つまり CEO、CIO、取締役の面々、VC など) ほど、決まってこれらの問題点がより頻発かつ深刻になる傾向にあるようです。

今までに ASP で何かしら開発を行ったことがあれば、次のような驚くほど役に立つ ASP のエラー メッセージが表示されたことがあるでしょう。


  HTTP 500.100 - 内部サーバー エラー - ASP エラー 
エラーの種類 : Microsoft VBScript ランタイム (0x800A01A8)

このエラー メッセージを見たとき、開発者としての最初の反応は普通 "はぁ ?" でしょう。 HTTP 500 内部サーバー エラーというエラーは、ASP コードに問題があることを示していると解釈します。次の部分はエラーの種類で、上記の場合は HRESULT コードの 0x800A01AB です。手に余るほど十分な時間がある場合は、MSDN を検索して (または Captain Crunch のおまけの COM HRESULT secret decoder ring を使用して)、このコードが存在しないメソッドに遅延バインディングを呼び出そうとしていることを理解できます。

あいにく、私たちのところでは、地位のあるテスタがこうした問題を発見するのは、きわめてありふれたことです。そして、彼らは通常バグをファイルに記録するのではなく、受話器を持ち上げ、だれか (普通は開発者の上司にですが) を怒鳴りつけるなど、あまり建設的ではない方法を取ることになります。よくある質問はこうです。「エラーが発生するのに、なぜ Microsoft ASP を使っているんだ ? ほら、ちゃんとここに 'ASP エラー' と書いてあるだろう。」

ASP.NET では、この種のエラーを簡単に処理できるようになり、その他の実行時の問題はさらに構造化され、ユーザーが扱いやすい方法で対処できます。アプリケーションは開発者に内容の豊富な、開発者固有のエラー メッセージを提供できるのですが、地位のあるテスタには美しく、非常にあいまいなメッセージを提供し、開発者はこのメッセージで自分のシステムの複雑さを喜ぶのです。

また、(自分で記述しない限りは) 「ASP.NET エラー」と表示される ASP.NET エラー ページをご覧になることはなくなるでしょう。開発者が正直であってほしいので、「アプリケーション エラー」と表示します。

ASP.NET 例外処理

ASP.NET を使って、 CLS (Common Language Specification) に準拠する任意の言語で Web アプリケーションを構築できます。 CLS は標準化のために ECMA に提案された仕様です。 CLS 準拠の言語は、CLR (Common Language Runtime) を実行できます。 CLR はガーベジ コレクション、安全なコード実行、構造化例外処理などの機能を提供します。

この言語中立の構造化例外処理を ASP.NET アプリケーション内で利用できます。構造化例外処理は、try/catch/finally ブロックを使って行われます。この処理を行うと、例外発生時に開発者が例外を処理する必要があります。例外が発生した場合、例外を明示的に処理しないと、一般的な ASP.NET の例外が発生し、コードを続行できない状態にしてアプリケーションを強制的に終了し、エラー ページを生成します。この構造化例外処理により、エラーの発見がきわめて容易になります。

これは ASP コードとは大きく異なります。 ASP コードでは、Visual Basic 6 の COM オブジェクトのメソッドを呼び出せます。メソッド内でエラーが発生すると、エラー コード (HRESULT) が返されます。ただしコードを調べるのは開発者の役目であり、通常はコード (擬似コード) を以下のようにラップします。


  <%
On Error Resume Next
...
dataObject.SomeBadMethod()
If Error.Number <> 0 Then
  Response.Write("SomeBadMethod() への呼び出しでエラーが発生しました")
  Response.End
End If
dataObject.SomeGoodMethod()
%>

上記の ASP の擬似コードで Error.Number を確認しなかった場合、コードは SomeBadMethod() の呼び出し後も実行を継続し、 SomeGoodMethod() の呼び出しまで実行します。

エラーを確認するという考え方は ASP.NET でも依然として存在しますが、エラー発生時に処理を行う必要があります。


  <Script runat=server>
Public Sub Page_Load(sender As Object, e As EventArgs)
  ・・・
    Begin Try
    dataObject.SomeBadMethod()
  Catch
    ' 例外が発生した場合、作業を行いエラーを
    ' 処理して例外をクリアするか、または失敗します
  End Try
  ・・・
End Sub
</Script>

重要な違いは、Try/Catch/Finally を排他的に使用して例外を処理するようになったことです。これは良いことです。エラーが発生すると、例外のインスタンス、または派生され、より特化されたインスタンスを取得します。派生され、より特化されたインスタンスとは、 null 値を許可しない SQL テーブルのある列に null 値を挿入しようとするときの NoNullAllowedException などです。

例外は、何が発生したかに関する詳細情報を提供します。たとえば、スタック トレース、内部例外の詳細、メッセージ、およびその他の詳細です。これは COM HRESULT エラー コードよりもはるかに優れています !

処理されない例外

上記で、ASP.NET、というより CLR により、エラー発生時に例外を処理する必要があると述べました。明らかにすべてのコードに Try/Catch/Finally ブロックを記述するとは限りません。このブロックを記述していない場合には、 ASP.NET は、次の 2 つのイベントを使って例外を処理するいくつかの機能提供します。

  • Page_Error
  • Application_Error

ページ エラーとアプリケーション エラー イベント ハンドラ

例外が予期せず、Try/Catch/Finally ブロック外で発生する場合があります。通常このような例外は、アプリケーションがかなりおかしな状態になっていることを示します。つまり、異常なエラーが発生しても、エラーを回避する有効な方法がないのです。誤ったコードを Try/Catch/Finally ブロックで囲んでいない場合でも、ページ レベルまたはアプリケーション レベルのいずれかでエラーを処理することを選択できます。

ページ レベルでのエラー処理を選択する場合は、 Page_Error イベントの実装を提供する必要があります。

VB .NET


  Public Sub Page_Error(sender As Object, e As EventArgs)
   ' ここに実装を記述します
End Sub

C#


  public void Page_Error(Object sender, EventArgs e) {
   // ここに実装を記述します
}

このイベントは、エラーが発生する可能性のあるページに実装します。

また、Application_Error イベントも実装できます。 Page_Error イベントとは異なり、 Application_Error イベントは global.asax に実装され、 (このイベントが実装済みの場合は) アプリケーション内で発生するすべての処理されないエラーがこのイベントを起動します。

VB .NET


  Public Sub Application_Error(sender As Object, e As EventArgs)
   ' ここに実装を記述します
End Sub

C#


  public void Application_Error(Object sender, EventArgs e) {
   // ここに実装を記述します
}

以上のイベントのどちらでも、管理者に電子メールを送信したり、エラーが発生したことを示すイベント ログを記述するなどのコードを実行できます。次回のコラムでは、Windows イベント ログへの書き込みを取り上げる予定です。

アプリケーション内で例外が発生したことを必要な人に警告するコードを実行する以外にも、例外をメッセージとして通知する機会があり、必要に応じて例外をクリアできます。 Exception クラスを返す Server.GetLastError() メソッドで発生する例外にアクセスし、 API Server.ClearError() を使用して例外をクリアできます。

まとめ

Common Language Runtime によって、 ASP.NET の開発者は Try/Catch/Finally ブロックを使用して、構造化されたエラー処理コードを記述できるようになりました。エラーが確認されないまま進行し、あとで混乱を招く可能性のある ASP/COM とは異なり、 CLR を使用するとエラー発生時にエラーを処理する必要があります。次回のコラムでは、ASP.NET のエラー処理を引き続き取り上げ、 ASP.NET が生成するカスタム エラー ページについて見てみましょう。また、Windows イベント ログに例外の詳細を記述できるコードの実装方法についても取り上げるつもりです。

コミュニティ 最新情報

ユーザー グループ: 先日セントルイスで大きな .NET ユーザー グループに講演を行いました。 8 月 9 日にはダラスで別のユーザー グループに講演します。詳細については http://www.dnug.net/ViewGroup.aspx?id=21 をご覧ください。その後 8 月 28 日にはサンディエゴのユーザー グループに講演を行います (URL は追ってお知らせします)。まだ .NET ユーザー グループに参加してない方や、 .NET を本気で勉強しようとしている方はぜひ参加することをお勧めします。

カンファレンス: フロリダ州オーランドで 9 月 18 日から 21 日まで開催される Microsoft ASP .NET Connections カンファレンスは、完全に ASP.NET に的を絞った重要なカンファレンスになるでしょう。詳細については http://www.asp-connections.com/ をご覧ください。

ASP.NET の雑誌: ここ 6 か月の間、ASP.NET チームは ASP.NET 開発者専門の雑誌を刊行するため、 Informant Communications Group 社と緊密に仕事を進めてきました。雑誌名は asp.netPRO で、今秋後半には入手可能になる予定です。 ASP.NET チームはこの雑誌に大々的に参加する予定です。 "Ask the ASP.NET Team" というコラムを設けて、並外れた才能を持つサード パーティの多くの ASP.NET 専門家以外に、 ASP.NET チームからも寄稿します。詳細および無料版の申し込みについては、 http://www.aspnetpro.com/ を参照してください。

Rob Howard は .NET Framework チームの ASP.NET プログラム マネージャです。彼がコードやコラムを書いていないときは、いつも家族と過ごすか東ワシントン川でフライ フィッシングを楽しんでいます。