internal async Task <bool> TryServeFromCacheAsync(OutputCacheContext cacheContext, IReadOnlyList <IOutputCachePolicy> policies) { CreateCacheKey(cacheContext); // Locking cache lookups by default // TODO: should it be part of the cache implementations or can we assume all caches would benefit from it? // It makes sense for caches that use IO (disk, network) or need to deserialize the state but could also be a global option var cacheEntry = await _outputCacheEntryDispatcher.ScheduleAsync(cacheContext.CacheKey, (Store : _store, CacheContext : cacheContext), static async (key, state) => await OutputCacheEntryFormatter.GetAsync(key, state.Store, state.CacheContext.HttpContext.RequestAborted));
private async Task InvokeAwaited(HttpContext httpContext, IReadOnlyList <IOutputCachePolicy> policies) { var context = new OutputCacheContext { HttpContext = httpContext }; // Add IOutputCacheFeature AddOutputCacheFeature(context); try { foreach (var policy in policies) { await policy.CacheRequestAsync(context, httpContext.RequestAborted); } // Should we attempt any caching logic? if (context.EnableOutputCaching) { // Can this request be served from cache? if (context.AllowCacheLookup) { if (await TryServeFromCacheAsync(context, policies)) { return; } } // Should we store the response to this request? if (context.AllowCacheStorage) { // It is also a pre-condition to reponse locking var executed = false; if (context.AllowLocking) { var cacheEntry = await _requestDispatcher.ScheduleAsync(context.CacheKey, key => ExecuteResponseAsync()); // The current request was processed, nothing more to do if (executed) { return; } // If the result was processed by another request, try to serve it from cache entry (no lookup) if (await TryServeCachedResponseAsync(context, cacheEntry, policies)) { return; } // If the cache entry couldn't be served, continue to processing the request as usual } await ExecuteResponseAsync(); async Task <OutputCacheEntry?> ExecuteResponseAsync() { // Hook up to listen to the response stream ShimResponseStream(context); try { await _next(httpContext); // The next middleware might change the policy foreach (var policy in policies) { await policy.ServeResponseAsync(context, httpContext.RequestAborted); } // If there was no response body, check the response headers now. We can cache things like redirects. StartResponse(context); // Finalize the cache entry await FinalizeCacheBodyAsync(context); executed = true; } finally { UnshimResponseStream(context); } return(context.CachedResponse); } return; } } await _next(httpContext); } finally { RemoveOutputCacheFeature(httpContext); } }