ASP.NET Core 5つの例外処理方法

ASP.NET Core 5つの例外処理方法

例外処理はプログラミングにおいて非常に重要です。ユーザーに親切なメッセージを提供し、プログラムの安全性を確保するためです。

最終更新 2021/04/29 9:27
.NET技术栈
読了目安 3 分
カテゴリ
ASP.NET Core
タグ
.NET C# ASP.NET Core セキュリティ 例外

例外処理はプログラミングにおいて非常に重要です。ユーザーに親切なメッセージを表示するとともに、プログラムの安全性を確保するためです。ASP.NET Coreでは、デフォルトで多くのソリューションが提供されていますので、以下にまとめます。

1. Controllerを継承し、OnActionExecutedをオーバーライド

Visual Studioでコントローラーを新規作成する際、デフォルトでControllerクラスを継承します。OnActionExecutedをオーバーライドして例外処理を追加します。通常はBaseControllerを作成し、すべてのコントローラーに継承させます。コードは以下の通りです。

public class BaseController : Controller
{
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        var exception = context.Exception;
        if (exception != null)
        {
            context.ExceptionHandled = true;
            context.Result = new ContentResult
            {
                Content = $"BaseControllerエラー : { exception.Message }"
            };
        }
        base.OnActionExecuted(context);
    }
}

この処理方法の利点はもちろんシンプルなことですが、欠点はcshtmlページでエラーが発生した場合、完全にキャッチできないことです。もちろん、プロジェクトがWeb APIプロジェクトでビューがない場合は問題ありません。

2. ActionFilterAttributeを使用する

ActionFilterAttributeは属性で、IActionFilterとIResultFilterを実装しています。そのため、アクション内のエラーでもビュー内のエラーでも、理論的にはキャッチ可能です。新しいExceptionActionFilterAttributeを作成し、OnActionExecutedとOnResultExecutedをオーバーライドして例外処理を追加します。完全なコードは以下の通りです。

public class ExceptionActionFilterAttribute:ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        var exception = context.Exception;
        if (exception != null)
        {
            context.ExceptionHandled = true;
            context.Result = new ContentResult
            {
                Content = $"エラー : { exception.Message }"
            };
        }
        base.OnActionExecuted(context);
    }

    public override void OnResultExecuted(ResultExecutedContext context)
    {
        var exception = context.Exception;
        if (exception != null)
        {
            context.ExceptionHandled = true;
            context.HttpContext.Response.WriteAsync($"エラー : {exception.Message}");
        }
        base.OnResultExecuted(context);
    }
}

使用方法は2通りあります。

  1. コントローラーに[TypeFilter(typeof(ExceptionActionFilter))]属性を付ける。
  2. Startupでフィルターとしてグローバルに注入する。
services.AddControllersWithViews(options =>
{
    options.Filters.Add<ExceptionActionFilterAttribute>();
})

3. IExceptionFilterを使用する

ASP.NET Coreには5種類のフィルターが用意されていますが、IExceptionFilterはその1つで、名前の通り例外処理に使用されます。ASP.NET CoreではExceptionFilterAttributeが既にIExceptionFilterを実装しているため、ExceptionFilterAttributeを継承し、その中のメソッドをオーバーライドするだけです。同様にCustomExceptionFilterAttributeを作成し、ExceptionFilterAttributeを継承してOnExceptionをオーバーライドし、例外処理を追加します。完全なコードは以下の通りです。

public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        context.ExceptionHandled = true;
        context.HttpContext.Response.WriteAsync($"CustomExceptionFilterAttributeエラー:{context.Exception.Message}");
        base.OnException(context);
    }
}

注意点として、ExceptionFilterAttributeは非同期メソッドOnExceptionAsyncも提供しています。これら2つの処理メソッドは、どちらか1つだけオーバーライドすればよく、両方ともオーバーライドすると両方のメソッドが1回ずつ実行されます。

使用方法は2通りあります。

  1. コントローラーに[CustomExceptionFilter]属性を付ける。
  2. Startupでフィルターとしてグローバルに注入する。
services.AddControllersWithViews(options =>
{
    options.Filters.Add<CustomExceptionFilterAttribute>();
})

4. ExceptionHandlerを使用する

Startupでは、Visual Studioで新規作成したプロジェクトにデフォルトで以下が追加されます。

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Home/Error");
}

上記のコードは、開発環境ではapp.UseDeveloperExceptionPage()を使用し、本番環境ではhome/errorページにリダイレクトすることを意味します。

UseDeveloperExceptionPageは、具体的なエラー情報(Stack trace、Query、Cookies、Headersの4つ)を表示します。.NET Core 3.1以降では、リクエストヘッダーにaccept:text/htmlがある場合はHTMLページを返し、それ以外の場合はエラーメッセージのみを返すように変更されました。

注意:app.UseDeveloperExceptionPage()は最初に配置する必要があります。

if (!env.IsDevelopment())
 {
     app.UseDeveloperExceptionPage();
 }
 else
 {
     app.UseExceptionHandler(builder =>
     {
         builder.Run(async context =>
         {
             var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
             await context.Response.WriteAsync($"error:{exceptionHandlerPathFeature.Error.Message}");
         });
     });
 }

5. カスタムMiddlewareで処理する

ミドルウェアを使用してグローバルに処理します。

public class ErrorHandlingMiddleware
{
   private readonly RequestDelegate next;

   public ErrorHandlingMiddleware(RequestDelegate next)
   {
        this.next = next;
   }

   public async Task Invoke(HttpContext context)
   {
        try
        {
           await next(context);
        }
        catch (System.Exception ex)
        {
           //例外処理
        }
   }
}
さらに探索

関連読書

その他の記事