/// <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));
        }
Beispiel #2
0
        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);
            }
        }
Beispiel #5
0
        /// <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);
            }
        }