using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using Ropin.Core.Common; using System.Threading; namespace Ropin.Core.Extensions { [AttributeUsage(AttributeTargets.Method, Inherited = true)] /// <summary> /// 限制多出频繁请求 /// </summary> /// <summary> /// api request idempotence /// </summary> public class HttpIdempotentAttribute : Attribute, IAsyncResourceFilter { static HttpIdempotentAttribute() { var thread = new Thread(() => { while (true) { var hashtableDatas = HashTable.Where(x => DateTime.Now > x.Value.Time).ToList(); if (hashtableDatas.Any()) { foreach (var hashtableData in hashtableDatas) { HashTable.Remove(hashtableData.Key); } } else { System.Threading.Thread.Sleep(2000); } } }); thread.IsBackground = true; thread.Start(); } /// <summary> /// http request parameter code collect /// </summary> private static readonly Dictionary<int, HashtableData> HashTable = new(); /// <summary> /// http request parameter code /// </summary> public int _httpHasCode; /// <summary> /// waiting for the last request , default value:1000 /// </summary> public double WaitMillisecond { get; set; } = 1000; /// <summary> /// result cache Millisecond , default value:1000 /// </summary> public double CacheMillisecond { get; set; } = 1000; /// <summary> /// interceptor /// </summary> /// <param name="context"></param> /// <param name="next"></param> /// <returns></returns> public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) { await this.SetHttpParameterHasCode(context.HttpContext); if (HashTable.ContainsKey(_httpHasCode)) { var hashtableData = (HashtableData)HashTable[_httpHasCode]; if (hashtableData != null) { if (DateTime.Now < hashtableData.Time) { context.Result = hashtableData.Value; return; } else if (DateTime.Now > hashtableData.Time) { HashTable.Remove(_httpHasCode); } else if (hashtableData.Value == null) { context.Result = new ContentResult() { Content = "正在执行..." }; //context.Result = new ObjectResult("正在执行...") //{ // StatusCode = StatusCodes.Status500InternalServerError //}; return; } } } HashTable.Add(_httpHasCode, new HashtableData(DateTime.Now.AddMilliseconds(WaitMillisecond), null)); try { var netResult = await next(); if (HashTable.ContainsKey(_httpHasCode)) { var hashtableData = (HashtableData)HashTable[_httpHasCode]; if (hashtableData != null) { hashtableData.Value = (IActionResult)netResult.Result; hashtableData.Time = DateTime.Now.AddMilliseconds(CacheMillisecond); } } } catch (Exception ex) { HashTable.Remove(_httpHasCode); } } /// <summary> /// get http request parameter code /// </summary> /// <returns></returns> private async Task SetHttpParameterHasCode(HttpContext httpContext) { object readFromJson = null; try { if (httpContext.Request.Method != "GET") { //readFromJson = await httpContext.Request.ReadFromJsonAsync<object>(); httpContext.Request.EnableBuffering();//设置流可以多次读取 readFromJson = await new StreamReader(httpContext.Request.Body).ReadToEndAsync(); httpContext.Request.Body.Seek(0, SeekOrigin.Begin);//设置流的栈位置回到起点 } } catch (Exception e) { Console.WriteLine(e); } //todo 根据实际项目情况处理,获取Headers toke var authorization = httpContext.Request.Headers["Authorization"]; var queryString = httpContext.Request.QueryString; var bodyString = readFromJson == null ? string.Empty : readFromJson.ToString(); var builder = $"{authorization}-{queryString}-{bodyString}"; this._httpHasCode = builder.GetHashCode(); } /// <summary> /// Hashtable parameter model /// </summary> private class HashtableData { public HashtableData(DateTime time, ObjectResult value) { Time = time; Value = value; } public DateTime Time { get; set; } public IActionResult Value { get; set; } } } }