在业务系统,异常处理是全部开发人员必须面对的问题,在必定程度上,异常处理的能力反映出开发者对业务的驾驭水平;本章将着重介绍如何在 WebApi 程序中对异常进行捕获,而后利用 Nlog 组件进行记录;同时,还将介绍两种不一样的
异常捕获方式:管道捕获/服务过滤;经过本练习,将学习到如何捕获异常、处理异常跳转、记录异常信息。git
首先,建立一个 WebApi 项目,选择 Asp.Net Core Web 应用程序;github
[HttpGet] public ActionResult<IEnumerable<string>> Get() { throw new Exception("出错了....."); return new string[] { "value1", "value2" }; }
若是你把环境变量设置为 ASPNETCORE_ENVIRONMENT=Production ,你会发现,在异常发生的时候,你获得了一个空白页。web
在传统的 Asp.Net MVC 应用程序中,咱们通常都使用服务过滤的方式去捕获和处理异常,这种方式很是常见,并且可用性来讲,体验也不错,幸运的是 Asp.Net Core 也完整的支持该方式,接下来建立一个全局异常处理类 CustomerExceptionFilterjson
public class CustomerExceptionFilter : Attribute, IExceptionFilter { private readonly ILogger logger = null; private readonly IHostingEnvironment environment = null; public CustomerExceptionFilter(ILogger<CustomerExceptionFilter> logger, IHostingEnvironment environment) { this.logger = logger; this.environment = environment; } public void OnException(ExceptionContext context) { Exception exception = context.Exception; string error = string.Empty; void ReadException(Exception ex) { error += string.Format("{0} | {1} | {2}", ex.Message, ex.StackTrace, ex.InnerException); if (ex.InnerException != null) { ReadException(ex.InnerException); } } ReadException(context.Exception); logger.LogError(error); ContentResult result = new ContentResult { StatusCode = 500, ContentType = "text/json;charset=utf-8;" }; if (environment.IsDevelopment()) { var json = new { message = exception.Message, detail = error }; result.Content = JsonConvert.SerializeObject(json); } else { result.Content = "抱歉,出错了"; } context.Result = result; context.ExceptionHandled = true; } }
构造方法中,定义了两个参数,用于记录异常日志和获取程序运行环境变量api
private readonly ILogger logger = null; private readonly IHostingEnvironment environment = null; public CustomerExceptionFilter(ILogger<CustomerExceptionFilter> logger, IHostingEnvironment environment) { this.logger = logger; this.environment = environment; }
须要引用 Nuget 包 NLog.Extensions.Logging/NLog.Web.AspNetCore ,并在 Startup.cs 文件的 Configure 方法中添加扩展安全
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory factory) { // 将 NLog factory.AddConsole(Configuration.GetSection("Logging")) .AddNLog() .AddDebug(); var nlogFile = System.IO.Path.Combine(env.ContentRootPath, "nlog.config"); env.ConfigureNLog(nlogFile); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(); }
<?xml version="1.0" encoding="utf-8" ?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" internalLogLevel="info"> <!-- Load the ASP.NET Core plugin --> <extensions> <add assembly="NLog.Web.AspNetCore"/> </extensions> <!-- Layout: https://github.com/NLog/NLog/wiki/Layout%20Renderers --> <targets> <target xsi:type="File" name="errorfile" fileName="/data/logs/logfilter/error-${shortdate}.log" layout="${longdate}|${logger}|${uppercase:${level}}| ${message} ${exception}|${aspnet-Request-Url}" /> <target xsi:type="Null" name="blackhole" /> </targets> <rules> <logger name="Microsoft.*" minlevel="Error" writeTo="blackhole" final="true" /> <logger name="*" minlevel="Error" writeTo="errorfile" /> </rules> </nlog>
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // 将异常过滤器注入到容器中 services.AddScoped<CustomerExceptionFilter>(); services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
[ServiceFilter(typeof(CustomerExceptionFilter))] [Route("api/[controller]"), ApiController] public class ValuesController : ControllerBase { // GET api/values [HttpGet] public ActionResult<IEnumerable<string>> Get() { throw new Exception("出错了....."); return new string[] { "value1", "value2" }; } }
接下来利用 .NetCore 的管道模式,在中间件中对异常进行捕获,首先,建立一个中间件app
public class ExceptionMiddleware { private readonly RequestDelegate next; private readonly ILogger logger; private IHostingEnvironment environment; public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger, IHostingEnvironment environment) { this.next = next; this.logger = logger; this.environment = environment; } public async Task Invoke(HttpContext context) { try { await next.Invoke(context); var features = context.Features; } catch (Exception e) { await HandleException(context, e); } } private async Task HandleException(HttpContext context, Exception e) { context.Response.StatusCode = 500; context.Response.ContentType = "text/json;charset=utf-8;"; string error = ""; void ReadException(Exception ex) { error += string.Format("{0} | {1} | {2}", ex.Message, ex.StackTrace, ex.InnerException); if (ex.InnerException != null) { ReadException(ex.InnerException); } } ReadException(e); if (environment.IsDevelopment()) { var json = new { message = e.Message, detail = error }; error = JsonConvert.SerializeObject(json); } else error = "抱歉,出错了"; await context.Response.WriteAsync(error); } }
建立 HandleException(HttpContext context, Exception e) 处理异常,判断是 Development 环境下,输出详细的错误信息,非 Development 环境仅提示调用者“抱歉,出错了”,同时使用 NLog 组件将日志写入硬盘;一样,在 Startup.cs 中将 ExceptionMiddleware 加入管道中框架
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory factory) { // 将 NLog factory.AddConsole(Configuration.GetSection("Logging")) .AddNLog() .AddDebug(); var nlogFile = System.IO.Path.Combine(env.ContentRootPath, "nlog.config"); env.ConfigureNLog(nlogFile); // ExceptionMiddleware 加入管道 app.UseMiddleware<ExceptionMiddleware>(); //if (env.IsDevelopment()) //{ // app.UseDeveloperExceptionPage(); //} app.UseMvc(); }
在本例中,经过依赖注入和管道中间件的方式,演示了两种不一样的全局捕获异常处理的过程;值得注意到是,两种方式对于 NLog 的使用,都是同样的,没有任何差异,代码无需改动;实际项目中,也是应当区分不一样的业务场景,输出不一样的日志信息,无论是从安全或者是用户体验友好性上面来讲,都是很是值得推荐的方式,全局异常捕获处理,彻底和业务剥离。async
https://github.com/lianggx/EasyAspNetCoreDemo/tree/master/Ron.LogFilter学习