private async Task HandleCachedAttribute( CachedAttribute attribute, MethodInfo actionMethod, ActionExecutingContext context, ActionExecutionDelegate next) { if (UserAgentSpecifiedNoCache(context.HttpContext.Request)) { await next(); return; } var scopeValue = _cacheParametersResolver.ResolveScopeValue(attribute.ScopeName, context); var outputContentType = actionMethod.GetCustomAttribute <ProducesAttribute>()?.ContentTypes.First() ?? RawActionResult.JsonContentType; var methodParameters = new CacheMethodParameters( actionMethod.GetUniqueIdentifier(), attribute.ScopeName, scopeValue, context.ActionArguments, outputContentType, attribute.ExpireAfterTimeSpan ?? _cacheParametersResolver.DefaultExpiration, attribute.SlidingExpiration); var cacheGetResult = await _cacheAdapter.GetOrUpdate(methodParameters, () => OnCacheMiss(next)); if (cacheGetResult.Hit) { context.Result = cacheGetResult.ValueFromCache; } }
private async Task CacheMethodResult( CacheMethodParameters parameters, string statusCodeFieldName, string payloadFieldName, string contentTypeFieldName, IActionResult result) { var entries = new List <HashEntry>(); switch (result) { // methods that does not return an IActionResult might not have the status code field populated: case ObjectResult objectResult: { var statusCode = objectResult.StatusCode ?? (int)HttpStatusCode.OK; var contentTypeFormatterId = parameters.OutputContentType.Split(';')[0]; if (objectResult.Value != null) { var payload = await _serializationHelper.Serialize(contentTypeFormatterId, objectResult.Value); entries.Add(new HashEntry(payloadFieldName, payload)); entries.Add(new HashEntry(contentTypeFieldName, parameters.OutputContentType)); } entries.Add(new HashEntry(statusCodeFieldName, statusCode)); break; } case StatusCodeResult statusCodeResult: { var statusCode = statusCodeResult.StatusCode; entries.Add(new HashEntry(statusCodeFieldName, statusCode)); break; } default: throw new UnsupportedContentTypeException("unsupported WebApi method result type!"); } var key = GetCacheKey(parameters.MethodName, parameters.ScopeName, parameters.ScopeValue); var database = _redisConnectionMultiplexer.GetDatabase(); await database.HashSetAsync(key, entries.ToArray()); if (parameters.ExpirationTime.HasValue) { await database.KeyExpireAsync(key, parameters.ExpirationTime.Value); } }
public async Task <CacheGetResult> GetOrUpdate( CacheMethodParameters parameters, Func <Task <IActionResult> > cacheMissResolver) { var database = _redisConnectionMultiplexer.GetDatabase(); var key = GetCacheKey(parameters.MethodName, parameters.ScopeName, parameters.ScopeValue); var(statusCodeFieldName, payloadFieldName, contentTypeFieldName) = GetCacheParametersFieldNames(parameters); var cached = await database.HashGetAsync(key, new RedisValue[] { statusCodeFieldName, payloadFieldName, contentTypeFieldName }); var statusCode = cached[0]; var payload = cached[1]; var contentType = cached[2]; if (!statusCode.HasValue) { var newValue = await cacheMissResolver(); if (newValue != null) { await CacheMethodResult(parameters, statusCodeFieldName, payloadFieldName, contentTypeFieldName, newValue); } return(new CacheGetResult(false, null)); } if (parameters.ExpirationTime.HasValue && parameters.IsSlidingExpiration) { await database.KeyExpireAsync(key, parameters.ExpirationTime.Value); } ActionResult result = new RawActionResult((int)statusCode, payload, contentType, new Dictionary <string, string> { ["x-served-from-cache"] = "true" }); return(new CacheGetResult(true, result)); }
GetCacheParametersFieldNames(CacheMethodParameters parameters) { var parametersHash = _serializationHelper.ComputeHash(parameters.Parameters); return($"{parametersHash}:statusCode", $"{parametersHash}:payload", $"{parametersHash}:contentType"); }