在ASP.NET Core中Startup类的Configure方法中,有一个扩展方法叫UseDeveloperExceptionPage,如下所示:
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage(); }
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
UseDeveloperExceptionPage方法是所属DeveloperExceptionPageExtensions类的IApplicationBuilder扩展方法,这个方法是新建ASP.NET Core项目时默认加入Startup类中的,它的作用是ASP.NET Core在开发环境(Development环境)下用于展示异常信息页面,如下所示:
![](https://www.codeprj.com/image/aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvNjYwNTkvMjAxOTAxLzY2MDU5LTIwMTkwMTE1MTY0MzE2NjQzLTE2NDgyNDI5NjIucG5n.png)
但是UseDeveloperExceptionPage方法有一个很坑的特性,那就是它会吃掉ASP.NET Core中Middleware管道中的异常。
我们来设想,假如我们定义了下面一个Middleware叫LoggerMiddleware,它使用try catch代码块,来记录所有发生在ASP.NET Core的Middleware管道中抛出的异常到日志:
public class LoggerMiddleware
{
private readonly RequestDelegate next;
public LoggerMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(
Microsoft.AspNetCore.Http.HttpContext context)
{
Logger logger = LogManager.GetCurrentClassLogger();
//logger.Log(NLog.LogLevel.Info, "Log tracking start!");
try
{
await next.Invoke(context);
}
catch (Exception ex)
{
LogMessageGenerator logMessageGenerator = new LogMessageGenerator(() =>
{
return ex.GetType().FullName + "\r\n" + ex.StackTrace;
});
logger.Log(NLog.LogLevel.Error, ex, logMessageGenerator);
throw;
}
//logger.Log(NLog.LogLevel.Info, "Log tracking end!");
}
}
还有其扩展类LoggerMiddlewareExtension:
public static class LoggerMiddlewareExtension
{
public static void UsePipelineLogger(this IApplicationBuilder app)
{
app.UseMiddleware<LoggerMiddleware>();
}
}
然后我在ASP.NET Core中Startup类的Configure方法中,将其(app.UsePipelineLogger)放在app.UseDeveloperExceptionPage方法前面:
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UsePipelineLogger();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
然后你会发现当MVC Controller中抛出异常时,LoggerMiddleware中的try catch代码块捕获不到任何异常。最开始我相当纳闷,这异常怎么活生生地就被吃掉了呢?
后来我在ASP.NET Core中Startup类的Configure方法中,将app.UsePipelineLogger放在了app.UseDeveloperExceptionPage后面:
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UsePipelineLogger();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
这下LoggerMiddleware中的try catch代码块就成功捕获到了MVC Controller中抛出的异常,这很明确地证明了是app.UseDeveloperExceptionPage方法的Middleware吃掉了ASP.NET Core管道中的异常。
虽然不知道app.UseExceptionHandler方法是不是也会吃掉异常,但是建议大家把捕获异常的Middleware(本例的app.UsePipelineLogger)方法,都放在app.UseDeveloperExceptionPage和app.UseExceptionHandler的后面!
|