異常處理在編程中非常重要,一來可以給用戶友好提示,二來也是為了程式安全。在 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);
}
}
使用方式有兩種,
- 在 controller 里打上 [typefilter(typeof(exceptionactionfilter)] 標籤。
- 在 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,這兩個處理方法,只需重寫一個即可,如果兩個都重寫,兩個方法都會執行一次。
使用方式有兩種,
- 在 controller 里打上 [customexceptionfilter] 標籤。
- 在 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)
{
//处理异常
}
}
}