/// <summary> /// Adds an Entry to the cache. /// <para/> /// This method can handle already existent items. /// </summary> /// <param name="key">The key of the response.</param> /// <param name="value">The response.</param> protected void AddStringResponseEntry(string key, ResponseCacheEntry <string> value) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (value == null) { throw new ArgumentNullException(nameof(value)); } var old = StringResponses[key]; if (old) { RemoveStringResponseEntry(key, old); } using (StringResponseLock.LockWrite()) { CurrentStringResponseCacheSize += (ulong)value.Response.Length; StringResponses[key] = value; } }
/// <summary> /// Sets a response to the cache. /// </summary> /// <param name="key">The key of the response.</param> /// <param name="response">The response.</param> /// <param name="refreshTime">The lifetime of this entry. If null this entry doesn't have to be refreshed.</param> public void SetCachedStringResponse(string key, string response, TimeSpan?refreshTime = null) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (response == null) { throw new ArgumentNullException(nameof(response)); } if (MaximumStringResponseCacheSize.HasValue && (ulong)response.Length > MaximumStringResponseCacheSize.Value) { Logger.LogDebugExcept($"The given response for '{key}' is to big to be cached."); return; } using (StringResponseLock.LockWrite()) { if (MaximumStringResponseCacheSize.HasValue && CurrentStringResponseCacheSize + (ulong)response.Length > MaximumStringResponseCacheSize.Value) { MakeRoom((ulong)response.Length); } ResponseCacheEntry <string> entry = new ResponseCacheEntry <string>() { LastUpdatedDateTime = DateTime.UtcNow, RefreshTime = refreshTime, Response = response }; AddStringResponseEntry(key, entry); } }
/// <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)); }
public Task SetAsync(string key, ResponseCacheEntry entry, TimeSpan expiration) { _cache.Set(key, entry, new MemoryCacheEntryOptions().SetAbsoluteExpiration(expiration)); _keys.TryAdd(key, key); return(Task.CompletedTask); }
public async Task ParallelGetSetResponseEntry() { //注意:redis所在系统时间与运行测试所在系统时间有误差时,会导致测试无法通过 var duration = 1; var tasks = Enumerable.Range(0, 500).Select(async index => { var key = $"ResponseCacheTestKey_{index}"; var contentType = $"application/json; charset=utf-8; idx={index}"; var body = Encoding.UTF8.GetBytes($"{SimResponseContent}_{index}"); var entry = new ResponseCacheEntry(contentType, body, duration); await ResponseCache.SetAsync(key, entry, CancellationToken.None); var cachedEntry = await ResponseCache.GetAsync(key, CancellationToken.None); TestUtil.EntryEquals(entry, cachedEntry); await Task.Delay(TimeSpan.FromSeconds(duration + 1)); cachedEntry = await ResponseCache.GetAsync(key, CancellationToken.None); Assert.IsNull(cachedEntry); }); await Task.WhenAll(tasks); }
public IResponseCacheEntry Get(string key) { try { CacheEntry cacheEntry = GetCacheEntry(key); if (cacheEntry is VaryByKeysCacheEntry) { VaryByKeysCacheEntry varyByKeysCacheEntry = (VaryByKeysCacheEntry)cacheEntry; return(new CachedVaryByRules() { Headers = new StringValues(varyByKeysCacheEntry.Headers), QueryKeys = new StringValues(varyByKeysCacheEntry.QueryKeys), VaryByKeyPrefix = varyByKeysCacheEntry.VaryByKeyPrefix }); } if (cacheEntry is ResponseCacheEntry) { ResponseCacheEntry responseCacheEntry = (ResponseCacheEntry)cacheEntry; return(new CachedResponse() { Body = new MemoryStream(responseCacheEntry.Body), Headers = new HeaderDictionary(responseCacheEntry.HeaderDictionary.ToDictionary(x => x.Key, x => new StringValues(x.Value))), StatusCode = responseCacheEntry.StatusCode, Created = responseCacheEntry.Created }); } } catch { } return(null); }
public Task <bool> OnResponseWritingAsync(ActionContext actionContext, ResponseCacheEntry entry, OnResponseWritingDelegate next) { actionContext.HttpContext.Response.Headers.Add(_headerKey, _headerValue); return(next(actionContext, entry)); }
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 cref="ResponseFromCacheEventData"/> public void ResponseFromCache(ActionContext actionContext, ResponseCacheEntry cacheEntry, ResponseCachingContext context) { if (DiagnosticSource != null && DiagnosticSource.IsEnabled(ResponseFromCacheEventData.EventName)) { DiagnosticSource.Write(ResponseFromCacheEventData.EventName, new ResponseFromCacheEventData(actionContext, cacheEntry, context)); } }
/// <summary> /// 执行请求 /// </summary> /// <returns></returns> private async Task <object> ExecRequestAsync() { using (var cancellation = this.CreateLinkedTokenSource()) { var cacheAttribute = this.ApiActionDescriptor.Cache; var cacheProvider = this.HttpApiConfig.ResponseCacheProvider; var cacheKey = default(string); var cacheResult = ResponseCacheResult.NoValue; var cacheEnable = cacheAttribute != null && cacheProvider != null; if (cacheEnable == true) { cacheKey = await cacheAttribute.GetCacheKeyAsync(this).ConfigureAwait(false); cacheResult = await cacheProvider.GetAsync(cacheKey).ConfigureAwait(false); } if (cacheResult.HasValue == true) { this.ResponseMessage = cacheResult.Value.ToResponseMessage(this.RequestMessage, cacheProvider.Name); } else { var completionOption = this.ApiActionDescriptor.Return.DataType.IsHttpResponseWrapper ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead; this.ResponseMessage = await this.HttpApiConfig.HttpClient .SendAsync(this.RequestMessage, completionOption, cancellation.Token) .ConfigureAwait(false); if (cacheEnable == true) { var cacheEntry = await ResponseCacheEntry.FromResponseMessageAsync(this.ResponseMessage).ConfigureAwait(false); await cacheProvider.SetAsync(cacheKey, cacheEntry, cacheAttribute.Expiration).ConfigureAwait(false); } } var result = await this.ApiActionDescriptor.Return.Attribute .GetTaskResult(this) .ConfigureAwait(false); ApiValidator.ValidateReturnValue(result, this.HttpApiConfig.UseReturnValuePropertyValidate); return(result); } }
/// <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); }
public async Task GetSetResponseEntry() { //注意:redis所在系统时间与运行测试所在系统时间有误差时,会导致测试无法通过 var duration = 1; var key = "ResponseCacheTestKey"; var contentType = "application/json; charset=utf-8"; var body = Encoding.UTF8.GetBytes(SimResponseContent); var entry = new ResponseCacheEntry(contentType, body, duration); await ResponseCache.SetAsync(key, entry, CancellationToken.None); var cachedEntry = await ResponseCache.GetAsync(key, CancellationToken.None); Assert.IsNotNull(cachedEntry, "获取到缓存为空,redis所在系统时间与运行测试所在系统时间有误差时,可能会导致测试无法通过"); TestUtil.EntryEquals(entry, cachedEntry); await Task.Delay(TimeSpan.FromSeconds(duration + 1)); cachedEntry = await ResponseCache.GetAsync(key, CancellationToken.None); Assert.IsNull(cachedEntry); }
/// <summary> /// Removes an entry from the cache. /// <para/> /// This method can handle non-existent items. /// </summary> /// <param name="key">The key of the entry.</param> /// <param name="value">The response if already retrieved. If null, the response will be retrieved automatically.</param> protected void RemoveStringResponseEntry(string key, ResponseCacheEntry <string> value) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (!value) { value = StringResponses[key]; } if (!value) // if it couldn't be retrieved. { return; } using (StringResponseLock.LockWrite()) { CurrentStringResponseCacheSize -= (ulong)value.Response.Length; StringResponses.Remove(key); } }
/// <summary> /// Removes a collection of entries from the cache. /// <para/> /// This method can handle non-existent items. /// </summary> /// <param name="keys">The keys of the entries.</param> protected void RemoveStringResponseEntries(ICollection <string> keys) { if (keys == null) { throw new ArgumentNullException(nameof(keys)); } using (StringResponseLock.LockWrite()) { foreach (string key in keys) { ResponseCacheEntry <string> value = StringResponses[key]; if (!value) // if the value couldn't be retrieved. { continue; } CurrentStringResponseCacheSize -= (ulong)value.Response.Length; StringResponses.Remove(key); } } }
public Task <ResponseCacheEntry> OnCacheStoringAsync(ActionContext actionContext, string key, ResponseCacheEntry entry, OnCacheStoringDelegate <ActionContext> next) { OnCacheStoringCallCount++; return(Task.FromResult <ResponseCacheEntry>(null)); }
/// <summary> /// 设置响应实体到缓存 /// </summary> /// <param name="key">键</param> /// <param name="entry">缓存实体</param> /// <param name="expiration">有效时间</param> /// <returns></returns> public async Task SetAsync(string key, ResponseCacheEntry entry, TimeSpan expiration) { var cacheProvider = _serviceProvider.GetService(typeof(IEasyCachingProvider)) as IEasyCachingProvider; await cacheProvider.SetAsync(key, entry, expiration); }
public static void EntryEquals(ResponseCacheEntry entry1, ResponseCacheEntry entry2) { Assert.AreEqual(entry1.ContentType, entry2.ContentType); Assert.AreEqual(entry1.Body.Length, entry2.Body.Length); Assert.IsTrue(Enumerable.SequenceEqual(entry1.Body.ToArray(), entry2.Body.ToArray())); }
/// <inheritdoc/> public bool CanCaching(ResourceExecutedContext context, ResponseCacheEntry cacheEntry) => context.HttpContext.Response.StatusCode == StatusCodes.Status200OK;
protected Task <bool> WriteCacheToResponseWithInterceptorAsync(ActionContext context, ResponseCacheEntry cacheEntry) { return(Context.Interceptors.OnResponseWritingAsync(context, cacheEntry, WriteCacheToResponseAsync)); }
private async Task <ResponseCacheEntry?> InternalStoreCacheAsync(ActionContext actionContext, string key, ResponseCacheEntry cacheEntry) { await ResponseCache.SetAsync(key, cacheEntry, actionContext.HttpContext.RequestAborted); return(cacheEntry); }
/// <inheritdoc cref="ResponseFromCacheEventData"/> public ResponseFromCacheEventData(ActionContext actionContext, ResponseCacheEntry cacheEntry, ResponseCachingContext context) : base(context) { ActionContext = actionContext; CacheEntry = cacheEntry; }
public Task <bool> OnResponseWritingAsync(ActionContext actionContext, ResponseCacheEntry entry, OnResponseWritingDelegate next) { OnResponseWritingCallCount++; return(next(actionContext, entry)); }
public Task <ResponseCacheEntry> OnCacheStoringAsync(ActionContext actionContext, string key, ResponseCacheEntry entry, OnCacheStoringDelegate <ActionContext> next) { OnCacheStoringCallCount++; return(next(actionContext, key, entry)); }
/// <summary> /// 设置响应实体到缓存 /// </summary> /// <param name="key">键</param> /// <param name="entry">缓存实体</param> /// <param name="expiration">有效时间</param> /// <returns></returns> public virtual Task SetAsync(string key, ResponseCacheEntry entry, TimeSpan expiration) { this.cache.Set(key, entry, DateTimeOffset.Now.Add(expiration)); return(Task.CompletedTask); }
public Task <bool> OnResponseWritingAsync(ActionContext actionContext, ResponseCacheEntry entry, OnResponseWritingDelegate next) { OnResponseWritingCallCount++; return(Task.FromResult(true)); }
protected Task <ResponseCacheEntry?> StoreCacheAsync(ActionContext actionContext, string key, ResponseCacheEntry cacheEntry) { return(Context.Interceptors.OnCacheStoringAsync(actionContext, key, cacheEntry, _onCacheStoringDelegate)); }
public Task SetAsync(string key, ResponseCacheEntry entry, CancellationToken cancellationToken) { _caches.AddOrUpdate(key, entry, (_, _) => entry); return(Task.CompletedTask); }