tekitoumemo’s diary

思ったことを書くだけ。長文版Twitter

ASP.NET Core MVCのエラーハンドリング【起動時編】

前回の記事の続きです。やっとみんなの洋楽ランキングの.NET Core対応が終わりましたので今週末に完全移行します!.Net CoreなのでAzureからGCPに行こうしようと思いましたがやっぱりAzure良い!特に不満がなければAzureで行こうと思ってます。近いうちにLinuxサーバーに切り替える予定。
tekitoumemo.hatenablog.com

話がそれましたが、「ASP.NET Core MVCのエラーハンドリング【起動時編】」です。

前回はあくまでMVCのエラーハンドリングなので、Startup.csやProgram.csのエラーが取れません。なので、起動時のエラーを取る必要があります。ということで公式を見てみます。

アプリの起動中に起こる例外はホスティング層だけが処理できます。 Web ホストを使うと、captureStartupErrors と detailedErrors キーを利用して、起動中のエラーに対するホストの動作を構成できます。

はい。captureStartupErrors と detailedErrorsを使えばなんとかなるらしいので試してみます。

CaptureStartupErrorsを扱う

Program.csのBuildWebHostに設定します。

public static IWebHost BuildWebHostDevelopment (string[] args) =>
    WebHost.CreateDefaultBuilder (args)
    .UseStartup<Startup> ()
    .CaptureStartupErrors (true) // これ
    .Build ();

Startup.csにエラーを仕込んで実行します。そうすると見慣れたエラー画面が出てきます。
f:id:tekitoumemo:20180528215528p:plain
Startupのエラーをキャッチするもののようです。本番でこんなの使えませんのでDevelopmentモードで使いましょう。

detailedErrorsを扱う

同じくProgram.csのBuildWebHostに設定します。

public static IWebHost BuildWebHostDevelopment (string[] args) =>
    WebHost.CreateDefaultBuilder (args)
    .UseStartup<Startup> ()
    .UseSetting ("detailedErrors", "true") // これ
    .Build ();

同じくStartup.csにエラーを仕込んで実行します。そうするとWEBサーバーが立ち上がることなく終了します。よくわからないのでググるIISのオプションでスタックトレースを返すオプションだそうです。

Controls whether a stack trace is returned when there is a fault generated from the Web service.

<detailedErrors> Element
こちらは本番のエラーハンドリングに使えそうです。

Production用とDevelopment 用のWeb ホストを用意する

CaptureStartupErrorsはDevelopmentに、detailedErrorsはProductionにとそれぞれWEBホストを用意します。

// Production
public static IWebHost BuildWebHosProduction (string[] args) =>
    WebHost.CreateDefaultBuilder (args)
    .UseStartup<Startup> ()
    .UseSetting ("detailedErrors", "true")
    .Build ();
// Development
public static IWebHost BuildWebHostDevelopment (string[] args) =>
    WebHost.CreateDefaultBuilder (args)
    .UseStartup<Startup> ()
    .CaptureStartupErrors (true)
    .Build ();
    }

これで環境によってエラーハンドリングの準備が整いました。次に切り替えを作成します。

環境によってWEBホストを切り替える

上記の例からProductionはBuildWebHosProduction、DevelopmentはBuildWebHostDevelopmentが実行されるよう以下のように記述します。

public static void Main (string[] args) {
    if (Environment.GetEnvironmentVariable ("ASPNETCORE_ENVIRONMENT") == "Development") {
        BuildWebHostDevelopment (args).Run ();
    } else {
        BuildWebHosProduction (args).Run ();
    }
}

Environment.GetEnvironmentVariable ("ASPNETCORE_ENVIRONMENT") で実行モードを取得できるので文字列によって、実行するWEBホストを切り替えます。最後に本番用にエラーを取得する処理を記述します。

起動時のエラーを取得する

Startup.csにtry、catchを記載します。正直これが綺麗なのかいまだに謎ですが、StackOverflowにそのやり方が書いてあったので信じましょう。

public static void Main (string[] args) {
    try {

        if (Environment.GetEnvironmentVariable ("ASPNETCORE_ENVIRONMENT") == "Development") {
            BuildWebHostDevelopment (args).Run ();
        } else {
            BuildWebHosProduction (args).Run ();
        }
    } catch (Exception ex) {
        var mailManager = new MailManager {
            Subject = "[みんなの洋楽ランキング]エラー報告",
            Body = string.Format ("{0}\n{1}", ex.Message, ex.StackTrace)
        };
        mailManager.SendEmailAsync ().Wait ();
    }
}

Developmentモードの場合はCaptureStartupErrorsがエラーをキャッチするので、tryで捕捉されずにブラウザに出力されます。Productionの場合はtryで捕捉し、メールを飛ばす仕様になっています。以下の例ではserilogの捕捉方法ですが、やりたいことはだいたい一緒です。C#の7.1からMainにasyncが使えるようですが、終了するだけなのでWaitを使ってます(MVCでもasyncが使えるのか不明)
github.com

アプリケーション全体にtry catchステートメントを追加することを躊躇しています

と言ってますので、やっぱりなんとも言えないやり方かもしれませんがエラーが取れるのでよしとしましょう。

ざっとですが、これでMVCアプリケーションのエラーが取れました。起動時のエラーハンドリングはなかなか定まってなさそうですが、状況にあった取得方法を考えていければと思います。


検索順位が1ページ目まで上がってきた!検索ボリュームがなかなか多いキーワードなので嬉しい!!ここらへんも何をやってるか随時ブログにあげていきます。
f:id:tekitoumemo:20180528223323p:plain