寫一個特性類,用來做標記
[AttributeUsage(AttributeTargets.Method)] //只對方法有效
public class ResourceFilterAttribute : Attribute
{
}
我這裡使用了MemoryCache來做快取,也可以使用字典來做,但一定要加上static,否則字典每一次請求都會new一個例項,快取的東西就丟了
private static Dictionary<string,object> caCheDic=new Dictionary<string, object>();
過濾器程式碼實現
public class ResourceFilter : IAsyncResourceFilter
{
private readonly IMemoryCache cache;
public ResourceFilter(IMemoryCache cache)
{
this.cache = cache;
}
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
//獲取當前正在處理的控制器動作方法的相關資訊,例如方法名、引數
var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
// 檢查當前請求是否為 Controller Action
if (actionDescriptor == null)
{
await next();
return;
}
// 檢查當前 Action 是否包含 ResourceFilterAttribute,如果沒有則繼續處理下一個中介軟體
if (!actionDescriptor.MethodInfo.GetCustomAttributes(typeof(ResourceFilterAttribute), true).Any())
{
await next();
return;
}
//把請求的ip和方法名當做快取的key
var cacheKey=context.HttpContext.Connection.RemoteIpAddress.ToString()+actionDescriptor.ActionName;
//去快取中找 如果有則直接返回
if (cache.TryGetValue(cacheKey, out IActionResult resultFromCache))
{
context.Result= resultFromCache;
return;
}
// 執行下一個中介軟體並獲取結果
var resultContext =await next();
// 如果結果是 IActionResult 型別,則將結果快取起來
if (resultContext.Result is IActionResult actionResult)
{
//快取時間
var cacheOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromSeconds(10));
cache.Set(cacheKey, actionResult, cacheOptions);
}
context.Result = resultContext.Result;
}
}
在program類中要注入MemoryCache
builder.Services.AddMemoryCache();
還要進行配置我們剛才寫的篩選器
builder.Services.AddControllers().AddMvcOptions(option => {
option.Filters.Add(typeof(ResourceFilter));
});
在需要進行快取的action頭上加上ResourceFilter特性,表示這個action的返回結果要進行快取
[Route("api/[controller]/[action]"), ApiController]
public class TestController : ControllerBase
{
List<SysUser> sysUsers = new List<SysUser>()
{
new SysUser("admin","123"),
new SysUser("admin2","123")
};
[HttpGet, ResourceFilter]
public List<SysUser> GetUsers() { return sysUsers; }
}
public record SysUser(string loginName,string loginPwd);
進行測試,我這裡直接就是截圖的第二次請求,可以看到,請求去快取裡面讀到了資料
還有需要注意的是,儘量不要在快取中儲存IQuerytable和IEnumtable等具有延遲執行的型別或介面的資料,因為是延遲執行,IQuerytable和IEnumtable都是生成的sql語句,所以在使用ef時,這些資料還是會去進行資料庫操作,這樣我們的快取也就沒有意義了。