Commit 9c8dd029 by lcx

Merge branch 'feature/限流中间件' into develop

parents 259016d2 67e7db5b
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Swagger; using Swashbuckle.AspNetCore.Swagger;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
...@@ -16,7 +17,7 @@ public static void AddSwaggerConfiguration(this IServiceCollection services) ...@@ -16,7 +17,7 @@ public static void AddSwaggerConfiguration(this IServiceCollection services)
services.AddSwaggerGen(c => services.AddSwaggerGen(c =>
{ {
c.SwaggerDoc("v1", new Info { Version = "v1.0", Title = "绩效API接口" }); c.SwaggerDoc("v1", new OpenApiInfo { Version = "v1.0", Title = "绩效API接口" });
//var xmlPath = new string[] //var xmlPath = new string[]
//{ //{
...@@ -34,13 +35,22 @@ public static void AddSwaggerConfiguration(this IServiceCollection services) ...@@ -34,13 +35,22 @@ public static void AddSwaggerConfiguration(this IServiceCollection services)
c.IncludeXmlComments(xmlPathsss, true); c.IncludeXmlComments(xmlPathsss, true);
// Token绑定到ConfigureServices // Token绑定到ConfigureServices
var security = new Dictionary<string, IEnumerable<string>> { { "Performance API", new string[] { } }, }; var security = new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference{ Type = ReferenceType.SecurityScheme, Id = "Bearer" }
},
new List<string>()
}
};
c.AddSecurityRequirement(security); c.AddSecurityRequirement(security);
c.AddSecurityDefinition("Performance API", new ApiKeyScheme c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{ {
Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)", Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)",
Name = "Authorization", Name = "Authorization",
In = "HEADER" In = ParameterLocation.Header
}); });
}); });
......
...@@ -41,58 +41,63 @@ public class RequestRateLimitingMiddleware ...@@ -41,58 +41,63 @@ public class RequestRateLimitingMiddleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
if (options == null || options.Endpoints == null || !options.Endpoints.Any(t => context.Request.Path.ToString().StartsWith(t))) if (!context.Response.HasStarted && options != null && options.Endpoints != null && options.Endpoints.Any(t => context.Request.Path.ToString().StartsWith(t)))
await next(context);
var ip = httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString();
var headers = context.Request.Headers;
if (headers.ContainsKey("X-Forwarded-For"))
{ {
ip = IPAddress.Parse(headers["X-Forwarded-For"].ToString().Split(',', StringSplitOptions.RemoveEmptyEntries)[0]).ToString(); var ip = httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString();
}
var requestKey = $"{ip}-{context.Request.Method}-{context.Request.Path}"; var headers = context.Request.Headers;
// logger.LogInformation($"请求地址:{requestKey}"); if (headers.ContainsKey("X-Forwarded-For"))
var cacheOptions = new MemoryCacheEntryOptions() {
{ ip = IPAddress.Parse(headers["X-Forwarded-For"].ToString().Split(',', StringSplitOptions.RemoveEmptyEntries)[0]).ToString();
AbsoluteExpiration = DateTime.Now.AddSeconds(options.Period) }
};
if (requestStore.TryGetValue(requestKey, out int hitCount)) var requestKey = $"{ip}-{context.Request.Method}-{context.Request.Path}";
{ // logger.LogInformation($"请求地址:{requestKey}");
if (hitCount < Limit) var cacheOptions = new MemoryCacheEntryOptions()
{ {
await ProcessRequest(context, requestKey, hitCount, cacheOptions); AbsoluteExpiration = DateTime.Now.AddSeconds(options.Period)
};
if (requestStore.TryGetValue(requestKey, out int hitCount))
{
if (hitCount < Limit)
{
await ProcessRequest(context, requestKey, hitCount, cacheOptions);
}
else
{
// X-RateLimit-RetryAfter:超出限制后能够再次正常访问的时间。
context.Response.Headers["X-RateLimit-RetryAfter"] = cacheOptions.AbsoluteExpiration?.ToString();
context.Response.StatusCode = StatusCodes.Status200OK;
context.Response.ContentType = "application/json; charset=utf-8";
var response = new ApiResponse
{
State = ResponseType.TooManyRequests,
Message = "访问过于频繁,请稍后重试"
};
await context.Response.WriteAsync(JsonHelper.Serialize(response));
}
} }
else else
{ {
// X-RateLimit-RetryAfter:超出限制后能够再次正常访问的时间。 await ProcessRequest(context, requestKey, hitCount, cacheOptions);
//context.Response.Headers["X-RateLimit-RetryAfter"] = cacheOptions.AbsoluteExpiration?.ToString();
context.Response.StatusCode = StatusCodes.Status200OK;
context.Response.ContentType = "application/json; charset=utf-8";
var response = new ApiResponse
{
State = ResponseType.TooManyRequests,
Message = "访问过于频繁,请稍后重试"
};
await context.Response.WriteAsync(JsonHelper.Serialize(response));
} }
} }
else else
{ {
await ProcessRequest(context, requestKey, hitCount, cacheOptions); await next(context);
} }
} }
private async Task ProcessRequest(HttpContext context, string requestKey, int hitCount, MemoryCacheEntryOptions cacheOptions) private async Task ProcessRequest(HttpContext context, string requestKey, int hitCount, MemoryCacheEntryOptions cacheOptions)
{ {
hitCount++; hitCount++;
requestStore.Set(requestKey, hitCount, cacheOptions); requestStore.Set(requestKey, hitCount, cacheOptions);
//// X-RateLimit-Limit:同一个时间段所允许的请求的最大数目 // X-RateLimit-Limit:同一个时间段所允许的请求的最大数目
//context.Response.Headers["X-RateLimit-Limit"] = Limit.ToString(); context.Response.Headers["X-RateLimit-Limit"] = Limit.ToString();
//// X-RateLimit-Remaining:在当前时间段内剩余的请求的数量。 // X-RateLimit-Remaining:在当前时间段内剩余的请求的数量。
//context.Response.Headers["X-RateLimit-Remaining"] = (Limit - hitCount).ToString(); context.Response.Headers["X-RateLimit-Remaining"] = (Limit - hitCount).ToString();
await next(context); await next(context);
} }
} }
......
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
<PackageReference Include="NLog" Version="4.5.11" /> <PackageReference Include="NLog" Version="4.5.11" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.4.0" /> <PackageReference Include="NLog.Extensions.Logging" Version="1.4.0" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.8.0" /> <PackageReference Include="NLog.Web.AspNetCore" Version="4.8.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.4" />
<PackageReference Include="Microsoft.AspNet.SignalR" Version="2.4.1" /> <PackageReference Include="Microsoft.AspNet.SignalR" Version="2.4.1" />
</ItemGroup> </ItemGroup>
......
...@@ -101,15 +101,15 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) ...@@ -101,15 +101,15 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
app.UseStatusCodePagesWithReExecute("/error/{0}"); app.UseStatusCodePagesWithReExecute("/error/{0}");
} }
app.UseMiddleware<RequestRateLimitingMiddleware>();
app.UseCors("SignalrCore"); app.UseCors("SignalrCore");
app.UseSignalR(routes => routes.MapHub<AllotLogHub>("/performance/allotLogHub")); app.UseSignalR(routes => routes.MapHub<AllotLogHub>("/performance/allotLogHub"));
app.UseSwaggerSetup(Configuration);
app.UseMiddleware<RequestRateLimitingMiddleware>();
app.UseMvc(); app.UseMvc();
app.UseSwaggerSetup(Configuration);
} }
private void JsonOptions(MvcJsonOptions json) private void JsonOptions(MvcJsonOptions json)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment