|
一、简介
Net Core跨平台项目开发多了,总会遇到各种各样的问题,我就遇到了一个这样的问题,不能访问 Cannot access a disposed object 错误,经过自己多方努力,查阅资料,终于找到了解决办法,引发这个问题的原因大多数是多次读取请求Body流造成的,需要换一种获取请求Body流方法,不能使用StreamRreader方式,使用Body.CopyTo(ms)方法。
我使用的环境:Visual Studio 2022
开发语言:C#
开发框架:Asp.Net Core Mvc
DotNet版本:Net 6.0
遇到问题是好事,说明自己还有不足,那就解决它,时间长了,技术和知识也就积累了。其实解决方法不难,话不多,直接上解决方案。
二、解决方案的具体实现。
解决方法很简单,不需要做过多解释,直接找个配置和编码就可以了,我贴完整源码,是便于以后查阅,不喜勿喷。
总共三步:红色字体写好了操作步骤(说明一下,红色字体是要解决方法,其他不要关注,把整个代码贴出来,是为了以后查阅)
我写了一个过滤器(实现日志功能),实现的是IActionFilter 过滤器,在过滤器中,获取Post请求Body的Context使用下面方式获取强调(强调,关注红色字体,其他无关,这是我写的日志功能,贴全部代码,便于以后查阅,不喜勿喷)- 1 using Microsoft.AspNetCore.Mvc.Filters;
- 2 using Newtonsoft.Json;
- 3 using OpticalTrap.Framework.Loggings;
- 4 using OpticalTrap.Web.ConstProvider;
- 5 using OpticalTrap.Web.Contracts;
- 6 using OpticalTrap.Web.Facade.Controllers;
- 7 using OpticalTrap.Web.Models;
- 8 using System.Reflection;
- 9 using System.Security.Claims;
- 10 using System.Text;
- 11
- 12 namespace OpticalTrap.Web.Facade.Extensions.Filters
- 13 {
- 14 /// <summary>
- 15 /// 该类型定义了全局处理操作日志的过滤器,该类型是密封类型。
- 16 /// </summary>
- 17 public sealed class GlobalOperationLogFilterAttribute : Attribute, IActionFilter, IAsyncActionFilter
- 18 {
- 19 #region 实例字段
- 20
- 21 private readonly ILogger<GlobalOperationLogFilterAttribute> _logger;
- 22 private readonly IServiceProvider _serviceProvider;
- 23
- 24 #endregion
- 25
- 26 #region 构造函数
- 27
- 28 /// <summary>
- 29 /// 初始化该类型的新实例。
- 30 /// </summary>
- 31 /// <param name="logger">需要注入的日志服务实例。</param>
- 32 /// <param name="serviceProvider">需要注入的服务提供器。</param>
- 33 public GlobalOperationLogFilterAttribute(ILogger<GlobalOperationLogFilterAttribute> logger, IServiceProvider serviceProvider)
- 34 {
- 35 _logger = logger;
- 36 _serviceProvider = serviceProvider;
- 37 }
- 38
- 39 #endregion
- 40
- 41 #region 操作日志的同步方法
- 42
- 43 /// <summary>
- 44 /// 在标注方法执行之前执行该方法。
- 45 /// </summary>
- 46 /// <param name="context">方法执行前的上下文。</param>
- 47 public async void OnActionExecuting(ActionExecutingContext context)
- 48 {
- 49 if (context.Controller.GetType() != typeof(ErrorHandlerController))
- 50 {
- 51 if (context.ActionDescriptor.EndpointMetadata.Any(c => c.GetType() == typeof(RequiredLogAttribute)))
- 52 {
- 53 #region 核心处理
- 54
- 55 var controllerType = context.Controller.GetType();
- 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 if (string.Compare(methodType, "get", true) == 0)
- 69 {
- 70 if (!string.IsNullOrEmpty(context.HttpContext.Request.QueryString.Value) && !string.IsNullOrWhiteSpace(context.HttpContext.Request.QueryString.Value))
- 71 {
- 72 parameterResult = context.HttpContext.Request.QueryString.Value;
- 73 }
- 74 }
- 75 else
- 76 {
- 77 //<strong>第三步:在同步方法里的使用:</strong><strong>启用倒带, 读取request.body里的的参数, 还必须在在Program.cs里也启用倒带功能</strong>
- 78 <strong> context.HttpContext.Request.EnableBuffering();
- </strong> 79 <strong>context.HttpContext.Request.Body.Position = 0;
- </strong> 80 <strong>using (var memoryStream = new MemoryStream())
- </strong> 81 <strong> {
- </strong> 82 <strong> context.HttpContext.Request.Body.CopyTo(memoryStream);
- </strong> 83 <strong>var streamBytes = memoryStream.ToArray();
- </strong> 84 <strong>parameterResult = Encoding.UTF8.GetString(streamBytes);</strong> //把body赋值给bodyStr
- 85 <strong> }
- </strong> 86 //using (var reader = new StreamReader(context.HttpContext.Request.Body, Encoding.UTF8))<br> //{<br> // var bodyRead = reader.ReadToEndAsync();<br> // bodyStr = bodyRead.Result; //把body赋值给bodyStr<br> // needKey = JsonConvert.DeserializeAnonymousType<br> // (bodyRead.Result, new Dictionary<string, object>())[dependencySource].ToString();<br> //}
复制代码- if (controllerType != typeof(ValidationController))
- 87 {
- 88 parameterResult = ProcessFormParameters(parameterResult);
- 89 }
- 90 else
- 91 {
- 92 parameterResult = ProcessLoginUserNameParameters(parameterResult, controllerType, nameof(ValidationController), currentMethodName, out loginName);
- 93 }
- 94 }
- 95
- 96 parameterResult = !string.IsNullOrEmpty(parameterResult) ? parameterResult : "没有传递任何参数";
- 97 loginName = !string.IsNullOrEmpty(loginName) ? loginName : "anonymous";
- 98 Guid userid = Guid.Empty;
- 99 if (string.IsNullOrEmpty(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid)) || string.IsNullOrWhiteSpace(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid)))
- 100 {
- 101 userid = Guid.Parse("a05897f9-0c86-4f5a-a581-e5da936d0e4c");
- 102 }
- 103 else
- 104 {
- 105 userid = Guid.Parse(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid));
- 106 }
- 107
- 108 OperationLog log = new OperationLog()
- 109 {
- 110 Id = Guid.NewGuid(),
- 111 Name = $"{loginName}在{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}执行了{currentMethodName}操作。",
- 112 LoginName = loginName,
- 113 Parameters = parameterResult,
- 114 ActionName = $"{controllerType.FullName}.{currentMethodName}",
- 115 ActionType = methodType,
- 116 Message = $"【{loginName}】用户在【{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}】执行了控制器【{controllerType.FullName}】的【{currentMethodName}】方法,执行方法的类型:{methodType},执行方法所需的参数:【{parameterResult}】,操作顺利完成。",
- 117 Remarks = "全局日志记录器记录的日志。",
- 118 CreateUserId = userid,
- 119 CreateDate = currentDateTime
- 120 };
- 121
- 122 try
- 123 {
- 124 MethodInfo? methodInfo;
- 125 if (controllerType.IsDefined(typeof(RequiredLogAttribute), false))
- 126 {
- 127 methodInfo = controllerType.GetMethod(currentMethodName);
- 128 if (methodInfo != null)
- 129 {
- 130 _logger.LogInformation(JsonConvert.SerializeObject(log));
- 131 }
- 132 }
- 133 else
- 134 {
- 135 methodInfo = controllerType.GetMethod(currentMethodName);
- 136 if (methodInfo != null)
- 137 {
- 138 if (methodInfo.IsDefined(typeof(RequiredLogAttribute), false))
- 139 {
- 140 _logger.LogInformation(JsonConvert.SerializeObject(log));
- 141 }
- 142 }
- 143 }
- 144 }
- 145 catch (Exception ex)
- 146 {
- 147 log.Name = $"异常操作日志:{loginName}在{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}执行了{currentMethodName}操作。";
- 148
- 149 log.Message = $"【{loginName}】用户在【{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}】执行了控制器【{controllerType.FullName}】的【{currentMethodName}】方法,执行方法的类型:{methodType},执行方法所需的参数:【{parameterResult}】,操作没有完成,系统发生了异常。<br/>异常详情:{ex.Message},<br/>异常堆栈:{ex.StackTrace}。";
- 150
- 151 _logger.LogInformation(JsonConvert.SerializeObject(log));
- 152 }
- 153
- 154 #endregion
- 155 }
- 156 }
- 157 }
- 158
- 159 /// <summary>
- 160 /// 在标注方法执行之后执行该方法。
- 161 /// </summary>
- 162 /// <param name="context">方法执行后的上下文。</param>
- 163 public void OnActionExecuted(ActionExecutedContext context) { }
- 164
- 165 #endregion
- 166
- 167 #region 操作日志的异步方法
- 168
- 169 /// <summary>
- 170 /// 全局日志记录器异步实现的操作日志的记录。
- 171 /// </summary>
- 172 /// <param name="context">方法执行前的上下文。</param>
- 173 /// <param name="next">方法执行的下一个环节代理。</param>
- 174 /// <returns></returns>
- 175 public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
- 176 {
- 177 if (context.Controller.GetType() != typeof(ErrorHandlerController))
- 178 {
- 179 if (context.ActionDescriptor.EndpointMetadata.Any(c => c.GetType() == typeof(RequiredLogAttribute)))
- 180 {
- 181 #region 核心处理
- 182
- 183 var controllerType = context.Controller.GetType();
- 184 var currentMethodName = context.ActionDescriptor.RouteValues["action"]!;
- 185
- 186 string? loginName = string.Empty;
- 187 var claimKeysProvider = _serviceProvider.GetService<ClaimKeysConstProvider>();
- 188 if (claimKeysProvider != null)
- 189 {
- 190 loginName = context.HttpContext.User.FindFirstValue(claimKeysProvider.ClaimStoreUserNameKey);
- 191 }
- 192
- 193 var currentDateTime = DateTime.Now;
- 194 var methodType = context.HttpContext.Request.Method;
- 195 string parameterResult = string.Empty;
- 196 if (string.Compare(methodType, "get", true) == 0)
- 197 {
- 198 if (!string.IsNullOrEmpty(context.HttpContext.Request.QueryString.Value) && !string.IsNullOrWhiteSpace(context.HttpContext.Request.QueryString.Value))
- 199 {
- 200 parameterResult = context.HttpContext.Request.QueryString.Value;
- 201 }
- 202 }
- 203 else
- 204 {
- 205 //<strong>第三步:在异步步方法里的使用:</strong><strong>启用倒带, 读取request.body里的的参数, 还必须在在Program.cs里也启用倒带功能</strong>
- 206 <strong> context.HttpContext.Request.EnableBuffering();
- </strong>207 <strong>context.HttpContext.Request.Body.Position = 0;
- </strong>208 <strong>using (var memoryStream = new MemoryStream())
- </strong>209 <strong> {
- </strong>210 <strong> context.HttpContext.Request.Body.CopyTo(memoryStream);
- </strong>211 <strong>var streamBytes = memoryStream.ToArray();
- </strong>212 <strong> parameterResult = Encoding.UTF8.GetString(streamBytes);</strong> //把body赋值给bodyStr
- 213 <strong> }</strong><br> //using (var reader = new StreamReader(context.HttpContext.Request.Body, Encoding.UTF8))<br> //{<br> // var bodyRead = reader.ReadToEndAsync();<br> // bodyStr = bodyRead.Result; //把body赋值给bodyStr<br> // needKey = JsonConvert.DeserializeAnonymousType<br> // (bodyRead.Result, new Dictionary<string, object>())[dependencySource].ToString();<br> //}
- 214 if (controllerType != typeof(ValidationController))
- 215 {
- 216 parameterResult = ProcessFormParameters(parameterResult);
- 217 }
- 218 else
- 219 {
- 220 parameterResult = ProcessLoginUserNameParameters(parameterResult, controllerType, nameof(ValidationController), currentMethodName, out loginName);
- 221 }
- 222 }
- 223
- 224 parameterResult = !string.IsNullOrEmpty(parameterResult) ? parameterResult : "没有传递任何参数";
- 225 loginName = !string.IsNullOrEmpty(loginName) ? loginName : "anonymous";
- 226 Guid userid = Guid.Empty;
- 227 if (string.IsNullOrEmpty(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid)) || string.IsNullOrWhiteSpace(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid)))
- 228 {
- 229 userid = Guid.Parse("a05897f9-0c86-4f5a-a581-e5da936d0e4c");
- 230 }
- 231 else
- 232 {
- 233 userid = Guid.Parse(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid));
- 234 }
- 235
- 236 OperationLog log = new OperationLog()
- 237 {
- 238 Id = Guid.NewGuid(),
- 239 Name = $"{loginName}在{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}执行了{currentMethodName}操作。",
- 240 LoginName = loginName,
- 241 Parameters = parameterResult,
- 242 ActionName = $"{controllerType.FullName}.{currentMethodName}",
- 243 ActionType = methodType,
- 244 Message = $"【{loginName}】用户在【{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}】执行了控制器【{controllerType.FullName}】的【{currentMethodName}】方法,执行方法的类型:{methodType},执行方法所需的参数:【{parameterResult}】,操作顺利完成。",
- 245 Remarks = "全局日志记录器记录的日志。",
- 246 CreateUserId = userid,
- 247 CreateDate = currentDateTime
- 248 };
- 249
- 250 try
- 251 {
- 252 MethodInfo? methodInfo;
- 253 if (controllerType.IsDefined(typeof(RequiredLogAttribute), false))
- 254 {
- 255 methodInfo = controllerType.GetMethod(currentMethodName);
- 256 if (methodInfo != null)
- 257 {
- 258 _logger.LogInformation(JsonConvert.SerializeObject(log));
- 259 }
- 260 }
- 261 else
- 262 {
- 263 methodInfo = controllerType.GetMethod(currentMethodName);
- 264 if (methodInfo != null)
- 265 {
- 266 if (methodInfo.IsDefined(typeof(RequiredLogAttribute), false))
- 267 {
- 268 _logger.LogInformation(JsonConvert.SerializeObject(log));
- 269 }
- 270 }
- 271 }
- 272 }
- 273 catch (Exception ex)
- 274 {
- 275 log.Name = $"异常操作日志:{loginName}在{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}执行了{currentMethodName}操作。";
- 276 log.Message = $"【{loginName}】用户在【{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}】执行了控制器【{controllerType.FullName}】的【{currentMethodName}】方法,执行方法的类型:{methodType},执行方法所需的参数:【{parameterResult}】,操作没有完成,系统发生了异常。<br/>异常详情:{ex.Message},<br/>异常堆栈:{ex.StackTrace}。";
- 277
- 278 _logger.LogInformation(JsonConvert.SerializeObject(log));
- 279 }
- 280
- 281 #endregion
- 282 }
- 283 }
- 284
- 285 await next.Invoke();
- 286 }
- 287
- 288 /// <summary>
- 289 /// 处理参数。
- 290 /// </summary>
- 291 /// <param name="parameters">要处理的参数字符串。</param>
- 292 /// <param name="controllerType">控制器的类型。</param>
- 293 /// <param name="controllerFullName">控制器的全名。</param>
- 294 /// <param name="currentMethodName">当前调用的方法名称。</param>
- 295 /// <param name="loginName">登录系统的用户名称。</param>
- 296 /// <returns></returns>
- 297 private string ProcessLoginUserNameParameters(string parameters, Type controllerType, string controllerFullName, string currentMethodName, out string loginName)
- 298 {
- 299 loginName = string.Empty;
- 300 if (parameters.IndexOf("&__RequestVerificationToken") != -1)
- 301 {
- 302 parameters = parameters.Substring(0, parameters.LastIndexOf("&__RequestVerificationToken"));
- 303 if ((string.Compare(controllerType.FullName, controllerFullName, true) == 0) && (string.Compare(currentMethodName, "login", true) == 0))
- 304 {
- 305 if (parameters.IndexOf("userName=") != -1)
- 306 {
- 307 loginName = parameters.Substring("userName=".Length, parameters.IndexOf("&password") - "&password".Length);
- 308 parameters = $"{parameters.Substring(0, parameters.LastIndexOf("=") + 1)}*********";
- 309 }
- 310 }
- 311 }
- 312 else
- 313 {
- 314 if ((string.Compare(controllerType.FullName, controllerFullName, true) == 0) && (string.Compare(currentMethodName, "login", true) == 0))
- 315 {
- 316 if (parameters.IndexOf("userName=") != -1)
- 317 {
- 318 loginName = parameters.Substring("userName=".Length, parameters.IndexOf("&password") - "&password".Length);
- 319 parameters = $"{parameters.Substring(0, parameters.LastIndexOf("=") + 1)}*********";
- 320 }
- 321 }
- 322 }
- 323 return parameters;
- 324 }
- 325
- 326 /// <summary>
- 327 /// 返回经过处理的 Form 表单参数。
- 328 /// </summary>
- 329 /// <param name="originalFormParameters">未经处理的、原始的 Form 表单参数。</param>
- 330 /// <returns></returns>
- 331 private string ProcessFormParameters(string originalFormParameters)
- 332 {
- 333 string result = "没有 Form 表单参数。";
- 334 if (string.IsNullOrEmpty(originalFormParameters) || string.IsNullOrWhiteSpace(originalFormParameters))
- 335 {
- 336 return result;
- 337 }
- 338
- 339 if (originalFormParameters.IndexOf("=") != -1 && (originalFormParameters.IndexOf("=") != originalFormParameters.LastIndexOf("=")))
- 340 {
- 341 var formParameters = originalFormParameters.Split(new string[] { "-----------------------------", "Content-Disposition: form-data;" }, StringSplitOptions.RemoveEmptyEntries);
- 342 var filterParameter = new List<string>();
- 343
- 344 //获取参数数据,包含=等号的就是form表单的值
- 345 foreach (var parameter in formParameters)
- 346 {
- 347 if (parameter.IndexOf("=") != -1 && parameter.IndexOf("__RequestVerificationToken", StringComparison.CurrentCultureIgnoreCase) == -1)
- 348 {
- 349 filterParameter.Add(parameter);
- 350 }
- 351 }
- 352 //整理表单数据格式为:name='xxxx' value='yyyyyy'\r\nname='xxxx2' value='yyyyyy2'....
- 353 if (filterParameter.Count > 0)
- 354 {
- 355 for (int i = 0; i < filterParameter.Count; i++)
- 356 {
- 357 filterParameter[i] = ProcessCore(filterParameter[i]);
- 358 }
- 359 }
- 360
- 361 //凭借结果值,并返回。
- 362 if (filterParameter.Count > 0)
- 363 {
- 364 result = string.Join("\r\n", filterParameter);
- 365 }
- 366 }
- 367
- 368 return result;
- 369 }
- 370
- 371 /// <summary>
- 372 /// 递归的处理参数的格式,将格式转换为 name='xxxx' value='yyyyyy'。
- 373 /// </summary>
- 374 /// <param name="parameter">要处理的参数。</param>
- 375 /// <returns>返回处理好的参数。</returns>
- 376 private string ProcessCore(string parameter)
- 377 {
- 378 //过滤Form表单中的图片,只获取字段名和值,具体上传文件的数据不保留。
- 379 if (parameter.IndexOf("Content-Type: image/", StringComparison.CurrentCultureIgnoreCase) != -1)
- 380 {
- 381 parameter = parameter.Substring(0, parameter.IndexOf("Content-Type: image/"));
- 382 }
- 383 else if (parameter.IndexOf(""") != -1)//替换数据中的斜杠和双引号为单引号
- 384 {
- 385 parameter = parameter.Replace(""", "'");
- 386 }
- 387 else if (parameter.IndexOf("\r\n\r\n") != -1)
- 388 //替换数据中的两个换行符为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>'”
- 389 {
- 390 parameter = parameter.Replace("\r\n\r\n", " value='");
- 391 }
- 392 else if (parameter.EndsWith("\r\n"))
- 393 //替换数据尾部的换行符为单引号,格式:“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>'”
- 394 {
- 395 parameter = parameter.Replace("\r\n", "'");
- 396 }
- 397 else if (parameter.IndexOf(";") != -1)
- 398 {
- 399 parameter = parameter.Replace(";", " ");
- 400 }
- 401 else if (parameter.IndexOf("''") != -1)
- 402 {
- 403 parameter = parameter.Replace("''", "'");
- 404 }
- 405 else
- 406 {
- 407 return parameter;
- 408 }
- 409 return ProcessCore(parameter);
- 410 }
- 411
- 412 #endregion
- 413 }
- 414 }
复制代码 三、总结
好了,写完了,今天又解决了一个问题,知识和技能就是在出现问题和解决问题的过程中积累的,不忘初心,继续努力,老天不会亏待努力的人。
来源:https://www.cnblogs.com/PatrickLiu/archive/2023/05/31/17445451.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
|