环境:.NetCore3.1、VS2019、Web API模板、SqlSugar、MSSqlServer
思路:我们要把每次HTTP请求响应的信息都保存到数据库,因为每次HTTP都会走一遍中间件,我们可以把写入数据库的操作写在自定义中间件上。但是它做的事情比较多,后续也可能变更需求,所有我决定把这个事情写在自定义服务上,让中间件使用该服务进行写入数据库操作。
1.新建服务对应的接口。
public interface IApiLogService { void DataSave(HttpContext context, long responseTime); }
2.新建服务。
2.1.需要安装Nuget包sqlSugarCore,当然也可以使用其他ORM,本例只支持单例模式连接数据库的ORM。
2.2.添加数据库连接。需要在此数据库连接字符串上的服务器地址新建数据库,本人使用本地的SqlServer。
1 services.AddSingleton(new SqlSugarClient(new ConnectionConfig 2 { 3 ConnectionString = "server=.;uid=sa;pwd=sasa;database=apilogdemo;MultipleActiveResultSets=true",//必填, 数据库连接字符串 4 DbType = DbType.SqlServer, //必填, 数据库类型 5 IsAutoCloseConnection = true, //默认false, 时候知道关闭数据库连接, 设置为true无需使用using或者Close操作 6 InitKeyType = InitKeyType.SystemTable //默认SystemTable, 字段信息读取, 如:该属性是不是主键,是不是标识列等等信息 7 }));
1 public class ApiLogService : IApiLogService 2 { 3 private readonly IConfiguration _configuration; 4 private readonly SqlSugarClient _dbContext; 5 6 public ApiLogService(IConfiguration configuration, SqlSugarClient dbContext) 7 { 8 _configuration = configuration; 9 _dbContext = dbContext; 10 } 11 12 public void DataSave(HttpContext context, long responseTime) 13 { 14 var isLog = _configuration.GetValue<bool>("ApiLog:IsEnable"); 15 if (isLog) 16 { 17 var requestMethod = context.Request.Method; 18 var requestURL = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}"; 19 //var accessToken = context.GetTokenAsync("access_token").Result;//添加身份验证的项目可以使用此方法获取到access_toekn 20 var accessToken = string.Empty; 21 var requestBody = string.Empty; 22 if (requestMethod == "POST") 23 { 24 context.Request.Body.Seek(0, SeekOrigin.Begin); 25 var _reader = new StreamReader(context.Request.Body); 26 requestBody = _reader.ReadToEnd(); 27 } 28 _dbContext.Insertable(new ApiLog 29 { 30 AccessToken = accessToken, 31 AccessTime = DateTime.Now, 32 AccessAction = requestMethod, 33 AccessApiUrl = requestURL, 34 QueryString = context.Request.QueryString.ToString(), 35 Body = requestBody, 36 HttpStatus = context.Response.StatusCode, 37 ClientIP = context.Connection.RemoteIpAddress.ToString(), 38 ResponseTime = responseTime 39 }).ExecuteCommand(); 40 } 41 } 42 }
2.3.需要在appsettion.json/appsetting.Development.json文件添加配置信息,用于控制是否启用响应日志,当然也可以不控制。
"ApiLog": { "IsEnable": true }
2.4.添加响应日志实体,需要在数据库上添加该表。
1 /// <summary> 2 /// 接口请求日志 3 /// </summary> 4 public class ApiLog 5 { 6 /// <summary> 7 /// Desc: 8 /// Default: 9 /// Nullable:False 10 /// </summary> 11 public int ALgID { get; set; } 12 13 /// <summary> 14 /// Desc: 15 /// Default: 16 /// Nullable:True 17 /// </summary> 18 public string ClientIP { get; set; } 19 20 /// <summary> 21 /// Desc: 22 /// Default: 23 /// Nullable:False 24 /// </summary> 25 public long ResponseTime { get; set; } 26 27 /// <summary> 28 /// Desc: 29 /// Default: 30 /// Nullable:True 31 /// </summary> 32 public string AccessToken { get; set; } 33 34 /// <summary> 35 /// Desc: 36 /// Default: 37 /// Nullable:False 38 /// </summary> 39 public DateTime AccessTime { get; set; } 40 41 /// <summary> 42 /// Desc: 43 /// Default: 44 /// Nullable:True 45 /// </summary> 46 public string AccessApiUrl { get; set; } 47 48 /// <summary> 49 /// Desc: 50 /// Default: 51 /// Nullable:True 52 /// </summary> 53 public string AccessAction { get; set; } 54 55 /// <summary> 56 /// Desc: 57 /// Default: 58 /// Nullable:True 59 /// </summary> 60 public string QueryString { get; set; } 61 62 /// <summary> 63 /// Desc: 64 /// Default: 65 /// Nullable:True 66 /// </summary> 67 public string Body { get; set; } 68 69 /// <summary> 70 /// Desc: 71 /// Default: 72 /// Nullable:False 73 /// </summary> 74 public int HttpStatus { get; set; } 75 }
3.此时,我们的服务已经定义好了。接下来是自定义一个中间件。
3.1.新建中间件,注入并使用刚才定义好的服务。
1 public class LogMiddleware 2 { 3 private readonly RequestDelegate _next; 4 private readonly IApiLogService _apiLogService; 5 private readonly IConfiguration _configration; 6 7 public LogMiddleware(RequestDelegate next, IApiLogService apiLogService, IConfiguration configration) 8 { 9 _next = next; 10 _apiLogService = apiLogService; 11 _configration = configration; 12 } 13 14 public Task Invoke(HttpContext context) 15 { 16 var watch = new Stopwatch(); 17 watch.Start(); 18 context.Response.OnStarting(() => 19 { 20 var isLog = _configration.GetValue<bool>("ApiLog:IsEnable"); 21 if (isLog) 22 { 23 watch.Stop(); 24 _apiLogService.DataSave(context, watch.ElapsedMilliseconds); 25 } 26 return Task.CompletedTask; 27 }); 28 29 return this._next(context); 30 } 31 }
3.2.新建暴露中间件扩展类。
1 public static class LogMiddlewareExtension 2 { 3 public static IApplicationBuilder UseLogMiddleware(this IApplicationBuilder builder) 4 { 5 return builder.UseMiddleware<LogMiddleware>(); 6 } 7 }
4.我们的工作已经做好了,接下来使用自定义的中间件就可以了。
4.1.添加自定义服务
services.AddSingleton<IApiLogService, ApiLogService>();
4.2.使用自定义中间件。
app.UseLogMiddleware();
5.运行项目,查看效果。