Exception が発生した際に、ログ出力、画面出力しています。
ユーザーにはメッセージのみ表示し、ソースコードはログを見ないと確認できないようにすることで、ソースコードの漏洩を防ぎ、尚且つ、バグなどで予期せず処理に失敗したことをユーザーへ通知しています。
ソースコードはGitHubで公開しています。
実装方式について
ASP.NET Core Blazor アプリのエラーを処理する | Microsoft Docs を参考に作った機能です。
ユーザが限られる業務系システム向けに作ったものなので例外メッセージを表示していますが、コンシューマ向けシステムで使う場合は例外メッセージの表示は避け、定型のエラーメッセージが表示されるように修正してから使った方が良いです。
全てのイベントハンドラに、この try-catch 処理を加えることで、「An unhandled exception has occurred. See browser dev tools for details. Reload」の表示を防げる。
「An unhandled exception has occurred. See browser dev tools for details. Reload」エラーが開発時に表示されるのは構わないが、本環境へリリース後、ユーザーにも表示されると、「Reload」先の画面でソースコードが漏洩し、セキュリティリスクが生まれてしまう。
ソースコード構成
・Blazor Server で使える、使い勝手の良い MessageBox に Blazor Server でログを出力する を加えたものをベースに、 Exception処理を追加しています。
ソースコード変更内容を解説
Shared/UnexpectedException.razor
・Exception を処理する共通部品。
・Exceptionをパラメータとして受け取り、 Exception の Message と StackTrace を、Visual Studio の「出力」ウィンドウとログファイルへ出力し、Exception の Message のみをユーザーに表示している。
・RenderFragment、CascadingValue を利用することで、共通部品を使用する側のソースコードを最小限にしている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@inject ILogger<UnexpectedException> Logger <CascadingValue Value=this> @ChildContent </CascadingValue> @code { [CascadingParameter] public IModalService Modal { get; set; } [Parameter] public RenderFragment ChildContent { get; set; } public void Show(Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message + "\r\n" + ex.StackTrace); Logger.LogError(ex.Message + "\r\n" + ex.StackTrace); _ = (new MessageBox(Modal)).Show("エラー", "処理に失敗しました。", ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error); } } |
App.razor
・UnexpectedException共通部品を全画面で使えるように、外側へ追加している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<CascadingBlazoredModal> <UnexpectedException> <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> </Found> <NotFound> <LayoutView Layout="@typeof(MainLayout)"> <p>Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router> </UnexpectedException> </CascadingBlazoredModal> |
Pages/Counter.razor
・イベントハンドラに try-catch を追加し、Exception を UnexpectedException共通部品で処理している。
・throw new NotImplementedException() はデモ用に加えた処理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
@page "/counter" @inject ILogger<Counter> _Logger <h1>Counter</h1> <p>Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { [CascadingParameter] private UnexpectedException _UnexpectedException { get; set; } private int currentCount = 0; private void IncrementCount() { try { currentCount++; _Logger.LogDebug($"Debug level log write.{currentCount}"); _Logger.LogInformation($"Information level log write.{currentCount}"); _Logger.LogWarning($"Warning level log write.{currentCount}"); _Logger.LogError($"Error level log write.{currentCount}"); throw new NotImplementedException(); } catch (Exception ex) { _UnexpectedException.Show(ex); } } } |
コメント