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()前面,不然会失效。

然后我们看一下效果。

asp.netcore web api访问限制AspNetCoreRateLimit

成功。


上面的这种可以返回更为复杂的提示信息。未去深究。

但是如果只是实现上面这种简单的返回提示,不需要这么麻烦的配置。只需要改配置文件就可以了。

asp.netcore web api访问限制AspNetCoreRateLimit

在IpRateLimiting节点下添加红框里的信息就可以了。contenttype可以是text也可以是json,看自己需要返回什么

postman结果:

asp.netcore web api访问限制AspNetCoreRateLimit

浏览器结果:

asp.netcore web api访问限制AspNetCoreRateLimit

都是乱码。

改一下配置文件的编码格式

用notepad++打开配置文件,设置utf-8格式

asp.netcore web api访问限制AspNetCoreRateLimit

添加notepad++的打开方式

asp.netcore web api访问限制AspNetCoreRateLimit

转为utf-8格式

asp.netcore web api访问限制AspNetCoreRateLimit

重新加载。

结果:

asp.netcore web api访问限制AspNetCoreRateLimit

成功。


在开头连接的文章的最后可以动态的加载和删除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(); 
        }
        
    }

结束。