環境:.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.運行項目,查看效果。