/// <summary> /// 转储并缓存响应 /// </summary> /// <param name="executingContext"></param> /// <param name="next"></param> /// <param name="key"></param> /// <returns></returns> protected async Task <ResponseCacheEntry?> DumpAndCacheResponseAsync(ResourceExecutingContext executingContext, ResourceExecutionDelegate next, string key) { var response = executingContext.HttpContext.Response; var originalBody = response.Body; using var dumpStream = executingContext.HttpContext.RequestServices.GetRequiredService <IResponseDumpStreamFactory>().Create(Context.DumpStreamCapacity); ResponseCacheEntry cacheEntry = null !; ResourceExecutedContext executedContext; try { response.Body = dumpStream; CachingDiagnostics.NoCachingFounded(key, executingContext, Context); executedContext = await next(); dumpStream.Position = 0; await dumpStream.CopyToAsync(originalBody); cacheEntry = new ResponseCacheEntry(response.ContentType, dumpStream.ToArray().AsMemory(), Context.Duration); } finally { response.Body = originalBody; } return(await CheckAndStoreCacheAsync(executingContext, executedContext, key, cacheEntry)); }
protected async Task <bool> WriteCacheToResponseAsync(ActionContext context, ResponseCacheEntry cacheEntry) { CachingDiagnostics.ResponseFromCache(context, cacheEntry, Context); context.HttpContext.Response.ContentType = cacheEntry.ContentType; await context.HttpContext.Response.BodyWriter.WriteAsync(cacheEntry.Body, context.HttpContext.RequestAborted); return(true); }
/// <inheritdoc/> protected override async Task ExecutingRequestInActionFilterAsync(ActionExecutingContext context, ActionExecutionDelegate next, string key) { var @lock = _executingLockPool.GetLock(key); if (@lock is null) { CachingDiagnostics.CannotExecutionThroughLock(key, context, Context); await Context.OnCannotExecutionThroughLock(key, context, () => next()); return; } bool gotLock = false; try { var waitTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); gotLock = await @lock.WaitAsync(Context.LockMillisecondsTimeout, context.HttpContext.RequestAborted); if (!gotLock) //没有获取到锁 { _executingLockPool.Return(@lock); await Context.OnExecutionLockTimeout(key, context, () => next()); return; } if (@lock.TryGetLocalCache(key, waitTime, out var responseCacheEntry)) { _ = WriteCacheToResponseWithInterceptorAsync(context, responseCacheEntry); @lock.Release(); _executingLockPool.Return(@lock); return; } else { context.HttpContext.Items.Add(ResponseCachingConstants.ResponseCachingExecutingLockKey, @lock); } } catch { if (gotLock) { @lock.Release(); } _executingLockPool.Return(@lock); throw; } //执行请求的后续处理逻辑 await ExecutingAndReplaceResponseStreamAsync(context, next, key); }
/// <summary> /// 执行请求 /// </summary> /// <param name="context"></param> /// <param name="next"></param> /// <param name="key"></param> /// <returns></returns> protected override async Task ExecutingRequestAsync(ResourceExecutingContext context, ResourceExecutionDelegate next, string key) { var @lock = _executingLockPool.GetLock(key); if (@lock is null) { CachingDiagnostics.CannotExecutionThroughLock(key, context, Context); await Context.OnCannotExecutionThroughLock(key, context, () => next()); return; } bool gotLock = false; try { var waitTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); gotLock = await @lock.WaitAsync(Context.LockMillisecondsTimeout, context.HttpContext.RequestAborted); if (!gotLock) //没有获取到锁 { await Context.OnExecutionLockTimeout(key, context, () => next()); return; } if (@lock.TryGetLocalCache(key, waitTime, out var responseCacheEntry)) { _ = WriteCacheToResponseWithInterceptorAsync(context, responseCacheEntry); } else { responseCacheEntry = await DumpAndCacheResponseAsync(context, next, key); if (responseCacheEntry is not null) { @lock.SetLocalCache(key, responseCacheEntry, DateTimeOffset.Now.ToUnixTimeMilliseconds() + Context.DurationMilliseconds); } } } finally { if (gotLock) { @lock.Release(); } _executingLockPool.Return(@lock); } }
/// <summary> /// 检查并保存缓存 /// </summary> /// <param name="executingContext"></param> /// <param name="executedContext"></param> /// <param name="key"></param> /// <param name="cacheEntry"></param> /// <returns></returns> protected async Task <ResponseCacheEntry?> CheckAndStoreCacheAsync(ResourceExecutingContext executingContext, ResourceExecutedContext executedContext, string key, ResponseCacheEntry cacheEntry) { if (Context.CacheDeterminer.CanCaching(executedContext, cacheEntry)) { if (cacheEntry.Body.Length <= Context.MaxCacheableResponseLength) { return(await Context.Interceptors.OnCacheStoringAsync(executingContext, key, cacheEntry, StoreCacheAsync)); } else { CachingDiagnostics.CacheBodyTooLarge(key, cacheEntry.Body, Context.MaxCacheableResponseLength, executingContext, Context); } } return(null); }
private async Task InternalOnResourceExecutionAsync(ResourceExecutingContext executingContext, ResourceExecutionDelegate next, CachingDiagnostics cachingDiagnostics) { cachingDiagnostics.StartProcessingCache(executingContext, Context); try { var key = (await Context.KeyGenerator.GenerateKeyAsync(executingContext)).ToLowerInvariant(); cachingDiagnostics.CacheKeyGenerated(executingContext, key, Context.KeyGenerator, Context); if (key.Length > Context.MaxCacheKeyLength) { cachingDiagnostics.CacheKeyTooLong(key, Context.MaxCacheKeyLength, executingContext, Context); await next(); return; } if (string.IsNullOrEmpty(key)) { cachingDiagnostics.NoCachingFounded(key, executingContext, Context); await next(); return; } if (await TryServeFromCacheAsync(executingContext, key)) { return; } await ExecutingRequestAsync(executingContext, next, key); } finally { cachingDiagnostics.EndProcessingCache(executingContext, Context); } }