我写了一个过滤器(实现日志功能),实现的是IActionFilter 过滤器,在过滤器中,获取Post请求Body的Context使用下面方式获取强调(
- 1 using Microsoft.AspNetCore.Mvc.Filters;
- 2 using OpticalTrap.Framework.Loggings;
- 3 using OpticalTrap.Web.ConstProvider;
- 4 using OpticalTrap.Web.Contracts;
- 5 using OpticalTrap.Web.Facade.Controllers;
- 6 using OpticalTrap.Web.Models;
- 7 using System.Reflection;
- 8 using System.Security.Claims;
- 9 using System.Text;
- 10
- 11 namespace OpticalTrap.Web.Facade.Extensions.Filters
- 12 {
- 13 /// <summary>
- 14 /// 该类型定义了全局处理操作日志的过滤器,该类型是密封类型。
- 15 /// </summary>
- 16 public sealed class GlobalOperationLogFilterAttribute : Attribute, IActionFilter, IAsyncActionFilter
- 17 {
- 18 #region 实例字段
- 19
- 20 private readonly IOperationLogService _operationLogService;
- 21 private readonly IServiceProvider _serviceProvider;
- 22
- 23 #endregion
- 24
- 25 #region 构造函数
- 26
- 27 /// <summary>
- 28 /// 初始化该类型的新实例。
- 29 /// </summary>
- 30 /// <param name="operationLogService">需要注入的操作日志服务实例。</param>
- 31 /// <param name="serviceProvider">需要注入的服务提供器。</param>
- 32 public GlobalOperationLogFilterAttribute(IOperationLogService operationLogService, IServiceProvider serviceProvider)
- 33 {
- 34 _operationLogService = operationLogService;
- 35 _serviceProvider = serviceProvider;
- 36 }
- 37
- 38 #endregion
- 39
- 40 #region 操作日志的同步方法
- 41
- 42 /// <summary>
- 43 /// 在标注方法执行之前执行该方法。
- 44 /// </summary>
- 45 /// <param name="context">方法执行前的上下文。</param>
- 46 public async void OnActionExecuting(ActionExecutingContext context)
- 47 {
- 48 if (context.Controller.GetType() != typeof(ErrorHandlerController))
- 49 {
- 50 if (context.ActionDescriptor.EndpointMetadata.Any(c => c.GetType() == typeof(RequiredLogAttribute)))
- 51 {
- 52 #region 核心处理
- 53
- 54 var controllerType = context.Controller.GetType();
- 55 var controllerName = controllerType.Name;
- 56 var currentMethodName = context.ActionDescriptor.RouteValues["action"]!;
- 57
- 58 string? loginName = string.Empty;
- 59 var claimKeysProvider = _serviceProvider.GetService<ClaimKeysConstProvider>();
- 60 if (claimKeysProvider != null)
- 61 {
- 62 loginName = context.HttpContext.User.FindFirstValue(claimKeysProvider.ClaimStoreUserNameKey);
- 63 }
- 64
- 65 var currentDateTime = DateTime.Now;
- 66 var methodType = context.HttpContext.Request.Method;
- 67 string parameterResult = string.Empty;
- 68 //获取 Get 方法传递的参数,包括查询参数(queryString的值)和Id({controller=home}/{action=index}/{id?})值,路由配置中的Id。
- 69 if (string.Compare(methodType, "get", true) == 0)
- 70 {
- 71 if (!string.IsNullOrEmpty(context.HttpContext.Request.QueryString.Value) && !string.IsNullOrWhiteSpace(context.HttpContext.Request.QueryString.Value))
- 72 {
- 73 parameterResult = context.HttpContext.Request.QueryString.Value;
- 74 }
- 75
- 76 if (context.ActionArguments.ContainsKey("id"))
- 77 {
- 78 string? id = context.ActionDescriptor.RouteValues["id"];
- 79 if (!string.IsNullOrEmpty(id) && !string.IsNullOrWhiteSpace(id))
- 80 {
- 81 if (string.IsNullOrEmpty(parameterResult) || string.IsNullOrWhiteSpace(parameterResult))
- 82 {
- 83 parameterResult = $"id={id}";
- 84 }
- 85 else
- 86 {
- 87 parameterResult += $"&id={id}";
- 88 }
- 89 }
- 90 }
- 91 }
- 92 else
- 93 {
- 94 //获取 Post 方法传递的参数,读取 request.body 里的的参数, 必须在在 Program.cs 里也启用倒带功能
- 95 context.HttpContext.Request.EnableBuffering();
- 96 context.HttpContext.Request.Body.Position = 0;
- 97 using (var memoryStream = new MemoryStream())
- 98 {
- 99 context.HttpContext.Request.Body.CopyTo(memoryStream);
- 100 var streamBytes = memoryStream.ToArray();
- 101 parameterResult = Encoding.UTF8.GetString(streamBytes);
- 102 }
- 103 //using (var reader = new StreamReader(context.HttpContext.Request.Body, Encoding.UTF8))
- 104 //{
- 105 // var bodyRead = reader.ReadToEndAsync();
- 106 // bodyStr = bodyRead.Result; //把body赋值给bodyStr
- 107 // needKey = JsonConvert.DeserializeAnonymousType
- 108 // (bodyRead.Result, new Dictionary<string, object>())[dependencySource].ToString();
- 109 //}
- 110 if (controllerType != typeof(ValidationController))
- 111 {
- 112 parameterResult = ProcessFormParameters(parameterResult);
- 113 }
- 114 else
- 115 {
- 116 parameterResult = ProcessLoginUserNameParameters(parameterResult, out loginName);
- 117 }
- 118 }
- 119
- 120 parameterResult = !string.IsNullOrEmpty(parameterResult) ? parameterResult : "没有传递任何参数";
- 121 loginName = !string.IsNullOrEmpty(loginName) ? loginName : "anonymous";
- 122 Guid userid = Guid.Empty;
- 123 if (string.IsNullOrEmpty(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid)) || string.IsNullOrWhiteSpace(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid)))
- 124 {
- 125 userid = Guid.Parse("a05897f9-0c86-4f5a-a581-e5da936d0e4c");
- 126 }
- 127 else
- 128 {
- 129 userid = Guid.Parse(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid));
- 130 }
- 131
- 132 OperationLog log = new OperationLog()
- 133 {
- 134 Id = Guid.NewGuid(),
- 135 Name = $"{loginName} 执行 {controllerName}.{currentMethodName} 操作。",
- 136 LoginName = loginName,
- 137 Parameters = parameterResult,
- 138 ActionName = $"{controllerType.FullName}.{currentMethodName}",
- 139 ActionType = methodType,
- 140 Message = $"【{loginName}】用户在【{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}】执行了控制器【{controllerType.FullName}】的【{currentMethodName}】方法,执行方法的类型:{methodType},执行方法所需的参数:【{parameterResult}】,操作顺利完成。",
- 141 Remarks = "全局日志记录器记录的日志。",
- 142 CreateUserId = userid,
- 143 CreateDate = currentDateTime
- 144 };
- 145
- 146 try
- 147 {
- 148 MethodInfo? methodInfo;
- 149 //如果 Controller 类型配置了 RequiredLogAttribute 特性,说明这个 Controller 里面的所有方法都需要记录日志。
- 150 //如果 Controler 配置了 RequiredLogAttribute 特性,但是方法配置 NoLogAttribute 特性,则不记录日志,否则记录日志。
- 151 if (controllerType.IsDefined(typeof(RequiredLogAttribute), false))
- 152 {
- 153 methodInfo = controllerType.GetMethod(currentMethodName);
- 154 if (methodInfo != null && !methodInfo.IsDefined(typeof(NoLogAttribute), false))
- 155 {
- 156 await _operationLogService.InsertAsync(log);
- 157 }
- 158 }
- 159 else
- 160 {
- 161 //针对方法配置了 RequiredLogAttribute 特性执行记录日志的功能
- 162 methodInfo = controllerType.GetMethod(currentMethodName);
- 163 if (methodInfo != null && methodInfo.IsDefined(typeof(RequiredLogAttribute), false))
- 164 {
- 165 await _operationLogService.InsertAsync(log);
- 166 }
- 167 }
- 168 }
- 169 catch (Exception ex)
- 170 {
- 171 log.Name = $"异常操作日志:{loginName}在{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}执行了{currentMethodName}操作。";
- 172 log.Message = $"【{loginName}】用户在【{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}】执行了控制器【{controllerType.FullName}】的【{currentMethodName}】方法,执行方法的类型:{methodType},执行方法所需的参数:【{parameterResult}】,操作没有完成,系统发生了异常。<br/>异常详情:{ex.Message},<br/>异常堆栈:{ex.StackTrace}。";
- 173
- 174 await _operationLogService.InsertAsync(log);
- 175 }
- 176
- 177 #endregion
- 178 }
- 179 }
- 180 }
- 181
- 182 /// <summary>
- 183 /// 在标注方法执行之后执行该方法。
- 184 /// </summary>
- 185 /// <param name="context">方法执行后的上下文。</param>
- 186 public void OnActionExecuted(ActionExecutedContext context) { }
- 187
- 188 #endregion
- 189
- 190 #region 操作日志的异步方法
- 191
- 192 /// <summary>
- 193 /// 全局日志记录器异步实现的操作日志的记录。
- 194 /// </summary>
- 195 /// <param name="context">方法执行前的上下文。</param>
- 196 /// <param name="next">方法执行的下一个环节代理。</param>
- 197 /// <returns></returns>
- 198 public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
- 199 {
- 200 if (context.Controller.GetType() != typeof(ErrorHandlerController))
- 201 {
- 202 if (context.ActionDescriptor.EndpointMetadata.Any(c => c.GetType() == typeof(RequiredLogAttribute)))
- 203 {
- 204 #region 核心处理
- 205
- 206 var controllerType = context.Controller.GetType();
- 207 var controllerName = controllerType.Name;
- 208 var currentMethodName = context.ActionDescriptor.RouteValues["action"]!;
- 209
- 210 string? loginName = string.Empty;
- 211 var claimKeysProvider = _serviceProvider.GetService<ClaimKeysConstProvider>();
- 212 if (claimKeysProvider != null)
- 213 {
- 214 loginName = context.HttpContext.User.FindFirstValue(claimKeysProvider.ClaimStoreUserNameKey);
- 215 }
- 216
- 217 var currentDateTime = DateTime.Now;
- 218 var methodType = context.HttpContext.Request.Method;
- 219 string parameterResult = string.Empty;
- 220 //获取 Get 方法传递的参数,包括查询参数(queryString的值)和Id({controller=home}/{action=index}/{id?})值,路由配置中的Id。
- 221 if (string.Compare(methodType, "get", true) == 0)
- 222 {
- 223 if (!string.IsNullOrEmpty(context.HttpContext.Request.QueryString.Value) && !string.IsNullOrWhiteSpace(context.HttpContext.Request.QueryString.Value))
- 224 {
- 225 parameterResult = context.HttpContext.Request.QueryString.Value;
- 226 }
- 227
- 228 if (context.ActionArguments.ContainsKey("id"))
- 229 {
- 230 string? id = context.ActionArguments["id"]!.ToString();
- 231 if (!string.IsNullOrEmpty(id) && !string.IsNullOrWhiteSpace(id))
- 232 {
- 233 if (string.IsNullOrEmpty(parameterResult) || string.IsNullOrWhiteSpace(parameterResult))
- 234 {
- 235 parameterResult = $"id={id}";
- 236 }
- 237 else
- 238 {
- 239 parameterResult += $"&id={id}";
- 240 }
- 241 }
- 242 }
- 243 }
- 244 else
- 245 {
- 246 //获取 Post 方法传递的参数,读取 request.body 里的的参数, 必须在在 Program.cs 里也启用倒带功能
- 247 context.HttpContext.Request.EnableBuffering();
- 248 context.HttpContext.Request.Body.Position = 0;
- 249 using (var memoryStream = new MemoryStream())
- 250 {
- 251 context.HttpContext.Request.Body.CopyTo(memoryStream);
- 252 var streamBytes = memoryStream.ToArray();
- 253 parameterResult = Encoding.UTF8.GetString(streamBytes);
- 254 }
- 255 //using (var reader = new StreamReader(context.HttpContext.Request.Body, Encoding.UTF8))
- 256 //{
- 257 // var bodyRead = reader.ReadToEndAsync();
- 258 // bodyStr = bodyRead.Result; //把body赋值给bodyStr
- 259 // needKey = JsonConvert.DeserializeAnonymousType
- 260 // (bodyRead.Result, new Dictionary<string, object>())[dependencySource].ToString();
- 261 //}
- 262 if (controllerType != typeof(ValidationController))
- 263 {
- 264 parameterResult = ProcessFormParameters(parameterResult);
- 265 }
- 266 else
- 267 {
- 268 parameterResult = ProcessLoginUserNameParameters(parameterResult, out loginName);
- 269 }
- 270 }
- 271
- 272 parameterResult = !string.IsNullOrEmpty(parameterResult) ? parameterResult : "没有传递任何参数";
- 273 loginName = !string.IsNullOrEmpty(loginName) ? loginName : "anonymous";
- 274 Guid userid = Guid.Empty;
- 275 if (string.IsNullOrEmpty(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid)) || string.IsNullOrWhiteSpace(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid)))
- 276 {
- 277 userid = Guid.Parse("a05897f9-0c86-4f5a-a581-e5da936d0e4c");
- 278 }
- 279 else
- 280 {
- 281 userid = Guid.Parse(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid));
- 282 }
- 283
- 284 OperationLog log = new OperationLog()
- 285 {
- 286 Id = Guid.NewGuid(),
- 287 Name = $"{loginName} 执行 {controllerName}.{currentMethodName} 操作。",
- 288 LoginName = loginName,
- 289 Parameters = parameterResult,
- 290 ActionName = $"{controllerType.FullName}.{currentMethodName}",
- 291 ActionType = methodType,
- 292 Message = $"【{loginName}】用户在【{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}】执行了控制器【{controllerType.FullName}】的【{currentMethodName}】方法,执行方法的类型:{methodType},执行方法所需的参数:【{parameterResult}】,操作顺利完成。",
- 293 Remarks = "全局日志记录器记录的日志。",
- 294 CreateUserId = userid,
- 295 CreateDate = currentDateTime
- 296 };
- 297
- 298 try
- 299 {
- 300 MethodInfo? methodInfo;
- 301 //如果 Controller 类型配置了 RequiredLogAttribute 特性,说明这个 Controller 里面的所有方法都需要记录日志。
- 302 //如果 Controler 配置了 RequiredLogAttribute 特性,但是方法配置 NoLogAttribute 特性,则不记录日志,否则记录日志。
- 303 if (controllerType.IsDefined(typeof(RequiredLogAttribute), false))
- 304 {
- 305 methodInfo = controllerType.GetMethod(currentMethodName);
- 306 if (methodInfo != null && !methodInfo.IsDefined(typeof(NoLogAttribute), false))
- 307 {
- 308 await _operationLogService.InsertAsync(log);
- 309 }
- 310 }
- 311 else
- 312 {
- 313 //针对方法配置了 RequiredLogAttribute 特性执行记录日志的功能
- 314 methodInfo = controllerType.GetMethod(currentMethodName);
- 315 if (methodInfo != null && methodInfo.IsDefined(typeof(RequiredLogAttribute), false))
- 316 {
- 317 await _operationLogService.InsertAsync(log);
- 318 }
- 319 }
- 320 }
- 321 catch (Exception ex)
- 322 {
- 323 log.Name = $"异常操作日志:{loginName}在{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}执行了{currentMethodName}操作。";
- 324 log.Message = $"【{loginName}】用户在【{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}】执行了控制器【{controllerType.FullName}】的【{currentMethodName}】方法,执行方法的类型:{methodType},执行方法所需的参数:【{parameterResult}】,操作没有完成,系统发生了异常。<br/>异常详情:{ex.Message},<br/>异常堆栈:{ex.StackTrace}。";
- 325
- 326 await _operationLogService.InsertAsync(log);
- 327 }
- 328
- 329 #endregion
- 330 }
- 331 }
- 332
- 333 await next.Invoke();
- 334 }
- 335
- 336 /// <summary>
- 337 /// 处理用户登录页面所传递的参数,如果包含用户的登录密码,替换成星号。
- 338 /// </summary>
- 339 /// <param name="parameters">要处理的参数字符串。</param>
- 340 /// <param name="loginName">返回用户的登录系统的用户名称。</param>
- 341 /// <returns></returns>
- 342 private string ProcessLoginUserNameParameters(string parameters, out string loginName)
- 343 {
- 344 loginName = string.Empty;
- 345 if (parameters.IndexOf("&__RequestVerificationToken") != -1)
- 346 {
- 347 parameters = parameters.Substring(0, parameters.LastIndexOf("&__RequestVerificationToken"));
- 348 if (parameters.IndexOf("userName=") != -1)
- 349 {
- 350 loginName = parameters.Substring("userName=".Length, parameters.IndexOf("&password") - "&password".Length);
- 351 parameters = $"{parameters.Substring(0, parameters.LastIndexOf("=") + 1)}*********";
- 352 }
- 353 }
- 354 else
- 355 {
- 356 if (parameters.IndexOf("userName=") != -1)
- 357 {
- 358 loginName = parameters.Substring("userName=".Length, parameters.IndexOf("&password") - "&password".Length);
- 359 parameters = $"{parameters.Substring(0, parameters.LastIndexOf("=") + 1)}*********";
- 360 }
- 361 }
- 362 return parameters;
- 363 }
- 364
- 365 /// <summary>
- 366 /// 返回经过处理的 Form 表单参数。
- 367 /// </summary>
- 368 /// <param name="originalFormParameters">未经处理的、原始的 Form 表单参数。</param>
- 369 /// <returns></returns>
- 370 private string ProcessFormParameters(string originalFormParameters)
- 371 {
- 372 string result = "没有 Form 表单参数。";
- 373 if (string.IsNullOrEmpty(originalFormParameters) || string.IsNullOrWhiteSpace(originalFormParameters))
- 374 {
- 375 return result;
- 376 }
- 377
- 378 if (originalFormParameters.IndexOf("=") != -1 && (originalFormParameters.IndexOf("=") != originalFormParameters.LastIndexOf("=")))
- 379 {
- 380 var formParameters = originalFormParameters.Split(new string[] { "-----------------------------", "Content-Disposition: form-data;" }, StringSplitOptions.RemoveEmptyEntries);
- 381 var filterParameter = new List<string>();
- 382
- 383 //获取参数数据,包含=等号的就是form表单的值
- 384 foreach (var parameter in formParameters)
- 385 {
- 386 if (parameter.IndexOf("=") != -1 && parameter.IndexOf("__RequestVerificationToken", StringComparison.CurrentCultureIgnoreCase) == -1)
- 387 {
- 388 filterParameter.Add(parameter);
- 389 }
- 390 }
- 391 //整理表单数据格式为:name='xxxx' value='yyyyyy'\r\nname='xxxx2' value='yyyyyy2'....
- 392 if (filterParameter.Count > 0)
- 393 {
- 394 for (int i = 0; i < filterParameter.Count; i++)
- 395 {
- 396 filterParameter[i] = ProcessCore(filterParameter[i]);
- 397 }
- 398 }
- 399
- 400 //凭借结果值,并返回。
- 401 if (filterParameter.Count > 0)
- 402 {
- 403 result = string.Join("\r\n", filterParameter);
- 404 }
- 405 }
- 406
- 407 return result;
- 408 }
- 409
- 410 /// <summary>
- 411 /// 递归的处理参数的格式,将格式转换为 name='xxxx' value='yyyyyy'。
- 412 /// </summary>
- 413 /// <param name="parameter">要处理的参数。</param>
- 414 /// <returns>返回处理好的参数。</returns>
- 415 private string ProcessCore(string parameter)
- 416 {
- 417 //过滤Form表单中的图片,只获取字段名和值,具体上传文件的数据不保留。
- 418 if (parameter.IndexOf("Content-Type: image/", StringComparison.CurrentCultureIgnoreCase) != -1)
- 419 {
- 420 parameter = parameter.Substring(0, parameter.IndexOf("Content-Type: image/"));
- 421 }
- 422 else if (parameter.IndexOf("\"") != -1)//替换数据中的斜杠和双引号为单引号
- 423 {
- 424 parameter = parameter.Replace("\"", "'");
- 425 }
- 426 else if (parameter.IndexOf("\r\n\r\n") != -1)
- 427 //替换数据中的两个换行符为value=',格式:“name=\"Details\"\r\n\r\n<p>asdfsadfas</p><p>asdfsadf</p><p>fasdfsadfsadf</p>\r\n”== “name=\"Details\" value='<p>asdfsadfas</p><p>asdfsadf</p><p>fasdfsadfsadf</p>'”
- 428 {
- 429 parameter = parameter.Replace("\r\n\r\n", " value='");
- 430 }
- 431 else if (parameter.EndsWith("\r\n"))
- 432 //替换数据尾部的换行符为单引号,格式:“name=\"Details\"\r\n\r\n<p>asdfsadfas</p><p>asdfsadf</p><p>fasdfsadfsadf</p>\r\n”== “name=\"Details\"\r\n\r\n<p>asdfsadfas</p><p>asdfsadf</p><p>fasdfsadfsadf</p>'”
- 433 {
- 434 parameter = parameter.Replace("\r\n", "'");
- 435 }
- 436 else if (parameter.IndexOf(";") != -1)
- 437 {
- 438 parameter = parameter.Replace(";", " ");
- 439 }
- 440 else if (parameter.IndexOf("''") != -1)
- 441 {
- 442 parameter = parameter.Replace("''", "'");
- 443 }
- 444 else
- 445 {
- 446 return parameter;
- 447 }
- 448 return ProcessCore(parameter);
- 449 }
- 450
- 451 #endregion
- 452 }
- 453 }