.net5 core Razor 进阶之三:异常和日志处理
异常和日志是软件开发中不可或缺的一部分,总是成对出现,
程序运行过程中出现了异常,就需要用日志将此异常信息记录下来,为后续 debug 做参考。
在.NET5 Core Razor 中,日志和异常的处理比较简单,我们只需要做一些配置和少许编码就可以了,具体步骤如下。
注:
.NET5 Core自带的日志处理接口可以将日志信息输出到控制台、事件管理器等终端上,
但目前还不支持输出到文件,这里我们使用第三方包 Serilog.Extensions.Logging.File 来实现。
----------------------------------------------------------------------
第1步:引用 Serilog.Extensions.Logging.File 包并配置使用环境 。
具体参考这篇文章:https://www.cnblogs.com/pfm33/p/14336356.html
第2步:扑捉异常信息并调用相应的 Logger 方法写日志文件。
代码一般是这样的(还是以之前的学生成绩管理为例):
public class ScoreMngModel : PageModel { private ILogger<ScoreMngModel> _logger; //定义日志对象 private readonly ScoreContext _context; //在构造函数中做依赖注入 public ScoreMngModel(ILogger<ScoreMngModel> logger, ScoreContext context) { _logger = logger; //注入日志实例 _context = context; //注入 AuthDbContext 实例 } public void OnGet() { try { _context.Database.ExecuteSqlRaw("UPDATE t_student SET class_name={0} WHERE stu_id={1}", "五(1)班", 1); //使用日志对象 _logger 的 LogInformation() 方法将信息写入日志文件 _logger.LogInformation("UPDATE t_student SET class_name={0} WHERE stu_id={1}", "五(1)班", 1); } catch (Exception ex) { _logger.LogError(ex, ex.Message); //使用日志对象 _logger 的 LogError() 方法将异常信息写入日志文件 } } // 其他代码...... }
运行后会在解决方案的根目录下生成对应的日志文件,如下:
除了 try-catch 这种方式外,还可以以 【Filter】 或 【中间件】 的方式来处理异常和记录日志 ,其区别是:
1. try-catch 方式需要在每个方法中使用 try {} catch {} 语句将代码包起来,工作量很大,不便于集中维护。
2. Filter 方式只需要实现相关的接口并做配置就可以了,非常适合捕获发生在操作中的异常,但 xxx.cshtml 中的异常无法捕获 。
3. 中间件 方式最灵活,其代码更简洁,后期维护也更方便,这是推荐的方式。
下面我们分别实现第2种和第3种方式。
一、Filter 方式的步骤如下:
第1步:在解决方案根目录中新建一个 Filters 的文件夹用来存放相关的类文件。
第2步:在 Filters 的文件夹下新增一个自定义 Filter 类 CustomExceptionFilter
,实现 IExceptionFilter
接口,如下:
代码如下:
namespace AuthManagement.Filters { public class CustomExceptionFilter : IExceptionFilter { private ILogger<CustomExceptionFilter> _logger; //定义日志处理对象 public CustomExceptionFilter(ILogger<CustomExceptionFilter> logger) { _logger = logger; //在构造函数中注入日志处理实例 } // 实现 IExceptionFilter 接口的 Onexception() 方法 public void OnException(ExceptionContext context) { //使用日志对象 _logger 的 LogError() 方法将异常信息写入日志文件 _logger.LogError(context.Exception, context.Exception.Message); } } }
第3步:在 Startup.cs 的 ConfigureServices() 方法中配置 Filter,代码如下:
public void ConfigureServices(IServiceCollection services) { services.AddScoped<CustomExceptionFilter>(); //将自定义的 ExceptionFilter 注入到容器 //...... }
删除 Logs 文件夹下之前产生的日志文件,在 .cs 文件中抛出一个 Exception , 如下:
public void OnGet() { throw new Exception("test exception ...."); }
编译并运行程序,可以看到 Logs 文件夹下新增了一个日志文件:
二、中间件 方式的步骤如下:
第1步:在解决方案下新增一个文件夹 Middlewares
第2步:在该文件夹下新增一个 ExceptionMiddleware.cs 的中间件
代码如下:
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace AuthManagement.Middlewares { // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project public class ExceptionMiddleware { private ILogger<ExceptionMiddleware> _logger; //定义日志处理对象 private readonly RequestDelegate _next; public ExceptionMiddleware(ILogger<ExceptionMiddleware> logger,RequestDelegate next) { _logger = logger; //在构造函数中注入日志处理对象的实例 _next = next; } public async Task Invoke(HttpContext httpContext) { try { await _next(httpContext); } catch (Exception ex) // 用 try-catch 来捕获异常。 { _logger.LogError(ex, "##异常来源:{0}", httpContext.Request.Path); //调用日志处理对象的实例记录日志 } } } // Extension method used to add the middleware to the HTTP request pipeline. public static class ExceptionMiddlewareExtensions { public static IApplicationBuilder UseExceptionMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware<ExceptionMiddleware>(); } } }
ExceptionMiddlewareExtensions 是新增中间件时自动生成的扩展方法类,用于异常中间件的启用,所以不需要在 Startup.cs 中做设置。
测试:
在 xxx.cshtml 文件中抛出异常:
@page @model AuthManagement.Pages.Auth.ScoreMngModel @using AuthManagement.DbUtil.Entity2; @{ throw new Exception("test ()()()$$$$$$$$$$$$$"); }
或者在 xxx.cshtml.cs 文件中抛出异常:
public void OnGet() { throw new Exception("test()()()77777......"); }
编译后运行程序,都可以在 /Logs 文件夹下看到生成的日志文件,如下:
这里虽然将异常记录到了日志文件中,实际项目中还需要做一下完善,
比如根据不同的异常类型跳转到一个自定义的错误页面。