本文將介紹在ASP.Net Core中處理異常的幾種方法
1使用開發人員異常頁面(The developer exception page)
2配置HTTP錯誤代碼頁 Configuring status code pages
3使用MVC過濾器 ExceptionFilter
4 自定義異常捕獲中間件 Middleware
一使用開發人員異常頁面(The developer exception page)
配置你的程序使其在發生異常時詳細的展示異常信息
安裝Microsoft.AspNetCore.Diagnostics NuGet package
.net core 2.0中包含Microsoft.AspNetCore.All,這里面包含了Microsoft.AspNetCore.Diagnostics,無需自安裝。
在Startup.cs 上添加對exception page的使用Configure method in the Startup class:
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();//使用異常記錄頁面
}
在控制器中拋一個異常:
public class HomeController : Controller
{
public IActionResult Index()
{
throw new Exception();
return View();
}
}
在開發環境下運行,異常展示頁面包含幾個標簽展示異常信息和HTTP請求。第一個選項卡包含一個堆棧跟蹤
image.png
它下一個選項卡顯示查詢字符串參數,如果有的話。
image.png
第三個是Cookies和Headers信息
image.png
提示:在任何你想要捕捉異常的中間件(middleware)之前注冊UseDeveloperExceptionPage 比如 app.UseMvc.
一般在項目開發中,UseDeveloperExceptionPage展示異常詳情信息對開發過程很有幫助,但在項目發布出去以后,在系統發生異常時更明智的是向用戶展示一個靜態的錯誤頁面。這時就要做如下更改了:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//判斷是否是開發環境
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/error");
}
依據上面代碼,app.UseExceptionHandler("/error");在生產環境下,發生系統錯誤時,跳轉到錯誤頁面
二配置HTTP錯誤代碼頁 Configuring status code pages
如果訪問項目上一個不存在的頁面,會如下顯示:
image.png
修改 Startup.Configure,使其能夠使用HTTP錯誤代碼頁
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
//開發環境異常處理
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
//生產環境異常處理
app.UseExceptionHandler("/Home/Error");
}
app.UseStatusCodePages();//使用HTTP錯誤代碼頁
}
再次訪問不存在的頁面
image.png
app.UseStatusCodePages支持多種擴展方法。其中一個方法接受一個lambda表達式:
app.UseStatusCodePages(async context =>
{
context.HttpContext.Response.ContentType = "text/plain";
await context.HttpContext.Response.WriteAsync(
"Status code page, status code: " +
context.HttpContext.Response.StatusCode);
});
還可以跳轉到指定頁面,並附加Response.StatusCode
app.UseStatusCodePagesWithReExecute("/Home/Error/{0}");
占位符{0}在這代表Response.StatusCode 如:404
image.png
三使用MVC過濾器
//
/// 自定義全局異常過濾器
///
public class GlobalExceptionFilter : IExceptionFilter
{
readonly ILoggerFactory _loggerFactory;//采用內置日志記錄
readonly IHostingEnvironment _env;//環境變量
public GlobalExceptionFilter(ILoggerFactory loggerFactory, IHostingEnvironment env)
{
_loggerFactory = loggerFactory;
_env = env;
}
public void OnException(ExceptionContext context)
{
var controller = context.ActionDescriptor;
ILog log = LogManager.GetLogger(Startup.Repository.Name, controller.ToString());//初始化Log4net日志
#region 記錄到內置日志
//var logger = _loggerFactory.CreateLogger(context.Exception.TargetSite.ReflectedType);
//logger.LogError(new EventId(context.Exception.HResult),
//context.Exception,
//context.Exception.Message);
#endregion
if (_env.IsDevelopment())
{
log.Error(context.Exception.ToString());
//var JsonMessage = new ErrorResponse("未知錯誤,請重試");
//JsonMessage.DeveloperMessage = context.Exception;
//context.Result = new ApplicationErrorResult(JsonMessage);
//context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
//context.ExceptionHandled = true;
}
else
{
log.Error(context.Exception.ToString());
context.ExceptionHandled = true;
context.Result=new RedirectResult("/home/Error");
}
}
public class ApplicationErrorResult : ObjectResult
{
public ApplicationErrorResult(object value) : base(value)
{
StatusCode = (int)HttpStatusCode.InternalServerError;
}
}
public class ErrorResponse
{
public ErrorResponse(string msg)
{
Message = msg;
}
public string Message { get; set; }
public object DeveloperMessage { get; set; }
}
}
}
使用OnException方法,當異常發生時,調用這個方法。
在上面代碼中,根據項目環境的不同,分開處理異常。
當開發環境中,通過Log4net把日志記錄到本地文件中。再這里注釋了一句很重要代碼context.ExceptionHandled = true;//代表異常已經處理,context.ExceptionHandled 代表異常是否處理,不是true時,異常記錄到日志文件中后,系統對異常的處理並未結束,如果這時系統使用了開發人員異常頁面(The developer exception page),系統在頁面上詳細展示系統異常信息。若果context.ExceptionHandled為true,異常通過Log4net把日志記錄到本地文件后,系統對異常的處理就結束了。
開發環境下異常展示.gif
生產環境下,通過Log4net把日志記錄到本地文件中后,context.ExceptionHandled = true;系統處理異常結束,讓系統跳轉到靜態錯誤頁。
生產環境下異常展示.gif
四自定義異常捕獲中間件 Middleware
對於一些非MVC引起的異常,MVC過濾器是不能捕獲異常的。
什么是中間件?
中間件是一種組裝到系統應用程序的請求管道用來處理請求和相應的框架。它的各個構成:
-
在程序管道中是否選擇將請求傳遞給下一個組件。
2)可以在系統請求管道的下一個中間件的執行的前面或者后面處理請求和相應
一個委托請求被用來構建請求管道。這個委托請求用來處理每一個HTTP請求。
委托請求使用方法Run,Map 和使用Use方法的擴展方法來配置
自己可以指定創建一個匿名的委托請求in-iline,也可以將將其定義在一個可重用的類里面。
這些可重用的類和in-line匿名方法就稱為中間件,或者中間組件。
在請求管道中的每一個中間件的任務就是調用下一個中間件,特定場景下也可以越過下一個中間件直接返回結果。
下面是一個自定義ExceptionHandlingMiddleware中間件的過程,當捕獲到異常時,存到日志中。///
/// 自定義異常處理中間件
///
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;public ExceptionHandlingMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { try { await _next(context); } catch (Exception ex) { var statusCode = context.Response.StatusCode; await HandleExceptionAsync(context, ex.ToString()); } } private Task HandleExceptionAsync(HttpContext context, string msg) { HandleExceptionHelper hannd = new HandleExceptionHelper(); hannd.log.Error(msg);//記錄到日志文件 return context.Response.WriteAsync("ERROR"); }}
在定義一個異常的中間件,並拋一個異常,用ExceptionHandlingMiddleware捕獲異常
app.Use(async (context, next) =>
{
throw new Exception();
await next.Invoke();
});
