Server-Side Caching for APIs with an Action Filter
Instead of sprinkling cache lookups/writes across controllers, add a reusable ActionFilter that caches responses in Redis based on the request path + querystring.
publicclassDefaultCacheFilterAttribute:ActionFilterAttribute{protectedTimeSpan_expireTime;// override in subclass for custom TTLsprivatereadonlyRedisService_redisService;publicDefaultCacheFilterAttribute(RedisServiceredisService)=>_redisService=redisService;publicoverridevoidOnActionExecuting(ActionExecutingContextcontext){if(context.HttpContext.Request.Query.ContainsKey("refresh"))return;varredisKey=GetRequestRedisKey(context.HttpContext);varcached=_redisService.ReadCache<JToken>(redisKey);if(cached!=null)context.Result=newObjectResult(cached);}publicoverridevoidOnActionExecuted(ActionExecutedContextcontext){varobj=(ObjectResult)context.Result;if(obj==null)return;varkey=GetRequestRedisKey(context.HttpContext);_redisService.WriteCache(key,JToken.FromObject(obj.Value));}privateKeyConfigGetRequestRedisKey(HttpContexthttp){varpath=http.Request.Path.Value;if(!string.IsNullOrEmpty(http.Request.QueryString.Value))path+=http.Request.QueryString.Value;if(http.Request.Query.ContainsKey("refresh"))path=path.Replace("?refresh=true","").Replace("refresh=true","");varkey=ConstRedisKey.HTTPRequest.CopyOne(path);if(_expireTime!=default)key.ExpireTime=_expireTime;returnkey;}}publicstaticclassConstRedisKey{publicstaticreadonlyKeyConfigHTTPRequest=newKeyConfig{Key="lemon_req_",ExpireTime=TimeSpan.FromMinutes(30),DBName=5};}