.net core api 请求实现接口幂等性
简单实现接口幂等性,根据参数的hascode实现:
- 参数介绍
- WaitMillisecond : 请求等待毫秒数
- CacheMillisecond:请求结果缓存毫秒数
- 参数具体使用场景
- WaitMillisecond :用户频繁发起多次请求,只处理第一次请求,后续请求在这个等待(时间范围内)的均返回“正在执行..”,超出这个预设时间会再次真实请求到后台业务逻辑
- CacheMillisecond:第一次请求成功并且拿到结果,用户频繁发起请求 在这个缓存时间在,将返回上次一样的结果。直至过期
1.使用方式
[HttpPost] [HttpIdempotent(WaitMillisecond = 10000,CacheMillisecond = 3000)] public async Task<IActionResult> Order(string orderNo)
{
//TODO
}
2.具体实现
1 /// <summary> 2 /// api request idempotence 3 /// </summary> 4 public class HttpIdempotentAttribute : Attribute, IAsyncResourceFilter 5 { 6 7 static HttpIdempotentAttribute() 8 { 9 var thread = new Thread(() => 10 { 11 while (true) 12 { 13 var hashtableDatas = HashTable.Where(x => DateTime.Now > x.Value.Time).ToList(); 14 15 if (hashtableDatas.Any()) 16 { 17 foreach (var hashtableData in hashtableDatas) 18 { 19 HashTable.Remove(hashtableData.Key); 20 } 21 } 22 else 23 { 24 System.Threading.Thread.Sleep(2000); 25 } 26 } 27 28 }); 29 30 thread.IsBackground = true; 31 thread.Start(); 32 } 33 34 /// <summary> 35 /// http request parameter code collect 36 /// </summary> 37 private static readonly Dictionary<int, HashtableData> HashTable = new(); 38 39 /// <summary> 40 /// http request parameter code 41 /// </summary> 42 public int _httpHasCode; 43 44 /// <summary> 45 /// waiting for the last request , default value:1000 46 /// </summary> 47 public double WaitMillisecond { get; set; } = 1000; 48 49 /// <summary> 50 /// result cache Millisecond , default value:1000 51 /// </summary> 52 public double CacheMillisecond { get; set; } = 1000; 53 54 /// <summary> 55 /// interceptor 56 /// </summary> 57 /// <param name="context"></param> 58 /// <param name="next"></param> 59 /// <returns></returns> 60 public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) 61 { 62 await this.SetHttpParameterHasCode(context.HttpContext); 63 64 if (HashTable.ContainsKey(_httpHasCode)) 65 { 66 var hashtableData = (HashtableData)HashTable[_httpHasCode]; 67 if (hashtableData != null) 68 { 69 if (DateTime.Now < hashtableData.Time) 70 { 71 context.Result = hashtableData.Value; 72 return; 73 } 74 else if (DateTime.Now > hashtableData.Time) 75 { 76 HashTable.Remove(_httpHasCode); 77 } 78 else if (hashtableData.Value.Value == null) 79 { 80 context.Result = new ContentResult() 81 { 82 Content = "正在执行..." 83 }; 84 return; 85 } 86 } 87 } 88 89 HashTable.Add(_httpHasCode, new HashtableData(DateTime.Now.AddMilliseconds(WaitMillisecond), null)); 90 91 try 92 { 93 var netResult = await next(); 94 95 if (HashTable.ContainsKey(_httpHasCode)) 96 { 97 var hashtableData = (HashtableData)HashTable[_httpHasCode]; 98 if (hashtableData != null) 99 { 100 hashtableData.Value = (ObjectResult)netResult.Result; 101 hashtableData.Time = DateTime.Now.AddMilliseconds(CacheMillisecond); 102 } 103 } 104 } 105 catch (Exception ex) 106 { 107 HashTable.Remove(_httpHasCode); 108 } 109 110 } 111 112 /// <summary> 113 /// get http request parameter code 114 /// </summary> 115 /// <returns></returns> 116 private async Task SetHttpParameterHasCode(HttpContext httpContext) 117 { 118 119 object readFromJson = null; 120 121 try 122 { 123 if (httpContext.Request.Method != "GET") 124 { 125 readFromJson = await httpContext.Request.ReadFromJsonAsync<object>(); 126 } 127 } 128 catch (Exception e) 129 { 130 Console.WriteLine(e); 131 } 132 133 //todo 根据实际项目情况处理,获取Headers toke 134 var authorization = httpContext.Request.Headers["Authorization"]; 135 136 var queryString = httpContext.Request.QueryString; 137 var bodyString = readFromJson == null ? string.Empty : readFromJson.ToString(); 138 139 var builder = $"{authorization}-{queryString}-{bodyString}"; 140 141 this._httpHasCode = builder.GetHashCode(); 142 } 143 144 /// <summary> 145 /// Hashtable parameter model 146 /// </summary> 147 private class HashtableData 148 { 149 public HashtableData(DateTime time, ObjectResult value) 150 { 151 Time = time; 152 Value = value; 153 } 154 public DateTime Time { get; set; } 155 public ObjectResult Value { get; set; } 156 } 157 }