RequestFrequencyLimitHandlers.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. using System;
  2. using System.Diagnostics;
  3. using System.Net;
  4. using System.Net.Http;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Lottomat.Application.Code;
  8. using Lottomat.Application.Entity.CommonEntity;
  9. using Lottomat.SOA.API.Caching;
  10. using Lottomat.Util.Extension;
  11. namespace Lottomat.SOA.API.Handlers
  12. {
  13. /// <summary>
  14. /// 请求频率限制
  15. /// </summary>
  16. public sealed class RequestFrequencyLimitHandlers : DelegatingHandler
  17. {
  18. private readonly IThrottleStore _store;
  19. private readonly Func<string, long> _maxRequestsForUserIdentifier;
  20. private readonly TimeSpan _period;
  21. private readonly string _message;
  22. private RequestFrequencyLimitHandlers()
  23. {
  24. }
  25. /// <summary>
  26. /// 构造
  27. /// </summary>
  28. /// <param name="store"></param>
  29. /// <param name="maxRequestsForUserIdentifier"></param>
  30. /// <param name="period"></param>
  31. /// <param name="message"></param>
  32. public RequestFrequencyLimitHandlers(IThrottleStore store, Func<string, long> maxRequestsForUserIdentifier, TimeSpan period, string message = "对不起,您的请求频率过高({0}次/分钟),请稍后再试。")
  33. {
  34. _store = store;
  35. _maxRequestsForUserIdentifier = maxRequestsForUserIdentifier;
  36. _period = period;
  37. _message = message;
  38. }
  39. /// <summary>
  40. /// 获取请求IP地址
  41. /// </summary>
  42. /// <param name="request"></param>
  43. /// <returns></returns>
  44. private string GetUserIdentifier(HttpRequestMessage request)
  45. {
  46. return request.GetClientIpAddress();
  47. }
  48. protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  49. {
  50. //Key,即IP地址
  51. string identifier = GetUserIdentifier(request);
  52. if (string.IsNullOrEmpty(identifier))
  53. {
  54. return CreateResponse(request, HttpStatusCode.Forbidden, "无法识别客户机.");
  55. }
  56. long maxRequests = _maxRequestsForUserIdentifier(identifier);
  57. Trace.WriteLine("IP地址为:" + identifier + ",请求了服务。");
  58. if (_store.TryGetValue(identifier, out ThrottleEntry entry))
  59. {
  60. if (entry.PeriodStart + _period < DateTime.UtcNow)
  61. {
  62. _store.Rollover(identifier);
  63. }
  64. }
  65. _store.IncrementRequests(identifier);
  66. if (!_store.TryGetValue(identifier, out entry))
  67. {
  68. return CreateResponse(request, HttpStatusCode.Forbidden, "无法识别客户机.");
  69. }
  70. Task<HttpResponseMessage> response = entry.Requests > maxRequests ? CreateResponse(request, HttpStatusCode.Conflict, string.Format(_message, maxRequests)) : base.SendAsync(request, cancellationToken);
  71. return response.ContinueWith(task =>
  72. {
  73. long remaining = maxRequests - entry.Requests;
  74. if (remaining < 0)
  75. {
  76. remaining = 0;
  77. }
  78. HttpResponseMessage httpResponse = task.Result;
  79. httpResponse.Headers.Add("RateLimit-Limit", maxRequests.ToString());
  80. httpResponse.Headers.Add("RateLimit-Remaining", remaining.ToString());
  81. Trace.WriteLine("剩余可调用次数为:" + remaining);
  82. return httpResponse;
  83. }, cancellationToken);
  84. }
  85. /// <summary>
  86. /// 返回到客户端的消息
  87. /// </summary>
  88. /// <param name="request"></param>
  89. /// <param name="statusCode"></param>
  90. /// <param name="message"></param>
  91. /// <returns></returns>
  92. private Task<HttpResponseMessage> CreateResponse(HttpRequestMessage request, HttpStatusCode statusCode, string message)
  93. {
  94. BaseJson<string> resultMsg = new BaseJson<string> { Status = (int)JsonObjectStatus.Fail, Message = message, Data = null };
  95. TaskCompletionSource<HttpResponseMessage> tsc = new TaskCompletionSource<HttpResponseMessage>();
  96. HttpResponseMessage response = resultMsg.TryToJson().ToHttpResponseMessage();
  97. //request.CreateResponse(statusCode);
  98. //response.ReasonPhrase = message;
  99. tsc.SetResult(response);
  100. return tsc.Task;
  101. }
  102. }
  103. }