カスタム エラー ページを表示する (C#)
ASP.NET Web アプリケーションでランタイム エラーが発生した場合、ユーザーには何が表示されますか? その答えは、Web サイトの <customErrors> がどのように構成されているかによって異なります。 既定では、ユーザーにはランタイム エラーが発生したことを示す目障りな黄色の画面が表示されます。 このチュートリアルでは、これらの設定をカスタマイズして、サイトの外観と一致する見栄えのよいカスタム エラー ページを表示する方法について説明します。
はじめに
理想の世界では、ランタイム エラーは発生しません。 プログラマはバグがまったくないコードを堅牢なユーザー入力検証を使って記述し、データベース サーバーや電子メール サーバーなどの外部リソースはオフラインになることはありません。 もちろん、現実にはエラーは避けられません。 .NET Framework のクラスは、例外をスローすることによってエラーを通知します。 たとえば、SqlConnection オブジェクトの Open メソッドを呼び出すと、接続文字列で指定されたデータベースへの接続が確立されます。 ただし、データベースがダウンしている場合や接続文字列の資格情報が無効な場合には、Open メソッドは SqlException
をスローします。 例外は、try/catch/finally
ブロックを使用することで処理できます。 try
ブロック内のコードが例外をスローした場合、制御は適切な catch ブロックに移動し、開発者はそこでエラーからの回復を試みることができます。 一致する catch ブロックがない場合、または例外をスローしたコードが try ブロックにない場合には、例外は try/catch/finally
ブロックを探して呼び出し履歴までパーコレートされます。
例外が処理されずに ASP.NET ランタイムまでバブリングすると、HttpApplication
クラスの Error
イベントが発生し、構成された "エラー ページ" が表示されます。 既定では、ASP.NET は、親しみを込めてYellow Screen of Death (死のイエロー スクリーン) (YSOD) と呼ばれるエラー ページを表示します。 YSOD には 2 つのバージョンがあります。1 つは例外の詳細やスタック トレースのほか、アプリケーションをデバッグする開発者にとって役立つその他の情報を表示します (図 1 を参照)。もう 1 つは、単にランタイム エラーが発生したことを表示します (図 2 を参照)。
例外の詳細の YSOD は、開発者がアプリケーションをデバッグする場合には非常に役立ちますが、エンド ユーザーに YSOD を表示することは悪趣味であり、プロフェッショナルではありません。 その代わりに、サイトの外観を維持し、ユーザーフレンドリな文章で状況を説明したエラー ページにエンド ユーザーを移動させる必要があります。 幸い、このようなカスタム エラー ページを作成することは非常に簡単です。 このチュートリアルでは、まず ASP.NET のさまざまなエラー ページを見ていきます。 次に、エラーが発生した場合に、ユーザーに対してカスタム エラー ページを表示するように Web アプリケーションを構成する方法を示します。
3 種類のエラー ページを探る
ASP.NET アプリケーションでハンドルされない例外が発生すると、以下の 3 種類のエラー ページのいずれかが表示されます。
- 例外の詳細のイエロー スクリーン エラー ページ
- ランタイム エラーのイエロー スクリーン エラー ページ
- カスタム エラー ページ
エラー ページの開発者が最も見慣れているのは、例外の詳細の YSOD です。 既定では、このページはローカルでアクセスしているユーザーに表示されるため、このページは開発環境でサイトをテストしているときにエラーが発生すると表示されます。 その名の通り、例外の詳細の YSOD では、例外の詳細 (型、メッセージ、スタック トレース) が示されます。 さらに、例外が ASP.NET ページの分離コード クラスのコードによって発生し、アプリケーションがデバッグ用に構成されている場合は、例外の詳細の YSOD にもこのコード行 (およびその上下数行のコード) が表示されます。
図 1 は、例外の詳細の YSOD ページを示しています。 ブラウザーのアドレス ウィンドウの URL (http://localhost:62275/Genre.aspx?ID=foo
) に注意してください。 Genre.aspx
ページには、特定のジャンルの書籍レビューが一覧表示されていることを思い出してください。 querystring を介して GenreId
値 (uniqueidentifier
) を渡す必要があります。たとえば、フィクションのレビューを表示するための適切な URL は Genre.aspx?ID=7683ab5d-4589-4f03-a139-1c26044d0146
です。 uniqueidentifier
以外の値 ("foo" など) が querystring を介して渡されると、例外がスローされます。
Note
ダウンロード可能なデモ Web アプリケーションでこのエラーを再現するには、Genre.aspx?ID=foo
に直接アクセスするか、Default.aspx
内の "ランタイム エラーの生成" リンクをクリックします。
図 1 に示されている例外情報に注意してください。 「文字列から uniqueidentifier に変換中、変換に失敗しました」という例外メッセージがページの上部に表示されます。 例外の型である System.Data.SqlClient.SqlException
も表示されます。 スタック トレースもあります。
図 1: 例外の詳細の YSOD には、例外に関する情報が含まれています
(クリックするとフルサイズの画像が表示されます)
もう 1 つの種類の YSOD は、図 2 に示すランタイム エラーの YSOD です。 ランタイム エラーの YSOD は、ランタイム エラーが発生したことを訪問者に通知しますが、スローされた例外に関する情報は含まれません。 (ただし、Web.config
ファイルを変更することでエラーの詳細を表示できるようにする手順の説明は示されます。これが、このような YSOD をプロフェッショナルでない見た目にしている一因です。)
既定では、図 2 上のブラウザーのアドレス バーの URL (http://httpruntime.web703.discountasp.net/Genre.aspx?ID=foo
) からわかるとおり、ランタイム エラーの YSOD はリモート (http://www.yoursite.com 経由) でアクセスしているユーザーに表示されます。 2 つの異なる YSOD 画面が存在するのは開発者がエラーの詳細を把握したいからですが、サイトにアクセスするユーザーに対して潜在的なセキュリティ脆弱性やその他の機密情報を明かしてしまうおそれがあるため、このような情報はライブ サイトには表示しないでください。
Note
これまでの手順に従って DiscountASP.NET を Web ホストとして使用している場合は、ライブ サイトにアクセスしてもランタイム エラーの YSOD が表示されないことに気づくかもしれません。 これは、DiscountASP.NET のサーバーが既定で例外の詳細の YSOD を表示するように構成されているためです。 幸い、Web.config
ファイルに <customErrors>
セクションを追加することで、この既定の動作をオーバーライドできます。 <customErrors>
セクションについて詳しくは、「表示されるエラー ページを構成する」をご覧ください。
図 2: ランタイム エラーの YSOD には、エラーの詳細は含まれません
(クリックするとフルサイズの画像が表示されます)
3 つ目の種類のエラー ページは、独自に作成する Web ページであるカスタム エラー ページです。 カスタム エラー ページの利点は、ページの外観に加えてユーザーに表示される情報を完全に制御できる点です。カスタム エラー ページでは、他のページと同じマスター ページとスタイルを使用できます。 「カスタム エラー ページを使用する」セクションでは、カスタム エラー ページを作成し、ハンドルされない例外が発生した場合にそれが表示されるように構成する手順について説明します。 図 3 は、このカスタム エラー ページのプレビューを示しています。 ご覧のとおり、このエラー ページの外観は、図 1 と図 2 に示されているいずれのイエロー スクリーンよりもはるかにプロフェッショナルなデザインです。
図 3: カスタム エラー ページは、よりカスタマイズされた外観を提供します
(クリックするとフルサイズの画像が表示されます)
図 3 のブラウザーのアドレス バーを調べてみてください。 アドレス バーには、カスタム エラー ページ (/ErrorPages/Oops.aspx
) の URL が表示されていることに注意してください。 図 1 と図 2 では、エラーの発生元 (Genre.aspx
) と同じページにイエロー スクリーンが表示されています。 カスタム エラー ページには、aspxerrorpath
querystring パラメーターを介してエラーが発生したページの URL が渡されます。
表示されるエラー ページを構成する
3 つのエラー ページのうちどれが表示されるかは、2 つの変数に基づいています。
<customErrors>
セクションの構成情報- ユーザーがサイトにローカルでアクセスしているか、リモートでアクセスしているか
Web.config
の <customErrors>
セクションには、defaultRedirect
と mode
という、どのエラー ページが表示されるかに影響する 2 つの属性があります。 defaultRedirect
属性は省略できます。 これが指定された場合、カスタム エラー ページの URL が指定され、ランタイム エラーの YSOD の代わりにカスタム エラー ページを表示する必要があることが示されます。 mode
属性は必須であり、On
、Off
、RemoteOnly
の 3 つの値のうちいずれかを受け入れます。 これらの値は以下のように動作します。
On
- カスタム エラー ページまたはランタイム エラーの YSOD が、ローカルかリモートかに関係なくすべての訪問者に表示されることを示します。Off
- 例外の詳細の YSOD が、ローカルかリモートかに関係なくすべての訪問者に表示されることを指定します。RemoteOnly
- カスタム エラー ページまたはランタイム エラーの YSOD がリモートの訪問者に表示され、例外の詳細の YSOD がローカルの訪問者に表示されることを示します。
特に指定しない限り、ASP.NET はモード属性を RemoteOnly
に設定し、defaultRedirect
値を指定しなかったかのように機能します。 つまり、既定の動作では、ランタイム エラーの YSOD がリモートの訪問者に表示され、例外の詳細の YSOD がローカルの訪問者に表示されます。 この既定の動作は、<customErrors>
セクションを Web アプリケーションの Web.config file.
に追加することでオーバーライドできます。
カスタム エラー ページを使用する
すべての Web アプリケーションには、カスタム エラー ページが必要です。 ランタイム エラーの YSOD に代わるものとしてよりプロフェッショナルなデザインで簡単に作成できる上、カスタム エラー ページを使用するようにアプリケーションを構成するのにほとんど時間はかかりません。 最初のステップは、カスタム エラー ページの作成です。 Book Reviews アプリケーションに ErrorPages
という新しいフォルダーを追加し、そこに Oops.aspx
という新しい ASP.NET ページを追加しました。 同じ外観を自動的に引き継ぐよう、そのページがサイトの他のページと同じマスター ページを使用するようにします。
図 4: カスタム エラー ページを作成します
次に、数分間時間をさいて、エラー ページのコンテンツを作成します。 予期しないエラーが発生したことを示すメッセージとサイトのホームページへのリンクを含む、やや単純なカスタム エラー ページを作成しました。
図 5: カスタム エラー ページを設計します
(クリックするとフルサイズの画像が表示されます)
エラー ページが完成したら、ランタイム エラーの YSOD の代わりにカスタム エラー ページを使用するように Web アプリケーションを構成します。 これを行うには、<customErrors>
セクションの defaultRedirect
属性でエラー ページの URL を指定します。 アプリケーションの Web.config
ファイルに以下のマークアップを追加します。
<configuration>
...
<system.web>
<customErrors mode="RemoteOnly"
defaultRedirect="~/ErrorPages/Oops.aspx" />
...
</system.web>
</configuration>
上記のマークアップにより、ローカルでアクセスするユーザーには例外の詳細の YSOD を表示しながら、リモートでアクセスするユーザーにはカスタム エラー ページ Oops.aspx を使用するよう、アプリケーションが構成されます。 この動作を確認するには、Web サイトを運用環境に配置し、querystring の値が無効になっているライブ サイトの Genre.aspx ページにアクセスします。 カスタム エラー ページが表示されます (図 3 を参照)。
カスタム エラー ページがリモート ユーザーにのみ表示されることを確認するには、無効な querystring を含む Genre.aspx
ページに開発環境からアクセスします。 例外の詳細の YSOD が表示されます (図 1 を参照)。 RemoteOnly
設定により、運用環境上のサイトにアクセスするユーザーにはカスタム エラー ページが表示される一方で、ローカルで作業している開発者は引き続き例外の詳細を確認できます。
開発者への通知とエラー詳細のログ記録
開発環境で発生するエラーは、コンピューターの前に座っている開発者が原因でした。 例外の詳細の YSOD によって例外の情報が表示され、彼女はエラーが発生したときにどのような手順を実施していたかを把握しています。 しかし、運用環境でエラーが発生した場合は、サイトにアクセスするエンド ユーザーが手間をかけてエラーの報告をした場合を除き、開発者はエラーが発生したことを認識できません。 また、エラーが発生したことをユーザーがわざわざ開発チームに通知してくれたとしても、例外の型やメッセージ、スタック トレースがわからなければ、エラーの修正どころか、原因の診断が困難な場合があります。
このような理由から、運用環境でのエラーがデータベースなどの永続的なストアに記録され、開発者にこのエラーが通知されることが非常に重要になります。 カスタム エラー ページは、このログ記録や通知を行うのに適した場所のように思えるかもしれません。 残念ながら、カスタム エラー ページからはエラーの詳細にアクセスできないため、この情報をログに記録するために使用することはできません。 幸い、エラーの詳細をインターセプトしログに記録する方法はいくつかあります。次の 3 つのチュートリアルでは、このトピックについて詳しく説明します。
個々の HTTP エラー状態に応じたカスタム エラー ページを使用する
ASP.NET ページによって例外がスローされ、処理されない場合、例外は ASP.NET ランタイムまでパーコレートされ、構成されたエラー ページが表示されます。 要求が ASP.NET エンジンに入っても何らかの理由で処理されない場合 (たとえば、要求されたファイルが見つからない、ファイルに対する読み取りアクセス許可が無効になっているなど) は、ASP.NET エンジンによって HttpException
が発生します。 この例外は、ASP.NET ページから発生した例外と同様に、ランタイムまでバブリングし、適切なエラー ページが表示されます。
つまり、運用環境の Web アプリケーションでは、見つからないページをユーザーが要求すると、カスタム エラー ページが表示されます。 図 6 はそのような例を示しています。 要求は存在しないページ (NoSuchPage.aspx
) に対するものであるため、HttpException
がスローされ、カスタム エラー ページが表示されます (aspxerrorpath
querystring パラメーター内の NoSuchPage.aspx
への参照に注意してください)。
図 6: ASP.NET ランタイムが、無効な要求に応答して構成済みのエラー ページを表示します (クリックすると、フルサイズの画像が表示されます)
既定では、すべての型のエラーで同じカスタム エラー ページが表示されます。 ただし、<customErrors>
セクション内の <error>
子要素を使用して、特定の HTTP 状態コードに対して別のカスタム エラー ページを指定できます。 たとえば、HTTP 状態コードが 404 のページが見つからないエラーが発生した場合に別のエラー ページを表示するには、<customErrors>
セクションを更新して以下のマークアップを含めます。
<customErrors mode="RemoteOnly" defaultRedirect="~/ErrorPages/Oops.aspx">
<error statusCode="404" redirect="~/ErrorPages/404.aspx" />
</customErrors>
この変更により、リモートでアクセスするユーザーが、存在しない ASP.NET リソースを要求するたびに、ユーザーは Oops.aspx
の代わりにカスタム エラー ページ 404.aspx
にリダイレクトされます。 図 7 に示すように、404.aspx
ページには、一般的なカスタム エラー ページよりも具体的なメッセージを含めることができます。
Note
効果的な 404 エラー ページの作成に関するガイダンスについては、「404 エラーページをもう一度」をご覧ください。
図 7: カスタム 404 エラー ページは、Oops.aspx
よりも対象を絞ったメッセージを表示します
(クリックするとフルサイズの画像が表示されます)
404.aspx
ページにアクセスがあるのは、見つからないページに対する要求をユーザーが行ったときのみであることがわかっているため、このカスタム エラー ページを拡張して、ユーザーがこの特定の種類のエラーに対処するのに役立つ機能を含めることができます。 たとえば、既知の不適切な URL を適切な URL にマップするデータベース テーブルを作成し、カスタム エラー ページ 404.aspx
でそのテーブルに対してクエリを実行することで、ユーザーがアクセスしようとしている可能性のあるページを提案することができます。
Note
カスタム エラー ページは、ASP.NET エンジンによって処理されるリソースに対して要求が行われた場合にのみ表示されます。 「IIS と ASP.NET 開発サーバーの間の主な違い」チュートリアルで説明したように、Web サーバーは特定の要求を自ら処理する場合があります。 既定では、IIS Web サーバーは、ASP.NET エンジンを呼び出さずに、画像や HTML ファイルなどの静的コンテンツへの要求を処理します。 したがって、ユーザーが存在しない画像ファイルを要求すると、ASP.NET の構成済みのエラー ページではなく、IIS の既定の 404 エラー メッセージが返されます。
まとめ
ASP.NET アプリケーションでハンドルされない例外が発生すると、ユーザーには 3 つのエラー ページ (例外の詳細のイエロー スクリーン、ランタイム エラーのイエロー スクリーン、またはカスタム エラー ページ) のいずれかが表示されます。 表示されるエラー ページは、アプリケーションの <customErrors>
構成と、ユーザーがローカルとリモートのどちらでアクセスしているかによって異なります。 既定の動作では、例外の詳細の YSOD がローカル訪問者に表示され、ランタイム エラーの YSOD がリモート訪問者に表示されます。
ランタイム エラーの YSOD では、サイトに訪問するユーザーには潜在的に機密性の高いエラー情報が表示されなくなりますが、サイトの外観とはかけ離れており、アプリケーションがバグだらけのように見えてしまいます。 より良い方法は、カスタム エラー ページを使用することです。このためには、カスタム エラー ページの作成と設計を行い、<customErrors>
セクションの defaultRedirect
属性で URL を指定します。 個々の HTTP エラー状態に対して複数のカスタム エラー ページを作成することもできます。
カスタム エラー ページは、運用環境にある Web サイトの包括的なエラー処理戦略の最初の手順です。 開発者にエラーを通知し、その詳細をログに記録することも重要な手順です。 次の 3 つのチュートリアルでは、エラー通知とログ記録の手法について説明します。
プログラミングに満足!
もっと読む
この記事で説明したトピックの詳細については、次のリソースを参照してください。