asp.net core 5種異常處理方案

asp.net core 5種異常處理方案

異常處理在編程中非常重要,一來可以給用戶友好提示,二來也是為了程式安全。

最后更新 2021/4/29 上午9:27
.NET技术栈
预计阅读 4 分钟
分类
ASP.NET Core
标签
.NET C# ASP.NET Core 安全 異常

異常處理在編程中非常重要,一來可以給用戶友好提示,二來也是為了程式安全。在 asp.net core 中,默認已經為我們了提供很多解決方案,下面就來總結一下。

1. 繼承 controller,重寫 onactionexecuted

使用 vs 新建 controller 時,默認都會繼承一個 controller 類,重寫 onactionexecuted,添加上異常處理即可。一般情況下我們會新建一個 basecontroller, 讓所有 controller 繼承 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 項目,沒有 view,這還是可以的。

2. 使用 actionfilterattribute

actionfilterattribute 是一個特性,本身實現了 iactionfilter 及 iresultfilter , 所以不管是 action 里拋錯,還是 view 里拋錯,理論上都可以捕獲。我們新建一個 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);
    }
}

使用方式有兩種,

  1. 在 controller 里打上 [typefilter(typeof(exceptionactionfilter)] 標籤。
  2. 在 startup 里以 filter 方式全局注入。
services.AddControllersWithViews(options =>
{
    options.Filters.Add<ExceptionActionFilterAttribute>();
})

3. 使用 iexceptionfilter

我們知道, asp.net core 提供了 5 類 filter, iexceptionfilter 是其中之一,顧名思義,這就是用來處理異常的。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,這兩個處理方法,只需重寫一個即可,如果兩個都重寫,兩個方法都會執行一次。

使用方式有兩種,

  1. 在 controller 里打上 [customexceptionfilter] 標籤。
  2. 在 startup 里以 filter 方式全局注入。
services.AddControllersWithViews(options =>
{
    options.Filters.Add<CustomExceptionFilterAttribute>();
})

4. 使用 exceptionhandler

在 startup 里,vs 新建的項目會默認加上

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

上邊這段代碼,大意為:開發環境使用 app.usedeveloperexceptionpage(); 。生產環境會跳轉至 home/error 頁面。

usedeveloperexceptionpage 會給出具體錯誤信息,具體包括 stack trace、query、cookies、headers 四部分。. 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. 自定義 middleare 處理

通過 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)
        {
           //处理异常
        }
   }
}
Keep Exploring

延伸阅读

更多文章