首页NetCore 正文

.Net Core中5种友好的异常处理方案

时间: 2022年2月10日 浏览 126

一,继承Controller,重写OnActionExecuted
二,使用 ActionFilterAttribute
三,使用 IExceptionFilter
四,使用ExceptionHandler
五,自定义Middleare处理

异常处理在编程中非常重要,一来可以给用户友好提示,二来也是为了程序安全。在Asp.Net Core中,默认已经为我们了提供很多解决方案,下面就来总结一下。

一,继承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,这还是可以的。

二,使用 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);
    }
}

使用方式有两种,

在controller里打上 [TypeFilter(typeof(ExceptionActionFilter)] 标签。
在Startup里以filter方式全局注入。

services.AddControllersWithViews(options =>
{
    options.Filters.Add<ExceptionActionFilterAttribute>();
})

三,使用 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,这两个处理方法,只需重写一个即可,如果两个都重写,两个方法都会执行一次。

使用方式有两种,

在controller里打上 [CustomExceptionFilter] 标签。
在Startup里以filter方式全局注入。

services.AddControllersWithViews(options =>
{
    options.Filters.Add<CustomExceptionFilterAttribute>();
})

四,使用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}");
         });
     });
 }

五,自定义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)
        {
           //处理异常
        }
   }
}