本文将介绍在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请求。第一个选项卡包含一个堆栈跟踪
它下一个选项卡显示查询字符串参数,如果有的话。
第三个是Cookies和Headers信息
提示:在任何你想要捕捉异常的中间件(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
如果访问项目上一个不存在的页面,会如下显示:
修改 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错误代码页
}
再次访问不存在的页面
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
三使用MVC过滤器
// <summary>
/// 自定义全局异常过滤器
/// </summary>
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把日志记录到本地文件后,系统对异常的处理就结束了。
-
生产环境下,通过Log4net把日志记录到本地文件中后,context.ExceptionHandled = true;系统处理异常结束,让系统跳转到静态错误页。
四自定义异常捕获中间件 Middleware
对于一些非MVC引起的异常,MVC过滤器是不能捕获异常的。
什么是中间件?
中间件是一种组装到系统应用程序的请求管道用来处理请求和相应的框架。它的各个构成:
1) 在程序管道中是否选择将请求传递给下一个组件。
2)可以在系统请求管道的下一个中间件的执行的前面或者后面处理请求和相应
一个委托请求被用来构建请求管道。这个委托请求用来处理每一个HTTP请求。
委托请求使用方法Run,Map 和使用Use方法的扩展方法来配置
自己可以指定创建一个匿名的委托请求in-iline,也可以将将其定义在一个可重用的类里面。
这些可重用的类和in-line匿名方法就称为中间件,或者中间组件。
在请求管道中的每一个中间件的任务就是调用下一个中间件,特定场景下也可以越过下一个中间件直接返回结果。
下面是一个自定义ExceptionHandlingMiddleware中间件的过程,当捕获到异常时,存到日志中。
/// <summary>
/// 自定义异常处理中间件
/// </summary>
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();
});