AspNetCoreRateLimit應用於MVC專案求助

妙妙屋(zy)發表於2023-05-07

AspNetCoreRateLimit應用於MVC專案求助

前言

之前發過一篇文章:

.NET Core WebApi介面ip限流實踐 - 妙妙屋(zy) - 部落格園 (cnblogs.com)

然後應用在前後端分離專案這個元件是非常好用的。但應用於不分離的專案,比如我的個人部落格就有點麻煩。

就是我的需求是評論介面限流,然後觸發限流後要回到文章頁面告訴使用者你觸發了限流,但是,使用這個元件,他會將返回資訊以頁面的形式返回給你,我並不知道該如何去讓他回到文章頁面,也是琢磨了很久,用中介軟體去實現了這個效果,但是感覺不是很理想,如果有大佬知道更好的辦法,可以私信或評論,感激不盡。

實現的效果圖

image

評論介面

image

文章介面

image

_messages.Warning是部落格開源作者封裝的提示資訊元件,可以採用別的方式去提示,問題不大。這裡就是將從快取中的提示資訊提取出來,然後因為這裡用的是快取,用session做的唯一值處理,所以用session去取出來,如果從快取中查出來存在,則提示被限流。

程式碼實現

原理就是把元件自帶的資訊提示設定為空字串,自己在中介軟體中去使用。

這是限流規則:

image

這裡引數就不做多的解釋,可以去看之前釋出的那篇文章。只要把Content設定為空字串即可。

然後就開始去寫中介軟體去處理觸發了限流該怎麼做

需要註冊快取服務

builder.Services.AddMemoryCache();

app.Use(async (context, next) =>
{
    var cache = context.RequestServices.GetRequiredService<IMemoryCache>();
    
    // 儲存原始響應流
    var originalBody = context.Response.Body;
    
    // 建立一個新的響應流
    using var responseBody = new MemoryStream();
    context.Response.Body = responseBody;
    
    // 載入當前使用者的 Session 物件
    await context.Session.LoadAsync();
    
    await next.Invoke();
    
    if (context.Response.StatusCode == 429)
    {
        var referer = context.Request.Headers["Referer"].ToString();
        
        // 從 Session 中獲取一個字串值
        var value = context.Session.GetString("key");
        if (string.IsNullOrEmpty(value))
        {
            // 如果 Session 中沒有值,則設定一個字串值
            context.Session.SetString("key", "value");
        }
        var sessionId = context.Session.Id;
        if (!cache.TryGetValue("Errors", out Dictionary<string, string> errors))
        {
            errors = new Dictionary<string, string>();
            cache.Set("Errors", errors, TimeSpan.FromSeconds(10));
        }
        
        errors[sessionId] = "您的請求已被限流,請稍後再試。";
        
        // 重置響應流位置
        responseBody.Seek(0, SeekOrigin.Begin);
        
        // 讀取響應內容
        // var bodyText = new StreamReader(responseBody).ReadToEnd();
        
        // 設定新的響應流
        context.Response.Body = originalBody;
        
        // 設定新的響應狀態碼
        context.Response.StatusCode = 302;

        context.Response.Headers["Location"] = referer;
        
    }
    else
    {
        // 將響應流寫回到原始響應流中
        responseBody.Seek(0, SeekOrigin.Begin);
        await responseBody.CopyToAsync(originalBody);
        
    }
});

注意這個中介軟體處理要放在app.UseRateLimit();前面。

結尾

AspNetCoreRateLimit原本就講限流的ip存放在redis當中了的,但是我就是查不出來,如果能用該元件自帶的方法查詢出來,就不需要再寫一箇中介軟體,當429的時候再用快取存一次會話了。

總之暫且先用這種辦法吧,如果有更好的方法可以評論喲~

相關文章