HttpIdempotentAttribute.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. using Microsoft.AspNetCore.Http;
  2. using Microsoft.AspNetCore.Mvc.Filters;
  3. using Microsoft.AspNetCore.Mvc;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using Ropin.Core.Common;
  11. using System.Threading;
  12. namespace Ropin.Core.Extensions
  13. {
  14. [AttributeUsage(AttributeTargets.Method, Inherited = true)]
  15. /// <summary>
  16. /// 限制多出频繁请求
  17. /// </summary>
  18. /// <summary>
  19. /// api request idempotence
  20. /// </summary>
  21. public class HttpIdempotentAttribute : Attribute, IAsyncResourceFilter
  22. {
  23. static HttpIdempotentAttribute()
  24. {
  25. var thread = new Thread(() =>
  26. {
  27. while (true)
  28. {
  29. var hashtableDatas = HashTable.Where(x => DateTime.Now > x.Value.Time).ToList();
  30. if (hashtableDatas.Any())
  31. {
  32. foreach (var hashtableData in hashtableDatas)
  33. {
  34. HashTable.Remove(hashtableData.Key);
  35. }
  36. }
  37. else
  38. {
  39. System.Threading.Thread.Sleep(2000);
  40. }
  41. }
  42. });
  43. thread.IsBackground = true;
  44. thread.Start();
  45. }
  46. /// <summary>
  47. /// http request parameter code collect
  48. /// </summary>
  49. private static readonly Dictionary<int, HashtableData> HashTable = new();
  50. /// <summary>
  51. /// http request parameter code
  52. /// </summary>
  53. public int _httpHasCode;
  54. /// <summary>
  55. /// waiting for the last request , default value:1000
  56. /// </summary>
  57. public double WaitMillisecond { get; set; } = 1000;
  58. /// <summary>
  59. /// result cache Millisecond , default value:1000
  60. /// </summary>
  61. public double CacheMillisecond { get; set; } = 1000;
  62. /// <summary>
  63. /// interceptor
  64. /// </summary>
  65. /// <param name="context"></param>
  66. /// <param name="next"></param>
  67. /// <returns></returns>
  68. public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
  69. {
  70. await this.SetHttpParameterHasCode(context.HttpContext);
  71. if (HashTable.ContainsKey(_httpHasCode))
  72. {
  73. var hashtableData = (HashtableData)HashTable[_httpHasCode];
  74. if (hashtableData != null)
  75. {
  76. if (DateTime.Now < hashtableData.Time)
  77. {
  78. context.Result = hashtableData.Value;
  79. return;
  80. }
  81. else if (DateTime.Now > hashtableData.Time)
  82. {
  83. HashTable.Remove(_httpHasCode);
  84. }
  85. else if (hashtableData.Value == null)
  86. {
  87. context.Result = new ContentResult()
  88. {
  89. Content = "正在执行..."
  90. };
  91. //context.Result = new ObjectResult("正在执行...")
  92. //{
  93. // StatusCode = StatusCodes.Status500InternalServerError
  94. //};
  95. return;
  96. }
  97. }
  98. }
  99. HashTable.Add(_httpHasCode, new HashtableData(DateTime.Now.AddMilliseconds(WaitMillisecond), null));
  100. try
  101. {
  102. var netResult = await next();
  103. if (HashTable.ContainsKey(_httpHasCode))
  104. {
  105. var hashtableData = (HashtableData)HashTable[_httpHasCode];
  106. if (hashtableData != null)
  107. {
  108. hashtableData.Value = (IActionResult)netResult.Result;
  109. hashtableData.Time = DateTime.Now.AddMilliseconds(CacheMillisecond);
  110. }
  111. }
  112. }
  113. catch (Exception ex)
  114. {
  115. HashTable.Remove(_httpHasCode);
  116. }
  117. }
  118. /// <summary>
  119. /// get http request parameter code
  120. /// </summary>
  121. /// <returns></returns>
  122. private async Task SetHttpParameterHasCode(HttpContext httpContext)
  123. {
  124. object readFromJson = null;
  125. try
  126. {
  127. if (httpContext.Request.Method != "GET")
  128. {
  129. //readFromJson = await httpContext.Request.ReadFromJsonAsync<object>();
  130. httpContext.Request.EnableBuffering();//设置流可以多次读取
  131. readFromJson = await new StreamReader(httpContext.Request.Body).ReadToEndAsync();
  132. httpContext.Request.Body.Seek(0, SeekOrigin.Begin);//设置流的栈位置回到起点
  133. }
  134. }
  135. catch (Exception e)
  136. {
  137. Console.WriteLine(e);
  138. }
  139. //todo 根据实际项目情况处理,获取Headers toke
  140. var authorization = httpContext.Request.Headers["Authorization"];
  141. var queryString = httpContext.Request.QueryString;
  142. var bodyString = readFromJson == null ? string.Empty : readFromJson.ToString();
  143. var builder = $"{authorization}-{queryString}-{bodyString}";
  144. this._httpHasCode = builder.GetHashCode();
  145. }
  146. /// <summary>
  147. /// Hashtable parameter model
  148. /// </summary>
  149. private class HashtableData
  150. {
  151. public HashtableData(DateTime time, ObjectResult value)
  152. {
  153. Time = time;
  154. Value = value;
  155. }
  156. public DateTime Time { get; set; }
  157. public IActionResult Value { get; set; }
  158. }
  159. }
  160. }