Blazorを使うのであれば早めに例外対応をするのがオススメ

この内容は [Blazor Advent Calendar 2019](https://qiita.com/advent-calendar/2019/blazor)の9日目の記事です

#最初は例外なんて考えずバリバリ作る人もBlazorでは例外対応をしよう！
Blazorを使うなら早めの例外対応をお進めします。特に最初は例外とか深く考えず、**バリバリ作ってみるタイプの人**でもやった方が良いと思います。実際、僕も新しい技術をさわる際に言語仕様を読み込んだり、例外を意識したプログラムを書いたりは殆どしない方です。まずサンプルさわってみて、そのサンプルを自分の作っているものに当て込んで、そこからバリバリ作って、色々落ち着いてきてから初めて例外を意識しました。でも、ことBlazorに関してはそこまで例外をほったらかしたのは、結構な時間を無駄にしたと感じました。

#Blazorのサンプル通りに作っていく場合の典型的な進め方
Blazorでデータベースなどから値を取得する場合、サンプルを参考にするならWebAPI経由で値を取ってくる形になると思います。

```cs:
    protected override async Task OnInitAsync()
    {
        //GetJsonAsyncでサーバーから値を取得
        forecasts = await Http.GetJsonAsync<WeatherForecast[]>("api/SampleData/WeatherForecasts");
    }
```

で、html側は値が取れるまでLoadingを表示していて、取れたらその値を表示するプログラムになると思います。

```cs:
    @if (forecasts == null) //←値が取れない時はローディング表示
    {
        <p><em>Loading...</em></p>
    }
    else                    //←値が取れたらその値を表示
    {
        @foreach (var forecast in forecasts)
        {
            <tr>
                <td>@forecast.Date.ToShortDateString()</td>
            </tr>
        }
    }
```
実際僕もこの作りのままで[自作サービス](https://manga-yonda.com/)を作り続けていました。

#この状態で例外が起きると
さて開発中は色々例外が起きます。例えばURLの打ち間違えとか、インターネットにつなげない状態でアクセスするとか、単純な実装ミスとか。そうなるとどうなるか。`GetJsonAsync`で例外が出て`forecasts`が`null`のままになります。

```cs:
    forecasts = await Http.GetJsonAsync<WeatherForecast[]>("api/SampleData/WeatherForecasts");
```

つまりhtmlはずーっとローディング表示になります。

```cs:
    @if (forecasts == null) //←値が取れない時はローディング表示
    {
        <p><em>Loading...</em></p>
    }
```

例外が起きている時=ローディングから次に進まない時です。これではローディング画面がいつもより長いなと思うまで例外と気づきません。またどこで例外が起きたかも分かりません。開発者ツールを使えばサーバー側の例外は分かりますが、クライアント側の例外の場所は特定できません。Windowsの開発なら例外が出た時点でVisual Studioが勝手に実行時例外が出る箇所を捕まえてくれます。この環境でしか開発したことがなかったので、自分で例外に対して何かするのは思いもしなかったというのが正直なところです。でもBlazorは現在実行時例外が起きても特に何もしてくれません。

#Blazorでの例外対応
で、対応ですが、一番のオススメは（特に開発中であれば）、エラーページに飛ばしてそのページに例外のスタックトレースを書いてしまうことです。その際少しはまったので、コツを書きます。先ず`Http.GetJsonAsync`を例外で囲んで、例外が起きたら`NavigationManager.NavigateTo`でエラーページに飛ばします。例外のメッセージをパラメータに投げる際には`WebUtility.UrlEncode`しましょう。これでエラーページにパラメータで例外内容を渡します。

```cs:
        try
        {
            forecasts = await Http.GetJsonAsync<WeatherForecast[]>("api/SampleData/WeatherForecasts");
        }
        catch (Exception ex)
        {
            NavigationManager.NavigateTo(WebUtility.UrlEncode($"error?message=" + ex.Message + Environment.NewLine + ex.StackTrace));
            throw;
        }
```
そしてエラーページで、パラメータを表示する際に`WebUtility.UrlDecode`します。このままだとスタックトレースが改行されずに表示するので`\n`から`<br>`に置き換えます。

```cs:
    protected override void OnInitialized()
    {
        var url = NavigationManager.Uri;
        Message = WebUtility.UrlDecode(url.ToValue("message"));
        Message = Message.Replace("\n", "<br>");
    }
```

さらにhtmlに書く際に、`@((MarkupString)Message)`してマークアップ文字をそのまま表示してやります。

```html:
    <div>
        <h1>ERROR</h1>
        @((MarkupString)Message)
        <br>
        <br>
        <a href="/">Topに戻る</a>
    </div>
```

そうするとこんな感じで例外が表示されます。例外によっては戻るボタンでは永遠に戻れないことがある（例外が解消していなければまた例外が起きて、同じページに飛ぶ）のでTOPに戻るリンクも用意すると良いと思います。
<img width="925" alt="error.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100916/1799bce6-b99c-6a2e-33b0-fb030f846811.png">

これでＯＫです。さあ、あとはバリバリ作りましょう！

# この内容はゴミ箱行き？
asp.net core 3.1が出て、最新のBlazorのポストを見たら`Attach to process debugging from Visual Studio`という一言がありました。ここに書いた内容はもういらなくなるかもと思ったですが、少なくとも何も考えずに未設定でattach出来るわけではなさそうでした。分かったら追記するかも。