asp.netcore web api访问限制AspNetCoreRateLimit
网上很多介绍都是一样的,如下。可以先看这个
https://www.jianshu.com/p/7c52ab7eb73f
本人想自定义返回信息,需要重写IpRateLimitMiddleware.ReturnQuotaExceededResponse
从github上下载了源码
https://github.com/stefanprodan/AspNetCoreRateLimit
在MiddleWave中找到了IpRateLimitMiddleware的源码,里面只有一个日志方法,有需要改日志的可以重写这个日志
protected override void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule)
{
_logger.LogInformation($"Request {identity.HttpVerb}:{identity.Path} from IP {identity.ClientIp} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule {rule.Endpoint}, TraceIdentifier {httpContext.TraceIdentifier}.");
}
然后找到IpRateLimitMiddleware的父类RateLimitMiddleware,里面是各种实现,也包含ReturnQuotaExceededResponse的方法
public virtual Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitRule rule, string retryAfter)
{
var message = string.Format(_options.QuotaExceededResponse?.Content ??
_options.QuotaExceededMessage ??"API calls quota exceeded! maximum admitted {0} per {1}.", rule.Limit, rule.Period, retryAfter);
if (!_options.DisableRateLimitHeaders)
{
httpContext.Response.Headers["Retry-After"] = retryAfter;
}
httpContext.Response.StatusCode = _options.QuotaExceededResponse?.StatusCode ??_options.HttpStatusCode;
httpContext.Response.ContentType = _options.QuotaExceededResponse?.ContentType ?? "text/plain";
return httpContext.Response.WriteAsync(message);
}
我们可以直接照抄这个方法,然后根据自己需要的格式来定义我们的返回数据。其中的rule.limit表示次数,rule.period表示秒数,retryAfter表示还剩多少秒。
接下来自定义一个类,实现自定义返回数据
public override Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitRule rule, string retryAfter)
{
httpContext.Response.ContentType = "application/json";
return httpContext.Response.WriteAsync($"{{ \"Code\": 429,\"Desc\": \"操作频率过快,要求是: 每{rule.Period}秒{rule.Limit}次,请在{retryAfter}秒后再试!\" }}");
}
然后将该类添加到中间件中
startup.cs
app.UseMiddleware<CustomIpRateLimitMiddleware>().UseIpRateLimiting();
UseMiddleware<CustomIpRateLimitMiddleware>()一定要放在UseIpRateLimiting()前面,不然会失效。
然后我们看一下效果。
成功。
上面的这种可以返回更为复杂的提示信息。未去深究。
但是如果只是实现上面这种简单的返回提示,不需要这么麻烦的配置。只需要改配置文件就可以了。
在IpRateLimiting节点下添加红框里的信息就可以了。contenttype可以是text也可以是json,看自己需要返回什么
postman结果:
浏览器结果:
都是乱码。
改一下配置文件的编码格式
用notepad++打开配置文件,设置utf-8格式
添加notepad++的打开方式
转为utf-8格式
重新加载。
结果:
成功。
在开头连接的文章的最后可以动态的加载和删除ip规则。
如果只复制文章中的下面这些东西,执行post那个方法的时候回报错。
public class IpRateLimitController : Controller
{
private readonly IpRateLimitOptions _options;
private readonly IIpPolicyStore _ipPolicyStore;
public IpRateLimitController(IOptions<IpRateLimitOptions> optionsAccessor, IIpPolicyStore ipPolicyStore)
{
_options = optionsAccessor.Value;
_ipPolicyStore = ipPolicyStore;
}
[HttpGet]
public IpRateLimitPolicies Get()
{
return _ipPolicyStore.Get(_options.IpPolicyPrefix);
}
[HttpPost]
public void Post()
{
var pol = _ipPolicyStore.Get(_options.IpPolicyPrefix);
pol.IpRules.Add(new IpRateLimitPolicy
{
Ip = "8.8.4.4",
Rules = new List<RateLimitRule>(new RateLimitRule[]
{
new RateLimitRule
{
Endpoint = "*:/api/testupdate",
Limit = 100,
Period = "1d"
}
})
});
_ipPolicyStore.Set(_options.IpPolicyPrefix, pol);
}
}
解决的办法就是执行一下Seed方法。注意:例子中的Get,Set还有Seed在.net core2.2中只有异步方法。
public class IpRateLimitController:Controller
{
private readonly IpRateLimitOptions _options;
private readonly IIpPolicyStore _ipPolicyStore;
public IpRateLimitController(IOptions<IpRateLimitOptions> optionsAccessor, IIpPolicyStore ipPolicyStore)
{
_options = optionsAccessor.Value; _ipPolicyStore = ipPolicyStore;
}
[HttpGet("options")]
public List<RateLimitRule> GetOptions()
{
return _options.GeneralRules;
}
[HttpGet("policy")]
public async Task<IpRateLimitPolicies> GetPolicies()
{
return await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
}
[HttpPost]
public async Task Post(string ip)
{
var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
pol.IpRules.Add(new IpRateLimitPolicy
{
Ip = ip,
Rules = new List<RateLimitRule>(new RateLimitRule[]
{
new RateLimitRule
{
Endpoint = "*:/api/Movie/GetByName",
Limit = 10,
Period = "3d"
}
})
});
await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
}
[HttpGet("seed")]
public async Task Seed()
{
//初始化种子数据,执行一次之后再执行都没有任何效果
await _ipPolicyStore.SeedAsync();
}
}
结束。