Purpose
ASP.NET core Web APIで例外をハンドルする。その2.今回は、Action Filterで躓いたため、そこを中心に調べている。
内容について
ASP.NET Core Web API のエラーを処理するの方法を中心に、例外ハンドルの方法をまとめる。
チュートリアルメモ
Step5 例外を使用して応答を変更する
まず、以下の例外クラスを任意の場所に作成する。
public class HttpResponseException : Exception { public int Status { get; set; } = 500; public object Value { get; set; } }
次に、Action Filterを作成する。
public class HttpResponseExceptionFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { } public void OnActionExecuted(ActionExecutedContext context) { if (context.Exception is HttpResponseException exception) { context.Result = new ObjectResult(exception.Value) { StatusCode = exception.Status, }; context.ExceptionHandled = true; } } }
これらをStartupクラスに追加すると、Controllerを実行した後の処理に、エラーをハンドルする処理を追加することができる。Action Filterは、Action Methodが実行される前後(OnActionExecuting/OnActionExecuted)に処理をはさむことができる機能。この場合、Action Methodが実行されたあと、ActionExecutedContextの中に定義した例外(HttpResponseException)があれば、追加の例外処理を実行するという流れになる。
これらをStartupクラスに追加する
にあるように、このActionFilterをグローバルなFilterとして設定することで、すべてのActionMethodの実行語に同様の処理を実行することができる。
public void ConfigureServices(IServiceCollection services) { services.AddMvc() .AddNewtonsoftJson(); services.AddControllers(options => { options.Filters.Add(new CustomExceptionFilter()); options.Filters.Add(new HttpResponseExceptionFilter()); options.Filters.Add(new CustomResultServiceFilter()); }); }
Action "Filter"について
概要
ASP.NET coreは、Requestを受信してからResponseを返すまでに、以下の順番でRquestを処理している。
https://docs.microsoft.com/ja-jp/aspnet/core/fundamentals/middleware/index/static/middleware-pipeline.svg?view=aspnetcore-5.0
まず、フレームワークで用意されているもの(Startup.csのConfigureメソッドの中で、app.UseXXX()とかしているやつ)や、カスタムで作成したものを含めて、いろいろなミドルウェアの処理が実行される。そのあと、開発者が実装したControllerの処理が実行される。この時、Controllerの処理が実行される前後に、Filter Pipelineの処理が実行される。Filterには、5つのFilterが用意されていて、その一つが、Action Filterである。Filterの実行順序は、フレームワーク側で定義されている。
Note:
ミドルウェアの実行順序は、Configureメソッドの中で設定した順番で実行される。どの順番で実行するのがより良いかは、公式のドキュメントで説明されている。ものによっては、以下のような制約もある。CORS を使うコンポーネントの前。 現時点では、こちらのバグのため、UseResponseCaching の前に UseCors を追加する必要があります。
メモ:フィルター5種 - 承認フィルター:認証するやつ。 - リソースフィルター:モデルバインディングする前後に処理を挟むやつ。 - アクションフィルター:アクションメソッド前後に処理を挟むやつ。 - 例外フィルター:例外発生時、未処理例外の処理をするやつ - 結果フィルター:アクションメソッドが正常終了した場合に結果を出力する処理の前後に処理を挟むやつ。
アクション vs 例外 vs 結果フィルター
認証。リソース、については、Action Methodの実行前の処理なのでスルーして、アクション、例外、結果フィルターについて見てみた。
実行される順番は、アクション→例外 or 結果である。もっというと、実際試した結果、Action Methodでハンドルされない例外がない場合、アクション→結果となり、ハンドルされていない例外がある場合は、アクション→例外となる。
結果フィルターは、ハンドルされていない例外がない場合に実行されるため、共通例外処理の実装には不適切だと思う。そのため、共通的な例外処理を実装するなら、アクションフィルターもしくは、例外フィルターになる。
とはいえ、ここまで調べた中では、チュートリアル:【例外を使用して応答を変更する】のようにAction Filterを利用するのではなく、例外フィルターを利用して実装するのがより適切なのではないかと思った。